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, ®map_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