162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/errno.h>
762306a36Sopenharmony_ci#include <linux/idr.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/of_device.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/spmi.h>
1462306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <dt-bindings/spmi/spmi.h>
1762306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
1862306a36Sopenharmony_ci#include <trace/events/spmi.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic bool is_registered;
2162306a36Sopenharmony_cistatic DEFINE_IDA(ctrl_ida);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic void spmi_dev_release(struct device *dev)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct spmi_device *sdev = to_spmi_device(dev);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	kfree(sdev);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic const struct device_type spmi_dev_type = {
3162306a36Sopenharmony_ci	.release	= spmi_dev_release,
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic void spmi_ctrl_release(struct device *dev)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct spmi_controller *ctrl = to_spmi_controller(dev);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	ida_free(&ctrl_ida, ctrl->nr);
3962306a36Sopenharmony_ci	kfree(ctrl);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic const struct device_type spmi_ctrl_type = {
4362306a36Sopenharmony_ci	.release	= spmi_ctrl_release,
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic int spmi_device_match(struct device *dev, struct device_driver *drv)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	if (of_driver_match_device(dev, drv))
4962306a36Sopenharmony_ci		return 1;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (drv->name)
5262306a36Sopenharmony_ci		return strncmp(dev_name(dev), drv->name,
5362306a36Sopenharmony_ci			       SPMI_NAME_SIZE) == 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/**
5962306a36Sopenharmony_ci * spmi_device_add() - add a device previously constructed via spmi_device_alloc()
6062306a36Sopenharmony_ci * @sdev:	spmi_device to be added
6162306a36Sopenharmony_ci */
6262306a36Sopenharmony_ciint spmi_device_add(struct spmi_device *sdev)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct spmi_controller *ctrl = sdev->ctrl;
6562306a36Sopenharmony_ci	int err;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	dev_set_name(&sdev->dev, "%d-%02x", ctrl->nr, sdev->usid);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	err = device_add(&sdev->dev);
7062306a36Sopenharmony_ci	if (err < 0) {
7162306a36Sopenharmony_ci		dev_err(&sdev->dev, "Can't add %s, status %d\n",
7262306a36Sopenharmony_ci			dev_name(&sdev->dev), err);
7362306a36Sopenharmony_ci		goto err_device_add;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	dev_dbg(&sdev->dev, "device %s registered\n", dev_name(&sdev->dev));
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cierr_device_add:
7962306a36Sopenharmony_ci	return err;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_device_add);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/**
8462306a36Sopenharmony_ci * spmi_device_remove(): remove an SPMI device
8562306a36Sopenharmony_ci * @sdev:	spmi_device to be removed
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_civoid spmi_device_remove(struct spmi_device *sdev)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	device_unregister(&sdev->dev);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_device_remove);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic inline int
9462306a36Sopenharmony_cispmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int ret;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type)
9962306a36Sopenharmony_ci		return -EINVAL;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	ret = ctrl->cmd(ctrl, opcode, sid);
10262306a36Sopenharmony_ci	trace_spmi_cmd(opcode, sid, ret);
10362306a36Sopenharmony_ci	return ret;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode,
10762306a36Sopenharmony_ci				u8 sid, u16 addr, u8 *buf, size_t len)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int ret;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type)
11262306a36Sopenharmony_ci		return -EINVAL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	trace_spmi_read_begin(opcode, sid, addr);
11562306a36Sopenharmony_ci	ret = ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len);
11662306a36Sopenharmony_ci	trace_spmi_read_end(opcode, sid, addr, ret, len, buf);
11762306a36Sopenharmony_ci	return ret;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode,
12162306a36Sopenharmony_ci				 u8 sid, u16 addr, const u8 *buf, size_t len)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	int ret;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type)
12662306a36Sopenharmony_ci		return -EINVAL;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	trace_spmi_write_begin(opcode, sid, addr, len, buf);
12962306a36Sopenharmony_ci	ret = ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len);
13062306a36Sopenharmony_ci	trace_spmi_write_end(opcode, sid, addr, ret);
13162306a36Sopenharmony_ci	return ret;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/**
13562306a36Sopenharmony_ci * spmi_register_read() - register read
13662306a36Sopenharmony_ci * @sdev:	SPMI device.
13762306a36Sopenharmony_ci * @addr:	slave register address (5-bit address).
13862306a36Sopenharmony_ci * @buf:	buffer to be populated with data from the Slave.
13962306a36Sopenharmony_ci *
14062306a36Sopenharmony_ci * Reads 1 byte of data from a Slave device register.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_ciint spmi_register_read(struct spmi_device *sdev, u8 addr, u8 *buf)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	/* 5-bit register address */
14562306a36Sopenharmony_ci	if (addr > 0x1F)
14662306a36Sopenharmony_ci		return -EINVAL;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return spmi_read_cmd(sdev->ctrl, SPMI_CMD_READ, sdev->usid, addr,
14962306a36Sopenharmony_ci			     buf, 1);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_register_read);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/**
15462306a36Sopenharmony_ci * spmi_ext_register_read() - extended register read
15562306a36Sopenharmony_ci * @sdev:	SPMI device.
15662306a36Sopenharmony_ci * @addr:	slave register address (8-bit address).
15762306a36Sopenharmony_ci * @buf:	buffer to be populated with data from the Slave.
15862306a36Sopenharmony_ci * @len:	the request number of bytes to read (up to 16 bytes).
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * Reads up to 16 bytes of data from the extended register space on a
16162306a36Sopenharmony_ci * Slave device.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_ciint spmi_ext_register_read(struct spmi_device *sdev, u8 addr, u8 *buf,
16462306a36Sopenharmony_ci			   size_t len)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	/* 8-bit register address, up to 16 bytes */
16762306a36Sopenharmony_ci	if (len == 0 || len > 16)
16862306a36Sopenharmony_ci		return -EINVAL;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READ, sdev->usid, addr,
17162306a36Sopenharmony_ci			     buf, len);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_ext_register_read);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/**
17662306a36Sopenharmony_ci * spmi_ext_register_readl() - extended register read long
17762306a36Sopenharmony_ci * @sdev:	SPMI device.
17862306a36Sopenharmony_ci * @addr:	slave register address (16-bit address).
17962306a36Sopenharmony_ci * @buf:	buffer to be populated with data from the Slave.
18062306a36Sopenharmony_ci * @len:	the request number of bytes to read (up to 8 bytes).
18162306a36Sopenharmony_ci *
18262306a36Sopenharmony_ci * Reads up to 8 bytes of data from the extended register space on a
18362306a36Sopenharmony_ci * Slave device using 16-bit address.
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_ciint spmi_ext_register_readl(struct spmi_device *sdev, u16 addr, u8 *buf,
18662306a36Sopenharmony_ci			    size_t len)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	/* 16-bit register address, up to 8 bytes */
18962306a36Sopenharmony_ci	if (len == 0 || len > 8)
19062306a36Sopenharmony_ci		return -EINVAL;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return spmi_read_cmd(sdev->ctrl, SPMI_CMD_EXT_READL, sdev->usid, addr,
19362306a36Sopenharmony_ci			     buf, len);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_ext_register_readl);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/**
19862306a36Sopenharmony_ci * spmi_register_write() - register write
19962306a36Sopenharmony_ci * @sdev:	SPMI device
20062306a36Sopenharmony_ci * @addr:	slave register address (5-bit address).
20162306a36Sopenharmony_ci * @data:	buffer containing the data to be transferred to the Slave.
20262306a36Sopenharmony_ci *
20362306a36Sopenharmony_ci * Writes 1 byte of data to a Slave device register.
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_ciint spmi_register_write(struct spmi_device *sdev, u8 addr, u8 data)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	/* 5-bit register address */
20862306a36Sopenharmony_ci	if (addr > 0x1F)
20962306a36Sopenharmony_ci		return -EINVAL;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return spmi_write_cmd(sdev->ctrl, SPMI_CMD_WRITE, sdev->usid, addr,
21262306a36Sopenharmony_ci			      &data, 1);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_register_write);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/**
21762306a36Sopenharmony_ci * spmi_register_zero_write() - register zero write
21862306a36Sopenharmony_ci * @sdev:	SPMI device.
21962306a36Sopenharmony_ci * @data:	the data to be written to register 0 (7-bits).
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * Writes data to register 0 of the Slave device.
22262306a36Sopenharmony_ci */
22362306a36Sopenharmony_ciint spmi_register_zero_write(struct spmi_device *sdev, u8 data)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	return spmi_write_cmd(sdev->ctrl, SPMI_CMD_ZERO_WRITE, sdev->usid, 0,
22662306a36Sopenharmony_ci			      &data, 1);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_register_zero_write);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci/**
23162306a36Sopenharmony_ci * spmi_ext_register_write() - extended register write
23262306a36Sopenharmony_ci * @sdev:	SPMI device.
23362306a36Sopenharmony_ci * @addr:	slave register address (8-bit address).
23462306a36Sopenharmony_ci * @buf:	buffer containing the data to be transferred to the Slave.
23562306a36Sopenharmony_ci * @len:	the request number of bytes to read (up to 16 bytes).
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * Writes up to 16 bytes of data to the extended register space of a
23862306a36Sopenharmony_ci * Slave device.
23962306a36Sopenharmony_ci */
24062306a36Sopenharmony_ciint spmi_ext_register_write(struct spmi_device *sdev, u8 addr, const u8 *buf,
24162306a36Sopenharmony_ci			    size_t len)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	/* 8-bit register address, up to 16 bytes */
24462306a36Sopenharmony_ci	if (len == 0 || len > 16)
24562306a36Sopenharmony_ci		return -EINVAL;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITE, sdev->usid, addr,
24862306a36Sopenharmony_ci			      buf, len);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_ext_register_write);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/**
25362306a36Sopenharmony_ci * spmi_ext_register_writel() - extended register write long
25462306a36Sopenharmony_ci * @sdev:	SPMI device.
25562306a36Sopenharmony_ci * @addr:	slave register address (16-bit address).
25662306a36Sopenharmony_ci * @buf:	buffer containing the data to be transferred to the Slave.
25762306a36Sopenharmony_ci * @len:	the request number of bytes to read (up to 8 bytes).
25862306a36Sopenharmony_ci *
25962306a36Sopenharmony_ci * Writes up to 8 bytes of data to the extended register space of a
26062306a36Sopenharmony_ci * Slave device using 16-bit address.
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_ciint spmi_ext_register_writel(struct spmi_device *sdev, u16 addr, const u8 *buf,
26362306a36Sopenharmony_ci			     size_t len)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	/* 4-bit Slave Identifier, 16-bit register address, up to 8 bytes */
26662306a36Sopenharmony_ci	if (len == 0 || len > 8)
26762306a36Sopenharmony_ci		return -EINVAL;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return spmi_write_cmd(sdev->ctrl, SPMI_CMD_EXT_WRITEL, sdev->usid,
27062306a36Sopenharmony_ci			      addr, buf, len);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_ext_register_writel);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/**
27562306a36Sopenharmony_ci * spmi_command_reset() - sends RESET command to the specified slave
27662306a36Sopenharmony_ci * @sdev:	SPMI device.
27762306a36Sopenharmony_ci *
27862306a36Sopenharmony_ci * The Reset command initializes the Slave and forces all registers to
27962306a36Sopenharmony_ci * their reset values. The Slave shall enter the STARTUP state after
28062306a36Sopenharmony_ci * receiving a Reset command.
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_ciint spmi_command_reset(struct spmi_device *sdev)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	return spmi_cmd(sdev->ctrl, SPMI_CMD_RESET, sdev->usid);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_command_reset);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci/**
28962306a36Sopenharmony_ci * spmi_command_sleep() - sends SLEEP command to the specified SPMI device
29062306a36Sopenharmony_ci * @sdev:	SPMI device.
29162306a36Sopenharmony_ci *
29262306a36Sopenharmony_ci * The Sleep command causes the Slave to enter the user defined SLEEP state.
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_ciint spmi_command_sleep(struct spmi_device *sdev)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	return spmi_cmd(sdev->ctrl, SPMI_CMD_SLEEP, sdev->usid);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_command_sleep);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/**
30162306a36Sopenharmony_ci * spmi_command_wakeup() - sends WAKEUP command to the specified SPMI device
30262306a36Sopenharmony_ci * @sdev:	SPMI device.
30362306a36Sopenharmony_ci *
30462306a36Sopenharmony_ci * The Wakeup command causes the Slave to move from the SLEEP state to
30562306a36Sopenharmony_ci * the ACTIVE state.
30662306a36Sopenharmony_ci */
30762306a36Sopenharmony_ciint spmi_command_wakeup(struct spmi_device *sdev)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	return spmi_cmd(sdev->ctrl, SPMI_CMD_WAKEUP, sdev->usid);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_command_wakeup);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/**
31462306a36Sopenharmony_ci * spmi_command_shutdown() - sends SHUTDOWN command to the specified SPMI device
31562306a36Sopenharmony_ci * @sdev:	SPMI device.
31662306a36Sopenharmony_ci *
31762306a36Sopenharmony_ci * The Shutdown command causes the Slave to enter the SHUTDOWN state.
31862306a36Sopenharmony_ci */
31962306a36Sopenharmony_ciint spmi_command_shutdown(struct spmi_device *sdev)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	return spmi_cmd(sdev->ctrl, SPMI_CMD_SHUTDOWN, sdev->usid);
32262306a36Sopenharmony_ci}
32362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_command_shutdown);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int spmi_drv_probe(struct device *dev)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
32862306a36Sopenharmony_ci	struct spmi_device *sdev = to_spmi_device(dev);
32962306a36Sopenharmony_ci	int err;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	pm_runtime_get_noresume(dev);
33262306a36Sopenharmony_ci	pm_runtime_set_active(dev);
33362306a36Sopenharmony_ci	pm_runtime_enable(dev);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	err = sdrv->probe(sdev);
33662306a36Sopenharmony_ci	if (err)
33762306a36Sopenharmony_ci		goto fail_probe;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return 0;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cifail_probe:
34262306a36Sopenharmony_ci	pm_runtime_disable(dev);
34362306a36Sopenharmony_ci	pm_runtime_set_suspended(dev);
34462306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
34562306a36Sopenharmony_ci	return err;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic void spmi_drv_remove(struct device *dev)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
35362306a36Sopenharmony_ci	if (sdrv->remove)
35462306a36Sopenharmony_ci		sdrv->remove(to_spmi_device(dev));
35562306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	pm_runtime_disable(dev);
35862306a36Sopenharmony_ci	pm_runtime_set_suspended(dev);
35962306a36Sopenharmony_ci	pm_runtime_put_noidle(dev);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void spmi_drv_shutdown(struct device *dev)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	const struct spmi_driver *sdrv = to_spmi_driver(dev->driver);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	if (sdrv && sdrv->shutdown)
36762306a36Sopenharmony_ci		sdrv->shutdown(to_spmi_device(dev));
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int spmi_drv_uevent(const struct device *dev, struct kobj_uevent_env *env)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	int ret;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	ret = of_device_uevent_modalias(dev, env);
37562306a36Sopenharmony_ci	if (ret != -ENODEV)
37662306a36Sopenharmony_ci		return ret;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return 0;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic struct bus_type spmi_bus_type = {
38262306a36Sopenharmony_ci	.name		= "spmi",
38362306a36Sopenharmony_ci	.match		= spmi_device_match,
38462306a36Sopenharmony_ci	.probe		= spmi_drv_probe,
38562306a36Sopenharmony_ci	.remove		= spmi_drv_remove,
38662306a36Sopenharmony_ci	.shutdown	= spmi_drv_shutdown,
38762306a36Sopenharmony_ci	.uevent		= spmi_drv_uevent,
38862306a36Sopenharmony_ci};
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci/**
39162306a36Sopenharmony_ci * spmi_device_from_of() - get the associated SPMI device from a device node
39262306a36Sopenharmony_ci *
39362306a36Sopenharmony_ci * @np:		device node
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * Returns the struct spmi_device associated with a device node or NULL.
39662306a36Sopenharmony_ci */
39762306a36Sopenharmony_cistruct spmi_device *spmi_device_from_of(struct device_node *np)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct device *dev = bus_find_device_by_of_node(&spmi_bus_type, np);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	if (dev)
40262306a36Sopenharmony_ci		return to_spmi_device(dev);
40362306a36Sopenharmony_ci	return NULL;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_device_from_of);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci/**
40862306a36Sopenharmony_ci * spmi_device_alloc() - Allocate a new SPMI device
40962306a36Sopenharmony_ci * @ctrl:	associated controller
41062306a36Sopenharmony_ci *
41162306a36Sopenharmony_ci * Caller is responsible for either calling spmi_device_add() to add the
41262306a36Sopenharmony_ci * newly allocated controller, or calling spmi_device_put() to discard it.
41362306a36Sopenharmony_ci */
41462306a36Sopenharmony_cistruct spmi_device *spmi_device_alloc(struct spmi_controller *ctrl)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct spmi_device *sdev;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
41962306a36Sopenharmony_ci	if (!sdev)
42062306a36Sopenharmony_ci		return NULL;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	sdev->ctrl = ctrl;
42362306a36Sopenharmony_ci	device_initialize(&sdev->dev);
42462306a36Sopenharmony_ci	sdev->dev.parent = &ctrl->dev;
42562306a36Sopenharmony_ci	sdev->dev.bus = &spmi_bus_type;
42662306a36Sopenharmony_ci	sdev->dev.type = &spmi_dev_type;
42762306a36Sopenharmony_ci	return sdev;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_device_alloc);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci/**
43262306a36Sopenharmony_ci * spmi_controller_alloc() - Allocate a new SPMI controller
43362306a36Sopenharmony_ci * @parent:	parent device
43462306a36Sopenharmony_ci * @size:	size of private data
43562306a36Sopenharmony_ci *
43662306a36Sopenharmony_ci * Caller is responsible for either calling spmi_controller_add() to add the
43762306a36Sopenharmony_ci * newly allocated controller, or calling spmi_controller_put() to discard it.
43862306a36Sopenharmony_ci * The allocated private data region may be accessed via
43962306a36Sopenharmony_ci * spmi_controller_get_drvdata()
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_cistruct spmi_controller *spmi_controller_alloc(struct device *parent,
44262306a36Sopenharmony_ci					      size_t size)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct spmi_controller *ctrl;
44562306a36Sopenharmony_ci	int id;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (WARN_ON(!parent))
44862306a36Sopenharmony_ci		return NULL;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL);
45162306a36Sopenharmony_ci	if (!ctrl)
45262306a36Sopenharmony_ci		return NULL;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	device_initialize(&ctrl->dev);
45562306a36Sopenharmony_ci	ctrl->dev.type = &spmi_ctrl_type;
45662306a36Sopenharmony_ci	ctrl->dev.bus = &spmi_bus_type;
45762306a36Sopenharmony_ci	ctrl->dev.parent = parent;
45862306a36Sopenharmony_ci	ctrl->dev.of_node = parent->of_node;
45962306a36Sopenharmony_ci	spmi_controller_set_drvdata(ctrl, &ctrl[1]);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	id = ida_alloc(&ctrl_ida, GFP_KERNEL);
46262306a36Sopenharmony_ci	if (id < 0) {
46362306a36Sopenharmony_ci		dev_err(parent,
46462306a36Sopenharmony_ci			"unable to allocate SPMI controller identifier.\n");
46562306a36Sopenharmony_ci		spmi_controller_put(ctrl);
46662306a36Sopenharmony_ci		return NULL;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	ctrl->nr = id;
47062306a36Sopenharmony_ci	dev_set_name(&ctrl->dev, "spmi-%d", id);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id);
47362306a36Sopenharmony_ci	return ctrl;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_controller_alloc);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic void of_spmi_register_devices(struct spmi_controller *ctrl)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct device_node *node;
48062306a36Sopenharmony_ci	int err;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (!ctrl->dev.of_node)
48362306a36Sopenharmony_ci		return;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	for_each_available_child_of_node(ctrl->dev.of_node, node) {
48662306a36Sopenharmony_ci		struct spmi_device *sdev;
48762306a36Sopenharmony_ci		u32 reg[2];
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		dev_dbg(&ctrl->dev, "adding child %pOF\n", node);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		err = of_property_read_u32_array(node, "reg", reg, 2);
49262306a36Sopenharmony_ci		if (err) {
49362306a36Sopenharmony_ci			dev_err(&ctrl->dev,
49462306a36Sopenharmony_ci				"node %pOF err (%d) does not have 'reg' property\n",
49562306a36Sopenharmony_ci				node, err);
49662306a36Sopenharmony_ci			continue;
49762306a36Sopenharmony_ci		}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci		if (reg[1] != SPMI_USID) {
50062306a36Sopenharmony_ci			dev_err(&ctrl->dev,
50162306a36Sopenharmony_ci				"node %pOF contains unsupported 'reg' entry\n",
50262306a36Sopenharmony_ci				node);
50362306a36Sopenharmony_ci			continue;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		if (reg[0] >= SPMI_MAX_SLAVE_ID) {
50762306a36Sopenharmony_ci			dev_err(&ctrl->dev, "invalid usid on node %pOF\n", node);
50862306a36Sopenharmony_ci			continue;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		dev_dbg(&ctrl->dev, "read usid %02x\n", reg[0]);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		sdev = spmi_device_alloc(ctrl);
51462306a36Sopenharmony_ci		if (!sdev)
51562306a36Sopenharmony_ci			continue;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		sdev->dev.of_node = node;
51862306a36Sopenharmony_ci		sdev->usid = (u8)reg[0];
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		err = spmi_device_add(sdev);
52162306a36Sopenharmony_ci		if (err) {
52262306a36Sopenharmony_ci			dev_err(&sdev->dev,
52362306a36Sopenharmony_ci				"failure adding device. status %d\n", err);
52462306a36Sopenharmony_ci			spmi_device_put(sdev);
52562306a36Sopenharmony_ci		}
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci/**
53062306a36Sopenharmony_ci * spmi_controller_add() - Add an SPMI controller
53162306a36Sopenharmony_ci * @ctrl:	controller to be registered.
53262306a36Sopenharmony_ci *
53362306a36Sopenharmony_ci * Register a controller previously allocated via spmi_controller_alloc() with
53462306a36Sopenharmony_ci * the SPMI core.
53562306a36Sopenharmony_ci */
53662306a36Sopenharmony_ciint spmi_controller_add(struct spmi_controller *ctrl)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	int ret;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	/* Can't register until after driver model init */
54162306a36Sopenharmony_ci	if (WARN_ON(!is_registered))
54262306a36Sopenharmony_ci		return -EAGAIN;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	ret = device_add(&ctrl->dev);
54562306a36Sopenharmony_ci	if (ret)
54662306a36Sopenharmony_ci		return ret;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_OF))
54962306a36Sopenharmony_ci		of_spmi_register_devices(ctrl);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	dev_dbg(&ctrl->dev, "spmi-%d registered: dev:%p\n",
55262306a36Sopenharmony_ci		ctrl->nr, &ctrl->dev);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return 0;
55562306a36Sopenharmony_ci};
55662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_controller_add);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci/* Remove a device associated with a controller */
55962306a36Sopenharmony_cistatic int spmi_ctrl_remove_device(struct device *dev, void *data)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct spmi_device *spmidev = to_spmi_device(dev);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (dev->type == &spmi_dev_type)
56462306a36Sopenharmony_ci		spmi_device_remove(spmidev);
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci/**
56962306a36Sopenharmony_ci * spmi_controller_remove(): remove an SPMI controller
57062306a36Sopenharmony_ci * @ctrl:	controller to remove
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci * Remove a SPMI controller.  Caller is responsible for calling
57362306a36Sopenharmony_ci * spmi_controller_put() to discard the allocated controller.
57462306a36Sopenharmony_ci */
57562306a36Sopenharmony_civoid spmi_controller_remove(struct spmi_controller *ctrl)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	if (!ctrl)
57862306a36Sopenharmony_ci		return;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	device_for_each_child(&ctrl->dev, NULL, spmi_ctrl_remove_device);
58162306a36Sopenharmony_ci	device_del(&ctrl->dev);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(spmi_controller_remove);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/**
58662306a36Sopenharmony_ci * __spmi_driver_register() - Register client driver with SPMI core
58762306a36Sopenharmony_ci * @sdrv:	client driver to be associated with client-device.
58862306a36Sopenharmony_ci * @owner:	module owner
58962306a36Sopenharmony_ci *
59062306a36Sopenharmony_ci * This API will register the client driver with the SPMI framework.
59162306a36Sopenharmony_ci * It is typically called from the driver's module-init function.
59262306a36Sopenharmony_ci */
59362306a36Sopenharmony_ciint __spmi_driver_register(struct spmi_driver *sdrv, struct module *owner)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	sdrv->driver.bus = &spmi_bus_type;
59662306a36Sopenharmony_ci	sdrv->driver.owner = owner;
59762306a36Sopenharmony_ci	return driver_register(&sdrv->driver);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__spmi_driver_register);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic void __exit spmi_exit(void)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	bus_unregister(&spmi_bus_type);
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_cimodule_exit(spmi_exit);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int __init spmi_init(void)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	int ret;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	ret = bus_register(&spmi_bus_type);
61262306a36Sopenharmony_ci	if (ret)
61362306a36Sopenharmony_ci		return ret;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	is_registered = true;
61662306a36Sopenharmony_ci	return 0;
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_cipostcore_initcall(spmi_init);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
62162306a36Sopenharmony_ciMODULE_DESCRIPTION("SPMI module");
62262306a36Sopenharmony_ciMODULE_ALIAS("platform:spmi");
623