From patchwork Mon Dec 11 23:42:57 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 121476 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp3430781qgn; Mon, 11 Dec 2017 15:45:28 -0800 (PST) X-Google-Smtp-Source: ACJfBoueeEuq2CztDR0Pcx9Etw9pLpmdD59tl4ryXjigC8MFTpP7bi2lISi/ccD3O7ANDM1ihZJ2 X-Received: by 10.99.136.198 with SMTP id l189mr226244pgd.307.1513035928598; Mon, 11 Dec 2017 15:45:28 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513035928; cv=none; d=google.com; s=arc-20160816; b=yAwn/bCrfeJjRtBwtoIK/hHT+Tor1hWahh6NuM2bamv6MJoqdrVREhZMVTEa6p94f8 H0155RI3VA1q5L6CeZ/dU8+f8BrxhqQ6GjIDvG7MFH+8RT388bAAY4hOfPG7FhzbQYzA bG7yw8gwe/VGqNHB+hPPYUKJs+OK7kh6Sh2JmxCAnpel3DE1rTQnBlWsrFWVoJsjKht9 XjyTPaCOg6JjfBUtzkrgwKsUzmIZkn9YZR7h40fVo8leYH7ABFfkGfPKYUrHnsnmHYJO pAQECqq7MSA+Zk0g3p9JJm8wDjYs0FzlnXs9WeVgs2Lp/cU5QoFs2f9eCu2Qxpvk5cev u/2g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=PG/i7YD51upogrZTbvSYd+Xyc4fPn1VkXGB4LoFCp2A=; b=vZgOtqdAXhgfHm3SerPPe0qTM2GyMF842qvZPqpLsqxduVuSItflhk0dRxcR5vOVG9 C4uzC9Gy3Um8tBxhsUh7YB4rgdQWpBp9fWeNPngYJWvYHfz03h5K+MURXWo4g0Ircwr/ HXXoY8/MXS0TtZSGkPeXw30ux/O2QQJH5DgFmXYdGs0dkDPtl1NtKESRHqFKP8guD+bB /OvrA0UXlDpMLSKdBxVcluXk7J+D3oX8vbd+UDyssdBjdhVhT8oekLjWmRWJXdq2iizg kqot21saaH5bOR7eqQfyPzw/cUbmfDLlRtplbZlRrosu1pRe2nwBheK4xzYKDCk8GwE5 eb1g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=ZpCbNuEQ; spf=pass (google.com: best guess record for domain of linux-arm-msm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-arm-msm-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id u73si11754108pfi.245.2017.12.11.15.45.28; Mon, 11 Dec 2017 15:45:28 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-arm-msm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=ZpCbNuEQ; spf=pass (google.com: best guess record for domain of linux-arm-msm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-arm-msm-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752447AbdLKXp1 (ORCPT + 10 others); Mon, 11 Dec 2017 18:45:27 -0500 Received: from mail-wm0-f68.google.com ([74.125.82.68]:33998 "EHLO mail-wm0-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752292AbdLKXpT (ORCPT ); Mon, 11 Dec 2017 18:45:19 -0500 Received: by mail-wm0-f68.google.com with SMTP id y82so15194117wmg.1 for ; Mon, 11 Dec 2017 15:45:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=K4hLTxhi2C8qQI8n9dPzRgcQq2zG7lB/Uole40iz+cQ=; b=ZpCbNuEQ/DQQ8tQLqxJ58SdsVXbErPPmdnvyq3cThqiMEULeaIBYYt7U5SVoB4ptGW erNTVIOHgDYXkX43LT4trZDI8VKwTUUQMdApM6WBjdc8+8K8flVdhPcDATVaZg9opKeG NFQBKNNR60aC4u1JGF/zNmRsRkvtAbBEHF6HA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=K4hLTxhi2C8qQI8n9dPzRgcQq2zG7lB/Uole40iz+cQ=; b=CZa6ptsQZyxNDtis3VNkkT2MVAoo3F/xSNNLEXgZXiYShip+17QGm06R0PNoOES3WV 6ywul0J8kIj/273gSbgVHtal8uSy5RPXGZRIKWpKmbsBNhXoOycPzej1KVSthYgOZeND MNh9jMvsO0fi2LeFwqg3QLJRCXNsW0yv+Sum/+w5HgtPpBrXjEWuVcFUy9bnUM3w1Txg Md+ZCRjWl6FCAfVfy5/YhAtW63lzLvGryGlO+jokdk/xdERmQrMV1dcAEP3+3jsMvGrC ZlvilqWA+u09zsWBIj22LMajDa3QEe+en93O43hAQWfpPD9/qOS1t/g3je2bXpvNKqRk xLMg== X-Gm-Message-State: AKGB3mKQj6I7Wo9HOuMnaohcMaJ2VpiPBQxedLLpHIQ0jn8KpUfH2pCa 0DoVbKjEz/B0IHfArrlbwePB8A== X-Received: by 10.28.35.4 with SMTP id j4mr19368wmj.55.1513035917876; Mon, 11 Dec 2017 15:45:17 -0800 (PST) Received: from localhost.localdomain (cpc90716-aztw32-2-0-cust92.18-1.cable.virginm.net. [86.26.100.93]) by smtp.gmail.com with ESMTPSA id k69sm10673615wmg.8.2017.12.11.15.45.16 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 11 Dec 2017 15:45:17 -0800 (PST) From: srinivas.kandagatla@linaro.org To: Mark Brown , Greg Kroah-Hartman , alsa-devel@alsa-project.org Cc: sdharia@codeaurora.org, Rob Herring , Mark Rutland , Jonathan Corbet , pombredanne@nexb.com, j.neuschaefer@gmx.net, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Srinivas Kandagatla Subject: [PATCH v10 03/13] slimbus: Add SLIMbus bus type Date: Mon, 11 Dec 2017 23:42:57 +0000 Message-Id: <20171211234307.14465-4-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171211234307.14465-1-srinivas.kandagatla@linaro.org> References: <20171211234307.14465-1-srinivas.kandagatla@linaro.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org From: Sagar Dharia SLIMbus (Serial Low Power Interchip Media Bus) is a specification developed by MIPI (Mobile Industry Processor Interface) alliance. SLIMbus is a 2-wire implementation, which is used to communicate with peripheral components like audio-codec. SLIMbus uses Time-Division-Multiplexing to accommodate multiple data channels, and control channel. Control channel has messages to do device-enumeration, messages to send/receive control-data to/from SLIMbus devices, messages for port/channel management, and messages to do bandwidth allocation. The framework supports multiple instances of the bus (1 controller per bus), and multiple slave devices per controller. This patch adds support to basic silmbus core which includes support to SLIMbus type, slimbus device registeration and some basic data structures. Signed-off-by: Sagar Dharia Signed-off-by: Srinivas Kandagatla --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/slimbus/Kconfig | 17 ++++++ drivers/slimbus/Makefile | 6 +++ drivers/slimbus/core.c | 108 +++++++++++++++++++++++++++++++++++++ include/linux/mod_devicetable.h | 13 +++++ include/linux/slimbus.h | 116 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 263 insertions(+) create mode 100644 drivers/slimbus/Kconfig create mode 100644 drivers/slimbus/Makefile create mode 100644 drivers/slimbus/core.c create mode 100644 include/linux/slimbus.h -- 2.15.0 -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/Kconfig b/drivers/Kconfig index 152744c5ef0f..c8e1d1c3f426 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -211,4 +211,6 @@ source "drivers/mux/Kconfig" source "drivers/opp/Kconfig" +source "drivers/slimbus/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 1d034b680431..d233a95b3b24 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_MTD) += mtd/ obj-$(CONFIG_SPI) += spi/ obj-$(CONFIG_SPMI) += spmi/ obj-$(CONFIG_HSI) += hsi/ +obj-$(CONFIG_SLIMBUS) += slimbus/ obj-y += net/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_FUSION) += message/ diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig new file mode 100644 index 000000000000..9b6bb84d66ed --- /dev/null +++ b/drivers/slimbus/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# SLIMbus driver configuration +# +menuconfig SLIMBUS + tristate "SLIMbus support" + help + SLIMbus is standard interface between System-on-Chip and audio codec, + and other peripheral components in typical embedded systems. + + If unsure, choose N. + +if SLIMBUS + +# SLIMbus controllers + +endif diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile new file mode 100644 index 000000000000..506ff17d6346 --- /dev/null +++ b/drivers/slimbus/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for kernel SLIMbus framework. +# +obj-$(CONFIG_SLIMBUS) += slimbus.o +slimbus-y := core.o diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c new file mode 100644 index 000000000000..02f5075a9309 --- /dev/null +++ b/drivers/slimbus/core.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2011-2017, The Linux Foundation + */ + +#include +#include +#include +#include +#include + +static const struct slim_device_id *slim_match(const struct slim_device_id *id, + const struct slim_device *sbdev) +{ + while (id->manf_id != 0 || id->prod_code != 0) { + if (id->manf_id == sbdev->e_addr.manf_id && + id->prod_code == sbdev->e_addr.prod_code) + return id; + id++; + } + return NULL; +} + +static int slim_device_match(struct device *dev, struct device_driver *drv) +{ + struct slim_device *sbdev = to_slim_device(dev); + struct slim_driver *sbdrv = to_slim_driver(drv); + + return !!slim_match(sbdrv->id_table, sbdev); +} + +static int slim_device_probe(struct device *dev) +{ + struct slim_device *sbdev = to_slim_device(dev); + struct slim_driver *sbdrv = to_slim_driver(dev->driver); + + return sbdrv->probe(sbdev); +} + +static int slim_device_remove(struct device *dev) +{ + struct slim_device *sbdev = to_slim_device(dev); + struct slim_driver *sbdrv; + + if (dev->driver) { + sbdrv = to_slim_driver(dev->driver); + if (sbdrv->remove) + sbdrv->remove(sbdev); + } + + return 0; +} + +struct bus_type slimbus_bus = { + .name = "slimbus", + .match = slim_device_match, + .probe = slim_device_probe, + .remove = slim_device_remove, +}; +EXPORT_SYMBOL_GPL(slimbus_bus); + +/* + * __slim_driver_register() - Client driver registration with SLIMbus + * + * @drv:Client driver to be associated with client-device. + * @owner: owning module/driver + * + * This API will register the client driver with the SLIMbus + * It is called from the driver's module-init function. + */ +int __slim_driver_register(struct slim_driver *drv, struct module *owner) +{ + /* ID table and probe are mandatory */ + if (!drv->id_table || !drv->probe) + return -EINVAL; + + drv->driver.bus = &slimbus_bus; + drv->driver.owner = owner; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(__slim_driver_register); + +/* + * slim_driver_unregister() - Undo effect of slim_driver_register + * + * @drv: Client driver to be unregistered + */ +void slim_driver_unregister(struct slim_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(slim_driver_unregister); + +static void __exit slimbus_exit(void) +{ + bus_unregister(&slimbus_bus); +} +module_exit(slimbus_exit); + +static int __init slimbus_init(void) +{ + return bus_register(&slimbus_bus); +} +postcore_initcall(slimbus_init); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SLIMbus core"); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index abb6dc2ebbf8..48e188327c02 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -452,6 +452,19 @@ struct spi_device_id { kernel_ulong_t driver_data; /* Data private to the driver */ }; +/* SLIMbus */ + +#define SLIMBUS_NAME_SIZE 32 +#define SLIMBUS_MODULE_PREFIX "slim:" + +struct slim_device_id { + __u16 manf_id, prod_code; + __u16 dev_index, instance; + + /* Data private to the driver */ + kernel_ulong_t driver_data; +}; + #define SPMI_NAME_SIZE 32 #define SPMI_MODULE_PREFIX "spmi:" diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h new file mode 100644 index 000000000000..6b4ed290fbb0 --- /dev/null +++ b/include/linux/slimbus.h @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2011-2017, The Linux Foundation + */ + +#ifndef _LINUX_SLIMBUS_H +#define _LINUX_SLIMBUS_H +#include +#include +#include + +extern struct bus_type slimbus_bus; + +/** + * struct slim_eaddr - Enumeration address for a SLIMbus device + * @manf_id: Manufacturer Id for the device + * @prod_code: Product code + * @dev_index: Device index + * @instance: Instance value + */ +struct slim_eaddr { + u16 manf_id; + u16 prod_code; + u8 dev_index; + u8 instance; +} __packed; + +/** + * enum slim_device_status - slim device status + * @SLIM_DEVICE_STATUS_DOWN: Slim device is absent or not reported yet. + * @SLIM_DEVICE_STATUS_UP: Slim device is announced on the bus. + * @SLIM_DEVICE_STATUS_RESERVED: Reserved for future use. + */ +enum slim_device_status { + SLIM_DEVICE_STATUS_DOWN = 0, + SLIM_DEVICE_STATUS_UP, + SLIM_DEVICE_STATUS_RESERVED, +}; + +/** + * struct slim_device - Slim device handle. + * @dev: Driver model representation of the device. + * @e_addr: Enumeration address of this device. + * @status: slim device status + * @laddr: 1-byte Logical address of this device. + * @is_laddr_valid: indicates if the laddr is valid or not + * + * This is the client/device handle returned when a SLIMbus + * device is registered with a controller. + * Pointer to this structure is used by client-driver as a handle. + */ +struct slim_device { + struct device dev; + struct slim_eaddr e_addr; + enum slim_device_status status; + u8 laddr; + bool is_laddr_valid; +}; + +#define to_slim_device(d) container_of(d, struct slim_device, dev) + +/** + * struct slim_driver - SLIMbus 'generic device' (slave) device driver + * (similar to 'spi_device' on SPI) + * @probe: Binds this driver to a SLIMbus device. + * @remove: Unbinds this driver from the SLIMbus device. + * @shutdown: Standard shutdown callback used during powerdown/halt. + * @device_status: This callback is called when + * - The device reports present and gets a laddr assigned + * - The device reports absent, or the bus goes down. + * @driver: SLIMbus device drivers should initialize name and owner field of + * this structure + * @id_table: List of SLIMbus devices supported by this driver + */ + +struct slim_driver { + int (*probe)(struct slim_device *sl); + void (*remove)(struct slim_device *sl); + void (*shutdown)(struct slim_device *sl); + int (*device_status)(struct slim_device *sl, + enum slim_device_status s); + struct device_driver driver; + const struct slim_device_id *id_table; +}; +#define to_slim_driver(d) container_of(d, struct slim_driver, driver) + +/* + * use a macro to avoid include chaining to get THIS_MODULE + */ +#define slim_driver_register(drv) \ + __slim_driver_register(drv, THIS_MODULE) +int __slim_driver_register(struct slim_driver *drv, struct module *owner); +void slim_driver_unregister(struct slim_driver *drv); + +/** + * module_slim_driver() - Helper macro for registering a SLIMbus driver + * @__slim_driver: slimbus_driver struct + * + * Helper macro for SLIMbus drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_slim_driver(__slim_driver) \ + module_driver(__slim_driver, slim_driver_register, \ + slim_driver_unregister) + +static inline void *slim_get_devicedata(const struct slim_device *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +static inline void slim_set_devicedata(struct slim_device *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} +#endif /* _LINUX_SLIMBUS_H */ From patchwork Mon Dec 11 23:43:05 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 121483 Delivered-To: patch@linaro.org Received: by 10.140.22.227 with SMTP id 90csp3431982qgn; Mon, 11 Dec 2017 15:47:06 -0800 (PST) X-Google-Smtp-Source: ACJfBotKLPxPddb8uiu1UA7Obr2/i0bkTs9NMRMFnxuS6UHPt6kuh5tPJfGsMgO5CXy3HTvW1ZQV X-Received: by 10.101.83.13 with SMTP id m13mr229147pgq.151.1513036026021; Mon, 11 Dec 2017 15:47:06 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1513036026; cv=none; d=google.com; s=arc-20160816; b=zdta4KrQTwdbISitgumB7tjMAKZ9WVZ7jPV5Z8qR4G9oaj+jQBm714Gwi/3YAXJMQh eMqhnHYjIZCax1Ljwh08I1SWhW3NcLvh9akzFewKdxL7id0a5G9QF0zv84eCPbMsxgW6 9oMl9tI+lIbSyUAiAzsGjdYQbiNM6miJVDuAC0Jnhx3Mm+JVrDao4Nf3NQGT+rT3nQzB JDOijFEBpapdAaIvW3ufsPRnuHDtEouYQmI9RWHNMG68SlUSs2Jdc8dERC4H3PXOlXQp 2ogWcehnyYR4mdhKsTmewlJLAm39koxZFN5PHl8HI6xx/Yo2ut0Caqr14PxFups2OyvW L7pw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=xIva0XJbgkn07BsqAik7Zj/ZuHCZSDUdFkJeQ81Kjno=; b=IGqx48zoBHeYtCPjbe4RT56S+6+rN2Bjia29EyfXYWEh3RLoBz67sUuOtfnxIj/+oH XlVKcSxXWuuqzYHQViEjnZ8dl5uMUs7Y4ZQ0WV1jj2FU+6gmCiek0m1mRCeaPBd+aqMV 7j4i0C4ln/gdCAPbPEpNZQ6AE/qQIZCxPCQOc1QDVljzidqIVjFHqhvKYJyNvnQTGwv/ WBPw5qIiTvrNG4MYhMMsQTUPEOHPXqiHHvz3uqY1uu5RUWOq4RNT2bcSFjhQLVI7Mxuw S9nND27hwGzcIrppLAQrKZ5ERLNimnxfqBm9y+44XgvkxY7YrruTZbEi3iKLSyTLzOZ3 2qtA== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=i5alaP7J; spf=pass (google.com: best guess record for domain of linux-arm-msm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-arm-msm-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id z6si11724244pfi.345.2017.12.11.15.47.05; Mon, 11 Dec 2017 15:47:06 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-arm-msm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org header.s=google header.b=i5alaP7J; spf=pass (google.com: best guess record for domain of linux-arm-msm-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-arm-msm-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752673AbdLKXrC (ORCPT + 10 others); Mon, 11 Dec 2017 18:47:02 -0500 Received: from mail-wm0-f65.google.com ([74.125.82.65]:38370 "EHLO mail-wm0-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752497AbdLKXp3 (ORCPT ); Mon, 11 Dec 2017 18:45:29 -0500 Received: by mail-wm0-f65.google.com with SMTP id 64so17187330wme.3 for ; Mon, 11 Dec 2017 15:45:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=m8ps2bNz/n2W2yuvpP1hjZOYOBF7nej8m3l0vPIi2LY=; b=i5alaP7J0B1DrlqJwuEAgTdU4p4iugN2Yzl7g0t5O/l7K/fKFZLLERxgyp/ETbwgGY 6hsvL125l+X85hvrT7cFHek4sqhqa8U4HiH+Ci2TxE1GUekcWs396hkB8hDHLBk1m1+/ hVKdtAIKLrKXmmjmH+HJc6W1iRunPTxoWTITo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=m8ps2bNz/n2W2yuvpP1hjZOYOBF7nej8m3l0vPIi2LY=; b=sFbL/rk7ElrjXPs/UoVo8KS2VvQ1ZmwNpKrUUQAbiwfpWVQtgbz1vN+F2cNEhdqkap ll9RTJCCiEKaFLOyWXAOKTx+A5ut/Tibmqc2PwLRp78ayfLwaHL7/GLWT7j71WWwVpoX tzYOh9MaL4Ow4gzY5d+f0Fy23+J3AU6sNR+wa1arMznnkj8F7jwtit5sNv9KBQ9kCdtY 5XcCw4s20LFGl+udSHj69CIps4+wv5aGHpqvThYkH5flWNylrexHNyaG2G7eJ3RJ1D3P UJbKpsMSR/r6RTcEXK0rJSYYI7So3Blnnbm3WnLsAeF/YjXB5oo/v18rfSn78TV1Nc6I 9eRg== X-Gm-Message-State: AKGB3mIG7dOUjqmtlh+wFfWmXi/H8P7xYSmlucqiEnRT5AdTC5vX6GWi E8RKmjH62yalMgkcsOc5AX7NmA== X-Received: by 10.28.135.135 with SMTP id j129mr25692wmd.144.1513035927881; Mon, 11 Dec 2017 15:45:27 -0800 (PST) Received: from localhost.localdomain (cpc90716-aztw32-2-0-cust92.18-1.cable.virginm.net. [86.26.100.93]) by smtp.gmail.com with ESMTPSA id k69sm10673615wmg.8.2017.12.11.15.45.26 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 11 Dec 2017 15:45:27 -0800 (PST) From: srinivas.kandagatla@linaro.org To: Mark Brown , Greg Kroah-Hartman , alsa-devel@alsa-project.org Cc: sdharia@codeaurora.org, Rob Herring , Mark Rutland , Jonathan Corbet , pombredanne@nexb.com, j.neuschaefer@gmx.net, linux-arm-msm@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Srinivas Kandagatla Subject: [PATCH v10 11/13] slimbus: qcom: Add Qualcomm Slimbus controller driver Date: Mon, 11 Dec 2017 23:43:05 +0000 Message-Id: <20171211234307.14465-12-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.15.0 In-Reply-To: <20171211234307.14465-1-srinivas.kandagatla@linaro.org> References: <20171211234307.14465-1-srinivas.kandagatla@linaro.org> Sender: linux-arm-msm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-arm-msm@vger.kernel.org From: Sagar Dharia This controller driver programs manager, interface, and framer devices for Qualcomm's slimbus HW block. Manager component currently implements logical address setting, and messaging interface. Interface device reports bus synchronization information, and framer device clocks the bus from the time it's woken up, until clock-pause is executed by the manager device. Signed-off-by: Sagar Dharia Signed-off-by: Srinivas Kandagatla --- drivers/slimbus/Kconfig | 6 + drivers/slimbus/Makefile | 4 + drivers/slimbus/qcom-ctrl.c | 655 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 665 insertions(+) create mode 100644 drivers/slimbus/qcom-ctrl.c -- 2.15.0 -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig index 9b6bb84d66ed..78bdd4808282 100644 --- a/drivers/slimbus/Kconfig +++ b/drivers/slimbus/Kconfig @@ -13,5 +13,11 @@ menuconfig SLIMBUS if SLIMBUS # SLIMbus controllers +config SLIM_QCOM_CTRL + tristate "Qualcomm SLIMbus Manager Component" + depends on SLIMBUS + help + Select driver if Qualcomm's SLIMbus Manager Component is + programmed using Linux kernel. endif diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile index cd6833ed521a..a35a3da4eb78 100644 --- a/drivers/slimbus/Makefile +++ b/drivers/slimbus/Makefile @@ -4,3 +4,7 @@ # obj-$(CONFIG_SLIMBUS) += slimbus.o slimbus-y := core.o messaging.o sched.o + +#Controllers +obj-$(CONFIG_SLIM_QCOM_CTRL) += slim-qcom-ctrl.o +slim-qcom-ctrl-y := qcom-ctrl.o diff --git a/drivers/slimbus/qcom-ctrl.c b/drivers/slimbus/qcom-ctrl.c new file mode 100644 index 000000000000..ace85ce3de90 --- /dev/null +++ b/drivers/slimbus/qcom-ctrl.c @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2011-2017, The Linux Foundation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "slimbus.h" + +/* Manager registers */ +#define MGR_CFG 0x200 +#define MGR_STATUS 0x204 +#define MGR_INT_EN 0x210 +#define MGR_INT_STAT 0x214 +#define MGR_INT_CLR 0x218 +#define MGR_TX_MSG 0x230 +#define MGR_RX_MSG 0x270 +#define MGR_IE_STAT 0x2F0 +#define MGR_VE_STAT 0x300 +#define MGR_CFG_ENABLE 1 + +/* Framer registers */ +#define FRM_CFG 0x400 +#define FRM_STAT 0x404 +#define FRM_INT_EN 0x410 +#define FRM_INT_STAT 0x414 +#define FRM_INT_CLR 0x418 +#define FRM_WAKEUP 0x41C +#define FRM_CLKCTL_DONE 0x420 +#define FRM_IE_STAT 0x430 +#define FRM_VE_STAT 0x440 + +/* Interface registers */ +#define INTF_CFG 0x600 +#define INTF_STAT 0x604 +#define INTF_INT_EN 0x610 +#define INTF_INT_STAT 0x614 +#define INTF_INT_CLR 0x618 +#define INTF_IE_STAT 0x630 +#define INTF_VE_STAT 0x640 + +/* Interrupt status bits */ +#define MGR_INT_TX_NACKED_2 BIT(25) +#define MGR_INT_MSG_BUF_CONTE BIT(26) +#define MGR_INT_RX_MSG_RCVD BIT(30) +#define MGR_INT_TX_MSG_SENT BIT(31) + +/* Framer config register settings */ +#define FRM_ACTIVE 1 +#define CLK_GEAR 7 +#define ROOT_FREQ 11 +#define REF_CLK_GEAR 15 +#define INTR_WAKE 19 + +#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \ + ((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16)) + +#define SLIM_ROOT_FREQ 24576000 + +/* MAX message size over control channel */ +#define SLIM_MSGQ_BUF_LEN 40 +#define QCOM_TX_MSGS 2 +#define QCOM_RX_MSGS 8 +#define QCOM_BUF_ALLOC_RETRIES 10 + +#define CFG_PORT(r, v) ((v) ? CFG_PORT_V2(r) : CFG_PORT_V1(r)) + +/* V2 Component registers */ +#define CFG_PORT_V2(r) ((r ## _V2)) +#define COMP_CFG_V2 4 +#define COMP_TRUST_CFG_V2 0x3000 + +/* V1 Component registers */ +#define CFG_PORT_V1(r) ((r ## _V1)) +#define COMP_CFG_V1 0 +#define COMP_TRUST_CFG_V1 0x14 + +/* Resource group info for manager, and non-ported generic device-components */ +#define EE_MGR_RSC_GRP (1 << 10) +#define EE_NGD_2 (2 << 6) +#define EE_NGD_1 0 + +struct slim_ctrl_buf { + void *base; + phys_addr_t phy; + spinlock_t lock; + int head; + int tail; + int sl_sz; + int n; +}; + +struct qcom_slim_ctrl { + struct slim_controller ctrl; + struct slim_framer framer; + struct device *dev; + void __iomem *base; + void __iomem *slew_reg; + + struct slim_ctrl_buf rx; + struct slim_ctrl_buf tx; + + struct completion **wr_comp; + int irq; + struct workqueue_struct *rxwq; + struct work_struct wd; + struct clk *rclk; + struct clk *hclk; +}; + +static void qcom_slim_queue_tx(struct qcom_slim_ctrl *ctrl, void *buf, + u8 len, u32 tx_reg) +{ + int count = (len + 3) >> 2; + + __iowrite32_copy(ctrl->base + tx_reg, buf, count); + + /* Ensure Oder of subsequent writes */ + mb(); +} + +static void *slim_alloc_rxbuf(struct qcom_slim_ctrl *ctrl) +{ + unsigned long flags; + int idx; + + spin_lock_irqsave(&ctrl->rx.lock, flags); + if ((ctrl->rx.tail + 1) % ctrl->rx.n == ctrl->rx.head) { + spin_unlock_irqrestore(&ctrl->rx.lock, flags); + dev_err(ctrl->dev, "RX QUEUE full!"); + return NULL; + } + idx = ctrl->rx.tail; + ctrl->rx.tail = (ctrl->rx.tail + 1) % ctrl->rx.n; + spin_unlock_irqrestore(&ctrl->rx.lock, flags); + + return ctrl->rx.base + (idx * ctrl->rx.sl_sz); +} + +void slim_ack_txn(struct qcom_slim_ctrl *ctrl, int err) +{ + struct completion *comp; + unsigned long flags; + int idx; + + spin_lock_irqsave(&ctrl->tx.lock, flags); + idx = ctrl->tx.head; + ctrl->tx.head = (ctrl->tx.head + 1) % ctrl->tx.n; + spin_unlock_irqrestore(&ctrl->tx.lock, flags); + + comp = ctrl->wr_comp[idx]; + ctrl->wr_comp[idx] = NULL; + + complete(comp); +} + +static irqreturn_t qcom_slim_handle_tx_irq(struct qcom_slim_ctrl *ctrl, + u32 stat) +{ + int err = 0; + + if (stat & MGR_INT_TX_MSG_SENT) + writel_relaxed(MGR_INT_TX_MSG_SENT, + ctrl->base + MGR_INT_CLR); + + if (stat & MGR_INT_TX_NACKED_2) { + u32 mgr_stat = readl_relaxed(ctrl->base + MGR_STATUS); + u32 mgr_ie_stat = readl_relaxed(ctrl->base + MGR_IE_STAT); + u32 frm_stat = readl_relaxed(ctrl->base + FRM_STAT); + u32 frm_cfg = readl_relaxed(ctrl->base + FRM_CFG); + u32 frm_intr_stat = readl_relaxed(ctrl->base + FRM_INT_STAT); + u32 frm_ie_stat = readl_relaxed(ctrl->base + FRM_IE_STAT); + u32 intf_stat = readl_relaxed(ctrl->base + INTF_STAT); + u32 intf_intr_stat = readl_relaxed(ctrl->base + INTF_INT_STAT); + u32 intf_ie_stat = readl_relaxed(ctrl->base + INTF_IE_STAT); + + writel_relaxed(MGR_INT_TX_NACKED_2, ctrl->base + MGR_INT_CLR); + + dev_err(ctrl->dev, "TX Nack MGR:int:0x%x, stat:0x%x\n", + stat, mgr_stat); + dev_err(ctrl->dev, "TX Nack MGR:ie:0x%x\n", mgr_ie_stat); + dev_err(ctrl->dev, "TX Nack FRM:int:0x%x, stat:0x%x\n", + frm_intr_stat, frm_stat); + dev_err(ctrl->dev, "TX Nack FRM:cfg:0x%x, ie:0x%x\n", + frm_cfg, frm_ie_stat); + dev_err(ctrl->dev, "TX Nack INTF:intr:0x%x, stat:0x%x\n", + intf_intr_stat, intf_stat); + dev_err(ctrl->dev, "TX Nack INTF:ie:0x%x\n", + intf_ie_stat); + err = -ENOTCONN; + } + + slim_ack_txn(ctrl, err); + + return IRQ_HANDLED; +} + +static irqreturn_t qcom_slim_handle_rx_irq(struct qcom_slim_ctrl *ctrl, + u32 stat) +{ + u32 *rx_buf, pkt[10]; + bool q_rx = false; + u8 mc, mt, len; + + pkt[0] = readl_relaxed(ctrl->base + MGR_RX_MSG); + mt = SLIM_HEADER_GET_MT(pkt[0]); + len = SLIM_HEADER_GET_RL(pkt[0]); + mc = SLIM_HEADER_GET_MC(pkt[0]>>8); + + /* + * this message cannot be handled by ISR, so + * let work-queue handle it + */ + if (mt == SLIM_MSG_MT_CORE && mc == SLIM_MSG_MC_REPORT_PRESENT) { + rx_buf = (u32 *)slim_alloc_rxbuf(ctrl); + if (!rx_buf) { + dev_err(ctrl->dev, "dropping RX:0x%x due to RX full\n", + pkt[0]); + goto rx_ret_irq; + } + rx_buf[0] = pkt[0]; + + } else { + rx_buf = pkt; + } + + __ioread32_copy(rx_buf + 1, ctrl->base + MGR_RX_MSG + 4, + DIV_ROUND_UP(len, 4)); + + switch (mc) { + + case SLIM_MSG_MC_REPORT_PRESENT: + q_rx = true; + break; + case SLIM_MSG_MC_REPLY_INFORMATION: + case SLIM_MSG_MC_REPLY_VALUE: + slim_msg_response(&ctrl->ctrl, (u8 *)(rx_buf + 1), + (u8)(*rx_buf >> 24), (len - 4)); + break; + default: + dev_err(ctrl->dev, "unsupported MC,%x MT:%x\n", + mc, mt); + break; + } +rx_ret_irq: + writel(MGR_INT_RX_MSG_RCVD, ctrl->base + + MGR_INT_CLR); + if (q_rx) + queue_work(ctrl->rxwq, &ctrl->wd); + + return IRQ_HANDLED; +} + +static irqreturn_t qcom_slim_interrupt(int irq, void *d) +{ + struct qcom_slim_ctrl *ctrl = d; + u32 stat = readl_relaxed(ctrl->base + MGR_INT_STAT); + int ret = IRQ_NONE; + + if (stat & MGR_INT_TX_MSG_SENT || stat & MGR_INT_TX_NACKED_2) + ret = qcom_slim_handle_tx_irq(ctrl, stat); + + if (stat & MGR_INT_RX_MSG_RCVD) + ret = qcom_slim_handle_rx_irq(ctrl, stat); + + return ret; +} + +void *slim_alloc_txbuf(struct qcom_slim_ctrl *ctrl, struct slim_msg_txn *txn, + struct completion *done) +{ + unsigned long flags; + int idx; + + spin_lock_irqsave(&ctrl->tx.lock, flags); + if (((ctrl->tx.head + 1) % ctrl->tx.n) == ctrl->tx.tail) { + spin_unlock_irqrestore(&ctrl->tx.lock, flags); + dev_err(ctrl->dev, "controller TX buf unavailable"); + return NULL; + } + idx = ctrl->tx.tail; + ctrl->wr_comp[idx] = done; + ctrl->tx.tail = (ctrl->tx.tail + 1) % ctrl->tx.n; + + spin_unlock_irqrestore(&ctrl->tx.lock, flags); + + return ctrl->tx.base + (idx * ctrl->tx.sl_sz); +} + + +static int qcom_xfer_msg(struct slim_controller *sctrl, + struct slim_msg_txn *txn) +{ + struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); + DECLARE_COMPLETION_ONSTACK(done); + void *pbuf = slim_alloc_txbuf(ctrl, txn, &done); + unsigned long ms = txn->rl + HZ; + u8 *puc; + int ret = 0, timeout, retries = QCOM_BUF_ALLOC_RETRIES; + u8 la = txn->la; + u32 *head; + /* HW expects length field to be excluded */ + txn->rl--; + + /* spin till buffer is made available */ + if (!pbuf) { + while (retries--) { + usleep_range(10000, 15000); + pbuf = slim_alloc_txbuf(ctrl, txn, &done); + if (pbuf) + break; + } + } + + if (!retries && !pbuf) + return -ENOMEM; + + puc = (u8 *)pbuf; + head = (u32 *)pbuf; + + if (txn->dt == SLIM_MSG_DEST_LOGICALADDR) { + *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, + txn->mc, 0, la); + puc += 3; + } else { + *head = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, + txn->mc, 1, la); + puc += 2; + } + + if (slim_tid_txn(txn->mt, txn->mc)) + *(puc++) = txn->tid; + + if (slim_ec_txn(txn->mt, txn->mc)) { + *(puc++) = (txn->ec & 0xFF); + *(puc++) = (txn->ec >> 8) & 0xFF; + } + + if (txn->msg && txn->msg->wbuf) + memcpy(puc, txn->msg->wbuf, txn->msg->num_bytes); + + qcom_slim_queue_tx(ctrl, head, txn->rl, MGR_TX_MSG); + timeout = wait_for_completion_timeout(&done, msecs_to_jiffies(ms)); + + if (!timeout) { + dev_err(ctrl->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc, + txn->mt); + ret = -ETIMEDOUT; + } + + return ret; + +} + +static int qcom_set_laddr(struct slim_controller *sctrl, + struct slim_eaddr *ead, u8 laddr) +{ + struct qcom_slim_ctrl *ctrl = dev_get_drvdata(sctrl->dev); + struct { + __be16 manf_id; + __be16 prod_code; + u8 dev_index; + u8 instance; + u8 laddr; + } __packed p; + struct slim_val_inf msg = {0}; + DEFINE_SLIM_EDEST_TXN(txn, SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS, + 10, laddr, &msg); + int ret; + + p.manf_id = cpu_to_be16(ead->manf_id); + p.prod_code = cpu_to_be16(ead->prod_code); + p.dev_index = ead->dev_index; + p.instance = ead->instance; + p.laddr = laddr; + + msg.wbuf = (void *)&p; + msg.num_bytes = 7; + ret = slim_do_transfer(&ctrl->ctrl, &txn); + + if (ret) + dev_err(ctrl->dev, "set LA:0x%x failed:ret:%d\n", + laddr, ret); + return ret; +} + +static int slim_get_current_rxbuf(struct qcom_slim_ctrl *ctrl, void *buf) +{ + unsigned long flags; + + spin_lock_irqsave(&ctrl->rx.lock, flags); + if (ctrl->rx.tail == ctrl->rx.head) { + spin_unlock_irqrestore(&ctrl->rx.lock, flags); + return -ENODATA; + } + memcpy(buf, ctrl->rx.base + (ctrl->rx.head * ctrl->rx.sl_sz), + ctrl->rx.sl_sz); + + ctrl->rx.head = (ctrl->rx.head + 1) % ctrl->rx.n; + spin_unlock_irqrestore(&ctrl->rx.lock, flags); + + return 0; +} + +static void qcom_slim_rxwq(struct work_struct *work) +{ + u8 buf[SLIM_MSGQ_BUF_LEN]; + u8 mc, mt, len; + int ret; + struct qcom_slim_ctrl *ctrl = container_of(work, struct qcom_slim_ctrl, + wd); + + while ((slim_get_current_rxbuf(ctrl, buf)) != -ENODATA) { + len = SLIM_HEADER_GET_RL(buf[0]); + mt = SLIM_HEADER_GET_MT(buf[0]); + mc = SLIM_HEADER_GET_MC(buf[1]); + if (mt == SLIM_MSG_MT_CORE && + mc == SLIM_MSG_MC_REPORT_PRESENT) { + struct slim_eaddr ea; + u8 laddr; + + ea.manf_id = be16_to_cpup((__be16 *)&buf[2]); + ea.prod_code = be16_to_cpup((__be16 *)&buf[4]); + ea.dev_index = buf[6]; + ea.instance = buf[7]; + + ret = slim_device_report_present(&ctrl->ctrl, &ea, + &laddr); + if (ret < 0) + dev_err(ctrl->dev, "assign laddr failed:%d\n", + ret); + } else { + dev_err(ctrl->dev, "unexpected message:mc:%x, mt:%x\n", + mc, mt); + } + } +} + +static void qcom_slim_prg_slew(struct platform_device *pdev, + struct qcom_slim_ctrl *ctrl) +{ + struct resource *slew_mem; + + if (!ctrl->slew_reg) { + /* SLEW RATE register for this SLIMbus */ + slew_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "slew"); + ctrl->slew_reg = devm_ioremap(&pdev->dev, slew_mem->start, + resource_size(slew_mem)); + if (!ctrl->slew_reg) + return; + } + + writel_relaxed(1, ctrl->slew_reg); + /* Make sure SLIMbus-slew rate enabling goes through */ + wmb(); +} + +static int qcom_slim_probe(struct platform_device *pdev) +{ + struct qcom_slim_ctrl *ctrl; + struct slim_controller *sctrl; + struct resource *slim_mem; + int ret, ver; + + ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->hclk = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(ctrl->hclk)) + return PTR_ERR(ctrl->hclk); + + ctrl->rclk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(ctrl->rclk)) + return PTR_ERR(ctrl->rclk); + + ret = clk_set_rate(ctrl->rclk, SLIM_ROOT_FREQ); + if (ret) { + dev_err(&pdev->dev, "ref-clock set-rate failed:%d\n", ret); + return ret; + } + + ctrl->irq = platform_get_irq(pdev, 0); + if (!ctrl->irq) { + dev_err(&pdev->dev, "no slimbus IRQ\n"); + return -ENODEV; + } + + sctrl = &ctrl->ctrl; + sctrl->dev = &pdev->dev; + ctrl->dev = &pdev->dev; + platform_set_drvdata(pdev, ctrl); + dev_set_drvdata(ctrl->dev, ctrl); + + slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); + ctrl->base = devm_ioremap_resource(ctrl->dev, slim_mem); + if (!ctrl->base) { + dev_err(&pdev->dev, "IOremap failed\n"); + return -ENOMEM; + } + + sctrl->set_laddr = qcom_set_laddr; + sctrl->xfer_msg = qcom_xfer_msg; + ctrl->tx.n = QCOM_TX_MSGS; + ctrl->tx.sl_sz = SLIM_MSGQ_BUF_LEN; + ctrl->rx.n = QCOM_RX_MSGS; + ctrl->rx.sl_sz = SLIM_MSGQ_BUF_LEN; + ctrl->wr_comp = kzalloc(sizeof(struct completion *) * QCOM_TX_MSGS, + GFP_KERNEL); + if (!ctrl->wr_comp) + return -ENOMEM; + + spin_lock_init(&ctrl->rx.lock); + spin_lock_init(&ctrl->tx.lock); + INIT_WORK(&ctrl->wd, qcom_slim_rxwq); + ctrl->rxwq = create_singlethread_workqueue("qcom_slim_rx"); + if (!ctrl->rxwq) { + dev_err(ctrl->dev, "Failed to start Rx WQ\n"); + return -ENOMEM; + } + + ctrl->framer.rootfreq = SLIM_ROOT_FREQ / 8; + ctrl->framer.superfreq = + ctrl->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8; + sctrl->a_framer = &ctrl->framer; + sctrl->clkgear = SLIM_MAX_CLK_GEAR; + + qcom_slim_prg_slew(pdev, ctrl); + + ret = devm_request_irq(&pdev->dev, ctrl->irq, qcom_slim_interrupt, + IRQF_TRIGGER_HIGH, "qcom_slim_irq", ctrl); + if (ret) { + dev_err(&pdev->dev, "request IRQ failed\n"); + goto err_request_irq_failed; + } + + ret = clk_prepare_enable(ctrl->hclk); + if (ret) + goto err_hclk_enable_failed; + + ret = clk_prepare_enable(ctrl->rclk); + if (ret) + goto err_rclk_enable_failed; + + ctrl->tx.base = dmam_alloc_coherent(&pdev->dev, + (ctrl->tx.sl_sz * ctrl->tx.n), + &ctrl->tx.phy, GFP_KERNEL); + if (!ctrl->tx.base) { + ret = -ENOMEM; + goto err; + } + + ctrl->rx.base = dmam_alloc_coherent(&pdev->dev, + (ctrl->rx.sl_sz * ctrl->rx.n), + &ctrl->rx.phy, GFP_KERNEL); + if (!ctrl->rx.base) { + ret = -ENOMEM; + goto err; + } + + /* Register with framework before enabling frame, clock */ + ret = slim_register_controller(&ctrl->ctrl); + if (ret) { + dev_err(ctrl->dev, "error adding controller\n"); + goto err; + } + + ver = readl_relaxed(ctrl->base); + /* Version info in 16 MSbits */ + ver >>= 16; + /* Component register initialization */ + writel(1, ctrl->base + CFG_PORT(COMP_CFG, ver)); + writel((EE_MGR_RSC_GRP | EE_NGD_2 | EE_NGD_1), + ctrl->base + CFG_PORT(COMP_TRUST_CFG, ver)); + + writel((MGR_INT_TX_NACKED_2 | + MGR_INT_MSG_BUF_CONTE | MGR_INT_RX_MSG_RCVD | + MGR_INT_TX_MSG_SENT), ctrl->base + MGR_INT_EN); + writel(1, ctrl->base + MGR_CFG); + /* Framer register initialization */ + writel((1 << INTR_WAKE) | (0xA << REF_CLK_GEAR) | + (0xA << CLK_GEAR) | (1 << ROOT_FREQ) | (1 << FRM_ACTIVE) | 1, + ctrl->base + FRM_CFG); + writel(MGR_CFG_ENABLE, ctrl->base + MGR_CFG); + writel(1, ctrl->base + INTF_CFG); + writel(1, ctrl->base + CFG_PORT(COMP_CFG, ver)); + + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, QCOM_SLIM_AUTOSUSPEND); + pm_runtime_set_active(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + dev_dbg(ctrl->dev, "QCOM SB controller is up:ver:0x%x!\n", ver); + return 0; + +err: + clk_disable_unprepare(ctrl->rclk); +err_rclk_enable_failed: + clk_disable_unprepare(ctrl->hclk); +err_hclk_enable_failed: +err_request_irq_failed: + destroy_workqueue(ctrl->rxwq); + return ret; +} + +static int qcom_slim_remove(struct platform_device *pdev) +{ + struct qcom_slim_ctrl *ctrl = platform_get_drvdata(pdev); + + disable_irq(ctrl->irq); + clk_disable_unprepare(ctrl->hclk); + clk_disable_unprepare(ctrl->rclk); + slim_unregister_controller(&ctrl->ctrl); + destroy_workqueue(ctrl->rxwq); + return 0; +} + +static const struct dev_pm_ops qcom_slim_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(qcom_slim_suspend, qcom_slim_resume) + SET_RUNTIME_PM_OPS( + qcom_slim_runtime_suspend, + qcom_slim_runtime_resume, + NULL + ) +}; + +static const struct of_device_id qcom_slim_dt_match[] = { + { .compatible = "qcom,slim", }, + { .compatible = "qcom,apq8064-slim", }, + {} +}; + +static struct platform_driver qcom_slim_driver = { + .probe = qcom_slim_probe, + .remove = qcom_slim_remove, + .driver = { + .name = "qcom_slim_ctrl", + .of_match_table = qcom_slim_dt_match, + }, +}; +module_platform_driver(qcom_slim_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm SLIMbus Controller");