xref: /openbmc/linux/drivers/bus/sunxi-rsb.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
13bb16560SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2d787dcdbSChen-Yu Tsai /*
3d787dcdbSChen-Yu Tsai  * RSB (Reduced Serial Bus) driver.
4d787dcdbSChen-Yu Tsai  *
5d787dcdbSChen-Yu Tsai  * Author: Chen-Yu Tsai <wens@csie.org>
6d787dcdbSChen-Yu Tsai  *
7d787dcdbSChen-Yu Tsai  * The RSB controller looks like an SMBus controller which only supports
8d787dcdbSChen-Yu Tsai  * byte and word data transfers. But, it differs from standard SMBus
9d787dcdbSChen-Yu Tsai  * protocol on several aspects:
10d787dcdbSChen-Yu Tsai  * - it uses addresses set at runtime to address slaves. Runtime addresses
11d787dcdbSChen-Yu Tsai  *   are sent to slaves using their 12bit hardware addresses. Up to 15
12d787dcdbSChen-Yu Tsai  *   runtime addresses are available.
13d787dcdbSChen-Yu Tsai  * - it adds a parity bit every 8bits of data and address for read and
14d787dcdbSChen-Yu Tsai  *   write accesses; this replaces the ack bit
15d787dcdbSChen-Yu Tsai  * - only one read access is required to read a byte (instead of a write
16d787dcdbSChen-Yu Tsai  *   followed by a read access in standard SMBus protocol)
17d787dcdbSChen-Yu Tsai  * - there's no Ack bit after each read access
18d787dcdbSChen-Yu Tsai  *
19d787dcdbSChen-Yu Tsai  * This means this bus cannot be used to interface with standard SMBus
20d787dcdbSChen-Yu Tsai  * devices. Devices known to support this interface include the AXP223,
21d787dcdbSChen-Yu Tsai  * AXP809, and AXP806 PMICs, and the AC100 audio codec, all from X-Powers.
22d787dcdbSChen-Yu Tsai  *
23d787dcdbSChen-Yu Tsai  * A description of the operation and wire protocol can be found in the
24d787dcdbSChen-Yu Tsai  * RSB section of Allwinner's A80 user manual, which can be found at
25d787dcdbSChen-Yu Tsai  *
26d787dcdbSChen-Yu Tsai  *     https://github.com/allwinner-zh/documents/tree/master/A80
27d787dcdbSChen-Yu Tsai  *
28d787dcdbSChen-Yu Tsai  * This document is officially released by Allwinner.
29d787dcdbSChen-Yu Tsai  *
30d787dcdbSChen-Yu Tsai  * This driver is based on i2c-sun6i-p2wi.c, the P2WI bus driver.
31d787dcdbSChen-Yu Tsai  */
32d787dcdbSChen-Yu Tsai 
33d787dcdbSChen-Yu Tsai #include <linux/clk.h>
34d787dcdbSChen-Yu Tsai #include <linux/clk/clk-conf.h>
35d787dcdbSChen-Yu Tsai #include <linux/device.h>
36d787dcdbSChen-Yu Tsai #include <linux/interrupt.h>
37d787dcdbSChen-Yu Tsai #include <linux/io.h>
38d787dcdbSChen-Yu Tsai #include <linux/iopoll.h>
39d787dcdbSChen-Yu Tsai #include <linux/module.h>
40d787dcdbSChen-Yu Tsai #include <linux/of.h>
41d787dcdbSChen-Yu Tsai #include <linux/of_irq.h>
42*53c5ae63SRob Herring #include <linux/of_device.h>
43d787dcdbSChen-Yu Tsai #include <linux/platform_device.h>
4484310749SSamuel Holland #include <linux/pm.h>
454a0dbc12SSamuel Holland #include <linux/pm_runtime.h>
46d787dcdbSChen-Yu Tsai #include <linux/regmap.h>
47d787dcdbSChen-Yu Tsai #include <linux/reset.h>
48d787dcdbSChen-Yu Tsai #include <linux/slab.h>
49d787dcdbSChen-Yu Tsai #include <linux/sunxi-rsb.h>
50d787dcdbSChen-Yu Tsai #include <linux/types.h>
51d787dcdbSChen-Yu Tsai 
52d787dcdbSChen-Yu Tsai /* RSB registers */
53d787dcdbSChen-Yu Tsai #define RSB_CTRL	0x0	/* Global control */
54d787dcdbSChen-Yu Tsai #define RSB_CCR		0x4	/* Clock control */
55d787dcdbSChen-Yu Tsai #define RSB_INTE	0x8	/* Interrupt controls */
56d787dcdbSChen-Yu Tsai #define RSB_INTS	0xc	/* Interrupt status */
57d787dcdbSChen-Yu Tsai #define RSB_ADDR	0x10	/* Address to send with read/write command */
58d787dcdbSChen-Yu Tsai #define RSB_DATA	0x1c	/* Data to read/write */
59d787dcdbSChen-Yu Tsai #define RSB_LCR		0x24	/* Line control */
60d787dcdbSChen-Yu Tsai #define RSB_DMCR	0x28	/* Device mode (init) control */
61d787dcdbSChen-Yu Tsai #define RSB_CMD		0x2c	/* RSB Command */
62d787dcdbSChen-Yu Tsai #define RSB_DAR		0x30	/* Device address / runtime address */
63d787dcdbSChen-Yu Tsai 
64d787dcdbSChen-Yu Tsai /* CTRL fields */
65d787dcdbSChen-Yu Tsai #define RSB_CTRL_START_TRANS		BIT(7)
66d787dcdbSChen-Yu Tsai #define RSB_CTRL_ABORT_TRANS		BIT(6)
67d787dcdbSChen-Yu Tsai #define RSB_CTRL_GLOBAL_INT_ENB		BIT(1)
68d787dcdbSChen-Yu Tsai #define RSB_CTRL_SOFT_RST		BIT(0)
69d787dcdbSChen-Yu Tsai 
70d787dcdbSChen-Yu Tsai /* CLK CTRL fields */
71d787dcdbSChen-Yu Tsai #define RSB_CCR_SDA_OUT_DELAY(v)	(((v) & 0x7) << 8)
72d787dcdbSChen-Yu Tsai #define RSB_CCR_MAX_CLK_DIV		0xff
73d787dcdbSChen-Yu Tsai #define RSB_CCR_CLK_DIV(v)		((v) & RSB_CCR_MAX_CLK_DIV)
74d787dcdbSChen-Yu Tsai 
75d787dcdbSChen-Yu Tsai /* STATUS fields */
76d787dcdbSChen-Yu Tsai #define RSB_INTS_TRANS_ERR_ACK		BIT(16)
77d787dcdbSChen-Yu Tsai #define RSB_INTS_TRANS_ERR_DATA_BIT(v)	(((v) >> 8) & 0xf)
78d787dcdbSChen-Yu Tsai #define RSB_INTS_TRANS_ERR_DATA		GENMASK(11, 8)
79d787dcdbSChen-Yu Tsai #define RSB_INTS_LOAD_BSY		BIT(2)
80d787dcdbSChen-Yu Tsai #define RSB_INTS_TRANS_ERR		BIT(1)
81d787dcdbSChen-Yu Tsai #define RSB_INTS_TRANS_OVER		BIT(0)
82d787dcdbSChen-Yu Tsai 
83d787dcdbSChen-Yu Tsai /* LINE CTRL fields*/
84d787dcdbSChen-Yu Tsai #define RSB_LCR_SCL_STATE		BIT(5)
85d787dcdbSChen-Yu Tsai #define RSB_LCR_SDA_STATE		BIT(4)
86d787dcdbSChen-Yu Tsai #define RSB_LCR_SCL_CTL			BIT(3)
87d787dcdbSChen-Yu Tsai #define RSB_LCR_SCL_CTL_EN		BIT(2)
88d787dcdbSChen-Yu Tsai #define RSB_LCR_SDA_CTL			BIT(1)
89d787dcdbSChen-Yu Tsai #define RSB_LCR_SDA_CTL_EN		BIT(0)
90d787dcdbSChen-Yu Tsai 
91d787dcdbSChen-Yu Tsai /* DEVICE MODE CTRL field values */
92d787dcdbSChen-Yu Tsai #define RSB_DMCR_DEVICE_START		BIT(31)
93d787dcdbSChen-Yu Tsai #define RSB_DMCR_MODE_DATA		(0x7c << 16)
94d787dcdbSChen-Yu Tsai #define RSB_DMCR_MODE_REG		(0x3e << 8)
95d787dcdbSChen-Yu Tsai #define RSB_DMCR_DEV_ADDR		0x00
96d787dcdbSChen-Yu Tsai 
97d787dcdbSChen-Yu Tsai /* CMD values */
98d787dcdbSChen-Yu Tsai #define RSB_CMD_RD8			0x8b
99d787dcdbSChen-Yu Tsai #define RSB_CMD_RD16			0x9c
100d787dcdbSChen-Yu Tsai #define RSB_CMD_RD32			0xa6
101d787dcdbSChen-Yu Tsai #define RSB_CMD_WR8			0x4e
102d787dcdbSChen-Yu Tsai #define RSB_CMD_WR16			0x59
103d787dcdbSChen-Yu Tsai #define RSB_CMD_WR32			0x63
104d787dcdbSChen-Yu Tsai #define RSB_CMD_STRA			0xe8
105d787dcdbSChen-Yu Tsai 
106d787dcdbSChen-Yu Tsai /* DAR fields */
107d787dcdbSChen-Yu Tsai #define RSB_DAR_RTA(v)			(((v) & 0xff) << 16)
108d787dcdbSChen-Yu Tsai #define RSB_DAR_DA(v)			((v) & 0xffff)
109d787dcdbSChen-Yu Tsai 
110d787dcdbSChen-Yu Tsai #define RSB_MAX_FREQ			20000000
111d787dcdbSChen-Yu Tsai 
112d787dcdbSChen-Yu Tsai #define RSB_CTRL_NAME			"sunxi-rsb"
113d787dcdbSChen-Yu Tsai 
114d787dcdbSChen-Yu Tsai struct sunxi_rsb_addr_map {
115d787dcdbSChen-Yu Tsai 	u16 hwaddr;
116d787dcdbSChen-Yu Tsai 	u8 rtaddr;
117d787dcdbSChen-Yu Tsai };
118d787dcdbSChen-Yu Tsai 
119d787dcdbSChen-Yu Tsai struct sunxi_rsb {
120d787dcdbSChen-Yu Tsai 	struct device *dev;
121d787dcdbSChen-Yu Tsai 	void __iomem *regs;
122d787dcdbSChen-Yu Tsai 	struct clk *clk;
123d787dcdbSChen-Yu Tsai 	struct reset_control *rstc;
124d787dcdbSChen-Yu Tsai 	struct completion complete;
125d787dcdbSChen-Yu Tsai 	struct mutex lock;
126d787dcdbSChen-Yu Tsai 	unsigned int status;
12722754ac9SSamuel Holland 	u32 clk_freq;
128d787dcdbSChen-Yu Tsai };
129d787dcdbSChen-Yu Tsai 
130d787dcdbSChen-Yu Tsai /* bus / slave device related functions */
131d787dcdbSChen-Yu Tsai static struct bus_type sunxi_rsb_bus;
132d787dcdbSChen-Yu Tsai 
sunxi_rsb_device_match(struct device * dev,struct device_driver * drv)133d787dcdbSChen-Yu Tsai static int sunxi_rsb_device_match(struct device *dev, struct device_driver *drv)
134d787dcdbSChen-Yu Tsai {
135d787dcdbSChen-Yu Tsai 	return of_driver_match_device(dev, drv);
136d787dcdbSChen-Yu Tsai }
137d787dcdbSChen-Yu Tsai 
sunxi_rsb_device_probe(struct device * dev)138d787dcdbSChen-Yu Tsai static int sunxi_rsb_device_probe(struct device *dev)
139d787dcdbSChen-Yu Tsai {
140d787dcdbSChen-Yu Tsai 	const struct sunxi_rsb_driver *drv = to_sunxi_rsb_driver(dev->driver);
141d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_device *rdev = to_sunxi_rsb_device(dev);
142d787dcdbSChen-Yu Tsai 	int ret;
143d787dcdbSChen-Yu Tsai 
144d787dcdbSChen-Yu Tsai 	if (!drv->probe)
145d787dcdbSChen-Yu Tsai 		return -ENODEV;
146d787dcdbSChen-Yu Tsai 
147d787dcdbSChen-Yu Tsai 	if (!rdev->irq) {
148d787dcdbSChen-Yu Tsai 		int irq = -ENOENT;
149d787dcdbSChen-Yu Tsai 
150d787dcdbSChen-Yu Tsai 		if (dev->of_node)
151d787dcdbSChen-Yu Tsai 			irq = of_irq_get(dev->of_node, 0);
152d787dcdbSChen-Yu Tsai 
153d787dcdbSChen-Yu Tsai 		if (irq == -EPROBE_DEFER)
154d787dcdbSChen-Yu Tsai 			return irq;
155d787dcdbSChen-Yu Tsai 		if (irq < 0)
156d787dcdbSChen-Yu Tsai 			irq = 0;
157d787dcdbSChen-Yu Tsai 
158d787dcdbSChen-Yu Tsai 		rdev->irq = irq;
159d787dcdbSChen-Yu Tsai 	}
160d787dcdbSChen-Yu Tsai 
161d787dcdbSChen-Yu Tsai 	ret = of_clk_set_defaults(dev->of_node, false);
162d787dcdbSChen-Yu Tsai 	if (ret < 0)
163d787dcdbSChen-Yu Tsai 		return ret;
164d787dcdbSChen-Yu Tsai 
165d787dcdbSChen-Yu Tsai 	return drv->probe(rdev);
166d787dcdbSChen-Yu Tsai }
167d787dcdbSChen-Yu Tsai 
sunxi_rsb_device_remove(struct device * dev)168fc7a6209SUwe Kleine-König static void sunxi_rsb_device_remove(struct device *dev)
169d787dcdbSChen-Yu Tsai {
170d787dcdbSChen-Yu Tsai 	const struct sunxi_rsb_driver *drv = to_sunxi_rsb_driver(dev->driver);
171d787dcdbSChen-Yu Tsai 
1723c15e00eSUwe Kleine-König 	drv->remove(to_sunxi_rsb_device(dev));
173d787dcdbSChen-Yu Tsai }
174d787dcdbSChen-Yu Tsai 
sunxi_rsb_device_modalias(const struct device * dev,struct kobj_uevent_env * env)1752a81ada3SGreg Kroah-Hartman static int sunxi_rsb_device_modalias(const struct device *dev, struct kobj_uevent_env *env)
176a77ad4bfSGreg Kroah-Hartman {
177a77ad4bfSGreg Kroah-Hartman 	return of_device_uevent_modalias(dev, env);
178a77ad4bfSGreg Kroah-Hartman }
179a77ad4bfSGreg Kroah-Hartman 
180d787dcdbSChen-Yu Tsai static struct bus_type sunxi_rsb_bus = {
181d787dcdbSChen-Yu Tsai 	.name		= RSB_CTRL_NAME,
182d787dcdbSChen-Yu Tsai 	.match		= sunxi_rsb_device_match,
183d787dcdbSChen-Yu Tsai 	.probe		= sunxi_rsb_device_probe,
184d787dcdbSChen-Yu Tsai 	.remove		= sunxi_rsb_device_remove,
185a77ad4bfSGreg Kroah-Hartman 	.uevent		= sunxi_rsb_device_modalias,
186d787dcdbSChen-Yu Tsai };
187d787dcdbSChen-Yu Tsai 
sunxi_rsb_dev_release(struct device * dev)188d787dcdbSChen-Yu Tsai static void sunxi_rsb_dev_release(struct device *dev)
189d787dcdbSChen-Yu Tsai {
190d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_device *rdev = to_sunxi_rsb_device(dev);
191d787dcdbSChen-Yu Tsai 
192d787dcdbSChen-Yu Tsai 	kfree(rdev);
193d787dcdbSChen-Yu Tsai }
194d787dcdbSChen-Yu Tsai 
195d787dcdbSChen-Yu Tsai /**
196d787dcdbSChen-Yu Tsai  * sunxi_rsb_device_create() - allocate and add an RSB device
197d787dcdbSChen-Yu Tsai  * @rsb:	RSB controller
198d787dcdbSChen-Yu Tsai  * @node:	RSB slave device node
199d787dcdbSChen-Yu Tsai  * @hwaddr:	RSB slave hardware address
200d787dcdbSChen-Yu Tsai  * @rtaddr:	RSB slave runtime address
201d787dcdbSChen-Yu Tsai  */
sunxi_rsb_device_create(struct sunxi_rsb * rsb,struct device_node * node,u16 hwaddr,u8 rtaddr)202d787dcdbSChen-Yu Tsai static struct sunxi_rsb_device *sunxi_rsb_device_create(struct sunxi_rsb *rsb,
203d787dcdbSChen-Yu Tsai 		struct device_node *node, u16 hwaddr, u8 rtaddr)
204d787dcdbSChen-Yu Tsai {
205d787dcdbSChen-Yu Tsai 	int err;
206d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_device *rdev;
207d787dcdbSChen-Yu Tsai 
208d787dcdbSChen-Yu Tsai 	rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
209d787dcdbSChen-Yu Tsai 	if (!rdev)
210d787dcdbSChen-Yu Tsai 		return ERR_PTR(-ENOMEM);
211d787dcdbSChen-Yu Tsai 
212d787dcdbSChen-Yu Tsai 	rdev->rsb = rsb;
213d787dcdbSChen-Yu Tsai 	rdev->hwaddr = hwaddr;
214d787dcdbSChen-Yu Tsai 	rdev->rtaddr = rtaddr;
215d787dcdbSChen-Yu Tsai 	rdev->dev.bus = &sunxi_rsb_bus;
216d787dcdbSChen-Yu Tsai 	rdev->dev.parent = rsb->dev;
217d787dcdbSChen-Yu Tsai 	rdev->dev.of_node = node;
218d787dcdbSChen-Yu Tsai 	rdev->dev.release = sunxi_rsb_dev_release;
219d787dcdbSChen-Yu Tsai 
220d787dcdbSChen-Yu Tsai 	dev_set_name(&rdev->dev, "%s-%x", RSB_CTRL_NAME, hwaddr);
221d787dcdbSChen-Yu Tsai 
222d787dcdbSChen-Yu Tsai 	err = device_register(&rdev->dev);
223d787dcdbSChen-Yu Tsai 	if (err < 0) {
224d787dcdbSChen-Yu Tsai 		dev_err(&rdev->dev, "Can't add %s, status %d\n",
225d787dcdbSChen-Yu Tsai 			dev_name(&rdev->dev), err);
226d787dcdbSChen-Yu Tsai 		goto err_device_add;
227d787dcdbSChen-Yu Tsai 	}
228d787dcdbSChen-Yu Tsai 
229d787dcdbSChen-Yu Tsai 	dev_dbg(&rdev->dev, "device %s registered\n", dev_name(&rdev->dev));
230d787dcdbSChen-Yu Tsai 
231fff8c103SChristophe JAILLET 	return rdev;
232fff8c103SChristophe JAILLET 
233d787dcdbSChen-Yu Tsai err_device_add:
234d787dcdbSChen-Yu Tsai 	put_device(&rdev->dev);
235d787dcdbSChen-Yu Tsai 
236d787dcdbSChen-Yu Tsai 	return ERR_PTR(err);
237d787dcdbSChen-Yu Tsai }
238d787dcdbSChen-Yu Tsai 
239d787dcdbSChen-Yu Tsai /**
240d787dcdbSChen-Yu Tsai  * sunxi_rsb_device_unregister(): unregister an RSB device
241d787dcdbSChen-Yu Tsai  * @rdev:	rsb_device to be removed
242d787dcdbSChen-Yu Tsai  */
sunxi_rsb_device_unregister(struct sunxi_rsb_device * rdev)243d787dcdbSChen-Yu Tsai static void sunxi_rsb_device_unregister(struct sunxi_rsb_device *rdev)
244d787dcdbSChen-Yu Tsai {
245d787dcdbSChen-Yu Tsai 	device_unregister(&rdev->dev);
246d787dcdbSChen-Yu Tsai }
247d787dcdbSChen-Yu Tsai 
sunxi_rsb_remove_devices(struct device * dev,void * data)248d787dcdbSChen-Yu Tsai static int sunxi_rsb_remove_devices(struct device *dev, void *data)
249d787dcdbSChen-Yu Tsai {
250d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_device *rdev = to_sunxi_rsb_device(dev);
251d787dcdbSChen-Yu Tsai 
252d787dcdbSChen-Yu Tsai 	if (dev->bus == &sunxi_rsb_bus)
253d787dcdbSChen-Yu Tsai 		sunxi_rsb_device_unregister(rdev);
254d787dcdbSChen-Yu Tsai 
255d787dcdbSChen-Yu Tsai 	return 0;
256d787dcdbSChen-Yu Tsai }
257d787dcdbSChen-Yu Tsai 
258d787dcdbSChen-Yu Tsai /**
259d787dcdbSChen-Yu Tsai  * sunxi_rsb_driver_register() - Register device driver with RSB core
260d787dcdbSChen-Yu Tsai  * @rdrv:	device driver to be associated with slave-device.
261d787dcdbSChen-Yu Tsai  *
262d787dcdbSChen-Yu Tsai  * This API will register the client driver with the RSB framework.
263d787dcdbSChen-Yu Tsai  * It is typically called from the driver's module-init function.
264d787dcdbSChen-Yu Tsai  */
sunxi_rsb_driver_register(struct sunxi_rsb_driver * rdrv)265d787dcdbSChen-Yu Tsai int sunxi_rsb_driver_register(struct sunxi_rsb_driver *rdrv)
266d787dcdbSChen-Yu Tsai {
267d787dcdbSChen-Yu Tsai 	rdrv->driver.bus = &sunxi_rsb_bus;
268d787dcdbSChen-Yu Tsai 	return driver_register(&rdrv->driver);
269d787dcdbSChen-Yu Tsai }
270d787dcdbSChen-Yu Tsai EXPORT_SYMBOL_GPL(sunxi_rsb_driver_register);
271d787dcdbSChen-Yu Tsai 
272d787dcdbSChen-Yu Tsai /* common code that starts a transfer */
_sunxi_rsb_run_xfer(struct sunxi_rsb * rsb)273d787dcdbSChen-Yu Tsai static int _sunxi_rsb_run_xfer(struct sunxi_rsb *rsb)
274d787dcdbSChen-Yu Tsai {
275077686daSSamuel Holland 	u32 int_mask, status;
276077686daSSamuel Holland 	bool timeout;
277077686daSSamuel Holland 
278d787dcdbSChen-Yu Tsai 	if (readl(rsb->regs + RSB_CTRL) & RSB_CTRL_START_TRANS) {
279d787dcdbSChen-Yu Tsai 		dev_dbg(rsb->dev, "RSB transfer still in progress\n");
280d787dcdbSChen-Yu Tsai 		return -EBUSY;
281d787dcdbSChen-Yu Tsai 	}
282d787dcdbSChen-Yu Tsai 
283d787dcdbSChen-Yu Tsai 	reinit_completion(&rsb->complete);
284d787dcdbSChen-Yu Tsai 
285077686daSSamuel Holland 	int_mask = RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR | RSB_INTS_TRANS_OVER;
286077686daSSamuel Holland 	writel(int_mask, rsb->regs + RSB_INTE);
287d787dcdbSChen-Yu Tsai 	writel(RSB_CTRL_START_TRANS | RSB_CTRL_GLOBAL_INT_ENB,
288d787dcdbSChen-Yu Tsai 	       rsb->regs + RSB_CTRL);
289d787dcdbSChen-Yu Tsai 
290077686daSSamuel Holland 	if (irqs_disabled()) {
291077686daSSamuel Holland 		timeout = readl_poll_timeout_atomic(rsb->regs + RSB_INTS,
292077686daSSamuel Holland 						    status, (status & int_mask),
293077686daSSamuel Holland 						    10, 100000);
294077686daSSamuel Holland 		writel(status, rsb->regs + RSB_INTS);
295077686daSSamuel Holland 	} else {
296077686daSSamuel Holland 		timeout = !wait_for_completion_io_timeout(&rsb->complete,
297077686daSSamuel Holland 							  msecs_to_jiffies(100));
298077686daSSamuel Holland 		status = rsb->status;
299077686daSSamuel Holland 	}
300077686daSSamuel Holland 
301077686daSSamuel Holland 	if (timeout) {
302d787dcdbSChen-Yu Tsai 		dev_dbg(rsb->dev, "RSB timeout\n");
303d787dcdbSChen-Yu Tsai 
304d787dcdbSChen-Yu Tsai 		/* abort the transfer */
305d787dcdbSChen-Yu Tsai 		writel(RSB_CTRL_ABORT_TRANS, rsb->regs + RSB_CTRL);
306d787dcdbSChen-Yu Tsai 
307d787dcdbSChen-Yu Tsai 		/* clear any interrupt flags */
308d787dcdbSChen-Yu Tsai 		writel(readl(rsb->regs + RSB_INTS), rsb->regs + RSB_INTS);
309d787dcdbSChen-Yu Tsai 
310d787dcdbSChen-Yu Tsai 		return -ETIMEDOUT;
311d787dcdbSChen-Yu Tsai 	}
312d787dcdbSChen-Yu Tsai 
313077686daSSamuel Holland 	if (status & RSB_INTS_LOAD_BSY) {
314d787dcdbSChen-Yu Tsai 		dev_dbg(rsb->dev, "RSB busy\n");
315d787dcdbSChen-Yu Tsai 		return -EBUSY;
316d787dcdbSChen-Yu Tsai 	}
317d787dcdbSChen-Yu Tsai 
318077686daSSamuel Holland 	if (status & RSB_INTS_TRANS_ERR) {
319077686daSSamuel Holland 		if (status & RSB_INTS_TRANS_ERR_ACK) {
320d787dcdbSChen-Yu Tsai 			dev_dbg(rsb->dev, "RSB slave nack\n");
321d787dcdbSChen-Yu Tsai 			return -EINVAL;
322d787dcdbSChen-Yu Tsai 		}
323d787dcdbSChen-Yu Tsai 
324077686daSSamuel Holland 		if (status & RSB_INTS_TRANS_ERR_DATA) {
325d787dcdbSChen-Yu Tsai 			dev_dbg(rsb->dev, "RSB transfer data error\n");
326d787dcdbSChen-Yu Tsai 			return -EIO;
327d787dcdbSChen-Yu Tsai 		}
328d787dcdbSChen-Yu Tsai 	}
329d787dcdbSChen-Yu Tsai 
330d787dcdbSChen-Yu Tsai 	return 0;
331d787dcdbSChen-Yu Tsai }
332d787dcdbSChen-Yu Tsai 
sunxi_rsb_read(struct sunxi_rsb * rsb,u8 rtaddr,u8 addr,u32 * buf,size_t len)333d787dcdbSChen-Yu Tsai static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
334d787dcdbSChen-Yu Tsai 			  u32 *buf, size_t len)
335d787dcdbSChen-Yu Tsai {
336d787dcdbSChen-Yu Tsai 	u32 cmd;
337d787dcdbSChen-Yu Tsai 	int ret;
338d787dcdbSChen-Yu Tsai 
339d787dcdbSChen-Yu Tsai 	if (!buf)
340d787dcdbSChen-Yu Tsai 		return -EINVAL;
341d787dcdbSChen-Yu Tsai 
342d787dcdbSChen-Yu Tsai 	switch (len) {
343d787dcdbSChen-Yu Tsai 	case 1:
344d787dcdbSChen-Yu Tsai 		cmd = RSB_CMD_RD8;
345d787dcdbSChen-Yu Tsai 		break;
346d787dcdbSChen-Yu Tsai 	case 2:
347d787dcdbSChen-Yu Tsai 		cmd = RSB_CMD_RD16;
348d787dcdbSChen-Yu Tsai 		break;
349d787dcdbSChen-Yu Tsai 	case 4:
350d787dcdbSChen-Yu Tsai 		cmd = RSB_CMD_RD32;
351d787dcdbSChen-Yu Tsai 		break;
352d787dcdbSChen-Yu Tsai 	default:
3532d3e8f70SAndre Przywara 		dev_err(rsb->dev, "Invalid access width: %zd\n", len);
354d787dcdbSChen-Yu Tsai 		return -EINVAL;
355d787dcdbSChen-Yu Tsai 	}
356d787dcdbSChen-Yu Tsai 
3574a0dbc12SSamuel Holland 	ret = pm_runtime_resume_and_get(rsb->dev);
3584a0dbc12SSamuel Holland 	if (ret)
3594a0dbc12SSamuel Holland 		return ret;
3604a0dbc12SSamuel Holland 
361d787dcdbSChen-Yu Tsai 	mutex_lock(&rsb->lock);
362d787dcdbSChen-Yu Tsai 
363d787dcdbSChen-Yu Tsai 	writel(addr, rsb->regs + RSB_ADDR);
364d787dcdbSChen-Yu Tsai 	writel(RSB_DAR_RTA(rtaddr), rsb->regs + RSB_DAR);
365d787dcdbSChen-Yu Tsai 	writel(cmd, rsb->regs + RSB_CMD);
366d787dcdbSChen-Yu Tsai 
367d787dcdbSChen-Yu Tsai 	ret = _sunxi_rsb_run_xfer(rsb);
368d787dcdbSChen-Yu Tsai 	if (ret)
36943675ffaSDan Carpenter 		goto unlock;
370d787dcdbSChen-Yu Tsai 
371a43ab30dSOndrej Jirman 	*buf = readl(rsb->regs + RSB_DATA) & GENMASK(len * 8 - 1, 0);
372d787dcdbSChen-Yu Tsai 
37343675ffaSDan Carpenter unlock:
374d787dcdbSChen-Yu Tsai 	mutex_unlock(&rsb->lock);
375d787dcdbSChen-Yu Tsai 
3764a0dbc12SSamuel Holland 	pm_runtime_mark_last_busy(rsb->dev);
3774a0dbc12SSamuel Holland 	pm_runtime_put_autosuspend(rsb->dev);
3784a0dbc12SSamuel Holland 
379d787dcdbSChen-Yu Tsai 	return ret;
380d787dcdbSChen-Yu Tsai }
381d787dcdbSChen-Yu Tsai 
sunxi_rsb_write(struct sunxi_rsb * rsb,u8 rtaddr,u8 addr,const u32 * buf,size_t len)382d787dcdbSChen-Yu Tsai static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr,
383d787dcdbSChen-Yu Tsai 			   const u32 *buf, size_t len)
384d787dcdbSChen-Yu Tsai {
385d787dcdbSChen-Yu Tsai 	u32 cmd;
386d787dcdbSChen-Yu Tsai 	int ret;
387d787dcdbSChen-Yu Tsai 
388d787dcdbSChen-Yu Tsai 	if (!buf)
389d787dcdbSChen-Yu Tsai 		return -EINVAL;
390d787dcdbSChen-Yu Tsai 
391d787dcdbSChen-Yu Tsai 	switch (len) {
392d787dcdbSChen-Yu Tsai 	case 1:
393d787dcdbSChen-Yu Tsai 		cmd = RSB_CMD_WR8;
394d787dcdbSChen-Yu Tsai 		break;
395d787dcdbSChen-Yu Tsai 	case 2:
396d787dcdbSChen-Yu Tsai 		cmd = RSB_CMD_WR16;
397d787dcdbSChen-Yu Tsai 		break;
398d787dcdbSChen-Yu Tsai 	case 4:
399d787dcdbSChen-Yu Tsai 		cmd = RSB_CMD_WR32;
400d787dcdbSChen-Yu Tsai 		break;
401d787dcdbSChen-Yu Tsai 	default:
4022d3e8f70SAndre Przywara 		dev_err(rsb->dev, "Invalid access width: %zd\n", len);
403d787dcdbSChen-Yu Tsai 		return -EINVAL;
404d787dcdbSChen-Yu Tsai 	}
405d787dcdbSChen-Yu Tsai 
4064a0dbc12SSamuel Holland 	ret = pm_runtime_resume_and_get(rsb->dev);
4074a0dbc12SSamuel Holland 	if (ret)
4084a0dbc12SSamuel Holland 		return ret;
4094a0dbc12SSamuel Holland 
410d787dcdbSChen-Yu Tsai 	mutex_lock(&rsb->lock);
411d787dcdbSChen-Yu Tsai 
412d787dcdbSChen-Yu Tsai 	writel(addr, rsb->regs + RSB_ADDR);
413d787dcdbSChen-Yu Tsai 	writel(RSB_DAR_RTA(rtaddr), rsb->regs + RSB_DAR);
414d787dcdbSChen-Yu Tsai 	writel(*buf, rsb->regs + RSB_DATA);
415d787dcdbSChen-Yu Tsai 	writel(cmd, rsb->regs + RSB_CMD);
416d787dcdbSChen-Yu Tsai 	ret = _sunxi_rsb_run_xfer(rsb);
417d787dcdbSChen-Yu Tsai 
418d787dcdbSChen-Yu Tsai 	mutex_unlock(&rsb->lock);
419d787dcdbSChen-Yu Tsai 
4204a0dbc12SSamuel Holland 	pm_runtime_mark_last_busy(rsb->dev);
4214a0dbc12SSamuel Holland 	pm_runtime_put_autosuspend(rsb->dev);
4224a0dbc12SSamuel Holland 
423d787dcdbSChen-Yu Tsai 	return ret;
424d787dcdbSChen-Yu Tsai }
425d787dcdbSChen-Yu Tsai 
426d787dcdbSChen-Yu Tsai /* RSB regmap functions */
427d787dcdbSChen-Yu Tsai struct sunxi_rsb_ctx {
428d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_device *rdev;
429d787dcdbSChen-Yu Tsai 	int size;
430d787dcdbSChen-Yu Tsai };
431d787dcdbSChen-Yu Tsai 
regmap_sunxi_rsb_reg_read(void * context,unsigned int reg,unsigned int * val)432d787dcdbSChen-Yu Tsai static int regmap_sunxi_rsb_reg_read(void *context, unsigned int reg,
433d787dcdbSChen-Yu Tsai 				     unsigned int *val)
434d787dcdbSChen-Yu Tsai {
435d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_ctx *ctx = context;
436d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_device *rdev = ctx->rdev;
437d787dcdbSChen-Yu Tsai 
438d787dcdbSChen-Yu Tsai 	if (reg > 0xff)
439d787dcdbSChen-Yu Tsai 		return -EINVAL;
440d787dcdbSChen-Yu Tsai 
441d787dcdbSChen-Yu Tsai 	return sunxi_rsb_read(rdev->rsb, rdev->rtaddr, reg, val, ctx->size);
442d787dcdbSChen-Yu Tsai }
443d787dcdbSChen-Yu Tsai 
regmap_sunxi_rsb_reg_write(void * context,unsigned int reg,unsigned int val)444d787dcdbSChen-Yu Tsai static int regmap_sunxi_rsb_reg_write(void *context, unsigned int reg,
445d787dcdbSChen-Yu Tsai 				      unsigned int val)
446d787dcdbSChen-Yu Tsai {
447d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_ctx *ctx = context;
448d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_device *rdev = ctx->rdev;
449d787dcdbSChen-Yu Tsai 
450d787dcdbSChen-Yu Tsai 	return sunxi_rsb_write(rdev->rsb, rdev->rtaddr, reg, &val, ctx->size);
451d787dcdbSChen-Yu Tsai }
452d787dcdbSChen-Yu Tsai 
regmap_sunxi_rsb_free_ctx(void * context)453d787dcdbSChen-Yu Tsai static void regmap_sunxi_rsb_free_ctx(void *context)
454d787dcdbSChen-Yu Tsai {
455d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_ctx *ctx = context;
456d787dcdbSChen-Yu Tsai 
457d787dcdbSChen-Yu Tsai 	kfree(ctx);
458d787dcdbSChen-Yu Tsai }
459d787dcdbSChen-Yu Tsai 
460d787dcdbSChen-Yu Tsai static struct regmap_bus regmap_sunxi_rsb = {
461d787dcdbSChen-Yu Tsai 	.reg_write = regmap_sunxi_rsb_reg_write,
462d787dcdbSChen-Yu Tsai 	.reg_read = regmap_sunxi_rsb_reg_read,
463d787dcdbSChen-Yu Tsai 	.free_context = regmap_sunxi_rsb_free_ctx,
464d787dcdbSChen-Yu Tsai 	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
465d787dcdbSChen-Yu Tsai 	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
466d787dcdbSChen-Yu Tsai };
467d787dcdbSChen-Yu Tsai 
regmap_sunxi_rsb_init_ctx(struct sunxi_rsb_device * rdev,const struct regmap_config * config)468d787dcdbSChen-Yu Tsai static struct sunxi_rsb_ctx *regmap_sunxi_rsb_init_ctx(struct sunxi_rsb_device *rdev,
469d787dcdbSChen-Yu Tsai 		const struct regmap_config *config)
470d787dcdbSChen-Yu Tsai {
471d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_ctx *ctx;
472d787dcdbSChen-Yu Tsai 
473d787dcdbSChen-Yu Tsai 	switch (config->val_bits) {
474d787dcdbSChen-Yu Tsai 	case 8:
475d787dcdbSChen-Yu Tsai 	case 16:
476d787dcdbSChen-Yu Tsai 	case 32:
477d787dcdbSChen-Yu Tsai 		break;
478d787dcdbSChen-Yu Tsai 	default:
479d787dcdbSChen-Yu Tsai 		return ERR_PTR(-EINVAL);
480d787dcdbSChen-Yu Tsai 	}
481d787dcdbSChen-Yu Tsai 
482d787dcdbSChen-Yu Tsai 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
483d787dcdbSChen-Yu Tsai 	if (!ctx)
484d787dcdbSChen-Yu Tsai 		return ERR_PTR(-ENOMEM);
485d787dcdbSChen-Yu Tsai 
486d787dcdbSChen-Yu Tsai 	ctx->rdev = rdev;
487d787dcdbSChen-Yu Tsai 	ctx->size = config->val_bits / 8;
488d787dcdbSChen-Yu Tsai 
489d787dcdbSChen-Yu Tsai 	return ctx;
490d787dcdbSChen-Yu Tsai }
491d787dcdbSChen-Yu Tsai 
__devm_regmap_init_sunxi_rsb(struct sunxi_rsb_device * rdev,const struct regmap_config * config,struct lock_class_key * lock_key,const char * lock_name)492d787dcdbSChen-Yu Tsai struct regmap *__devm_regmap_init_sunxi_rsb(struct sunxi_rsb_device *rdev,
493d787dcdbSChen-Yu Tsai 					    const struct regmap_config *config,
494d787dcdbSChen-Yu Tsai 					    struct lock_class_key *lock_key,
495d787dcdbSChen-Yu Tsai 					    const char *lock_name)
496d787dcdbSChen-Yu Tsai {
497d787dcdbSChen-Yu Tsai 	struct sunxi_rsb_ctx *ctx = regmap_sunxi_rsb_init_ctx(rdev, config);
498d787dcdbSChen-Yu Tsai 
499d787dcdbSChen-Yu Tsai 	if (IS_ERR(ctx))
500d787dcdbSChen-Yu Tsai 		return ERR_CAST(ctx);
501d787dcdbSChen-Yu Tsai 
502d787dcdbSChen-Yu Tsai 	return __devm_regmap_init(&rdev->dev, &regmap_sunxi_rsb, ctx, config,
503d787dcdbSChen-Yu Tsai 				  lock_key, lock_name);
504d787dcdbSChen-Yu Tsai }
505d787dcdbSChen-Yu Tsai EXPORT_SYMBOL_GPL(__devm_regmap_init_sunxi_rsb);
506d787dcdbSChen-Yu Tsai 
507d787dcdbSChen-Yu Tsai /* RSB controller driver functions */
sunxi_rsb_irq(int irq,void * dev_id)508d787dcdbSChen-Yu Tsai static irqreturn_t sunxi_rsb_irq(int irq, void *dev_id)
509d787dcdbSChen-Yu Tsai {
510d787dcdbSChen-Yu Tsai 	struct sunxi_rsb *rsb = dev_id;
511d787dcdbSChen-Yu Tsai 	u32 status;
512d787dcdbSChen-Yu Tsai 
513d787dcdbSChen-Yu Tsai 	status = readl(rsb->regs + RSB_INTS);
514d787dcdbSChen-Yu Tsai 	rsb->status = status;
515d787dcdbSChen-Yu Tsai 
516d787dcdbSChen-Yu Tsai 	/* Clear interrupts */
517d787dcdbSChen-Yu Tsai 	status &= (RSB_INTS_LOAD_BSY | RSB_INTS_TRANS_ERR |
518d787dcdbSChen-Yu Tsai 		   RSB_INTS_TRANS_OVER);
519d787dcdbSChen-Yu Tsai 	writel(status, rsb->regs + RSB_INTS);
520d787dcdbSChen-Yu Tsai 
521d787dcdbSChen-Yu Tsai 	complete(&rsb->complete);
522d787dcdbSChen-Yu Tsai 
523d787dcdbSChen-Yu Tsai 	return IRQ_HANDLED;
524d787dcdbSChen-Yu Tsai }
525d787dcdbSChen-Yu Tsai 
sunxi_rsb_init_device_mode(struct sunxi_rsb * rsb)526d787dcdbSChen-Yu Tsai static int sunxi_rsb_init_device_mode(struct sunxi_rsb *rsb)
527d787dcdbSChen-Yu Tsai {
528d787dcdbSChen-Yu Tsai 	int ret = 0;
529d787dcdbSChen-Yu Tsai 	u32 reg;
530d787dcdbSChen-Yu Tsai 
531d787dcdbSChen-Yu Tsai 	/* send init sequence */
532d787dcdbSChen-Yu Tsai 	writel(RSB_DMCR_DEVICE_START | RSB_DMCR_MODE_DATA |
533d787dcdbSChen-Yu Tsai 	       RSB_DMCR_MODE_REG | RSB_DMCR_DEV_ADDR, rsb->regs + RSB_DMCR);
534d787dcdbSChen-Yu Tsai 
535d787dcdbSChen-Yu Tsai 	readl_poll_timeout(rsb->regs + RSB_DMCR, reg,
536d787dcdbSChen-Yu Tsai 			   !(reg & RSB_DMCR_DEVICE_START), 100, 250000);
537d787dcdbSChen-Yu Tsai 	if (reg & RSB_DMCR_DEVICE_START)
538d787dcdbSChen-Yu Tsai 		ret = -ETIMEDOUT;
539d787dcdbSChen-Yu Tsai 
540d787dcdbSChen-Yu Tsai 	/* clear interrupt status bits */
541d787dcdbSChen-Yu Tsai 	writel(readl(rsb->regs + RSB_INTS), rsb->regs + RSB_INTS);
542d787dcdbSChen-Yu Tsai 
543d787dcdbSChen-Yu Tsai 	return ret;
544d787dcdbSChen-Yu Tsai }
545d787dcdbSChen-Yu Tsai 
546d787dcdbSChen-Yu Tsai /*
547d787dcdbSChen-Yu Tsai  * There are 15 valid runtime addresses, though Allwinner typically
548d787dcdbSChen-Yu Tsai  * skips the first, for unknown reasons, and uses the following three.
549d787dcdbSChen-Yu Tsai  *
550d787dcdbSChen-Yu Tsai  * 0x17, 0x2d, 0x3a, 0x4e, 0x59, 0x63, 0x74, 0x8b,
551d787dcdbSChen-Yu Tsai  * 0x9c, 0xa6, 0xb1, 0xc5, 0xd2, 0xe8, 0xff
552d787dcdbSChen-Yu Tsai  *
553d787dcdbSChen-Yu Tsai  * No designs with 2 RSB slave devices sharing identical hardware
554d787dcdbSChen-Yu Tsai  * addresses on the same bus have been seen in the wild. All designs
555d787dcdbSChen-Yu Tsai  * use 0x2d for the primary PMIC, 0x3a for the secondary PMIC if
556d787dcdbSChen-Yu Tsai  * there is one, and 0x45 for peripheral ICs.
557d787dcdbSChen-Yu Tsai  *
558d787dcdbSChen-Yu Tsai  * The hardware does not seem to support re-setting runtime addresses.
559d787dcdbSChen-Yu Tsai  * Attempts to do so result in the slave devices returning a NACK.
560d787dcdbSChen-Yu Tsai  * Hence we just hardcode the mapping here, like Allwinner does.
561d787dcdbSChen-Yu Tsai  */
562d787dcdbSChen-Yu Tsai 
563d787dcdbSChen-Yu Tsai static const struct sunxi_rsb_addr_map sunxi_rsb_addr_maps[] = {
564427d6e48SChen-Yu Tsai 	{ 0x3a3, 0x2d }, /* Primary PMIC: AXP223, AXP809, AXP81X, ... */
565d787dcdbSChen-Yu Tsai 	{ 0x745, 0x3a }, /* Secondary PMIC: AXP806, ... */
566bccd240fSChen-Yu Tsai 	{ 0xe89, 0x4e }, /* Peripheral IC: AC100, ... */
567d787dcdbSChen-Yu Tsai };
568d787dcdbSChen-Yu Tsai 
sunxi_rsb_get_rtaddr(u16 hwaddr)569d787dcdbSChen-Yu Tsai static u8 sunxi_rsb_get_rtaddr(u16 hwaddr)
570d787dcdbSChen-Yu Tsai {
571d787dcdbSChen-Yu Tsai 	int i;
572d787dcdbSChen-Yu Tsai 
573d787dcdbSChen-Yu Tsai 	for (i = 0; i < ARRAY_SIZE(sunxi_rsb_addr_maps); i++)
574d787dcdbSChen-Yu Tsai 		if (hwaddr == sunxi_rsb_addr_maps[i].hwaddr)
575d787dcdbSChen-Yu Tsai 			return sunxi_rsb_addr_maps[i].rtaddr;
576d787dcdbSChen-Yu Tsai 
577d787dcdbSChen-Yu Tsai 	return 0; /* 0 is an invalid runtime address */
578d787dcdbSChen-Yu Tsai }
579d787dcdbSChen-Yu Tsai 
of_rsb_register_devices(struct sunxi_rsb * rsb)580d787dcdbSChen-Yu Tsai static int of_rsb_register_devices(struct sunxi_rsb *rsb)
581d787dcdbSChen-Yu Tsai {
582d787dcdbSChen-Yu Tsai 	struct device *dev = rsb->dev;
583d787dcdbSChen-Yu Tsai 	struct device_node *child, *np = dev->of_node;
584d787dcdbSChen-Yu Tsai 	u32 hwaddr;
585d787dcdbSChen-Yu Tsai 	u8 rtaddr;
586d787dcdbSChen-Yu Tsai 	int ret;
587d787dcdbSChen-Yu Tsai 
588d787dcdbSChen-Yu Tsai 	if (!np)
589d787dcdbSChen-Yu Tsai 		return -EINVAL;
590d787dcdbSChen-Yu Tsai 
591d787dcdbSChen-Yu Tsai 	/* Runtime addresses for all slaves should be set first */
592d787dcdbSChen-Yu Tsai 	for_each_available_child_of_node(np, child) {
5939c0982d8SRob Herring 		dev_dbg(dev, "setting child %pOF runtime address\n",
5949c0982d8SRob Herring 			child);
595d787dcdbSChen-Yu Tsai 
596d787dcdbSChen-Yu Tsai 		ret = of_property_read_u32(child, "reg", &hwaddr);
597d787dcdbSChen-Yu Tsai 		if (ret) {
5989c0982d8SRob Herring 			dev_err(dev, "%pOF: invalid 'reg' property: %d\n",
5999c0982d8SRob Herring 				child, ret);
600d787dcdbSChen-Yu Tsai 			continue;
601d787dcdbSChen-Yu Tsai 		}
602d787dcdbSChen-Yu Tsai 
603d787dcdbSChen-Yu Tsai 		rtaddr = sunxi_rsb_get_rtaddr(hwaddr);
604d787dcdbSChen-Yu Tsai 		if (!rtaddr) {
6059c0982d8SRob Herring 			dev_err(dev, "%pOF: unknown hardware device address\n",
6069c0982d8SRob Herring 				child);
607d787dcdbSChen-Yu Tsai 			continue;
608d787dcdbSChen-Yu Tsai 		}
609d787dcdbSChen-Yu Tsai 
610d787dcdbSChen-Yu Tsai 		/*
611d787dcdbSChen-Yu Tsai 		 * Since no devices have been registered yet, we are the
612d787dcdbSChen-Yu Tsai 		 * only ones using the bus, we can skip locking the bus.
613d787dcdbSChen-Yu Tsai 		 */
614d787dcdbSChen-Yu Tsai 
615d787dcdbSChen-Yu Tsai 		/* setup command parameters */
616d787dcdbSChen-Yu Tsai 		writel(RSB_CMD_STRA, rsb->regs + RSB_CMD);
617d787dcdbSChen-Yu Tsai 		writel(RSB_DAR_RTA(rtaddr) | RSB_DAR_DA(hwaddr),
618d787dcdbSChen-Yu Tsai 		       rsb->regs + RSB_DAR);
619d787dcdbSChen-Yu Tsai 
620d787dcdbSChen-Yu Tsai 		/* send command */
621d787dcdbSChen-Yu Tsai 		ret = _sunxi_rsb_run_xfer(rsb);
622d787dcdbSChen-Yu Tsai 		if (ret)
6239c0982d8SRob Herring 			dev_warn(dev, "%pOF: set runtime address failed: %d\n",
6249c0982d8SRob Herring 				 child, ret);
625d787dcdbSChen-Yu Tsai 	}
626d787dcdbSChen-Yu Tsai 
627d787dcdbSChen-Yu Tsai 	/* Then we start adding devices and probing them */
628d787dcdbSChen-Yu Tsai 	for_each_available_child_of_node(np, child) {
629d787dcdbSChen-Yu Tsai 		struct sunxi_rsb_device *rdev;
630d787dcdbSChen-Yu Tsai 
6319c0982d8SRob Herring 		dev_dbg(dev, "adding child %pOF\n", child);
632d787dcdbSChen-Yu Tsai 
633d787dcdbSChen-Yu Tsai 		ret = of_property_read_u32(child, "reg", &hwaddr);
634d787dcdbSChen-Yu Tsai 		if (ret)
635d787dcdbSChen-Yu Tsai 			continue;
636d787dcdbSChen-Yu Tsai 
637d787dcdbSChen-Yu Tsai 		rtaddr = sunxi_rsb_get_rtaddr(hwaddr);
638d787dcdbSChen-Yu Tsai 		if (!rtaddr)
639d787dcdbSChen-Yu Tsai 			continue;
640d787dcdbSChen-Yu Tsai 
641d787dcdbSChen-Yu Tsai 		rdev = sunxi_rsb_device_create(rsb, child, hwaddr, rtaddr);
642d787dcdbSChen-Yu Tsai 		if (IS_ERR(rdev))
6439c0982d8SRob Herring 			dev_err(dev, "failed to add child device %pOF: %ld\n",
6449c0982d8SRob Herring 				child, PTR_ERR(rdev));
645d787dcdbSChen-Yu Tsai 	}
646d787dcdbSChen-Yu Tsai 
647d787dcdbSChen-Yu Tsai 	return 0;
648d787dcdbSChen-Yu Tsai }
649d787dcdbSChen-Yu Tsai 
sunxi_rsb_hw_init(struct sunxi_rsb * rsb)65022754ac9SSamuel Holland static int sunxi_rsb_hw_init(struct sunxi_rsb *rsb)
65122754ac9SSamuel Holland {
65222754ac9SSamuel Holland 	struct device *dev = rsb->dev;
65322754ac9SSamuel Holland 	unsigned long p_clk_freq;
65422754ac9SSamuel Holland 	u32 clk_delay, reg;
65522754ac9SSamuel Holland 	int clk_div, ret;
65622754ac9SSamuel Holland 
65722754ac9SSamuel Holland 	ret = clk_prepare_enable(rsb->clk);
65822754ac9SSamuel Holland 	if (ret) {
65922754ac9SSamuel Holland 		dev_err(dev, "failed to enable clk: %d\n", ret);
66022754ac9SSamuel Holland 		return ret;
66122754ac9SSamuel Holland 	}
66222754ac9SSamuel Holland 
66322754ac9SSamuel Holland 	ret = reset_control_deassert(rsb->rstc);
66422754ac9SSamuel Holland 	if (ret) {
66522754ac9SSamuel Holland 		dev_err(dev, "failed to deassert reset line: %d\n", ret);
66622754ac9SSamuel Holland 		goto err_clk_disable;
66722754ac9SSamuel Holland 	}
66822754ac9SSamuel Holland 
66922754ac9SSamuel Holland 	/* reset the controller */
67022754ac9SSamuel Holland 	writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
67122754ac9SSamuel Holland 	readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
67222754ac9SSamuel Holland 			   !(reg & RSB_CTRL_SOFT_RST), 1000, 100000);
67322754ac9SSamuel Holland 
67422754ac9SSamuel Holland 	/*
67522754ac9SSamuel Holland 	 * Clock frequency and delay calculation code is from
67622754ac9SSamuel Holland 	 * Allwinner U-boot sources.
67722754ac9SSamuel Holland 	 *
67822754ac9SSamuel Holland 	 * From A83 user manual:
67922754ac9SSamuel Holland 	 * bus clock frequency = parent clock frequency / (2 * (divider + 1))
68022754ac9SSamuel Holland 	 */
68122754ac9SSamuel Holland 	p_clk_freq = clk_get_rate(rsb->clk);
68222754ac9SSamuel Holland 	clk_div = p_clk_freq / rsb->clk_freq / 2;
68322754ac9SSamuel Holland 	if (!clk_div)
68422754ac9SSamuel Holland 		clk_div = 1;
68522754ac9SSamuel Holland 	else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
68622754ac9SSamuel Holland 		clk_div = RSB_CCR_MAX_CLK_DIV + 1;
68722754ac9SSamuel Holland 
68822754ac9SSamuel Holland 	clk_delay = clk_div >> 1;
68922754ac9SSamuel Holland 	if (!clk_delay)
69022754ac9SSamuel Holland 		clk_delay = 1;
69122754ac9SSamuel Holland 
69222754ac9SSamuel Holland 	dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
69322754ac9SSamuel Holland 	writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
69422754ac9SSamuel Holland 	       rsb->regs + RSB_CCR);
69522754ac9SSamuel Holland 
69622754ac9SSamuel Holland 	return 0;
69722754ac9SSamuel Holland 
69822754ac9SSamuel Holland err_clk_disable:
69922754ac9SSamuel Holland 	clk_disable_unprepare(rsb->clk);
70022754ac9SSamuel Holland 
70122754ac9SSamuel Holland 	return ret;
70222754ac9SSamuel Holland }
70322754ac9SSamuel Holland 
sunxi_rsb_hw_exit(struct sunxi_rsb * rsb)70422754ac9SSamuel Holland static void sunxi_rsb_hw_exit(struct sunxi_rsb *rsb)
70522754ac9SSamuel Holland {
70622754ac9SSamuel Holland 	reset_control_assert(rsb->rstc);
707017a716eSJernej Skrabec 
708017a716eSJernej Skrabec 	/* Keep the clock and PM reference counts consistent. */
709017a716eSJernej Skrabec 	if (!pm_runtime_status_suspended(rsb->dev))
71022754ac9SSamuel Holland 		clk_disable_unprepare(rsb->clk);
71122754ac9SSamuel Holland }
71222754ac9SSamuel Holland 
sunxi_rsb_runtime_suspend(struct device * dev)7134a0dbc12SSamuel Holland static int __maybe_unused sunxi_rsb_runtime_suspend(struct device *dev)
7144a0dbc12SSamuel Holland {
7154a0dbc12SSamuel Holland 	struct sunxi_rsb *rsb = dev_get_drvdata(dev);
7164a0dbc12SSamuel Holland 
7174a0dbc12SSamuel Holland 	clk_disable_unprepare(rsb->clk);
7184a0dbc12SSamuel Holland 
7194a0dbc12SSamuel Holland 	return 0;
7204a0dbc12SSamuel Holland }
7214a0dbc12SSamuel Holland 
sunxi_rsb_runtime_resume(struct device * dev)7224a0dbc12SSamuel Holland static int __maybe_unused sunxi_rsb_runtime_resume(struct device *dev)
7234a0dbc12SSamuel Holland {
7244a0dbc12SSamuel Holland 	struct sunxi_rsb *rsb = dev_get_drvdata(dev);
7254a0dbc12SSamuel Holland 
7264a0dbc12SSamuel Holland 	return clk_prepare_enable(rsb->clk);
7274a0dbc12SSamuel Holland }
7284a0dbc12SSamuel Holland 
sunxi_rsb_suspend(struct device * dev)72984310749SSamuel Holland static int __maybe_unused sunxi_rsb_suspend(struct device *dev)
73084310749SSamuel Holland {
73184310749SSamuel Holland 	struct sunxi_rsb *rsb = dev_get_drvdata(dev);
73284310749SSamuel Holland 
73384310749SSamuel Holland 	sunxi_rsb_hw_exit(rsb);
73484310749SSamuel Holland 
73584310749SSamuel Holland 	return 0;
73684310749SSamuel Holland }
73784310749SSamuel Holland 
sunxi_rsb_resume(struct device * dev)73884310749SSamuel Holland static int __maybe_unused sunxi_rsb_resume(struct device *dev)
73984310749SSamuel Holland {
74084310749SSamuel Holland 	struct sunxi_rsb *rsb = dev_get_drvdata(dev);
74184310749SSamuel Holland 
74284310749SSamuel Holland 	return sunxi_rsb_hw_init(rsb);
74384310749SSamuel Holland }
74484310749SSamuel Holland 
sunxi_rsb_probe(struct platform_device * pdev)745d787dcdbSChen-Yu Tsai static int sunxi_rsb_probe(struct platform_device *pdev)
746d787dcdbSChen-Yu Tsai {
747d787dcdbSChen-Yu Tsai 	struct device *dev = &pdev->dev;
748d787dcdbSChen-Yu Tsai 	struct device_node *np = dev->of_node;
749d787dcdbSChen-Yu Tsai 	struct sunxi_rsb *rsb;
75022754ac9SSamuel Holland 	u32 clk_freq = 3000000;
75122754ac9SSamuel Holland 	int irq, ret;
752d787dcdbSChen-Yu Tsai 
753d787dcdbSChen-Yu Tsai 	of_property_read_u32(np, "clock-frequency", &clk_freq);
754d787dcdbSChen-Yu Tsai 	if (clk_freq > RSB_MAX_FREQ) {
755d787dcdbSChen-Yu Tsai 		dev_err(dev,
756d787dcdbSChen-Yu Tsai 			"clock-frequency (%u Hz) is too high (max = 20MHz)\n",
757d787dcdbSChen-Yu Tsai 			clk_freq);
758d787dcdbSChen-Yu Tsai 		return -EINVAL;
759d787dcdbSChen-Yu Tsai 	}
760d787dcdbSChen-Yu Tsai 
761d787dcdbSChen-Yu Tsai 	rsb = devm_kzalloc(dev, sizeof(*rsb), GFP_KERNEL);
762d787dcdbSChen-Yu Tsai 	if (!rsb)
763d787dcdbSChen-Yu Tsai 		return -ENOMEM;
764d787dcdbSChen-Yu Tsai 
765d787dcdbSChen-Yu Tsai 	rsb->dev = dev;
76622754ac9SSamuel Holland 	rsb->clk_freq = clk_freq;
767d787dcdbSChen-Yu Tsai 	platform_set_drvdata(pdev, rsb);
76801b76ae6SYangtao Li 	rsb->regs = devm_platform_ioremap_resource(pdev, 0);
769d787dcdbSChen-Yu Tsai 	if (IS_ERR(rsb->regs))
770d787dcdbSChen-Yu Tsai 		return PTR_ERR(rsb->regs);
771d787dcdbSChen-Yu Tsai 
772d787dcdbSChen-Yu Tsai 	irq = platform_get_irq(pdev, 0);
7739d4db2f5SStephen Boyd 	if (irq < 0)
774d787dcdbSChen-Yu Tsai 		return irq;
775d787dcdbSChen-Yu Tsai 
776d787dcdbSChen-Yu Tsai 	rsb->clk = devm_clk_get(dev, NULL);
777d787dcdbSChen-Yu Tsai 	if (IS_ERR(rsb->clk)) {
778d787dcdbSChen-Yu Tsai 		ret = PTR_ERR(rsb->clk);
779d787dcdbSChen-Yu Tsai 		dev_err(dev, "failed to retrieve clk: %d\n", ret);
780d787dcdbSChen-Yu Tsai 		return ret;
781d787dcdbSChen-Yu Tsai 	}
782d787dcdbSChen-Yu Tsai 
783d787dcdbSChen-Yu Tsai 	rsb->rstc = devm_reset_control_get(dev, NULL);
784d787dcdbSChen-Yu Tsai 	if (IS_ERR(rsb->rstc)) {
785d787dcdbSChen-Yu Tsai 		ret = PTR_ERR(rsb->rstc);
786d787dcdbSChen-Yu Tsai 		dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
78722754ac9SSamuel Holland 		return ret;
788d787dcdbSChen-Yu Tsai 	}
789d787dcdbSChen-Yu Tsai 
790d787dcdbSChen-Yu Tsai 	init_completion(&rsb->complete);
791d787dcdbSChen-Yu Tsai 	mutex_init(&rsb->lock);
792d787dcdbSChen-Yu Tsai 
793d787dcdbSChen-Yu Tsai 	ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb);
794d787dcdbSChen-Yu Tsai 	if (ret) {
795d787dcdbSChen-Yu Tsai 		dev_err(dev, "can't register interrupt handler irq %d: %d\n",
796d787dcdbSChen-Yu Tsai 			irq, ret);
79722754ac9SSamuel Holland 		return ret;
798d787dcdbSChen-Yu Tsai 	}
799d787dcdbSChen-Yu Tsai 
80022754ac9SSamuel Holland 	ret = sunxi_rsb_hw_init(rsb);
80122754ac9SSamuel Holland 	if (ret)
80222754ac9SSamuel Holland 		return ret;
80322754ac9SSamuel Holland 
804d787dcdbSChen-Yu Tsai 	/* initialize all devices on the bus into RSB mode */
805d787dcdbSChen-Yu Tsai 	ret = sunxi_rsb_init_device_mode(rsb);
806d787dcdbSChen-Yu Tsai 	if (ret)
807d787dcdbSChen-Yu Tsai 		dev_warn(dev, "Initialize device mode failed: %d\n", ret);
808d787dcdbSChen-Yu Tsai 
8094a0dbc12SSamuel Holland 	pm_suspend_ignore_children(dev, true);
8104a0dbc12SSamuel Holland 	pm_runtime_set_active(dev);
8114a0dbc12SSamuel Holland 	pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
8124a0dbc12SSamuel Holland 	pm_runtime_use_autosuspend(dev);
8134a0dbc12SSamuel Holland 	pm_runtime_enable(dev);
8144a0dbc12SSamuel Holland 
815d787dcdbSChen-Yu Tsai 	of_rsb_register_devices(rsb);
816d787dcdbSChen-Yu Tsai 
817d787dcdbSChen-Yu Tsai 	return 0;
818d787dcdbSChen-Yu Tsai }
819d787dcdbSChen-Yu Tsai 
sunxi_rsb_remove(struct platform_device * pdev)820d787dcdbSChen-Yu Tsai static int sunxi_rsb_remove(struct platform_device *pdev)
821d787dcdbSChen-Yu Tsai {
822d787dcdbSChen-Yu Tsai 	struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
823d787dcdbSChen-Yu Tsai 
824d787dcdbSChen-Yu Tsai 	device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices);
8254a0dbc12SSamuel Holland 	pm_runtime_disable(&pdev->dev);
82622754ac9SSamuel Holland 	sunxi_rsb_hw_exit(rsb);
827d787dcdbSChen-Yu Tsai 
828d787dcdbSChen-Yu Tsai 	return 0;
829d787dcdbSChen-Yu Tsai }
830d787dcdbSChen-Yu Tsai 
83184310749SSamuel Holland static const struct dev_pm_ops sunxi_rsb_dev_pm_ops = {
8324a0dbc12SSamuel Holland 	SET_RUNTIME_PM_OPS(sunxi_rsb_runtime_suspend,
8334a0dbc12SSamuel Holland 			   sunxi_rsb_runtime_resume, NULL)
83484310749SSamuel Holland 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sunxi_rsb_suspend, sunxi_rsb_resume)
83584310749SSamuel Holland };
83684310749SSamuel Holland 
837014d65b6SSamuel Holland static const struct of_device_id sunxi_rsb_of_match_table[] = {
838014d65b6SSamuel Holland 	{ .compatible = "allwinner,sun8i-a23-rsb" },
839014d65b6SSamuel Holland 	{}
840014d65b6SSamuel Holland };
841014d65b6SSamuel Holland MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table);
842014d65b6SSamuel Holland 
843d787dcdbSChen-Yu Tsai static struct platform_driver sunxi_rsb_driver = {
844d787dcdbSChen-Yu Tsai 	.probe = sunxi_rsb_probe,
845d787dcdbSChen-Yu Tsai 	.remove	= sunxi_rsb_remove,
846d787dcdbSChen-Yu Tsai 	.driver	= {
847d787dcdbSChen-Yu Tsai 		.name = RSB_CTRL_NAME,
848d787dcdbSChen-Yu Tsai 		.of_match_table = sunxi_rsb_of_match_table,
84984310749SSamuel Holland 		.pm = &sunxi_rsb_dev_pm_ops,
850d787dcdbSChen-Yu Tsai 	},
851d787dcdbSChen-Yu Tsai };
852d787dcdbSChen-Yu Tsai 
sunxi_rsb_init(void)853d787dcdbSChen-Yu Tsai static int __init sunxi_rsb_init(void)
854d787dcdbSChen-Yu Tsai {
855d787dcdbSChen-Yu Tsai 	int ret;
856d787dcdbSChen-Yu Tsai 
857d787dcdbSChen-Yu Tsai 	ret = bus_register(&sunxi_rsb_bus);
858d787dcdbSChen-Yu Tsai 	if (ret) {
859d787dcdbSChen-Yu Tsai 		pr_err("failed to register sunxi sunxi_rsb bus: %d\n", ret);
860d787dcdbSChen-Yu Tsai 		return ret;
861d787dcdbSChen-Yu Tsai 	}
862d787dcdbSChen-Yu Tsai 
863f71eaf27SYuan Can 	ret = platform_driver_register(&sunxi_rsb_driver);
864f71eaf27SYuan Can 	if (ret) {
865f71eaf27SYuan Can 		bus_unregister(&sunxi_rsb_bus);
866f71eaf27SYuan Can 		return ret;
867f71eaf27SYuan Can 	}
868f71eaf27SYuan Can 
869f71eaf27SYuan Can 	return 0;
870d787dcdbSChen-Yu Tsai }
871d787dcdbSChen-Yu Tsai module_init(sunxi_rsb_init);
872d787dcdbSChen-Yu Tsai 
sunxi_rsb_exit(void)873d787dcdbSChen-Yu Tsai static void __exit sunxi_rsb_exit(void)
874d787dcdbSChen-Yu Tsai {
875d787dcdbSChen-Yu Tsai 	platform_driver_unregister(&sunxi_rsb_driver);
876d787dcdbSChen-Yu Tsai 	bus_unregister(&sunxi_rsb_bus);
877d787dcdbSChen-Yu Tsai }
878d787dcdbSChen-Yu Tsai module_exit(sunxi_rsb_exit);
879d787dcdbSChen-Yu Tsai 
880d787dcdbSChen-Yu Tsai MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
881d787dcdbSChen-Yu Tsai MODULE_DESCRIPTION("Allwinner sunXi Reduced Serial Bus controller driver");
882d787dcdbSChen-Yu Tsai MODULE_LICENSE("GPL v2");
883