From patchwork Fri Oct 6 15:51:30 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 115081 Delivered-To: patch@linaro.org Received: by 10.80.163.170 with SMTP id s39csp1521376edb; Fri, 6 Oct 2017 08:53:57 -0700 (PDT) X-Received: by 10.101.67.73 with SMTP id k9mr1518492pgq.188.1507305237019; Fri, 06 Oct 2017 08:53:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1507305237; cv=none; d=google.com; s=arc-20160816; b=sJu5ZoBywbV1HSkt/5cvWU5tHNV7I/H+WfW4P+0jcl/rfcafIevZEKvs8/BFQqlapy b5msEn3Q8VX1Ms9iE9SJCJT1OW7D1Kni2w22nLztUt/mn2vhUClwgQx72BHCUiY+HE31 RCSioC0UE1lmkopmMdwLtcagFVw+h2hfxaSZKKwGfJalUCbh8YyDQAIod8WMyvL/zwvQ o8zhCB+r2qKmcNeUBO+fVrht2cQSfduk8Phlo3RqMuvsijYsbFLRuK+i4bsjjO6ooSpJ AffYcABadkSGnqLjSgxpMRvNEg36LV0bUlyQwDovIER5XsEC2Kp1UpauVRLg84cVYgwk H0Wg== 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=uryGDJ05ZrWmtvnMzLqrUeCfREAmNwP8QDb/PqRo6vk=; b=i3gf/u00zb3MvZzfBl8z+smG4kqhjy5SXf5WMv41HB5EMFHxByxxu0AgmchTiobCqj NJFdWgKr0QtmC7HO5pMMfikyx0qZEMz4XywOtp4l4lLpyG5urU6TtOM72cvDlYQaDXQr jj/w1xIeSrdk6TTH01ivdL1iSU5/EgcV404QEh1uCp3oP9OBPP8V2ixKAUkEepRfw62f YgDQt/llz6kQp38zCc0qfQ/qScXo97m3F7x9Z2a3ZIc3HYymIZh0v0jwWxmiNs0JeC/H zUO6ZY90Ao+i64hS8PpvtrHym4ZQzhX0GCb4Wkd6/XMNZgjzYCKM49GGuS95R/zutilz kXAw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=W0fWegN3; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (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 3si1446836plx.21.2017.10.06.08.53.56; Fri, 06 Oct 2017 08:53:57 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=W0fWegN3; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753062AbdJFPxz (ORCPT + 26 others); Fri, 6 Oct 2017 11:53:55 -0400 Received: from mail-wm0-f51.google.com ([74.125.82.51]:51734 "EHLO mail-wm0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751938AbdJFPv4 (ORCPT ); Fri, 6 Oct 2017 11:51:56 -0400 Received: by mail-wm0-f51.google.com with SMTP id f4so8772749wme.0 for ; Fri, 06 Oct 2017 08:51:55 -0700 (PDT) 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=uryGDJ05ZrWmtvnMzLqrUeCfREAmNwP8QDb/PqRo6vk=; b=W0fWegN3WuYxlLFT5/rQ6+NmRYqUaaaszBEwR/kKCIvH/iLMYihiTzp+NO+JNVm7U0 vn+nmih+bk5owtH+T3Ncciaa1qPkQ/mNPEjGqr0pdJo848naO1KTyR6YV9wtK+Upmhli ws/oCXY37YzjZN8vFvSOox0gLa3BAtPZjHXc8= 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=uryGDJ05ZrWmtvnMzLqrUeCfREAmNwP8QDb/PqRo6vk=; b=t6TzsMnJ0zCN+gL3gIK3pJD8bv6sCQeXULk6u8NpIcf7L8Dnh/N+7fNv+5rRd9Lqhl 4MpEAKM00xZOCJsxFRmDpmd9RSLvWJ3A6oTJK8XpZXkkxZwgYWEZJoGo97b6Ky4crEBP fIcxYPj2uAwMQSbC6UwMxwSRnl3PNlSwO7dqddSun9J2vX8jKhJKNF3iKgmnwf1Ys2xm F/NsFKbIjdYKQaDX/OXqZdVlo778FhUL+51Ckl6qmPQR9T7Nv8ivQiGJVBPEnc1Mex6T komidg6I0sy4RqdnfwpPjlQXj4TFb2AckCFrIk0kUWEca824VmDoqAidvooRSq3rzJvl 6S2A== X-Gm-Message-State: AMCzsaVnIpsjQbwtrmbiU6gD5pKIZPS5DRG776AWCz3tILq9OnocsV+v FRvrO7ODdvdGSgd533zgwgoAiQ== X-Google-Smtp-Source: AOwi7QA07mMTS7AFs4OFmPuodWHUnKT+JqyydHTHm/fT0dBA23ykla++yhwBvaye15pxSEcm3IqkIg== X-Received: by 10.28.196.79 with SMTP id u76mr2180356wmf.95.1507305114014; Fri, 06 Oct 2017 08:51:54 -0700 (PDT) Received: from localhost.localdomain (static.8.26.4.46.clients.your-server.de. [46.4.26.8]) by smtp.gmail.com with ESMTPSA id r21sm1510327wmd.26.2017.10.06.08.51.52 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 06 Oct 2017 08:51:53 -0700 (PDT) From: srinivas.kandagatla@linaro.org To: gregkh@linuxfoundation.org, broonie@kernel.org, alsa-devel@alsa-project.org Cc: sdharia@codeaurora.org, bp@suse.de, poeschel@lemonage.de, treding@nvidia.com, gong.chen@linux.intel.com, andreas.noever@gmail.com, alan@linux.intel.com, mathieu.poirier@linaro.org, daniel@ffwll.ch, jkosina@suse.cz, sharon.dvir1@mail.huji.ac.il, joe@perches.com, davem@davemloft.net, james.hogan@imgtec.com, michael.opdenacker@free-electrons.com, robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, kheitke@audience.com, linux-arm-msm@vger.kernel.org, arnd@arndb.de, Srinivas Kandagatla Subject: [Patch v6 1/7] slimbus: Device management on SLIMbus Date: Fri, 6 Oct 2017 17:51:30 +0200 Message-Id: <20171006155136.4682-2-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171006155136.4682-1-srinivas.kandagatla@linaro.org> References: <20171006155136.4682-1-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@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 does device enumeration, logical address assignment, informing device when the device reports present/absent etc. Reporting present may need the driver to do the needful (e.g. turning on voltage regulators powering the device). Additionally device is probed when it reports present if that device doesn't need any such steps mentioned above. Signed-off-by: Sagar Dharia Signed-off-by: Srinivas Kandagatla --- Documentation/devicetree/bindings/slimbus/bus.txt | 57 ++ Documentation/slimbus/summary | 109 ++++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/slimbus/Kconfig | 11 + drivers/slimbus/Makefile | 5 + drivers/slimbus/slim-core.c | 695 ++++++++++++++++++++++ include/linux/mod_devicetable.h | 13 + include/linux/slimbus.h | 299 ++++++++++ 9 files changed, 1192 insertions(+) create mode 100644 Documentation/devicetree/bindings/slimbus/bus.txt create mode 100644 Documentation/slimbus/summary create mode 100644 drivers/slimbus/Kconfig create mode 100644 drivers/slimbus/Makefile create mode 100644 drivers/slimbus/slim-core.c create mode 100644 include/linux/slimbus.h -- 2.9.3 diff --git a/Documentation/devicetree/bindings/slimbus/bus.txt b/Documentation/devicetree/bindings/slimbus/bus.txt new file mode 100644 index 0000000..cb658bb --- /dev/null +++ b/Documentation/devicetree/bindings/slimbus/bus.txt @@ -0,0 +1,57 @@ +SLIM(Serial Low Power Interchip Media Bus) bus + +SLIMbus is a 2-wire bus, and is used to communicate with peripheral +components like audio-codec. + +Controller is a normal device using binding for whatever bus it is +on (e.g. platform bus). +Required property for SLIMbus controller node: +- compatible - name of SLIMbus controller following generic names + recommended practice. +- #address-cells - should be 2 +- #size-cells - should be 0 + +No other properties are required in the SLIMbus controller bus node. + +Child nodes: +Every SLIMbus controller node can contain zero or more child nodes +representing slave devices on the bus. Every SLIMbus slave device is +uniquely determined by the enumeration address containing 4 fields: +Manufacturer ID, Product code, Device index, and Instance value for +the device. +If child node is not present and it is instantiated after device +discovery (slave device reporting itself present). + +In some cases it may be necessary to describe non-probeable device +details such as non-standard ways of powering up a device. In +such cases, child nodes for those devices will be present as +slaves of the slimbus-controller, as detailed below. + +Required property for SLIMbus child node if it is present: +- reg - Is Duplex (Device index, Instance ID) from Enumeration + Address. + Device Index Uniquely identifies multiple Devices within + a single Component. + Instance ID Is for the cases where multiple Devices of the + same type or Class are attached to the bus. + +- compatible -"slimMID,PID". The textual representation of Manufacturer ID, + Product Code, shall be in lower case hexadecimal with leading + zeroes suppressed + +SLIMbus example for Qualcomm's slimbus manager component: + + slim@28080000 { + compatible = "qcom,slim-msm"; + reg = <0x28080000 0x2000>, + interrupts = <0 33 0>; + clocks = <&lcc SLIMBUS_SRC>, <&lcc AUDIO_SLIMBUS_CLK>; + clock-names = "iface_clk", "core_clk"; + #address-cells = <2>; + #size-cells = <0>; + + codec: wcd9310@1{ + compatible = "slim217,60""; + reg = <1 0>; + }; + }; diff --git a/Documentation/slimbus/summary b/Documentation/slimbus/summary new file mode 100644 index 0000000..e7f90bb --- /dev/null +++ b/Documentation/slimbus/summary @@ -0,0 +1,109 @@ +Overview of Linux kernel SLIMbus support +======================================== + +What is SLIMbus? +---------------- +SLIMbus (Serial Low Power Interchip Media Bus) is a specification developed by +MIPI (Mobile Industry Processor Interface) alliance. The bus uses master/slave +configuration, and is a 2-wire multi-drop implementation (clock, and data). + +Currently, SLIMbus is used to interface between application processors of SoCs +(System-on-Chip) and peripheral components (typically codec).SLIMbus uses +Time-Division-Multiplexing to accommodate multiple data channels, and +a control channel. + +The control channel is used for various control functions such as bus +management, configuration and status updates.These messages can be unicast (e.g. +reading/writing device specific values), or multicast (e.g. data channel +reconfiguration sequence is a broadcast message announced to all devices) + +A data channel is used for data-transfer between 2 Slimbus devices. Data +channel uses dedicated ports on the device. + +Hardware description: +--------------------- +Slimbus specification has different types of device classifications based on +their capabilities. +A manager device is responsible for enumeration, configuration, and dynamic +channel allocation. Every bus has 1 active manager. + +A generic device is a device providing application functionality (e.g. codec). + +Framer device is responsible for clocking the bus, and transmitting frame-sync +and framing information on the bus. + +Each SLIMbus component has an interface device for monitoring physical layer. + +Typically each SoC contains SLIMbus component having 1 manager, 1 framer device, +1 generic device (for data channel support), and 1 interface device. +External peripheral SLIMbus component usually has 1 generic device (for +functionality/data channel support), and an associated interface device. +The generic device's registers are mapped as 'value elements' so that they can +be written/read using Slimbus control channel exchanging control/status type of +information. +In case there are multiple framer devices on the same bus, manager device is +responsible to select the active-framer for clocking the bus. + +Per specification, Slimbus uses "clock gears" to do power management based on +current frequency and bandwidth requirements. There are 10 clock gears and each +gear changes the Slimbus frequency to be twice its previous gear. + +Each device has a 6-byte enumeration-address and the manager assigns every +device with a 1-byte logical address after the devices report presence on the +bus. + +Software description: +--------------------- +There are 2 types of SLIMbus drivers: + +slim_controller represents a 'controller' for SLIMbus. This driver should +implement duties needed by the SoC (manager device, associated +interface device for monitoring the layers and reporting errors, default +framer device). + +slim_device represents the 'generic device/component' for SLIMbus, and a +slim_driver should implement driver for that slim_device. + +Device notifications to the driver: +----------------------------------- +Since SLIMbus devices have mechanisms for reporting their presence, the +framework allows drivers to bind when corresponding devices report their +presence on the bus. +However, it is possible that the driver needs to be probed +first so that it can enable corresponding SLIMbus devie (e.g. power it up and/or +take it out of reset). To support that behavior, the framework allows drivers +to probe first as well (e.g. using standard DeviceTree compatbility field). +This creates the necessity for the driver to know when the device is functional +(i.e. reported present). device_up callback is used for that reason when the +device reports present and is assigned a logical address by the controller. + +Similarly, SLIMbus devices 'report absent' when they go down. A 'device_down' +callback notifies the driver when the device reports absent and its logical +address assignment is invalidated by the controller. + +Another notification "boot_device" is used to notify the slim_driver when +controller resets the bus. This notification allows the driver to take necessary +steps to boot the device so that it's functional after the bus has been reset. + +Clock-pause: +------------ +SLIMbus mandates that a reconfiguration sequence (known as clock-pause) be +broadcast to all active devices on the bus before the bus can enter low-power +mode. Controller uses this sequence when it decides to enter low-power mode so +that corresponding clocks and/or power-rails can be turned off to save power. +Clock-pause is exited by waking up framer device (if controller driver initiates +exiting low power mode), or by toggling the data line (if a slave device wants +to initiate it). + +Messaging APIs: +--------------- +The framework supports APIs to exchange control-information with a SLIMbus +device. APIs can be synchronous or asynchronous. +From controller's perspective, multiple buffers can be queued to/from +hardware for sending/receiving data using slim_ctrl_buf circular buffer. +The header file has more documentation about messaging APIs. + +----------------------------------------------------------------- + +------------------------------------------------------------------ diff --git a/drivers/Kconfig b/drivers/Kconfig index 505c676..8010c67 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -208,4 +208,6 @@ source "drivers/tee/Kconfig" source "drivers/mux/Kconfig" +source "drivers/slimbus/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index d90fdc4..0449c7c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -86,6 +86,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 0000000..f0b118a --- /dev/null +++ b/drivers/slimbus/Kconfig @@ -0,0 +1,11 @@ +# +# 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. + diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile new file mode 100644 index 0000000..f580704 --- /dev/null +++ b/drivers/slimbus/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for kernel slimbus framework. +# +obj-$(CONFIG_SLIMBUS) += slimbus.o +slimbus-y := slim-core.o diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c new file mode 100644 index 0000000..de3ef79 --- /dev/null +++ b/drivers/slimbus/slim-core.c @@ -0,0 +1,695 @@ +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_MUTEX(slim_lock); +static DEFINE_IDR(ctrl_idr); + +static bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b) +{ + + return (a->manf_id == b->manf_id && + a->prod_code == b->prod_code && + a->dev_index == b->dev_index && + a->instance == b->instance); +} + +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 && + id->dev_index == sbdev->e_addr.dev_index) + 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); + + /* Attempt an OF style match first */ + if (of_driver_match_device(dev, drv)) + return 1; + + /* Then try to match against the id table */ + if (sbdrv->id_table) + return slim_match(sbdrv->id_table, sbdev) != NULL; + + return 0; +} + +struct sb_report_wd { + struct work_struct wd; + struct slim_device *sbdev; + bool report; +}; + +static void slim_report(struct work_struct *work) +{ + struct slim_driver *sbdrv; + struct sb_report_wd *sbw = container_of(work, struct sb_report_wd, wd); + struct slim_device *sbdev = sbw->sbdev; + + mutex_lock(&sbdev->report_lock); + if (!sbdev->dev.driver) + goto report_exit; + + /* check if device-up or down needs to be called */ + if ((!sbdev->reported && !sbdev->notified) || + (sbdev->reported && sbdev->notified)) + goto report_exit; + + sbdrv = to_slim_driver(sbdev->dev.driver); + + /** + * address no longer valid, means device reported absent, whereas + * address valid, means device reported present + */ + if (sbdev->notified && !sbdev->reported) { + sbdev->notified = false; + if (sbdrv->device_down) + sbdrv->device_down(sbdev); + } else if (!sbdev->notified && sbdev->reported) { + sbdev->notified = true; + if (sbdrv->device_up) + sbdrv->device_up(sbdev); + } +report_exit: + mutex_unlock(&sbdev->report_lock); + kfree(sbw); +} + +/** + * Report callbacks(device_up, device_down) are implemented by slimbus-devices. + * The calls are scheduled into a workqueue to avoid holding up controller + * initialization/tear-down. + */ +static void schedule_slim_report(struct slim_controller *ctrl, + struct slim_device *sb, bool report) +{ + struct sb_report_wd *sbw; + + dev_dbg(&ctrl->dev, "report:%d for slave:%s\n", report, sb->name); + + sbw = kmalloc(sizeof(*sbw), GFP_KERNEL); + if (!sbw) + return; + + INIT_WORK(&sbw->wd, slim_report); + sbw->sbdev = sb; + sbw->report = report; + if (!queue_work(ctrl->wq, &sbw->wd)) { + dev_err(&ctrl->dev, "failed to queue report:%d slave:%s\n", + report, sb->name); + kfree(sbw); + } +} + +static int slim_device_probe(struct device *dev) +{ + struct slim_device *sbdev; + struct slim_driver *sbdrv; + int status = 0; + + sbdev = to_slim_device(dev); + sbdrv = to_slim_driver(dev->driver); + + sbdev->driver = sbdrv; + + if (sbdrv->probe) + status = sbdrv->probe(sbdev); + + if (status) + sbdev->driver = NULL; + else if (sbdrv->device_up) + schedule_slim_report(sbdev->ctrl, sbdev, true); + + return status; +} + +static int slim_device_remove(struct device *dev) +{ + struct slim_device *sbdev; + struct slim_driver *sbdrv; + int status = 0; + + sbdev = to_slim_device(dev); + if (!dev->driver) + return 0; + + sbdrv = to_slim_driver(dev->driver); + if (sbdrv->remove) + status = sbdrv->remove(sbdev); + + mutex_lock(&sbdev->report_lock); + sbdev->notified = false; + if (status == 0) + sbdev->driver = NULL; + mutex_unlock(&sbdev->report_lock); + return status; +} + +struct bus_type slimbus_type = { + .name = "slimbus", + .match = slim_device_match, + .probe = slim_device_probe, + .remove = slim_device_remove, +}; +EXPORT_SYMBOL_GPL(slimbus_type); + +/** + * 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) +{ + drv->driver.bus = &slimbus_type; + 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) +{ + if (drv) + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(slim_driver_unregister); + +static struct slim_controller *slim_ctrl_get(struct slim_controller *ctrl) +{ + if (!ctrl || !get_device(&ctrl->dev)) + return NULL; + + return ctrl; +} + +static void slim_ctrl_put(struct slim_controller *ctrl) +{ + if (ctrl) + put_device(&ctrl->dev); +} + +static void slim_dev_release(struct device *dev) +{ + struct slim_device *sbdev = to_slim_device(dev); + + slim_ctrl_put(sbdev->ctrl); + kfree(sbdev->name); + kfree(sbdev); +} + +static int slim_add_device(struct slim_controller *ctrl, + struct slim_device *sbdev) +{ + sbdev->dev.bus = &slimbus_type; + sbdev->dev.parent = &ctrl->dev; + sbdev->dev.release = slim_dev_release; + sbdev->dev.driver = NULL; + sbdev->ctrl = ctrl; + + slim_ctrl_get(ctrl); + sbdev->name = kasprintf(GFP_KERNEL, "%x:%x:%x:%x", + sbdev->e_addr.manf_id, + sbdev->e_addr.prod_code, + sbdev->e_addr.dev_index, + sbdev->e_addr.instance); + if (!sbdev->name) + return -ENOMEM; + + dev_set_name(&sbdev->dev, "%s", sbdev->name); + mutex_init(&sbdev->report_lock); + + /* probe slave on this controller */ + return device_register(&sbdev->dev); +} + +/* Helper to get hex Manufacturer ID and Product id from compatible */ +static unsigned long str2hex(unsigned char *str) +{ + int value = 0; + + while (*str) { + char c = *str++; + + value = value << 4; + if (c >= '0' && c <= '9') + value |= (c - '0'); + if (c >= 'a' && c <= 'f') + value |= (c - 'a' + 10); + + } + + return value; +} + +/* OF helpers for SLIMbus */ +static void of_register_slim_devices(struct slim_controller *ctrl) +{ + struct device *dev = &ctrl->dev; + struct device_node *node; + + if (!ctrl->dev.of_node) + return; + + for_each_child_of_node(ctrl->dev.of_node, node) { + struct slim_device *slim; + const char *compat = NULL; + char *p, *tok; + int reg[2], ret; + + slim = kzalloc(sizeof(*slim), GFP_KERNEL); + if (!slim) + continue; + + slim->dev.of_node = of_node_get(node); + + compat = of_get_property(node, "compatible", NULL); + if (!compat) + continue; + + p = kasprintf(GFP_KERNEL, "%s", compat + strlen("slim")); + + tok = strsep(&p, ","); + if (!tok) { + dev_err(dev, "No valid Manufacturer ID found\n"); + kfree(p); + continue; + } + slim->e_addr.manf_id = str2hex(tok); + + tok = strsep(&p, ","); + if (!tok) { + dev_err(dev, "No valid Product ID found\n"); + kfree(p); + continue; + } + slim->e_addr.prod_code = str2hex(tok); + kfree(p); + + ret = of_property_read_u32_array(node, "reg", reg, 2); + if (ret) { + dev_err(dev, "Device and Instance id not found:%d\n", + ret); + continue; + } + slim->e_addr.dev_index = reg[0]; + slim->e_addr.instance = reg[1]; + + ret = slim_add_device(ctrl, slim); + if (ret) + dev_err(dev, "of_slim device register err:%d\n", ret); + } +} + +/** + * slim_register_controller: Controller bring-up and registration. + * @ctrl: Controller to be registered. + * A controller is registered with the framework using this API. + * If devices on a controller were registered before controller, + * this will make sure that they get probed when controller is up + */ +int slim_register_controller(struct slim_controller *ctrl) +{ + int id, ret = 0; + + mutex_lock(&slim_lock); + id = idr_alloc(&ctrl_idr, ctrl, ctrl->nr, -1, GFP_KERNEL); + mutex_unlock(&slim_lock); + + if (id < 0) + return id; + + ctrl->nr = id; + + dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr); + ctrl->num_dev = 0; + + if (!ctrl->min_cg) + ctrl->min_cg = SLIM_MIN_CLK_GEAR; + if (!ctrl->max_cg) + ctrl->max_cg = SLIM_MAX_CLK_GEAR; + + mutex_init(&ctrl->m_ctrl); + ret = device_register(&ctrl->dev); + if (ret) + goto dev_reg_failed; + + dev_dbg(&ctrl->dev, "Bus [%s] registered:dev:%p\n", + ctrl->name, &ctrl->dev); + + ctrl->wq = create_singlethread_workqueue(dev_name(&ctrl->dev)); + if (!ctrl->wq) + goto err_workq_failed; + + of_register_slim_devices(ctrl); + + return 0; + +err_workq_failed: + device_unregister(&ctrl->dev); +dev_reg_failed: + mutex_lock(&slim_lock); + idr_remove(&ctrl_idr, ctrl->nr); + mutex_unlock(&slim_lock); + return ret; +} +EXPORT_SYMBOL_GPL(slim_register_controller); + +/* slim_remove_device: Remove the effect of slim_add_device() */ +static void slim_remove_device(struct slim_device *sbdev) +{ + device_unregister(&sbdev->dev); +} + +static int slim_ctrl_remove_device(struct device *dev, void *null) +{ + slim_remove_device(to_slim_device(dev)); + return 0; +} + +/** + * slim_del_controller: Controller tear-down. + * @ctrl: Controller to tear-down. + */ +int slim_del_controller(struct slim_controller *ctrl) +{ + struct slim_controller *found; + + /* First make sure that this bus was added */ + mutex_lock(&slim_lock); + found = idr_find(&ctrl_idr, ctrl->nr); + mutex_unlock(&slim_lock); + if (found != ctrl) + return -EINVAL; + + /* Remove all clients */ + device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device); + + + destroy_workqueue(ctrl->wq); + + /* free bus id */ + mutex_lock(&slim_lock); + idr_remove(&ctrl_idr, ctrl->nr); + mutex_unlock(&slim_lock); + + device_unregister(&ctrl->dev); + return 0; +} +EXPORT_SYMBOL_GPL(slim_del_controller); + +/** + * slim_report_absent: Controller calls this function when a device + * reports absent, OR when the device cannot be communicated with + * @sbdev: Device that cannot be reached, or sent report absent + */ +void slim_report_absent(struct slim_device *sbdev) +{ + struct slim_controller *ctrl; + int i; + + if (!sbdev) + return; + ctrl = sbdev->ctrl; + if (!ctrl) + return; + + /* invalidate logical addresses */ + mutex_lock(&ctrl->m_ctrl); + for (i = 0; i < ctrl->num_dev; i++) { + if (sbdev->laddr == ctrl->addrt[i].laddr) + ctrl->addrt[i].valid = false; + } + mutex_unlock(&ctrl->m_ctrl); + + mutex_lock(&sbdev->report_lock); + sbdev->reported = false; + schedule_slim_report(ctrl, sbdev, false); + mutex_unlock(&sbdev->report_lock); +} +EXPORT_SYMBOL_GPL(slim_report_absent); + +static int slim_boot_child(struct device *dev, void *unused) +{ + struct slim_driver *sbdrv; + struct slim_device *sbdev = to_slim_device(dev); + + if (sbdev && sbdev->dev.driver) { + sbdrv = to_slim_driver(sbdev->dev.driver); + if (sbdrv->boot_device) + sbdrv->boot_device(sbdev); + } + return 0; +} + +static int slim_match_dev(struct device *dev, void *data) +{ + struct slim_eaddr *e_addr = data; + struct slim_device *slim = to_slim_device(dev); + + return slim_eaddr_equal(&slim->e_addr, e_addr); +} + +/** + * slim_framer_booted: This function is called by controller after the active + * framer has booted (using Bus Reset sequence, or after it has shutdown and has + * come back up). + * @ctrl: Controller associated with this framer + * Components, devices on the bus may be in undefined state, + * and this function triggers their drivers to do the needful + * to bring them back in Reset state so that they can acquire sync, report + * present and be operational again. + */ +void slim_framer_booted(struct slim_controller *ctrl) +{ + if (!ctrl) + return; + + device_for_each_child(&ctrl->dev, NULL, slim_boot_child); +} +EXPORT_SYMBOL_GPL(slim_framer_booted); + +/** + * slim_query_device: Query and get handle to a device. + * @ctrl: Controller on which this device will be added/queried + * @e_addr: Enumeration address of the device to be queried + * Returns pointer to a device if it has already reported. Creates a new + * device and returns pointer to it if the device has not yet enumerated. + */ +struct slim_device *slim_query_device(struct slim_controller *ctrl, + struct slim_eaddr *e_addr) +{ + struct device *dev; + struct slim_device *slim = NULL; + + dev = device_find_child(&ctrl->dev, e_addr, slim_match_dev); + if (dev) { + slim = to_slim_device(dev); + return slim; + } + + slim = kzalloc(sizeof(struct slim_device), GFP_KERNEL); + if (IS_ERR(slim)) + return NULL; + + slim->e_addr = *e_addr; + if (slim_add_device(ctrl, slim) != 0) { + kfree(slim); + return NULL; + } + return slim; +} +EXPORT_SYMBOL_GPL(slim_query_device); + +static int ctrl_getaddr_entry(struct slim_controller *ctrl, + struct slim_eaddr *eaddr, u8 *entry) +{ + int i; + + for (i = 0; i < ctrl->num_dev; i++) { + if (ctrl->addrt[i].valid && + slim_eaddr_equal(&ctrl->addrt[i].eaddr, eaddr)) { + *entry = i; + return 0; + } + } + return -ENXIO; +} + +/** + * slim_assign_laddr: Assign logical address to a device enumerated. + * @ctrl: Controller with which device is enumerated. + * @e_addr: Enumeration address of the device. + * @laddr: Return logical address (if valid flag is false) + * @valid: true if laddr holds a valid address that controller wants to + * set for this enumeration address. Otherwise framework sets index into + * address table as logical address. + * Called by controller in response to REPORT_PRESENT. Framework will assign + * a logical address to this enumeration address. + * Function returns -EXFULL to indicate that all logical addresses are already + * taken. + */ +int slim_assign_laddr(struct slim_controller *ctrl, struct slim_eaddr *e_addr, + u8 *laddr, bool valid) +{ + int ret; + u8 i = 0; + bool exists = false; + struct slim_device *slim; + struct slim_addrt *temp; + + mutex_lock(&ctrl->m_ctrl); + /* already assigned */ + if (ctrl_getaddr_entry(ctrl, e_addr, &i) == 0) { + *laddr = ctrl->addrt[i].laddr; + exists = true; + } else { + if (ctrl->num_dev >= (SLIM_LA_MANAGER - 1)) { + ret = -EXFULL; + goto ret_assigned_laddr; + } + for (i = 0; i < ctrl->num_dev; i++) { + if (ctrl->addrt[i].valid == false) + break; + } + if (i == ctrl->num_dev) { + temp = krealloc(ctrl->addrt, + (ctrl->num_dev + 1) * + sizeof(struct slim_addrt), + GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + goto ret_assigned_laddr; + } + ctrl->addrt = temp; + ctrl->num_dev++; + } + ctrl->addrt[i].eaddr = *e_addr; + ctrl->addrt[i].valid = true; + + /* Preferred address is index into table */ + if (!valid) + *laddr = i; + } + + ret = ctrl->set_laddr(ctrl, &ctrl->addrt[i].eaddr, *laddr); + if (ret) { + ctrl->addrt[i].valid = false; + goto ret_assigned_laddr; + } + ctrl->addrt[i].laddr = *laddr; + +ret_assigned_laddr: + mutex_unlock(&ctrl->m_ctrl); + if (exists || ret) + return ret; + + dev_info(&ctrl->dev, "setting slimbus l-addr:%x, ea:%x,%x,%x,%x\n", + *laddr, e_addr->manf_id, e_addr->prod_code, + e_addr->dev_index, e_addr->instance); + + /** + * Add this device to list of devices on this controller if it's + * not already present + */ + slim = slim_query_device(ctrl, e_addr); + if (!slim) { + ret = -ENODEV; + } else { + struct slim_driver *sbdrv; + + slim->laddr = *laddr; + mutex_lock(&slim->report_lock); + slim->reported = true; + if (slim->dev.driver) { + sbdrv = to_slim_driver(slim->dev.driver); + if (sbdrv->device_up) + schedule_slim_report(ctrl, slim, true); + } + mutex_unlock(&slim->report_lock); + } + return ret; +} +EXPORT_SYMBOL_GPL(slim_assign_laddr); + +/** + * slim_get_logical_addr: Return the logical address of a slimbus device. + * @sb: client handle requesting the address. + * @e_addr: Enumeration address of the device. + * @laddr: output buffer to store the address + * context: can sleep + * -EINVAL is returned in case of invalid parameters, and -ENXIO is returned if + * the device with this enumeration address is not found. + */ +int slim_get_logical_addr(struct slim_device *sb, struct slim_eaddr *e_addr, + u8 *laddr) +{ + int ret; + u8 entry; + struct slim_controller *ctrl = sb->ctrl; + + if (!ctrl || !laddr || !e_addr) + return -EINVAL; + + mutex_lock(&ctrl->m_ctrl); + ret = ctrl_getaddr_entry(ctrl, e_addr, &entry); + if (!ret) + *laddr = ctrl->addrt[entry].laddr; + mutex_unlock(&ctrl->m_ctrl); + + if (ret == -ENXIO && ctrl->get_laddr) { + ret = ctrl->get_laddr(ctrl, e_addr, laddr); + if (!ret) + ret = slim_assign_laddr(ctrl, e_addr, laddr, true); + } + return ret; +} +EXPORT_SYMBOL_GPL(slim_get_logical_addr); + +static void __exit slimbus_exit(void) +{ + bus_unregister(&slimbus_type); +} +module_exit(slimbus_exit); + +static int __init slimbus_init(void) +{ + return bus_register(&slimbus_type); +} +postcore_initcall(slimbus_init); + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); +MODULE_DESCRIPTION("Slimbus module"); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 694cebb..015e5f6 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -448,6 +448,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; + __u8 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 0000000..b5064b6 --- /dev/null +++ b/include/linux/slimbus.h @@ -0,0 +1,299 @@ +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _LINUX_SLIMBUS_H +#define _LINUX_SLIMBUS_H +#include +#include +#include +#include + +/** + * Interfaces between SLIMbus manager drivers, SLIMbus client drivers, and + * SLIMbus infrastructure. + */ + +extern struct bus_type slimbus_type; + +/* Standard values per SLIMbus spec needed by controllers and devices */ +#define SLIM_CL_PER_SUPERFRAME 6144 +#define SLIM_CL_PER_SUPERFRAME_DIV8 (SLIM_CL_PER_SUPERFRAME >> 3) +#define SLIM_MAX_CLK_GEAR 10 +#define SLIM_MIN_CLK_GEAR 1 +#define SLIM_CL_PER_SL 4 +#define SLIM_SL_PER_SUPERFRAME (SLIM_CL_PER_SUPERFRAME >> 2) +#define SLIM_FRM_SLOTS_PER_SUPERFRAME 16 +#define SLIM_GDE_SLOTS_PER_SUPERFRAME 2 + +struct slim_controller; +struct slim_device; + +/** + * 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; +}; + +/** + * struct slim_framer - Represents Slimbus framer. + * Every controller may have multiple framers. There is 1 active framer device + * responsible for clocking the bus. + * Manager is responsible for framer hand-over. + * @dev: Driver model representation of the device. + * @e_addr: Enumeration address of the framer. + * @rootfreq: Root Frequency at which the framer can run. This is maximum + * frequency ('clock gear 10') at which the bus can operate. + * @superfreq: Superframes per root frequency. Every frame is 6144 bits. + */ +struct slim_framer { + struct device dev; + struct slim_eaddr e_addr; + int rootfreq; + int superfreq; +}; + +#define to_slim_framer(d) container_of(d, struct slim_framer, dev) + +/** + * struct slim_addrt: slimbus address used internally by the slimbus framework. + * @valid: If the device is present. Valid is set to false when device reports + * absent. + * @eaddr: Enumeration address + * @laddr: It is possible that controller will set a predefined logical address + * rather than the one assigned by framework. (i.e. logical address may + * not be same as index into this table). This entry will store the + * logical address value for this enumeration address. + */ +struct slim_addrt { + bool valid; + struct slim_eaddr eaddr; + u8 laddr; +}; + +/* SLIMbus message types. Related to interpretation of message code. */ +#define SLIM_MSG_MT_CORE 0x0 +#define SLIM_MSG_MT_DEST_REFERRED_CLASS 0x1 +#define SLIM_MSG_MT_DEST_REFERRED_USER 0x2 +#define SLIM_MSG_MT_SRC_REFERRED_CLASS 0x5 +#define SLIM_MSG_MT_SRC_REFERRED_USER 0x6 + +/* SLIMbus core type Message Codes. */ +/* Device management messages used by this framework */ +#define SLIM_MSG_MC_REPORT_PRESENT 0x1 +#define SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS 0x2 +#define SLIM_MSG_MC_REPORT_ABSENT 0xF + +/* Destination type Values */ +#define SLIM_MSG_DEST_LOGICALADDR 0 +#define SLIM_MSG_DEST_ENUMADDR 1 +#define SLIM_MSG_DEST_BROADCAST 3 + +/** + * struct slim_controller: Controls every instance of SLIMbus + * (similar to 'master' on SPI) + * 'Manager device' is responsible for device management, bandwidth + * allocation, channel setup, and port associations per channel. + * Device management means Logical address assignment/removal based on + * enumeration (report-present, report-absent) if a device. + * Bandwidth allocation is done dynamically by the manager based on active + * channels on the bus, message-bandwidth requests made by slimbus devices. + * Based on current bandwidth usage, manager chooses a frequency to run + * the bus at (in steps of 'clock-gear', 1 through 10, each clock gear + * representing twice the frequency than the previous gear). + * Manager is also responsible for entering (and exiting) low-power-mode + * (known as 'clock pause'). + * Manager can do handover of framer if there are multiple framers on the + * bus and a certain usecase warrants using certain framer to avoid keeping + * previous framer being powered-on. + * + * Controller here performs duties of the manager device, and 'interface + * device'. Interface device is responsible for monitoring the bus and + * reporting information such as loss-of-synchronization, data + * slot-collision. + * @dev: Device interface to this driver + * @nr: Board-specific number identifier for this controller/bus + * @list: Link with other slimbus controllers + * @name: Name for this controller + * @min_cg: Minimum clock gear supported by this controller (default value: 1) + * @max_cg: Maximum clock gear supported by this controller (default value: 10) + * @clkgear: Current clock gear in which this bus is running + * @a_framer: Active framer which is clocking the bus managed by this controller + * @m_ctrl: Mutex protecting controller data structures + * @addrt: Logical address table + * @num_dev: Number of active slimbus slaves on this bus + * @wq: Workqueue per controller used to notify devices when they report present + * @xfer_msg: Transfer a message on this controller (this can be a broadcast + * control/status message like data channel setup, or a unicast message + * like value element read/write. + * @set_laddr: Setup logical address at laddr for the slave with elemental + * address e_addr. Drivers implementing controller will be expected to + * send unicast message to this device with its logical address. + * @get_laddr: It is possible that controller needs to set fixed logical + * address table and get_laddr can be used in that case so that controller + * can do this assignment. + */ +struct slim_controller { + struct device dev; + unsigned int nr; + char name[SLIMBUS_NAME_SIZE]; + int min_cg; + int max_cg; + int clkgear; + struct slim_framer *a_framer; + struct mutex m_ctrl; + struct slim_addrt *addrt; + u8 num_dev; + struct workqueue_struct *wq; + int (*set_laddr)(struct slim_controller *ctrl, + struct slim_eaddr *ea, u8 laddr); + int (*get_laddr)(struct slim_controller *ctrl, + struct slim_eaddr *ea, u8 *laddr); +}; + +#define to_slim_controller(d) container_of(d, struct slim_controller, 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. + * @suspend: Standard suspend callback used during system suspend + * @resume: Standard resume callback used during system resume + * @device_up: This callback is called when the device reports present and + * gets a logical address assigned to it + * @device_down: This callback is called when device reports absent, or the + * bus goes down. Device will report present when bus is up and + * device_up callback will be called again when that happens + * @boot_device: This callback is called after framer is booted. + * Driver should do the needful to boot the device, + * so that device acquires sync and be operational. + * @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); + int (*remove)(struct slim_device *sl); + void (*shutdown)(struct slim_device *sl); + int (*suspend)(struct slim_device *sl, + pm_message_t pmesg); + int (*resume)(struct slim_device *sl); + int (*device_up)(struct slim_device *sl); + int (*device_down)(struct slim_device *sl); + int (*boot_device)(struct slim_device *sl); + + struct device_driver driver; + const struct slim_device_id *id_table; +}; + +#define to_slim_driver(d) container_of(d, struct slim_driver, driver) + +/** + * Client/device handle (struct slim_device): + * ------------------------------------------ + * 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. + * @dev: Driver model representation of the device. + * @name: Name of driver to use with this device. + * @e_addr: Enumeration address of this device. + * @driver: Device's driver. Pointer to access routines. + * @ctrl: Slimbus controller managing the bus hosting this device. + * @laddr: 1-byte Logical address of this device. + * @reported: Flag to indicate whether this device reported present. The flag + * is set when device reports present, and is reset when it reports + * absent. This flag alongwith notified flag below is used to call + * device_up, or device_down callbacks for driver of this device. + * @notified: Flag to indicate whether this device has been notified. The + * device may report present multiple times, but should be notified only + * first time it has reported present. + * @report_lock: Lock to protect reporting and notification for this device + */ +struct slim_device { + struct device dev; + char *name; + struct slim_eaddr e_addr; + struct slim_driver *driver; + struct slim_controller *ctrl; + u8 laddr; + bool reported; + bool notified; + struct mutex report_lock; +}; + +#define to_slim_device(d) container_of(d, struct slim_device, dev) + +/* Manager's logical address is set to 0xFF per spec */ +#define SLIM_LA_MANAGER 0xFF + +int slim_get_logical_addr(struct slim_device *sb, + struct slim_eaddr *e_addr, u8 *laddr); + +/* + * 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 + * @__slimbus_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) + +int slim_del_controller(struct slim_controller *ctrl); +int slim_assign_laddr(struct slim_controller *ctrl, + struct slim_eaddr *e_addr, u8 *laddr, bool valid); +void slim_report_absent(struct slim_device *sbdev); +void slim_framer_booted(struct slim_controller *ctrl); +int slim_register_controller(struct slim_controller *ctrl); + +static inline void *slim_get_ctrldata(const struct slim_controller *dev) +{ + return dev_get_drvdata(&dev->dev); +} + +static inline void slim_set_ctrldata(struct slim_controller *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +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 Fri Oct 6 15:51:33 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srinivas Kandagatla X-Patchwork-Id: 115078 Delivered-To: patch@linaro.org Received: by 10.80.163.170 with SMTP id s39csp1519727edb; Fri, 6 Oct 2017 08:52:13 -0700 (PDT) X-Received: by 10.84.194.1 with SMTP id g1mr2371949pld.74.1507305133680; Fri, 06 Oct 2017 08:52:13 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1507305133; cv=none; d=google.com; s=arc-20160816; b=l5OHHKyrIXK+gv9yxiAJOU6H5iA1crh7yTeXkQfzlDVpNCcpZdu3T2Z7hkNVQ7O6D9 6m4piwNXxvgP2R9s9RvCC15YhrEn7iSyAvS91HfiBN6yOyD1c24O4vVjIo6HemW/qndL p/wtTwtY2b6JRYlnHh0O89xaU0dafokkwQklXPwQjVYDxxXlLl4AVu19ax6WtWzeE711 yIgFpCciYrBMu8LwNhsbs2GGBwDAl1UekNbjeZz/xwwSNFvyLRTS1ddTt4o/oqYKRP7k rOeEFVROGKH1tIz70L4uBegNioz+UwIVIAYAoAgnSf4nnaSLHmb7m2qmFgU/XeRpYgHy cQYg== 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=ItMaphWTJIq7Yw5RylGFLd7yNVDfsWsnCHVhKdzAS+o=; b=HC69wpu2IvCDWk6i8ySvjBCUuc6ub7tGQxlNKj9xfZBTVOSjYF+ZBsRvOWDp0AHbKx 6pFUJglSPTjnKez0w26xYuH2RnR3ovcPSBS54LsRWw5Gsvxkkydhu+5Eu9UHZL1wQg76 14cMIw+hIhd+hvBvEmJ8+btGGTxGiplEnEaD1JLON88vYF+ZLDf2qUIFqFIZMPqMlx9Z nLtsdIau5FU274O5+N+npnGzP1lGf0bgEXa5tsFwkOnx4g0F/FuJN/yHtPe75lgWFl9u tKeh4adn2vLgHHFg4EjA7gyMt4vR7OE5vjhVgy1DPtz6Xb3F5s3Z6VcwF4pBpWRzz0P2 CiDw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ToKhB01e; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (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 n10si589485pfe.24.2017.10.06.08.52.13; Fri, 06 Oct 2017 08:52:13 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ToKhB01e; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752944AbdJFPwK (ORCPT + 26 others); Fri, 6 Oct 2017 11:52:10 -0400 Received: from mail-wm0-f51.google.com ([74.125.82.51]:47865 "EHLO mail-wm0-f51.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752678AbdJFPv7 (ORCPT ); Fri, 6 Oct 2017 11:51:59 -0400 Received: by mail-wm0-f51.google.com with SMTP id t69so8381485wmt.2 for ; Fri, 06 Oct 2017 08:51:59 -0700 (PDT) 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=ItMaphWTJIq7Yw5RylGFLd7yNVDfsWsnCHVhKdzAS+o=; b=ToKhB01eJX15FertQSyKQ7sPbGqGVlmzxpbfSAfqVYoP6JRFtfB1cB4U40Olvn5qQA 8XPfeoULgov3fp4wJiL6sK07VGDnn4PsAvTiMgp0p04EtbzvWDWu9LFBrJepcrMltsJm NYreDOOcpy3eiqKnhKV5EQCuA+wPgW9EJWgCE= 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=ItMaphWTJIq7Yw5RylGFLd7yNVDfsWsnCHVhKdzAS+o=; b=WlfkaFxEwkFe8Uxu1eGXMMD/V692kWqcFrq83umuu7PXDcrshIOk2mX3n1nH/FfiO6 BtiFwQSjY0rLtZIz/RFFCna35pI+cTbH4n6rSa7VltCjl4iBEYN4CPZI9NV7VXRbcgay jt0e3KCzARPg0lGxbnubyUNa9En0WI1KNmW0OJ9sSv2j8RMFQeqEJKAcBqsOIE/22FEn aBFXaXVvs3EyJ2zm8viyut5RHYYkP3ayOkS/8jf8OkexmaGH3yYYmfDMagyY1V58cXtz TV+R4rmTHNukXBNnrkfO9GaqaGbptpJ9gp9Hs7gJXqdR5uXjcixdRjvnAf+FDgOrAZ8v nR0g== X-Gm-Message-State: AMCzsaUaqHiaG3ZxCeCodC/RoOt7NN12oe3vaSuS3oTEj9aS/yTrqN1C wRcSbXKXPA5lDsO5ke9do1cuaA== X-Google-Smtp-Source: AOwi7QDyLV8GCAZyWB8/vJ7qWraztDsvkW0FGB1JcM7sADeFvTD2+fqzOHLTmt2CIXWNy1EYrmNmOw== X-Received: by 10.28.67.133 with SMTP id q127mr2268586wma.71.1507305118142; Fri, 06 Oct 2017 08:51:58 -0700 (PDT) Received: from localhost.localdomain (static.8.26.4.46.clients.your-server.de. [46.4.26.8]) by smtp.gmail.com with ESMTPSA id r21sm1510327wmd.26.2017.10.06.08.51.56 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 06 Oct 2017 08:51:57 -0700 (PDT) From: srinivas.kandagatla@linaro.org To: gregkh@linuxfoundation.org, broonie@kernel.org, alsa-devel@alsa-project.org Cc: sdharia@codeaurora.org, bp@suse.de, poeschel@lemonage.de, treding@nvidia.com, gong.chen@linux.intel.com, andreas.noever@gmail.com, alan@linux.intel.com, mathieu.poirier@linaro.org, daniel@ffwll.ch, jkosina@suse.cz, sharon.dvir1@mail.huji.ac.il, joe@perches.com, davem@davemloft.net, james.hogan@imgtec.com, michael.opdenacker@free-electrons.com, robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, kheitke@audience.com, linux-arm-msm@vger.kernel.org, arnd@arndb.de, Srinivas Kandagatla Subject: [Patch v6 4/7] slimbus: Add support for 'clock-pause' feature Date: Fri, 6 Oct 2017 17:51:33 +0200 Message-Id: <20171006155136.4682-5-srinivas.kandagatla@linaro.org> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20171006155136.4682-1-srinivas.kandagatla@linaro.org> References: <20171006155136.4682-1-srinivas.kandagatla@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Sagar Dharia Per slimbus specification, a reconfiguration sequence known as 'clock pause' needs to be broadcast over the bus while entering low- power mode. Clock-pause is initiated by the controller driver. To exit clock-pause, controller typically wakes up the framer device. Since wakeup precedure is controller-specific, framework calls it via controller's function pointer to invoke it. Signed-off-by: Sagar Dharia Signed-off-by: Srinivas Kandagatla --- drivers/slimbus/Makefile | 2 +- drivers/slimbus/slim-core.c | 15 +++++ drivers/slimbus/slim-messaging.c | 50 ++++++++++++++-- drivers/slimbus/slim-sched.c | 126 +++++++++++++++++++++++++++++++++++++++ include/linux/slimbus.h | 54 ++++++++++++++++- 5 files changed, 239 insertions(+), 8 deletions(-) create mode 100644 drivers/slimbus/slim-sched.c -- 2.9.3 diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile index ed8521a..8fc8b50 100644 --- a/drivers/slimbus/Makefile +++ b/drivers/slimbus/Makefile @@ -2,7 +2,7 @@ # Makefile for kernel slimbus framework. # obj-$(CONFIG_SLIMBUS) += slimbus.o -slimbus-y := slim-core.o slim-messaging.o +slimbus-y := slim-core.o slim-messaging.o slim-sched.o #Controllers obj-$(CONFIG_SLIM_QCOM_CTRL) += slim-qcom-ctrl.o diff --git a/drivers/slimbus/slim-core.c b/drivers/slimbus/slim-core.c index 5b1da28..a5cb31b 100644 --- a/drivers/slimbus/slim-core.c +++ b/drivers/slimbus/slim-core.c @@ -364,6 +364,8 @@ int slim_register_controller(struct slim_controller *ctrl) mutex_init(&ctrl->m_ctrl); spin_lock_init(&ctrl->tx.lock); spin_lock_init(&ctrl->rx.lock); + mutex_init(&ctrl->sched.m_reconf); + init_completion(&ctrl->sched.pause_comp); ctrl->pending_wr = kcalloc((ctrl->tx.n - 1), sizeof(struct slim_pending), @@ -432,6 +434,8 @@ int slim_del_controller(struct slim_controller *ctrl) /* Remove all clients */ device_for_each_child(&ctrl->dev, NULL, slim_ctrl_remove_device); + /* Enter clock pause */ + slim_ctrl_clk_pause(ctrl, false, 0); destroy_workqueue(ctrl->wq); @@ -585,6 +589,14 @@ int slim_assign_laddr(struct slim_controller *ctrl, struct slim_eaddr *e_addr, struct slim_device *slim; struct slim_addrt *temp; + ret = pm_runtime_get_sync(ctrl->dev.parent); + + if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) { + dev_err(&ctrl->dev, "slim ctrl not active,state:%d, ret:%d\n", + ctrl->sched.clk_state, ret); + goto slimbus_not_active; + } + mutex_lock(&ctrl->m_ctrl); /* already assigned */ if (ctrl_getaddr_entry(ctrl, e_addr, &i) == 0) { @@ -655,6 +667,9 @@ int slim_assign_laddr(struct slim_controller *ctrl, struct slim_eaddr *e_addr, } mutex_unlock(&slim->report_lock); } +slimbus_not_active: + pm_runtime_mark_last_busy(ctrl->dev.parent); + pm_runtime_put_autosuspend(ctrl->dev.parent); return ret; } EXPORT_SYMBOL_GPL(slim_assign_laddr); diff --git a/drivers/slimbus/slim-messaging.c b/drivers/slimbus/slim-messaging.c index adcba67..f6a0fa2 100644 --- a/drivers/slimbus/slim-messaging.c +++ b/drivers/slimbus/slim-messaging.c @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ #include +#include #include /** @@ -43,6 +44,9 @@ void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len) memcpy(msg->rbuf, reply, len); if (msg->comp_cb) msg->comp_cb(msg->ctx, 0); + /* Remove runtime-pm vote now that response was received for TID txn */ + pm_runtime_mark_last_busy(ctrl->dev.parent); + pm_runtime_put_autosuspend(ctrl->dev.parent); } EXPORT_SYMBOL_GPL(slim_msg_response); @@ -77,11 +81,21 @@ int slim_processtxn(struct slim_controller *ctrl, int ret, i = 0; unsigned long flags; u8 *buf; - bool async = false; + bool async = false, clk_pause_msg = false; struct slim_cb_data cbd; DECLARE_COMPLETION_ONSTACK(done); bool need_tid = slim_tid_txn(txn->mt, txn->mc); + /* + * do not vote for runtime-PM if the transactions are part of clock + * pause sequence + */ + if (ctrl->sched.clk_state == SLIM_CLK_ENTERING_PAUSE && + (txn->mt == SLIM_MSG_MT_CORE && + txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION && + txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) + clk_pause_msg = true; + if (!txn->msg->comp_cb) { txn->msg->comp_cb = slim_sync_default_cb; cbd.comp = &done; @@ -90,7 +104,7 @@ int slim_processtxn(struct slim_controller *ctrl, async = true; } - buf = slim_get_tx(ctrl, txn, need_tid); + buf = slim_get_tx(ctrl, txn, need_tid, clk_pause_msg); if (!buf) return -ENOMEM; @@ -104,7 +118,8 @@ int slim_processtxn(struct slim_controller *ctrl, if (ctrl->last_tid == (SLIM_MAX_TIDS - 1)) { spin_unlock_irqrestore(&ctrl->txn_lock, flags); slim_return_tx(ctrl, -ENOMEM); - return -ENOMEM; + ret = cbd.ret; + return ret; } ctrl->last_tid++; } @@ -429,6 +444,14 @@ void slim_return_tx(struct slim_controller *ctrl, int err) cur.cb(cur.ctx, err); up(&ctrl->tx_sem); + if (!cur.clk_pause && (!cur.need_tid || err)) { + /** + * remove runtime-pm vote if this was TX only, or + * if there was error during this transaction + */ + pm_runtime_mark_last_busy(ctrl->dev.parent); + pm_runtime_put_autosuspend(ctrl->dev.parent); + } } EXPORT_SYMBOL_GPL(slim_return_tx); @@ -441,15 +464,25 @@ EXPORT_SYMBOL_GPL(slim_return_tx); * back to the pool. */ void *slim_get_tx(struct slim_controller *ctrl, struct slim_msg_txn *txn, - bool need_tid) + bool need_tid, bool clk_pause) { unsigned long flags; int ret, idx; + if (!clk_pause) { + ret = pm_runtime_get_sync(ctrl->dev.parent); + + if (ctrl->sched.clk_state != SLIM_CLK_ACTIVE) { + dev_err(&ctrl->dev, "ctrl wrong state:%d, ret:%d\n", + ctrl->sched.clk_state, ret); + goto slim_tx_err; + } + } + ret = down_interruptible(&ctrl->tx_sem); if (ret < 0) { dev_err(&ctrl->dev, "TX semaphore down returned:%d", ret); - return NULL; + goto slim_tx_err; } spin_lock_irqsave(&ctrl->tx.lock, flags); @@ -457,15 +490,20 @@ void *slim_get_tx(struct slim_controller *ctrl, struct slim_msg_txn *txn, spin_unlock_irqrestore(&ctrl->tx.lock, flags); dev_err(&ctrl->dev, "controller TX buf unavailable"); up(&ctrl->tx_sem); - return NULL; + goto slim_tx_err; } idx = ctrl->tx.tail; ctrl->tx.tail = (ctrl->tx.tail + 1) % ctrl->tx.n; ctrl->pending_wr[idx].cb = txn->msg->comp_cb; ctrl->pending_wr[idx].ctx = txn->msg->ctx; ctrl->pending_wr[idx].need_tid = need_tid; + ctrl->pending_wr[idx].clk_pause = clk_pause; spin_unlock_irqrestore(&ctrl->tx.lock, flags); return ctrl->tx.base + (idx * ctrl->tx.sl_sz); +slim_tx_err: + pm_runtime_mark_last_busy(ctrl->dev.parent); + pm_runtime_put_autosuspend(ctrl->dev.parent); + return NULL; } EXPORT_SYMBOL_GPL(slim_get_tx); diff --git a/drivers/slimbus/slim-sched.c b/drivers/slimbus/slim-sched.c new file mode 100644 index 0000000..3ed075f --- /dev/null +++ b/drivers/slimbus/slim-sched.c @@ -0,0 +1,126 @@ +/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +/** + * slim_ctrl_clk_pause: Called by slimbus controller to enter/exit 'clock pause' + * Slimbus specification needs this sequence to turn-off clocks for the bus. + * The sequence involves sending 3 broadcast messages (reconfiguration + * sequence) to inform all devices on the bus. + * To exit clock-pause, controller typically wakes up active framer device. + * @ctrl: controller requesting bus to be paused or woken up + * @wakeup: Wakeup this controller from clock pause. + * @restart: Restart time value per spec used for clock pause. This value + * isn't used when controller is to be woken up. + * This API executes clock pause reconfiguration sequence if wakeup is false. + * If wakeup is true, controller's wakeup is called. + * For entering clock-pause, -EBUSY is returned if a message txn in pending. + */ +int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart) +{ + int i, ret = 0; + unsigned long flags; + struct slim_sched *sched = &ctrl->sched; + struct slim_val_inf msg = {0, 0, NULL, NULL, NULL, NULL}; + + DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, + 3, SLIM_LA_MANAGER, &msg); + + if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED) + return -EINVAL; + + mutex_lock(&sched->m_reconf); + if (wakeup) { + if (sched->clk_state == SLIM_CLK_ACTIVE) { + mutex_unlock(&sched->m_reconf); + return 0; + } + + /** + * Fine-tune calculation based on clock gear, + * message-bandwidth after bandwidth management + */ + ret = wait_for_completion_timeout(&sched->pause_comp, + msecs_to_jiffies(100)); + if (!ret) { + mutex_unlock(&sched->m_reconf); + pr_err("Previous clock pause did not finish"); + return -ETIMEDOUT; + } + ret = 0; + + /** + * Slimbus framework will call controller wakeup + * Controller should make sure that it sets active framer + * out of clock pause + */ + if (sched->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup) + ret = ctrl->wakeup(ctrl); + if (!ret) + sched->clk_state = SLIM_CLK_ACTIVE; + mutex_unlock(&sched->m_reconf); + + return ret; + } + + /* already paused */ + if (ctrl->sched.clk_state == SLIM_CLK_PAUSED) { + mutex_unlock(&sched->m_reconf); + return 0; + } + + spin_lock_irqsave(&ctrl->txn_lock, flags); + for (i = 0; i < ctrl->last_tid; i++) { + /* Pending response for a message */ + if (ctrl->tid_tbl[i]) { + spin_unlock_irqrestore(&ctrl->txn_lock, flags); + mutex_unlock(&sched->m_reconf); + return -EBUSY; + } + } + spin_unlock_irqrestore(&ctrl->txn_lock, flags); + + sched->clk_state = SLIM_CLK_ENTERING_PAUSE; + + /* clock pause sequence */ + ret = slim_processtxn(ctrl, &txn); + if (ret) + goto clk_pause_ret; + + txn.mc = SLIM_MSG_MC_NEXT_PAUSE_CLOCK; + txn.rl = 4; + msg.num_bytes = 1; + msg.wbuf = &restart; + ret = slim_processtxn(ctrl, &txn); + if (ret) + goto clk_pause_ret; + + txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW; + txn.rl = 3; + msg.num_bytes = 1; + msg.wbuf = NULL; + ret = slim_processtxn(ctrl, &txn); + +clk_pause_ret: + if (ret) { + sched->clk_state = SLIM_CLK_ACTIVE; + } else { + sched->clk_state = SLIM_CLK_PAUSED; + complete(&sched->pause_comp); + } + mutex_unlock(&sched->m_reconf); + + return ret; +} +EXPORT_SYMBOL_GPL(slim_ctrl_clk_pause); diff --git a/include/linux/slimbus.h b/include/linux/slimbus.h index 080d86a..549f78f 100644 --- a/include/linux/slimbus.h +++ b/include/linux/slimbus.h @@ -117,11 +117,21 @@ struct slim_addrt { #define SLIM_MSG_MC_REPLY_VALUE 0x64 #define SLIM_MSG_MC_CHANGE_VALUE 0x68 +/* Clock pause Reconfiguration messages */ +#define SLIM_MSG_MC_BEGIN_RECONFIGURATION 0x40 +#define SLIM_MSG_MC_NEXT_PAUSE_CLOCK 0x4A +#define SLIM_MSG_MC_RECONFIGURE_NOW 0x5F + /* Destination type Values */ #define SLIM_MSG_DEST_LOGICALADDR 0 #define SLIM_MSG_DEST_ENUMADDR 1 #define SLIM_MSG_DEST_BROADCAST 3 +/* Clock pause values per slimbus spec */ +#define SLIM_CLK_FAST 0 +#define SLIM_CLK_CONST_PHASE 1 +#define SLIM_CLK_UNSPECIFIED 2 + /** * struct slim_val_inf: Slimbus value or information element * @start_offset: Specifies starting offset in information/value element map @@ -205,11 +215,46 @@ struct slim_ctrl_buf { * @cb: callback for this transfer * @ctx: contex for the callback function * @need_tid: True if this transfer need Transaction ID + * @clk_pause: True if this transfer is part of clock-pause sequence */ struct slim_pending { void (*cb)(void *ctx, int err); void *ctx; bool need_tid; + bool clk_pause; +}; + +/** + * enum slim_clk_state: Slimbus controller's clock state used internally for + * maintaining current clock state. + * @SLIM_CLK_ACTIVE: Slimbus clock is active + * @SLIM_CLK_ENTERING_PAUSE: Slimbus clock pause sequence is being sent on the + * bus. If this succeeds, state changes to SLIM_CLK_PAUSED. If the + * transition fails, state changes back to SLIM_CLK_ACTIVE + * @SLIM_CLK_PAUSED: Slimbus controller clock has paused. + */ +enum slim_clk_state { + SLIM_CLK_ACTIVE, + SLIM_CLK_ENTERING_PAUSE, + SLIM_CLK_PAUSED, +}; + +/** + * struct slim_sched: Framework uses this structure internally for scheduling. + * @clk_state: Controller's clock state from enum slim_clk_state + * @pause_comp: Signals completion of clock pause sequence. This is useful when + * client tries to call slimbus transaction when controller is entering + * clock pause. + * @m_reconf: This mutex is held until current reconfiguration (data channel + * scheduling, message bandwidth reservation) is done. Message APIs can + * use the bus concurrently when this mutex is held since elemental access + * messages can be sent on the bus when reconfiguration is in progress. + */ +struct slim_sched { + int clkgear; + enum slim_clk_state clk_state; + struct completion pause_comp; + struct mutex m_reconf; }; /** @@ -256,6 +301,7 @@ struct slim_pending { * @pending_wr: Pending write transactions to be acknowledged by controller * @tx_sem: Semaphore for available TX buffers for this controller * @last_tid: Last used entry for TID transactions + * @sched: scheduler structure used by the controller * @xfer_msg: Transfer a message on this controller (this can be a broadcast * control/status message like data channel setup, or a unicast message * like value element read/write. @@ -265,6 +311,9 @@ struct slim_pending { * @get_laddr: It is possible that controller needs to set fixed logical * address table and get_laddr can be used in that case so that controller * can do this assignment. + * @wakeup: This function pointer implements controller-specific procedure + * to wake it up from clock-pause. Framework will call this to bring + * the controller out of clock pause. */ struct slim_controller { struct device dev; @@ -285,12 +334,14 @@ struct slim_controller { struct slim_ctrl_buf rx; struct slim_pending *pending_wr; struct semaphore tx_sem; + struct slim_sched sched; int (*xfer_msg)(struct slim_controller *ctrl, struct slim_msg_txn *tx, void *buf); int (*set_laddr)(struct slim_controller *ctrl, struct slim_eaddr *ea, u8 laddr); int (*get_laddr)(struct slim_controller *ctrl, struct slim_eaddr *ea, u8 *laddr); + int (*wakeup)(struct slim_controller *ctrl); }; #define to_slim_controller(d) container_of(d, struct slim_controller, dev) @@ -444,7 +495,7 @@ int slim_processtxn(struct slim_controller *ctrl, struct slim_msg_txn *txn); void *slim_get_rx(struct slim_controller *ctrl); int slim_return_rx(struct slim_controller *ctrl, void *buf); void *slim_get_tx(struct slim_controller *ctrl, struct slim_msg_txn *txn, - bool need_tid); + bool need_tid, bool clk_pause); void slim_return_tx(struct slim_controller *ctrl, int err); static inline bool slim_tid_txn(u8 mt, u8 mc) @@ -456,5 +507,6 @@ static inline bool slim_tid_txn(u8 mt, u8 mc) mc == SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION)); } /* end of message apis */ +int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart); #endif /* _LINUX_SLIMBUS_H */