diff options
Diffstat (limited to 'target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch')
-rw-r--r-- | target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch | 943 |
1 files changed, 943 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch b/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch new file mode 100644 index 0000000..8b59438 --- /dev/null +++ b/target/linux/ipq806x/patches/0055-spmi-Linux-driver-framework-for-SPMI.patch @@ -0,0 +1,943 @@ +From 1b0018dfd6295cbcc87738601b84bf49f3004419 Mon Sep 17 00:00:00 2001 +From: Kenneth Heitke <kheitke@codeaurora.org> +Date: Wed, 12 Feb 2014 13:44:22 -0600 +Subject: [PATCH 055/182] spmi: Linux driver framework for SPMI + +System Power Management Interface (SPMI) is a specification +developed by the MIPI (Mobile Industry Process Interface) Alliance +optimized for the real time control of Power Management ICs (PMIC). + +SPMI is a two-wire serial interface that supports up to 4 master +devices and up to 16 logical slaves. + +The framework supports message APIs, multiple busses (1 controller +per bus) and multiple clients/slave devices per controller. + +Signed-off-by: Kenneth Heitke <kheitke@codeaurora.org> +Signed-off-by: Michael Bohan <mbohan@codeaurora.org> +Signed-off-by: Josh Cartwright <joshc@codeaurora.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> +--- + drivers/Kconfig | 2 + + drivers/Makefile | 1 + + drivers/spmi/Kconfig | 9 + + drivers/spmi/Makefile | 4 + + drivers/spmi/spmi.c | 609 +++++++++++++++++++++++++++++++++++++++ + include/dt-bindings/spmi/spmi.h | 18 ++ + include/linux/mod_devicetable.h | 8 + + include/linux/spmi.h | 191 ++++++++++++ + 8 files changed, 842 insertions(+) + create mode 100644 drivers/spmi/Kconfig + create mode 100644 drivers/spmi/Makefile + create mode 100644 drivers/spmi/spmi.c + create mode 100644 include/dt-bindings/spmi/spmi.h + create mode 100644 include/linux/spmi.h + +diff --git a/drivers/Kconfig b/drivers/Kconfig +index b3138fb..e0a4ae6 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig" + + source "drivers/spi/Kconfig" + ++source "drivers/spmi/Kconfig" ++ + source "drivers/hsi/Kconfig" + + source "drivers/pps/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index 8e3b8b0..3d6de8b 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -66,6 +66,7 @@ obj-$(CONFIG_ATA) += ata/ + obj-$(CONFIG_TARGET_CORE) += target/ + obj-$(CONFIG_MTD) += mtd/ + obj-$(CONFIG_SPI) += spi/ ++obj-$(CONFIG_SPMI) += spmi/ + obj-y += hsi/ + obj-y += net/ + obj-$(CONFIG_ATM) += atm/ +diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig +new file mode 100644 +index 0000000..1dbfee0 +--- /dev/null ++++ b/drivers/spmi/Kconfig +@@ -0,0 +1,9 @@ ++# ++# SPMI driver configuration ++# ++menuconfig SPMI ++ tristate "SPMI support" ++ help ++ SPMI (System Power Management Interface) is a two-wire ++ serial interface between baseband and application processors ++ and Power Management Integrated Circuits (PMIC). +diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile +new file mode 100644 +index 0000000..1de1acd +--- /dev/null ++++ b/drivers/spmi/Makefile +@@ -0,0 +1,4 @@ ++# ++# Makefile for kernel SPMI framework. ++# ++obj-$(CONFIG_SPMI) += spmi.o +diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c +new file mode 100644 +index 0000000..6122c8f +--- /dev/null ++++ b/drivers/spmi/spmi.c +@@ -0,0 +1,609 @@ ++/* Copyright (c) 2012-2013, 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 <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/idr.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_device.h> ++#include <linux/platform_device.h> ++#include <linux/spmi.h> ++#include <linux/module.h> ++#include <linux/pm_runtime.h> ++ ++#include <dt-bindings/spmi/spmi.h> ++ ++static DEFINE_IDA(ctrl_ida); ++ ++static void spmi_dev_release(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ kfree(sdev); ++} ++ ++static const struct device_type spmi_dev_type = { ++ .release = spmi_dev_release, ++}; ++ ++static void spmi_ctrl_release(struct device *dev) ++{ ++ struct spmi_controller *ctrl = to_spmi_controller(dev); ++ ida_simple_remove(&ctrl_ida, ctrl->nr); ++ kfree(ctrl); ++} ++ ++static const struct device_type spmi_ctrl_type = { ++ .release = spmi_ctrl_release, ++}; ++ ++#ifdef CONFIG_PM_RUNTIME ++static int spmi_runtime_suspend(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ err = pm_generic_runtime_suspend(dev); ++ if (err) ++ return err; ++ ++ return spmi_command_sleep(sdev); ++} ++ ++static int spmi_runtime_resume(struct device *dev) ++{ ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ err = spmi_command_wakeup(sdev); ++ if (err) ++ return err; ++ ++ return pm_generic_runtime_resume(dev); ++} ++#endif ++ ++static const struct dev_pm_ops spmi_pm_ops = { ++ SET_RUNTIME_PM_OPS( ++ spmi_runtime_suspend, ++ spmi_runtime_resume, ++ NULL ++ ) ++}; ++ ++static int spmi_device_match(struct device *dev, struct device_driver *drv) ++{ ++ if (of_driver_match_device(dev, drv)) ++ return 1; ++ ++ if (drv->name) ++ return strncmp(dev_name(dev), drv->name, ++ SPMI_NAME_SIZE) == 0; ++ ++ return 0; ++} ++ ++/** ++ * spmi_device_add() - add a device previously constructed via spmi_device_alloc() ++ * @sdev: spmi_device to be added ++ */ ++int spmi_device_add(struct spmi_device *sdev) ++{ ++ struct spmi_controller *ctrl = sdev->ctrl; ++ int err; ++ ++ dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid); ++ ++ err = device_add(&sdev->dev); ++ if (err < 0) { ++ dev_err(&sdev->dev, "Can't add %s, status %d\n", ++ dev_name(&sdev->dev), err); ++ goto err_device_add; ++ } ++ ++ dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev)); ++ ++err_device_add: ++ return err; ++} ++EXPORT_SYMBOL_GPL(spmi_device_add); ++ ++/** ++ * spmi_device_remove(): remove an SPMI device ++ * @sdev: spmi_device to be removed ++ */ ++void spmi_device_remove(struct spmi_device *sdev) ++{ ++ device_unregister(&sdev->dev); ++} ++EXPORT_SYMBOL_GPL(spmi_device_remove); ++ ++static inline int ++spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid) ++{ ++ if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->cmd(ctrl, opcode, sid); ++} ++ ++static inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, u8 *buf, size_t len) ++{ ++ if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len); ++} ++ ++static inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, const u8 *buf, size_t len) ++{ ++ if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type) ++ return -EINVAL; ++ ++ return ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len); ++} ++ ++/** ++ * spmi_register_read() - register read ++ * @sdev: SPMI device. ++ * @addr: slave register address (5-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * ++ * Reads 1 byte of data from a Slave device register. ++ */ ++int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf) ++{ ++ /* 5-bit register address */ ++ if (addr > 0x1F) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_READ, sdev->usid, addr, ++ buf, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_read); ++ ++/** ++ * spmi_ext_register_read() - extended register read ++ * @sdev: SPMI device. ++ * @addr: slave register address (8-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * @len: the request number of bytes to read (up to 16 bytes). ++ * ++ * Reads up to 16 bytes of data from the extended register space on a ++ * Slave device. ++ */ ++int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, ++ size_t len) ++{ ++ /* 8-bit register address, up to 16 bytes */ ++ if (len == 0 || len > 16) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READ, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_read); ++ ++/** ++ * spmi_ext_register_readl() - extended register read long ++ * @sdev: SPMI device. ++ * @addr: slave register address (16-bit address). ++ * @buf: buffer to be populated with data from the Slave. ++ * @len: the request number of bytes to read (up to 8 bytes). ++ * ++ * Reads up to 8 bytes of data from the extended register space on a ++ * Slave device using 16-bit address. ++ */ ++int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf, ++ size_t len) ++{ ++ /* 16-bit register address, up to 8 bytes */ ++ if (len == 0 || len > 8) ++ return -EINVAL; ++ ++ return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READL, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_readl); ++ ++/** ++ * spmi_register_write() - register write ++ * @sdev: SPMI device ++ * @addr: slave register address (5-bit address). ++ * @data: buffer containing the data to be transferred to the Slave. ++ * ++ * Writes 1 byte of data to a Slave device register. ++ */ ++int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data) ++{ ++ /* 5-bit register address */ ++ if (addr > 0x1F) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_WRITE, sdev->usid, addr, ++ &data, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_write); ++ ++/** ++ * spmi_register_zero_write() - register zero write ++ * @sdev: SPMI device. ++ * @data: the data to be written to register 0 (7-bits). ++ * ++ * Writes data to register 0 of the Slave device. ++ */ ++int spmi_register_zero_write(struct spmi_device *sdev, u8 data) ++{ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_ZERO_WRITE, sdev->usid, 0, ++ &data, 1); ++} ++EXPORT_SYMBOL_GPL(spmi_register_zero_write); ++ ++/** ++ * spmi_ext_register_write() - extended register write ++ * @sdev: SPMI device. ++ * @addr: slave register address (8-bit address). ++ * @buf: buffer containing the data to be transferred to the Slave. ++ * @len: the request number of bytes to read (up to 16 bytes). ++ * ++ * Writes up to 16 bytes of data to the extended register space of a ++ * Slave device. ++ */ ++int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, const u8 *buf, ++ size_t len) ++{ ++ /* 8-bit register address, up to 16 bytes */ ++ if (len == 0 || len > 16) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITE, sdev->usid, addr, ++ buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_write); ++ ++/** ++ * spmi_ext_register_writel() - extended register write long ++ * @sdev: SPMI device. ++ * @addr: slave register address (16-bit address). ++ * @buf: buffer containing the data to be transferred to the Slave. ++ * @len: the request number of bytes to read (up to 8 bytes). ++ * ++ * Writes up to 8 bytes of data to the extended register space of a ++ * Slave device using 16-bit address. ++ */ ++int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, const u8 *buf, ++ size_t len) ++{ ++ /* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */ ++ if (len == 0 || len > 8) ++ return -EINVAL; ++ ++ return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITEL, sdev->usid, ++ addr, buf, len); ++} ++EXPORT_SYMBOL_GPL(spmi_ext_register_writel); ++ ++/** ++ * spmi_command_reset() - sends RESET command to the specified slave ++ * @sdev: SPMI device. ++ * ++ * The Reset command initializes the Slave and forces all registers to ++ * their reset values. The Slave shall enter the STARTUP state after ++ * receiving a Reset command. ++ */ ++int spmi_command_reset(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_RESET, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_reset); ++ ++/** ++ * spmi_command_sleep() - sends SLEEP command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Sleep command causes the Slave to enter the user defined SLEEP state. ++ */ ++int spmi_command_sleep(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_SLEEP, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_sleep); ++ ++/** ++ * spmi_command_wakeup() - sends WAKEUP command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Wakeup command causes the Slave to move from the SLEEP state to ++ * the ACTIVE state. ++ */ ++int spmi_command_wakeup(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_WAKEUP, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_wakeup); ++ ++/** ++ * spmi_command_shutdown() - sends SHUTDOWN command to the specified SPMI device ++ * @sdev: SPMI device. ++ * ++ * The Shutdown command causes the Slave to enter the SHUTDOWN state. ++ */ ++int spmi_command_shutdown(struct spmi_device *sdev) ++{ ++ return spmi_cmd(sdev->ctrl, SPMI_CMD_SHUTDOWN, sdev->usid); ++} ++EXPORT_SYMBOL_GPL(spmi_command_shutdown); ++ ++static int spmi_drv_probe(struct device *dev) ++{ ++ const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); ++ struct spmi_device *sdev = to_spmi_device(dev); ++ int err; ++ ++ /* Ensure the slave is in ACTIVE state */ ++ err = spmi_command_wakeup(sdev); ++ if (err) ++ goto fail_wakeup; ++ ++ pm_runtime_get_noresume(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ err = sdrv->probe(sdev); ++ if (err) ++ goto fail_probe; ++ ++ return 0; ++ ++fail_probe: ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++fail_wakeup: ++ return err; ++} ++ ++static int spmi_drv_remove(struct device *dev) ++{ ++ const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); ++ ++ pm_runtime_get_sync(dev); ++ sdrv->remove(to_spmi_device(dev)); ++ pm_runtime_put_noidle(dev); ++ ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++ return 0; ++} ++ ++static struct bus_type spmi_bus_type = { ++ .name = "spmi", ++ .match = spmi_device_match, ++ .pm = &spmi_pm_ops, ++ .probe = spmi_drv_probe, ++ .remove = spmi_drv_remove, ++}; ++ ++/** ++ * spmi_controller_alloc() - Allocate a new SPMI device ++ * @ctrl: associated controller ++ * ++ * Caller is responsible for either calling spmi_device_add() to add the ++ * newly allocated controller, or calling spmi_device_put() to discard it. ++ */ ++struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl) ++{ ++ struct spmi_device *sdev; ++ ++ sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); ++ if (!sdev) ++ return NULL; ++ ++ sdev->ctrl = ctrl; ++ device_initialize(&sdev->dev); ++ sdev->dev.parent = &ctrl->dev; ++ sdev->dev.bus = &spmi_bus_type; ++ sdev->dev.type = &spmi_dev_type; ++ return sdev; ++} ++EXPORT_SYMBOL_GPL(spmi_device_alloc); ++ ++/** ++ * spmi_controller_alloc() - Allocate a new SPMI controller ++ * @parent: parent device ++ * @size: size of private data ++ * ++ * Caller is responsible for either calling spmi_controller_add() to add the ++ * newly allocated controller, or calling spmi_controller_put() to discard it. ++ * The allocated private data region may be accessed via ++ * spmi_controller_get_drvdata() ++ */ ++struct spmi_controller *spmi_controller_alloc(struct device *parent, ++ size_t size) ++{ ++ struct spmi_controller *ctrl; ++ int id; ++ ++ if (WARN_ON(!parent)) ++ return NULL; ++ ++ ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); ++ if (!ctrl) ++ return NULL; ++ ++ device_initialize(&ctrl->dev); ++ ctrl->dev.type = &spmi_ctrl_type; ++ ctrl->dev.bus = &spmi_bus_type; ++ ctrl->dev.parent = parent; ++ ctrl->dev.of_node = parent->of_node; ++ spmi_controller_set_drvdata(ctrl, &ctrl[1]); ++ ++ id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); ++ if (id < 0) { ++ dev_err(parent, ++ "unable to allocate SPMI controller identifier.\n"); ++ spmi_controller_put(ctrl); ++ return NULL; ++ } ++ ++ ctrl->nr = id; ++ dev_set_name(&ctrl->dev, "spmi-%d", id); ++ ++ dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); ++ return ctrl; ++} ++EXPORT_SYMBOL_GPL(spmi_controller_alloc); ++ ++static void of_spmi_register_devices(struct spmi_controller *ctrl) ++{ ++ struct device_node *node; ++ int err; ++ ++ if (!ctrl->dev.of_node) ++ return; ++ ++ for_each_available_child_of_node(ctrl->dev.of_node, node) { ++ struct spmi_device *sdev; ++ u32 reg[2]; ++ ++ dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name); ++ ++ err = of_property_read_u32_array(node, "reg", reg, 2); ++ if (err) { ++ dev_err(&ctrl->dev, ++ "node %s err (%d) does not have 'reg' property\n", ++ node->full_name, err); ++ continue; ++ } ++ ++ if (reg[1] != SPMI_USID) { ++ dev_err(&ctrl->dev, ++ "node %s contains unsupported 'reg' entry\n", ++ node->full_name); ++ continue; ++ } ++ ++ if (reg[0] >= SPMI_MAX_SLAVE_ID) { ++ dev_err(&ctrl->dev, ++ "invalid usid on node %s\n", ++ node->full_name); ++ continue; ++ } ++ ++ dev_dbg(&ctrl->dev, "read usid %02x\n", reg[0]); ++ ++ sdev = spmi_device_alloc(ctrl); ++ if (!sdev) ++ continue; ++ ++ sdev->dev.of_node = node; ++ sdev->usid = (u8) reg[0]; ++ ++ err = spmi_device_add(sdev); ++ if (err) { ++ dev_err(&sdev->dev, ++ "failure adding device. status %d\n", err); ++ spmi_device_put(sdev); ++ } ++ } ++} ++ ++/** ++ * spmi_controller_add() - Add an SPMI controller ++ * @ctrl: controller to be registered. ++ * ++ * Register a controller previously allocated via spmi_controller_alloc() with ++ * the SPMI core. ++ */ ++int spmi_controller_add(struct spmi_controller *ctrl) ++{ ++ int ret; ++ ++ /* Can't register until after driver model init */ ++ if (WARN_ON(!spmi_bus_type.p)) ++ return -EAGAIN; ++ ++ ret = device_add(&ctrl->dev); ++ if (ret) ++ return ret; ++ ++ if (IS_ENABLED(CONFIG_OF)) ++ of_spmi_register_devices(ctrl); ++ ++ dev_dbg(&ctrl->dev, "spmi-%d registered: dev:%p\n", ++ ctrl->nr, &ctrl->dev); ++ ++ return 0; ++}; ++EXPORT_SYMBOL_GPL(spmi_controller_add); ++ ++/* Remove a device associated with a controller */ ++static int spmi_ctrl_remove_device(struct device *dev, void *data) ++{ ++ struct spmi_device *spmidev = to_spmi_device(dev); ++ if (dev->type == &spmi_dev_type) ++ spmi_device_remove(spmidev); ++ return 0; ++} ++ ++/** ++ * spmi_controller_remove(): remove an SPMI controller ++ * @ctrl: controller to remove ++ * ++ * Remove a SPMI controller. Caller is responsible for calling ++ * spmi_controller_put() to discard the allocated controller. ++ */ ++void spmi_controller_remove(struct spmi_controller *ctrl) ++{ ++ int dummy; ++ ++ if (!ctrl) ++ return; ++ ++ dummy = device_for_each_child(&ctrl->dev, NULL, ++ spmi_ctrl_remove_device); ++ device_del(&ctrl->dev); ++} ++EXPORT_SYMBOL_GPL(spmi_controller_remove); ++ ++/** ++ * spmi_driver_register() - Register client driver with SPMI core ++ * @sdrv: client driver to be associated with client-device. ++ * ++ * This API will register the client driver with the SPMI framework. ++ * It is typically called from the driver's module-init function. ++ */ ++int spmi_driver_register(struct spmi_driver *sdrv) ++{ ++ sdrv->driver.bus = &spmi_bus_type; ++ return driver_register(&sdrv->driver); ++} ++EXPORT_SYMBOL_GPL(spmi_driver_register); ++ ++static void __exit spmi_exit(void) ++{ ++ bus_unregister(&spmi_bus_type); ++} ++module_exit(spmi_exit); ++ ++static int __init spmi_init(void) ++{ ++ return bus_register(&spmi_bus_type); ++} ++postcore_initcall(spmi_init); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("SPMI module"); ++MODULE_ALIAS("platform:spmi"); +diff --git a/include/dt-bindings/spmi/spmi.h b/include/dt-bindings/spmi/spmi.h +new file mode 100644 +index 0000000..d11e1e5 +--- /dev/null ++++ b/include/dt-bindings/spmi/spmi.h +@@ -0,0 +1,18 @@ ++/* Copyright (c) 2013, 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 __DT_BINDINGS_SPMI_H ++#define __DT_BINDINGS_SPMI_H ++ ++#define SPMI_USID 0 ++#define SPMI_GSID 1 ++ ++#endif +diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h +index 45e9214..677e474 100644 +--- a/include/linux/mod_devicetable.h ++++ b/include/linux/mod_devicetable.h +@@ -432,6 +432,14 @@ struct spi_device_id { + kernel_ulong_t driver_data; /* Data private to the driver */ + }; + ++#define SPMI_NAME_SIZE 32 ++#define SPMI_MODULE_PREFIX "spmi:" ++ ++struct spmi_device_id { ++ char name[SPMI_NAME_SIZE]; ++ kernel_ulong_t driver_data; /* Data private to the driver */ ++}; ++ + /* dmi */ + enum dmi_field { + DMI_NONE, +diff --git a/include/linux/spmi.h b/include/linux/spmi.h +new file mode 100644 +index 0000000..91f5eab +--- /dev/null ++++ b/include/linux/spmi.h +@@ -0,0 +1,191 @@ ++/* Copyright (c) 2012-2013, 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_SPMI_H ++#define _LINUX_SPMI_H ++ ++#include <linux/types.h> ++#include <linux/device.h> ++#include <linux/mod_devicetable.h> ++ ++/* Maximum slave identifier */ ++#define SPMI_MAX_SLAVE_ID 16 ++ ++/* SPMI Commands */ ++#define SPMI_CMD_EXT_WRITE 0x00 ++#define SPMI_CMD_RESET 0x10 ++#define SPMI_CMD_SLEEP 0x11 ++#define SPMI_CMD_SHUTDOWN 0x12 ++#define SPMI_CMD_WAKEUP 0x13 ++#define SPMI_CMD_AUTHENTICATE 0x14 ++#define SPMI_CMD_MSTR_READ 0x15 ++#define SPMI_CMD_MSTR_WRITE 0x16 ++#define SPMI_CMD_TRANSFER_BUS_OWNERSHIP 0x1A ++#define SPMI_CMD_DDB_MASTER_READ 0x1B ++#define SPMI_CMD_DDB_SLAVE_READ 0x1C ++#define SPMI_CMD_EXT_READ 0x20 ++#define SPMI_CMD_EXT_WRITEL 0x30 ++#define SPMI_CMD_EXT_READL 0x38 ++#define SPMI_CMD_WRITE 0x40 ++#define SPMI_CMD_READ 0x60 ++#define SPMI_CMD_ZERO_WRITE 0x80 ++ ++/** ++ * struct spmi_device - Basic representation of an SPMI device ++ * @dev: Driver model representation of the device. ++ * @ctrl: SPMI controller managing the bus hosting this device. ++ * @usid: This devices' Unique Slave IDentifier. ++ */ ++struct spmi_device { ++ struct device dev; ++ struct spmi_controller *ctrl; ++ u8 usid; ++}; ++ ++static inline struct spmi_device *to_spmi_device(struct device *d) ++{ ++ return container_of(d, struct spmi_device, dev); ++} ++ ++static inline void *spmi_device_get_drvdata(const struct spmi_device *sdev) ++{ ++ return dev_get_drvdata(&sdev->dev); ++} ++ ++static inline void spmi_device_set_drvdata(struct spmi_device *sdev, void *data) ++{ ++ dev_set_drvdata(&sdev->dev, data); ++} ++ ++struct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl); ++ ++static inline void spmi_device_put(struct spmi_device *sdev) ++{ ++ if (sdev) ++ put_device(&sdev->dev); ++} ++ ++int spmi_device_add(struct spmi_device *sdev); ++ ++void spmi_device_remove(struct spmi_device *sdev); ++ ++/** ++ * struct spmi_controller - interface to the SPMI master controller ++ * @dev: Driver model representation of the device. ++ * @nr: board-specific number identifier for this controller/bus ++ * @cmd: sends a non-data command sequence on the SPMI bus. ++ * @read_cmd: sends a register read command sequence on the SPMI bus. ++ * @write_cmd: sends a register write command sequence on the SPMI bus. ++ */ ++struct spmi_controller { ++ struct device dev; ++ unsigned int nr; ++ int (*cmd)(struct spmi_controller *ctrl, u8 opcode, u8 sid); ++ int (*read_cmd)(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, u8 *buf, size_t len); ++ int (*write_cmd)(struct spmi_controller *ctrl, u8 opcode, ++ u8 sid, u16 addr, const u8 *buf, size_t len); ++}; ++ ++static inline struct spmi_controller *to_spmi_controller(struct device *d) ++{ ++ return container_of(d, struct spmi_controller, dev); ++} ++ ++static inline ++void *spmi_controller_get_drvdata(const struct spmi_controller *ctrl) ++{ ++ return dev_get_drvdata(&ctrl->dev); ++} ++ ++static inline void spmi_controller_set_drvdata(struct spmi_controller *ctrl, ++ void *data) ++{ ++ dev_set_drvdata(&ctrl->dev, data); ++} ++ ++struct spmi_controller *spmi_controller_alloc(struct device *parent, ++ size_t size); ++ ++/** ++ * spmi_controller_put() - decrement controller refcount ++ * @ctrl SPMI controller. ++ */ ++static inline void spmi_controller_put(struct spmi_controller *ctrl) ++{ ++ if (ctrl) ++ put_device(&ctrl->dev); ++} ++ ++int spmi_controller_add(struct spmi_controller *ctrl); ++void spmi_controller_remove(struct spmi_controller *ctrl); ++ ++/** ++ * struct spmi_driver - SPMI slave device driver ++ * @driver: SPMI device drivers should initialize name and owner field of ++ * this structure. ++ * @probe: binds this driver to a SPMI device. ++ * @remove: unbinds this driver from the SPMI 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. ++ * ++ * If PM runtime support is desired for a slave, a device driver can call ++ * pm_runtime_put() from their probe() routine (and a balancing ++ * pm_runtime_get() in remove()). PM runtime support for a slave is ++ * implemented by issuing a SLEEP command to the slave on runtime_suspend(), ++ * transitioning the slave into the SLEEP state. On runtime_resume(), a WAKEUP ++ * command is sent to the slave to bring it back to ACTIVE. ++ */ ++struct spmi_driver { ++ struct device_driver driver; ++ int (*probe)(struct spmi_device *sdev); ++ void (*remove)(struct spmi_device *sdev); ++}; ++ ++static inline struct spmi_driver *to_spmi_driver(struct device_driver *d) ++{ ++ return container_of(d, struct spmi_driver, driver); ++} ++ ++int spmi_driver_register(struct spmi_driver *sdrv); ++ ++/** ++ * spmi_driver_unregister() - unregister an SPMI client driver ++ * @sdrv: the driver to unregister ++ */ ++static inline void spmi_driver_unregister(struct spmi_driver *sdrv) ++{ ++ if (sdrv) ++ driver_unregister(&sdrv->driver); ++} ++ ++#define module_spmi_driver(__spmi_driver) \ ++ module_driver(__spmi_driver, spmi_driver_register, \ ++ spmi_driver_unregister) ++ ++int spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf); ++int spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, ++ size_t len); ++int spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf, ++ size_t len); ++int spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data); ++int spmi_register_zero_write(struct spmi_device *sdev, u8 data); ++int spmi_ext_register_write(struct spmi_device *sdev, u8 addr, ++ const u8 *buf, size_t len); ++int spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, ++ const u8 *buf, size_t len); ++int spmi_command_reset(struct spmi_device *sdev); ++int spmi_command_sleep(struct spmi_device *sdev); ++int spmi_command_wakeup(struct spmi_device *sdev); ++int spmi_command_shutdown(struct spmi_device *sdev); ++ ++#endif +-- +1.7.10.4 + |