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