GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / spmi / hisi-spmi-controller.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/delay.h>
4 #include <linux/err.h>
5 #include <linux/interrupt.h>
6 #include <linux/io.h>
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/of.h>
10 #include <linux/platform_device.h>
11 #include <linux/seq_file.h>
12 #include <linux/slab.h>
13 #include <linux/spmi.h>
14
15 /*
16  * SPMI register addr
17  */
18 #define SPMI_CHANNEL_OFFSET                             0x0300
19 #define SPMI_SLAVE_OFFSET                               0x20
20
21 #define SPMI_APB_SPMI_CMD_BASE_ADDR                     0x0100
22
23 #define SPMI_APB_SPMI_WDATA0_BASE_ADDR                  0x0104
24 #define SPMI_APB_SPMI_WDATA1_BASE_ADDR                  0x0108
25 #define SPMI_APB_SPMI_WDATA2_BASE_ADDR                  0x010c
26 #define SPMI_APB_SPMI_WDATA3_BASE_ADDR                  0x0110
27
28 #define SPMI_APB_SPMI_STATUS_BASE_ADDR                  0x0200
29
30 #define SPMI_APB_SPMI_RDATA0_BASE_ADDR                  0x0204
31 #define SPMI_APB_SPMI_RDATA1_BASE_ADDR                  0x0208
32 #define SPMI_APB_SPMI_RDATA2_BASE_ADDR                  0x020c
33 #define SPMI_APB_SPMI_RDATA3_BASE_ADDR                  0x0210
34
35 #define SPMI_PER_DATAREG_BYTE                           4
36 /*
37  * SPMI cmd register
38  */
39 #define SPMI_APB_SPMI_CMD_EN                            BIT(31)
40 #define SPMI_APB_SPMI_CMD_TYPE_OFFSET                   24
41 #define SPMI_APB_SPMI_CMD_LENGTH_OFFSET                 20
42 #define SPMI_APB_SPMI_CMD_SLAVEID_OFFSET                16
43 #define SPMI_APB_SPMI_CMD_ADDR_OFFSET                   0
44
45 /* Command Opcodes */
46
47 enum spmi_controller_cmd_op_code {
48         SPMI_CMD_REG_ZERO_WRITE = 0,
49         SPMI_CMD_REG_WRITE = 1,
50         SPMI_CMD_REG_READ = 2,
51         SPMI_CMD_EXT_REG_WRITE = 3,
52         SPMI_CMD_EXT_REG_READ = 4,
53         SPMI_CMD_EXT_REG_WRITE_L = 5,
54         SPMI_CMD_EXT_REG_READ_L = 6,
55         SPMI_CMD_REG_RESET = 7,
56         SPMI_CMD_REG_SLEEP = 8,
57         SPMI_CMD_REG_SHUTDOWN = 9,
58         SPMI_CMD_REG_WAKEUP = 10,
59 };
60
61 /*
62  * SPMI status register
63  */
64 #define SPMI_APB_TRANS_DONE                     BIT(0)
65 #define SPMI_APB_TRANS_FAIL                     BIT(2)
66
67 /* Command register fields */
68 #define SPMI_CONTROLLER_CMD_MAX_BYTE_COUNT      16
69
70 /* Maximum number of support PMIC peripherals */
71 #define SPMI_CONTROLLER_TIMEOUT_US              1000
72 #define SPMI_CONTROLLER_MAX_TRANS_BYTES         16
73
74 struct spmi_controller_dev {
75         struct spmi_controller  *controller;
76         struct device           *dev;
77         void __iomem            *base;
78         spinlock_t              lock;
79         u32                     channel;
80 };
81
82 static int spmi_controller_wait_for_done(struct device *dev,
83                                          struct spmi_controller_dev *ctrl_dev,
84                                          void __iomem *base, u8 sid, u16 addr)
85 {
86         u32 timeout = SPMI_CONTROLLER_TIMEOUT_US;
87         u32 status, offset;
88
89         offset  = SPMI_APB_SPMI_STATUS_BASE_ADDR;
90         offset += SPMI_CHANNEL_OFFSET * ctrl_dev->channel + SPMI_SLAVE_OFFSET * sid;
91
92         do {
93                 status = readl(base + offset);
94
95                 if (status & SPMI_APB_TRANS_DONE) {
96                         if (status & SPMI_APB_TRANS_FAIL) {
97                                 dev_err(dev, "%s: transaction failed (0x%x)\n",
98                                         __func__, status);
99                                 return -EIO;
100                         }
101                         dev_dbg(dev, "%s: status 0x%x\n", __func__, status);
102                         return 0;
103                 }
104                 udelay(1);
105         } while (timeout--);
106
107         dev_err(dev, "%s: timeout, status 0x%x\n", __func__, status);
108         return -ETIMEDOUT;
109 }
110
111 static int spmi_read_cmd(struct spmi_controller *ctrl,
112                          u8 opc, u8 slave_id, u16 slave_addr, u8 *__buf, size_t bc)
113 {
114         struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
115         u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
116         unsigned long flags;
117         u8 *buf = __buf;
118         u32 cmd, data;
119         int rc;
120         u8 op_code, i;
121
122         if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
123                 dev_err(&ctrl->dev,
124                         "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
125                         SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
126                 return  -EINVAL;
127         }
128
129         switch (opc) {
130         case SPMI_CMD_READ:
131                 op_code = SPMI_CMD_REG_READ;
132                 break;
133         case SPMI_CMD_EXT_READ:
134                 op_code = SPMI_CMD_EXT_REG_READ;
135                 break;
136         case SPMI_CMD_EXT_READL:
137                 op_code = SPMI_CMD_EXT_REG_READ_L;
138                 break;
139         default:
140                 dev_err(&ctrl->dev, "invalid read cmd 0x%x\n", opc);
141                 return -EINVAL;
142         }
143
144         cmd = SPMI_APB_SPMI_CMD_EN |
145              (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
146              ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
147              ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |  /* slvid */
148              ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET); /* slave_addr */
149
150         spin_lock_irqsave(&spmi_controller->lock, flags);
151
152         writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
153
154         rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
155                                            spmi_controller->base, slave_id, slave_addr);
156         if (rc)
157                 goto done;
158
159         for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
160                 data = readl(spmi_controller->base + chnl_ofst +
161                              SPMI_SLAVE_OFFSET * slave_id +
162                              SPMI_APB_SPMI_RDATA0_BASE_ADDR +
163                              i * SPMI_PER_DATAREG_BYTE);
164                 data = be32_to_cpu((__be32 __force)data);
165                 if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {
166                         memcpy(buf, &data, sizeof(data));
167                         buf += sizeof(data);
168                 } else {
169                         memcpy(buf, &data, bc % SPMI_PER_DATAREG_BYTE);
170                         buf += (bc % SPMI_PER_DATAREG_BYTE);
171                 }
172         }
173
174 done:
175         spin_unlock_irqrestore(&spmi_controller->lock, flags);
176         if (rc)
177                 dev_err(&ctrl->dev,
178                         "spmi read wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",
179                         opc, slave_id, slave_addr, bc + 1);
180         else
181                 dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, read value: %*ph\n",
182                         __func__, slave_id, slave_addr, (int)bc, __buf);
183
184         return rc;
185 }
186
187 static int spmi_write_cmd(struct spmi_controller *ctrl,
188                           u8 opc, u8 slave_id, u16 slave_addr, const u8 *__buf, size_t bc)
189 {
190         struct spmi_controller_dev *spmi_controller = dev_get_drvdata(&ctrl->dev);
191         u32 chnl_ofst = SPMI_CHANNEL_OFFSET * spmi_controller->channel;
192         const u8 *buf = __buf;
193         unsigned long flags;
194         u32 cmd, data;
195         int rc;
196         u8 op_code, i;
197
198         if (bc > SPMI_CONTROLLER_MAX_TRANS_BYTES) {
199                 dev_err(&ctrl->dev,
200                         "spmi_controller supports 1..%d bytes per trans, but:%zu requested\n",
201                         SPMI_CONTROLLER_MAX_TRANS_BYTES, bc);
202                 return  -EINVAL;
203         }
204
205         switch (opc) {
206         case SPMI_CMD_WRITE:
207                 op_code = SPMI_CMD_REG_WRITE;
208                 break;
209         case SPMI_CMD_EXT_WRITE:
210                 op_code = SPMI_CMD_EXT_REG_WRITE;
211                 break;
212         case SPMI_CMD_EXT_WRITEL:
213                 op_code = SPMI_CMD_EXT_REG_WRITE_L;
214                 break;
215         default:
216                 dev_err(&ctrl->dev, "invalid write cmd 0x%x\n", opc);
217                 return -EINVAL;
218         }
219
220         cmd = SPMI_APB_SPMI_CMD_EN |
221               (op_code << SPMI_APB_SPMI_CMD_TYPE_OFFSET) |
222               ((bc - 1) << SPMI_APB_SPMI_CMD_LENGTH_OFFSET) |
223               ((slave_id & 0xf) << SPMI_APB_SPMI_CMD_SLAVEID_OFFSET) |
224               ((slave_addr & 0xffff)  << SPMI_APB_SPMI_CMD_ADDR_OFFSET);
225
226         /* Write data to FIFOs */
227         spin_lock_irqsave(&spmi_controller->lock, flags);
228
229         for (i = 0; bc > i * SPMI_PER_DATAREG_BYTE; i++) {
230                 data = 0;
231                 if ((bc - i * SPMI_PER_DATAREG_BYTE) >> 2) {
232                         memcpy(&data, buf, sizeof(data));
233                         buf += sizeof(data);
234                 } else {
235                         memcpy(&data, buf, bc % SPMI_PER_DATAREG_BYTE);
236                         buf += (bc % SPMI_PER_DATAREG_BYTE);
237                 }
238
239                 writel((u32 __force)cpu_to_be32(data),
240                        spmi_controller->base + chnl_ofst +
241                        SPMI_APB_SPMI_WDATA0_BASE_ADDR +
242                        SPMI_PER_DATAREG_BYTE * i);
243         }
244
245         /* Start the transaction */
246         writel(cmd, spmi_controller->base + chnl_ofst + SPMI_APB_SPMI_CMD_BASE_ADDR);
247
248         rc = spmi_controller_wait_for_done(&ctrl->dev, spmi_controller,
249                                            spmi_controller->base, slave_id,
250                                            slave_addr);
251         spin_unlock_irqrestore(&spmi_controller->lock, flags);
252
253         if (rc)
254                 dev_err(&ctrl->dev, "spmi write wait timeout op:0x%x slave_id:%d slave_addr:0x%x bc:%zu\n",
255                         opc, slave_id, slave_addr, bc);
256         else
257                 dev_dbg(&ctrl->dev, "%s: id:%d slave_addr:0x%x, wrote value: %*ph\n",
258                         __func__, slave_id, slave_addr, (int)bc, __buf);
259
260         return rc;
261 }
262
263 static int spmi_controller_probe(struct platform_device *pdev)
264 {
265         struct spmi_controller_dev *spmi_controller;
266         struct spmi_controller *ctrl;
267         struct resource *iores;
268         int ret;
269
270         ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*spmi_controller));
271         if (!ctrl) {
272                 dev_err(&pdev->dev, "can not allocate spmi_controller data\n");
273                 return -ENOMEM;
274         }
275         spmi_controller = spmi_controller_get_drvdata(ctrl);
276         spmi_controller->controller = ctrl;
277
278         iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
279         if (!iores) {
280                 dev_err(&pdev->dev, "can not get resource!\n");
281                 ret = -EINVAL;
282                 goto err_put_controller;
283         }
284
285         spmi_controller->base = devm_ioremap(&pdev->dev, iores->start,
286                                              resource_size(iores));
287         if (!spmi_controller->base) {
288                 dev_err(&pdev->dev, "can not remap base addr!\n");
289                 ret = -EADDRNOTAVAIL;
290                 goto err_put_controller;
291         }
292
293         ret = of_property_read_u32(pdev->dev.of_node, "hisilicon,spmi-channel",
294                                    &spmi_controller->channel);
295         if (ret) {
296                 dev_err(&pdev->dev, "can not get channel\n");
297                 ret = -ENODEV;
298                 goto err_put_controller;
299         }
300
301         platform_set_drvdata(pdev, spmi_controller);
302         dev_set_drvdata(&ctrl->dev, spmi_controller);
303
304         spin_lock_init(&spmi_controller->lock);
305
306         ctrl->nr = spmi_controller->channel;
307         ctrl->dev.parent = pdev->dev.parent;
308         ctrl->dev.of_node = of_node_get(pdev->dev.of_node);
309
310         /* Callbacks */
311         ctrl->read_cmd = spmi_read_cmd;
312         ctrl->write_cmd = spmi_write_cmd;
313
314         ret = spmi_controller_add(ctrl);
315         if (ret) {
316                 dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret);
317                 goto err_put_controller;
318         }
319
320         return 0;
321
322 err_put_controller:
323         spmi_controller_put(ctrl);
324         return ret;
325 }
326
327 static int spmi_del_controller(struct platform_device *pdev)
328 {
329         struct spmi_controller *ctrl = platform_get_drvdata(pdev);
330
331         spmi_controller_remove(ctrl);
332         spmi_controller_put(ctrl);
333         return 0;
334 }
335
336 static const struct of_device_id spmi_controller_match_table[] = {
337         {
338                 .compatible = "hisilicon,kirin970-spmi-controller",
339         },
340         {}
341 };
342 MODULE_DEVICE_TABLE(of, spmi_controller_match_table);
343
344 static struct platform_driver spmi_controller_driver = {
345         .probe          = spmi_controller_probe,
346         .remove         = spmi_del_controller,
347         .driver         = {
348                 .name   = "hisi_spmi_controller",
349                 .of_match_table = spmi_controller_match_table,
350         },
351 };
352
353 static int __init spmi_controller_init(void)
354 {
355         return platform_driver_register(&spmi_controller_driver);
356 }
357 postcore_initcall(spmi_controller_init);
358
359 static void __exit spmi_controller_exit(void)
360 {
361         platform_driver_unregister(&spmi_controller_driver);
362 }
363 module_exit(spmi_controller_exit);
364
365 MODULE_LICENSE("GPL v2");
366 MODULE_VERSION("1.0");
367 MODULE_ALIAS("platform:spmi_controller");