18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/kernel.h> 68c2ecf20Sopenharmony_ci#include <linux/errno.h> 78c2ecf20Sopenharmony_ci#include <linux/idr.h> 88c2ecf20Sopenharmony_ci#include <linux/slab.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/of_device.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/spmi.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <dt-bindings/spmi/spmi.h> 178c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS 188c2ecf20Sopenharmony_ci#include <trace/events/spmi.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic bool is_registered; 218c2ecf20Sopenharmony_cistatic DEFINE_IDA(ctrl_ida); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void spmi_dev_release(struct device *dev) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct spmi_device *sdev = to_spmi_device(dev); 268c2ecf20Sopenharmony_ci kfree(sdev); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic const struct device_type spmi_dev_type = { 308c2ecf20Sopenharmony_ci .release = spmi_dev_release, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic void spmi_ctrl_release(struct device *dev) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct spmi_controller *ctrl = to_spmi_controller(dev); 368c2ecf20Sopenharmony_ci ida_simple_remove(&ctrl_ida, ctrl->nr); 378c2ecf20Sopenharmony_ci kfree(ctrl); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic const struct device_type spmi_ctrl_type = { 418c2ecf20Sopenharmony_ci .release = spmi_ctrl_release, 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int spmi_device_match(struct device *dev, struct device_driver *drv) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci if (of_driver_match_device(dev, drv)) 478c2ecf20Sopenharmony_ci return 1; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci if (drv->name) 508c2ecf20Sopenharmony_ci return strncmp(dev_name(dev), drv->name, 518c2ecf20Sopenharmony_ci SPMI_NAME_SIZE) == 0; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci return 0; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * spmi_device_add() - add a device previously constructed via spmi_device_alloc() 588c2ecf20Sopenharmony_ci * @sdev: spmi_device to be added 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ciint spmi_device_add(struct spmi_device *sdev) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct spmi_controller *ctrl = sdev->ctrl; 638c2ecf20Sopenharmony_ci int err; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci err = device_add(&sdev->dev); 688c2ecf20Sopenharmony_ci if (err < 0) { 698c2ecf20Sopenharmony_ci dev_err(&sdev->dev, "Can't add %s, status %d\n", 708c2ecf20Sopenharmony_ci dev_name(&sdev->dev), err); 718c2ecf20Sopenharmony_ci goto err_device_add; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev)); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cierr_device_add: 778c2ecf20Sopenharmony_ci return err; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_device_add); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/** 828c2ecf20Sopenharmony_ci * spmi_device_remove(): remove an SPMI device 838c2ecf20Sopenharmony_ci * @sdev: spmi_device to be removed 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_civoid spmi_device_remove(struct spmi_device *sdev) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci device_unregister(&sdev->dev); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_device_remove); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic inline int 928c2ecf20Sopenharmony_cispmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci int ret; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type) 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ret = ctrl->cmd(ctrl, opcode, sid); 1008c2ecf20Sopenharmony_ci trace_spmi_cmd(opcode, sid, ret); 1018c2ecf20Sopenharmony_ci return ret; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode, 1058c2ecf20Sopenharmony_ci u8 sid, u16 addr, u8 *buf, size_t len) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type) 1108c2ecf20Sopenharmony_ci return -EINVAL; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci trace_spmi_read_begin(opcode, sid, addr); 1138c2ecf20Sopenharmony_ci ret = ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len); 1148c2ecf20Sopenharmony_ci trace_spmi_read_end(opcode, sid, addr, ret, len, buf); 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode, 1198c2ecf20Sopenharmony_ci u8 sid, u16 addr, const u8 *buf, size_t len) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci int ret; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type) 1248c2ecf20Sopenharmony_ci return -EINVAL; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci trace_spmi_write_begin(opcode, sid, addr, len, buf); 1278c2ecf20Sopenharmony_ci ret = ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len); 1288c2ecf20Sopenharmony_ci trace_spmi_write_end(opcode, sid, addr, ret); 1298c2ecf20Sopenharmony_ci return ret; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/** 1338c2ecf20Sopenharmony_ci * spmi_register_read() - register read 1348c2ecf20Sopenharmony_ci * @sdev: SPMI device. 1358c2ecf20Sopenharmony_ci * @addr: slave register address (5-bit address). 1368c2ecf20Sopenharmony_ci * @buf: buffer to be populated with data from the Slave. 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * Reads 1 byte of data from a Slave device register. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ciint spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci /* 5-bit register address */ 1438c2ecf20Sopenharmony_ci if (addr > 0x1F) 1448c2ecf20Sopenharmony_ci return -EINVAL; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return spmi_read_cmd(sdev->ctrl, SPMI_CMD_READ, sdev->usid, addr, 1478c2ecf20Sopenharmony_ci buf, 1); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_register_read); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/** 1528c2ecf20Sopenharmony_ci * spmi_ext_register_read() - extended register read 1538c2ecf20Sopenharmony_ci * @sdev: SPMI device. 1548c2ecf20Sopenharmony_ci * @addr: slave register address (8-bit address). 1558c2ecf20Sopenharmony_ci * @buf: buffer to be populated with data from the Slave. 1568c2ecf20Sopenharmony_ci * @len: the request number of bytes to read (up to 16 bytes). 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * Reads up to 16 bytes of data from the extended register space on a 1598c2ecf20Sopenharmony_ci * Slave device. 1608c2ecf20Sopenharmony_ci */ 1618c2ecf20Sopenharmony_ciint spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf, 1628c2ecf20Sopenharmony_ci size_t len) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci /* 8-bit register address, up to 16 bytes */ 1658c2ecf20Sopenharmony_ci if (len == 0 || len > 16) 1668c2ecf20Sopenharmony_ci return -EINVAL; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READ, sdev->usid, addr, 1698c2ecf20Sopenharmony_ci buf, len); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_ext_register_read); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/** 1748c2ecf20Sopenharmony_ci * spmi_ext_register_readl() - extended register read long 1758c2ecf20Sopenharmony_ci * @sdev: SPMI device. 1768c2ecf20Sopenharmony_ci * @addr: slave register address (16-bit address). 1778c2ecf20Sopenharmony_ci * @buf: buffer to be populated with data from the Slave. 1788c2ecf20Sopenharmony_ci * @len: the request number of bytes to read (up to 8 bytes). 1798c2ecf20Sopenharmony_ci * 1808c2ecf20Sopenharmony_ci * Reads up to 8 bytes of data from the extended register space on a 1818c2ecf20Sopenharmony_ci * Slave device using 16-bit address. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ciint spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf, 1848c2ecf20Sopenharmony_ci size_t len) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci /* 16-bit register address, up to 8 bytes */ 1878c2ecf20Sopenharmony_ci if (len == 0 || len > 8) 1888c2ecf20Sopenharmony_ci return -EINVAL; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READL, sdev->usid, addr, 1918c2ecf20Sopenharmony_ci buf, len); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_ext_register_readl); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/** 1968c2ecf20Sopenharmony_ci * spmi_register_write() - register write 1978c2ecf20Sopenharmony_ci * @sdev: SPMI device 1988c2ecf20Sopenharmony_ci * @addr: slave register address (5-bit address). 1998c2ecf20Sopenharmony_ci * @data: buffer containing the data to be transferred to the Slave. 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * Writes 1 byte of data to a Slave device register. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ciint spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci /* 5-bit register address */ 2068c2ecf20Sopenharmony_ci if (addr > 0x1F) 2078c2ecf20Sopenharmony_ci return -EINVAL; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return spmi_write_cmd(sdev->ctrl, SPMI_CMD_WRITE, sdev->usid, addr, 2108c2ecf20Sopenharmony_ci &data, 1); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_register_write); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/** 2158c2ecf20Sopenharmony_ci * spmi_register_zero_write() - register zero write 2168c2ecf20Sopenharmony_ci * @sdev: SPMI device. 2178c2ecf20Sopenharmony_ci * @data: the data to be written to register 0 (7-bits). 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * Writes data to register 0 of the Slave device. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ciint spmi_register_zero_write(struct spmi_device *sdev, u8 data) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci return spmi_write_cmd(sdev->ctrl, SPMI_CMD_ZERO_WRITE, sdev->usid, 0, 2248c2ecf20Sopenharmony_ci &data, 1); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_register_zero_write); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/** 2298c2ecf20Sopenharmony_ci * spmi_ext_register_write() - extended register write 2308c2ecf20Sopenharmony_ci * @sdev: SPMI device. 2318c2ecf20Sopenharmony_ci * @addr: slave register address (8-bit address). 2328c2ecf20Sopenharmony_ci * @buf: buffer containing the data to be transferred to the Slave. 2338c2ecf20Sopenharmony_ci * @len: the request number of bytes to read (up to 16 bytes). 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Writes up to 16 bytes of data to the extended register space of a 2368c2ecf20Sopenharmony_ci * Slave device. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ciint spmi_ext_register_write(struct spmi_device *sdev, u8 addr, const u8 *buf, 2398c2ecf20Sopenharmony_ci size_t len) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci /* 8-bit register address, up to 16 bytes */ 2428c2ecf20Sopenharmony_ci if (len == 0 || len > 16) 2438c2ecf20Sopenharmony_ci return -EINVAL; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITE, sdev->usid, addr, 2468c2ecf20Sopenharmony_ci buf, len); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_ext_register_write); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci/** 2518c2ecf20Sopenharmony_ci * spmi_ext_register_writel() - extended register write long 2528c2ecf20Sopenharmony_ci * @sdev: SPMI device. 2538c2ecf20Sopenharmony_ci * @addr: slave register address (16-bit address). 2548c2ecf20Sopenharmony_ci * @buf: buffer containing the data to be transferred to the Slave. 2558c2ecf20Sopenharmony_ci * @len: the request number of bytes to read (up to 8 bytes). 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * Writes up to 8 bytes of data to the extended register space of a 2588c2ecf20Sopenharmony_ci * Slave device using 16-bit address. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ciint spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, const u8 *buf, 2618c2ecf20Sopenharmony_ci size_t len) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci /* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */ 2648c2ecf20Sopenharmony_ci if (len == 0 || len > 8) 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITEL, sdev->usid, 2688c2ecf20Sopenharmony_ci addr, buf, len); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_ext_register_writel); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/** 2738c2ecf20Sopenharmony_ci * spmi_command_reset() - sends RESET command to the specified slave 2748c2ecf20Sopenharmony_ci * @sdev: SPMI device. 2758c2ecf20Sopenharmony_ci * 2768c2ecf20Sopenharmony_ci * The Reset command initializes the Slave and forces all registers to 2778c2ecf20Sopenharmony_ci * their reset values. The Slave shall enter the STARTUP state after 2788c2ecf20Sopenharmony_ci * receiving a Reset command. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ciint spmi_command_reset(struct spmi_device *sdev) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci return spmi_cmd(sdev->ctrl, SPMI_CMD_RESET, sdev->usid); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_command_reset); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci/** 2878c2ecf20Sopenharmony_ci * spmi_command_sleep() - sends SLEEP command to the specified SPMI device 2888c2ecf20Sopenharmony_ci * @sdev: SPMI device. 2898c2ecf20Sopenharmony_ci * 2908c2ecf20Sopenharmony_ci * The Sleep command causes the Slave to enter the user defined SLEEP state. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ciint spmi_command_sleep(struct spmi_device *sdev) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci return spmi_cmd(sdev->ctrl, SPMI_CMD_SLEEP, sdev->usid); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_command_sleep); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/** 2998c2ecf20Sopenharmony_ci * spmi_command_wakeup() - sends WAKEUP command to the specified SPMI device 3008c2ecf20Sopenharmony_ci * @sdev: SPMI device. 3018c2ecf20Sopenharmony_ci * 3028c2ecf20Sopenharmony_ci * The Wakeup command causes the Slave to move from the SLEEP state to 3038c2ecf20Sopenharmony_ci * the ACTIVE state. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ciint spmi_command_wakeup(struct spmi_device *sdev) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci return spmi_cmd(sdev->ctrl, SPMI_CMD_WAKEUP, sdev->usid); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_command_wakeup); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/** 3128c2ecf20Sopenharmony_ci * spmi_command_shutdown() - sends SHUTDOWN command to the specified SPMI device 3138c2ecf20Sopenharmony_ci * @sdev: SPMI device. 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * The Shutdown command causes the Slave to enter the SHUTDOWN state. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ciint spmi_command_shutdown(struct spmi_device *sdev) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci return spmi_cmd(sdev->ctrl, SPMI_CMD_SHUTDOWN, sdev->usid); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_command_shutdown); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic int spmi_drv_probe(struct device *dev) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); 3268c2ecf20Sopenharmony_ci struct spmi_device *sdev = to_spmi_device(dev); 3278c2ecf20Sopenharmony_ci int err; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci pm_runtime_get_noresume(dev); 3308c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 3318c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci err = sdrv->probe(sdev); 3348c2ecf20Sopenharmony_ci if (err) 3358c2ecf20Sopenharmony_ci goto fail_probe; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cifail_probe: 3408c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 3418c2ecf20Sopenharmony_ci pm_runtime_set_suspended(dev); 3428c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 3438c2ecf20Sopenharmony_ci return err; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int spmi_drv_remove(struct device *dev) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci const struct spmi_driver *sdrv = to_spmi_driver(dev->driver); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 3518c2ecf20Sopenharmony_ci if (sdrv->remove) 3528c2ecf20Sopenharmony_ci sdrv->remove(to_spmi_device(dev)); 3538c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 3568c2ecf20Sopenharmony_ci pm_runtime_set_suspended(dev); 3578c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int spmi_drv_uevent(struct device *dev, struct kobj_uevent_env *env) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci int ret; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = of_device_uevent_modalias(dev, env); 3668c2ecf20Sopenharmony_ci if (ret != -ENODEV) 3678c2ecf20Sopenharmony_ci return ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic struct bus_type spmi_bus_type = { 3738c2ecf20Sopenharmony_ci .name = "spmi", 3748c2ecf20Sopenharmony_ci .match = spmi_device_match, 3758c2ecf20Sopenharmony_ci .probe = spmi_drv_probe, 3768c2ecf20Sopenharmony_ci .remove = spmi_drv_remove, 3778c2ecf20Sopenharmony_ci .uevent = spmi_drv_uevent, 3788c2ecf20Sopenharmony_ci}; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/** 3818c2ecf20Sopenharmony_ci * spmi_controller_alloc() - Allocate a new SPMI device 3828c2ecf20Sopenharmony_ci * @ctrl: associated controller 3838c2ecf20Sopenharmony_ci * 3848c2ecf20Sopenharmony_ci * Caller is responsible for either calling spmi_device_add() to add the 3858c2ecf20Sopenharmony_ci * newly allocated controller, or calling spmi_device_put() to discard it. 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_cistruct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct spmi_device *sdev; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); 3928c2ecf20Sopenharmony_ci if (!sdev) 3938c2ecf20Sopenharmony_ci return NULL; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci sdev->ctrl = ctrl; 3968c2ecf20Sopenharmony_ci device_initialize(&sdev->dev); 3978c2ecf20Sopenharmony_ci sdev->dev.parent = &ctrl->dev; 3988c2ecf20Sopenharmony_ci sdev->dev.bus = &spmi_bus_type; 3998c2ecf20Sopenharmony_ci sdev->dev.type = &spmi_dev_type; 4008c2ecf20Sopenharmony_ci return sdev; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_device_alloc); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci/** 4058c2ecf20Sopenharmony_ci * spmi_controller_alloc() - Allocate a new SPMI controller 4068c2ecf20Sopenharmony_ci * @parent: parent device 4078c2ecf20Sopenharmony_ci * @size: size of private data 4088c2ecf20Sopenharmony_ci * 4098c2ecf20Sopenharmony_ci * Caller is responsible for either calling spmi_controller_add() to add the 4108c2ecf20Sopenharmony_ci * newly allocated controller, or calling spmi_controller_put() to discard it. 4118c2ecf20Sopenharmony_ci * The allocated private data region may be accessed via 4128c2ecf20Sopenharmony_ci * spmi_controller_get_drvdata() 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_cistruct spmi_controller *spmi_controller_alloc(struct device *parent, 4158c2ecf20Sopenharmony_ci size_t size) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci struct spmi_controller *ctrl; 4188c2ecf20Sopenharmony_ci int id; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (WARN_ON(!parent)) 4218c2ecf20Sopenharmony_ci return NULL; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); 4248c2ecf20Sopenharmony_ci if (!ctrl) 4258c2ecf20Sopenharmony_ci return NULL; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci device_initialize(&ctrl->dev); 4288c2ecf20Sopenharmony_ci ctrl->dev.type = &spmi_ctrl_type; 4298c2ecf20Sopenharmony_ci ctrl->dev.bus = &spmi_bus_type; 4308c2ecf20Sopenharmony_ci ctrl->dev.parent = parent; 4318c2ecf20Sopenharmony_ci ctrl->dev.of_node = parent->of_node; 4328c2ecf20Sopenharmony_ci spmi_controller_set_drvdata(ctrl, &ctrl[1]); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); 4358c2ecf20Sopenharmony_ci if (id < 0) { 4368c2ecf20Sopenharmony_ci dev_err(parent, 4378c2ecf20Sopenharmony_ci "unable to allocate SPMI controller identifier.\n"); 4388c2ecf20Sopenharmony_ci spmi_controller_put(ctrl); 4398c2ecf20Sopenharmony_ci return NULL; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ctrl->nr = id; 4438c2ecf20Sopenharmony_ci dev_set_name(&ctrl->dev, "spmi-%d", id); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id); 4468c2ecf20Sopenharmony_ci return ctrl; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_controller_alloc); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic void of_spmi_register_devices(struct spmi_controller *ctrl) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct device_node *node; 4538c2ecf20Sopenharmony_ci int err; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (!ctrl->dev.of_node) 4568c2ecf20Sopenharmony_ci return; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci for_each_available_child_of_node(ctrl->dev.of_node, node) { 4598c2ecf20Sopenharmony_ci struct spmi_device *sdev; 4608c2ecf20Sopenharmony_ci u32 reg[2]; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci dev_dbg(&ctrl->dev, "adding child %pOF\n", node); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci err = of_property_read_u32_array(node, "reg", reg, 2); 4658c2ecf20Sopenharmony_ci if (err) { 4668c2ecf20Sopenharmony_ci dev_err(&ctrl->dev, 4678c2ecf20Sopenharmony_ci "node %pOF err (%d) does not have 'reg' property\n", 4688c2ecf20Sopenharmony_ci node, err); 4698c2ecf20Sopenharmony_ci continue; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (reg[1] != SPMI_USID) { 4738c2ecf20Sopenharmony_ci dev_err(&ctrl->dev, 4748c2ecf20Sopenharmony_ci "node %pOF contains unsupported 'reg' entry\n", 4758c2ecf20Sopenharmony_ci node); 4768c2ecf20Sopenharmony_ci continue; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (reg[0] >= SPMI_MAX_SLAVE_ID) { 4808c2ecf20Sopenharmony_ci dev_err(&ctrl->dev, "invalid usid on node %pOF\n", node); 4818c2ecf20Sopenharmony_ci continue; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci dev_dbg(&ctrl->dev, "read usid %02x\n", reg[0]); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci sdev = spmi_device_alloc(ctrl); 4878c2ecf20Sopenharmony_ci if (!sdev) 4888c2ecf20Sopenharmony_ci continue; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci sdev->dev.of_node = node; 4918c2ecf20Sopenharmony_ci sdev->usid = (u8) reg[0]; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci err = spmi_device_add(sdev); 4948c2ecf20Sopenharmony_ci if (err) { 4958c2ecf20Sopenharmony_ci dev_err(&sdev->dev, 4968c2ecf20Sopenharmony_ci "failure adding device. status %d\n", err); 4978c2ecf20Sopenharmony_ci spmi_device_put(sdev); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/** 5038c2ecf20Sopenharmony_ci * spmi_controller_add() - Add an SPMI controller 5048c2ecf20Sopenharmony_ci * @ctrl: controller to be registered. 5058c2ecf20Sopenharmony_ci * 5068c2ecf20Sopenharmony_ci * Register a controller previously allocated via spmi_controller_alloc() with 5078c2ecf20Sopenharmony_ci * the SPMI core. 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ciint spmi_controller_add(struct spmi_controller *ctrl) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci int ret; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* Can't register until after driver model init */ 5148c2ecf20Sopenharmony_ci if (WARN_ON(!is_registered)) 5158c2ecf20Sopenharmony_ci return -EAGAIN; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci ret = device_add(&ctrl->dev); 5188c2ecf20Sopenharmony_ci if (ret) 5198c2ecf20Sopenharmony_ci return ret; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_OF)) 5228c2ecf20Sopenharmony_ci of_spmi_register_devices(ctrl); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci dev_dbg(&ctrl->dev, "spmi-%d registered: dev:%p\n", 5258c2ecf20Sopenharmony_ci ctrl->nr, &ctrl->dev); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci}; 5298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_controller_add); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci/* Remove a device associated with a controller */ 5328c2ecf20Sopenharmony_cistatic int spmi_ctrl_remove_device(struct device *dev, void *data) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct spmi_device *spmidev = to_spmi_device(dev); 5358c2ecf20Sopenharmony_ci if (dev->type == &spmi_dev_type) 5368c2ecf20Sopenharmony_ci spmi_device_remove(spmidev); 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/** 5418c2ecf20Sopenharmony_ci * spmi_controller_remove(): remove an SPMI controller 5428c2ecf20Sopenharmony_ci * @ctrl: controller to remove 5438c2ecf20Sopenharmony_ci * 5448c2ecf20Sopenharmony_ci * Remove a SPMI controller. Caller is responsible for calling 5458c2ecf20Sopenharmony_ci * spmi_controller_put() to discard the allocated controller. 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_civoid spmi_controller_remove(struct spmi_controller *ctrl) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci int dummy; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (!ctrl) 5528c2ecf20Sopenharmony_ci return; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci dummy = device_for_each_child(&ctrl->dev, NULL, 5558c2ecf20Sopenharmony_ci spmi_ctrl_remove_device); 5568c2ecf20Sopenharmony_ci device_del(&ctrl->dev); 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_controller_remove); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci/** 5618c2ecf20Sopenharmony_ci * spmi_driver_register() - Register client driver with SPMI core 5628c2ecf20Sopenharmony_ci * @sdrv: client driver to be associated with client-device. 5638c2ecf20Sopenharmony_ci * 5648c2ecf20Sopenharmony_ci * This API will register the client driver with the SPMI framework. 5658c2ecf20Sopenharmony_ci * It is typically called from the driver's module-init function. 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ciint __spmi_driver_register(struct spmi_driver *sdrv, struct module *owner) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci sdrv->driver.bus = &spmi_bus_type; 5708c2ecf20Sopenharmony_ci sdrv->driver.owner = owner; 5718c2ecf20Sopenharmony_ci return driver_register(&sdrv->driver); 5728c2ecf20Sopenharmony_ci} 5738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__spmi_driver_register); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic void __exit spmi_exit(void) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci bus_unregister(&spmi_bus_type); 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_cimodule_exit(spmi_exit); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int __init spmi_init(void) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci int ret; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci ret = bus_register(&spmi_bus_type); 5868c2ecf20Sopenharmony_ci if (ret) 5878c2ecf20Sopenharmony_ci return ret; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci is_registered = true; 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_cipostcore_initcall(spmi_init); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SPMI module"); 5968c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:spmi"); 597