1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright (c) 2009-2013, The Linux Foundation. All rights reserved. |
3 | * Copyright (c) 2010, Google Inc. |
4 | * |
5 | * Original authors: Code Aurora Forum |
6 | * |
7 | * Author: Dima Zavin <dima@android.com> |
8 | * - Largely rewritten from original to not be an i2c driver. |
9 | */ |
10 | |
11 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
12 | |
13 | #include <linux/delay.h> |
14 | #include <linux/err.h> |
15 | #include <linux/io.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/of.h> |
19 | #include <linux/of_platform.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/ssbi.h> |
23 | |
24 | /* SSBI 2.0 controller registers */ |
25 | #define SSBI2_CMD 0x0008 |
26 | #define SSBI2_RD 0x0010 |
27 | #define SSBI2_STATUS 0x0014 |
28 | #define SSBI2_MODE2 0x001C |
29 | |
30 | /* SSBI_CMD fields */ |
31 | #define SSBI_CMD_RDWRN (1 << 24) |
32 | |
33 | /* SSBI_STATUS fields */ |
34 | #define SSBI_STATUS_RD_READY (1 << 2) |
35 | #define SSBI_STATUS_READY (1 << 1) |
36 | #define SSBI_STATUS_MCHN_BUSY (1 << 0) |
37 | |
38 | /* SSBI_MODE2 fields */ |
39 | #define SSBI_MODE2_REG_ADDR_15_8_SHFT 0x04 |
40 | #define SSBI_MODE2_REG_ADDR_15_8_MASK (0x7f << SSBI_MODE2_REG_ADDR_15_8_SHFT) |
41 | |
42 | #define SET_SSBI_MODE2_REG_ADDR_15_8(MD, AD) \ |
43 | (((MD) & 0x0F) | ((((AD) >> 8) << SSBI_MODE2_REG_ADDR_15_8_SHFT) & \ |
44 | SSBI_MODE2_REG_ADDR_15_8_MASK)) |
45 | |
46 | /* SSBI PMIC Arbiter command registers */ |
47 | #define SSBI_PA_CMD 0x0000 |
48 | #define SSBI_PA_RD_STATUS 0x0004 |
49 | |
50 | /* SSBI_PA_CMD fields */ |
51 | #define SSBI_PA_CMD_RDWRN (1 << 24) |
52 | #define SSBI_PA_CMD_ADDR_MASK 0x7fff /* REG_ADDR_7_0, REG_ADDR_8_14*/ |
53 | |
54 | /* SSBI_PA_RD_STATUS fields */ |
55 | #define SSBI_PA_RD_STATUS_TRANS_DONE (1 << 27) |
56 | #define SSBI_PA_RD_STATUS_TRANS_DENIED (1 << 26) |
57 | |
58 | #define SSBI_TIMEOUT_US 100 |
59 | |
60 | enum ssbi_controller_type { |
61 | MSM_SBI_CTRL_SSBI = 0, |
62 | MSM_SBI_CTRL_SSBI2, |
63 | MSM_SBI_CTRL_PMIC_ARBITER, |
64 | }; |
65 | |
66 | struct ssbi { |
67 | struct device *slave; |
68 | void __iomem *base; |
69 | spinlock_t lock; |
70 | enum ssbi_controller_type controller_type; |
71 | int (*read)(struct ssbi *, u16 addr, u8 *buf, int len); |
72 | int (*write)(struct ssbi *, u16 addr, const u8 *buf, int len); |
73 | }; |
74 | |
75 | static inline u32 ssbi_readl(struct ssbi *ssbi, u32 reg) |
76 | { |
77 | return readl(addr: ssbi->base + reg); |
78 | } |
79 | |
80 | static inline void ssbi_writel(struct ssbi *ssbi, u32 val, u32 reg) |
81 | { |
82 | writel(val, addr: ssbi->base + reg); |
83 | } |
84 | |
85 | /* |
86 | * Via private exchange with one of the original authors, the hardware |
87 | * should generally finish a transaction in about 5us. The worst |
88 | * case, is when using the arbiter and both other CPUs have just |
89 | * started trying to use the SSBI bus will result in a time of about |
90 | * 20us. It should never take longer than this. |
91 | * |
92 | * As such, this wait merely spins, with a udelay. |
93 | */ |
94 | static int ssbi_wait_mask(struct ssbi *ssbi, u32 set_mask, u32 clr_mask) |
95 | { |
96 | u32 timeout = SSBI_TIMEOUT_US; |
97 | u32 val; |
98 | |
99 | while (timeout--) { |
100 | val = ssbi_readl(ssbi, SSBI2_STATUS); |
101 | if (((val & set_mask) == set_mask) && ((val & clr_mask) == 0)) |
102 | return 0; |
103 | udelay(1); |
104 | } |
105 | |
106 | return -ETIMEDOUT; |
107 | } |
108 | |
109 | static int |
110 | ssbi_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) |
111 | { |
112 | u32 cmd = SSBI_CMD_RDWRN | ((addr & 0xff) << 16); |
113 | int ret = 0; |
114 | |
115 | if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { |
116 | u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); |
117 | mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); |
118 | ssbi_writel(ssbi, val: mode2, SSBI2_MODE2); |
119 | } |
120 | |
121 | while (len) { |
122 | ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, clr_mask: 0); |
123 | if (ret) |
124 | goto err; |
125 | |
126 | ssbi_writel(ssbi, val: cmd, SSBI2_CMD); |
127 | ret = ssbi_wait_mask(ssbi, SSBI_STATUS_RD_READY, clr_mask: 0); |
128 | if (ret) |
129 | goto err; |
130 | *buf++ = ssbi_readl(ssbi, SSBI2_RD) & 0xff; |
131 | len--; |
132 | } |
133 | |
134 | err: |
135 | return ret; |
136 | } |
137 | |
138 | static int |
139 | ssbi_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len) |
140 | { |
141 | int ret = 0; |
142 | |
143 | if (ssbi->controller_type == MSM_SBI_CTRL_SSBI2) { |
144 | u32 mode2 = ssbi_readl(ssbi, SSBI2_MODE2); |
145 | mode2 = SET_SSBI_MODE2_REG_ADDR_15_8(mode2, addr); |
146 | ssbi_writel(ssbi, val: mode2, SSBI2_MODE2); |
147 | } |
148 | |
149 | while (len) { |
150 | ret = ssbi_wait_mask(ssbi, SSBI_STATUS_READY, clr_mask: 0); |
151 | if (ret) |
152 | goto err; |
153 | |
154 | ssbi_writel(ssbi, val: ((addr & 0xff) << 16) | *buf, SSBI2_CMD); |
155 | ret = ssbi_wait_mask(ssbi, set_mask: 0, SSBI_STATUS_MCHN_BUSY); |
156 | if (ret) |
157 | goto err; |
158 | buf++; |
159 | len--; |
160 | } |
161 | |
162 | err: |
163 | return ret; |
164 | } |
165 | |
166 | /* |
167 | * See ssbi_wait_mask for an explanation of the time and the |
168 | * busywait. |
169 | */ |
170 | static inline int |
171 | ssbi_pa_transfer(struct ssbi *ssbi, u32 cmd, u8 *data) |
172 | { |
173 | u32 timeout = SSBI_TIMEOUT_US; |
174 | u32 rd_status = 0; |
175 | |
176 | ssbi_writel(ssbi, val: cmd, SSBI_PA_CMD); |
177 | |
178 | while (timeout--) { |
179 | rd_status = ssbi_readl(ssbi, SSBI_PA_RD_STATUS); |
180 | |
181 | if (rd_status & SSBI_PA_RD_STATUS_TRANS_DENIED) |
182 | return -EPERM; |
183 | |
184 | if (rd_status & SSBI_PA_RD_STATUS_TRANS_DONE) { |
185 | if (data) |
186 | *data = rd_status & 0xff; |
187 | return 0; |
188 | } |
189 | udelay(1); |
190 | } |
191 | |
192 | return -ETIMEDOUT; |
193 | } |
194 | |
195 | static int |
196 | ssbi_pa_read_bytes(struct ssbi *ssbi, u16 addr, u8 *buf, int len) |
197 | { |
198 | u32 cmd; |
199 | int ret = 0; |
200 | |
201 | cmd = SSBI_PA_CMD_RDWRN | (addr & SSBI_PA_CMD_ADDR_MASK) << 8; |
202 | |
203 | while (len) { |
204 | ret = ssbi_pa_transfer(ssbi, cmd, data: buf); |
205 | if (ret) |
206 | goto err; |
207 | buf++; |
208 | len--; |
209 | } |
210 | |
211 | err: |
212 | return ret; |
213 | } |
214 | |
215 | static int |
216 | ssbi_pa_write_bytes(struct ssbi *ssbi, u16 addr, const u8 *buf, int len) |
217 | { |
218 | u32 cmd; |
219 | int ret = 0; |
220 | |
221 | while (len) { |
222 | cmd = (addr & SSBI_PA_CMD_ADDR_MASK) << 8 | *buf; |
223 | ret = ssbi_pa_transfer(ssbi, cmd, NULL); |
224 | if (ret) |
225 | goto err; |
226 | buf++; |
227 | len--; |
228 | } |
229 | |
230 | err: |
231 | return ret; |
232 | } |
233 | |
234 | int ssbi_read(struct device *dev, u16 addr, u8 *buf, int len) |
235 | { |
236 | struct ssbi *ssbi = dev_get_drvdata(dev); |
237 | unsigned long flags; |
238 | int ret; |
239 | |
240 | spin_lock_irqsave(&ssbi->lock, flags); |
241 | ret = ssbi->read(ssbi, addr, buf, len); |
242 | spin_unlock_irqrestore(lock: &ssbi->lock, flags); |
243 | |
244 | return ret; |
245 | } |
246 | EXPORT_SYMBOL_GPL(ssbi_read); |
247 | |
248 | int ssbi_write(struct device *dev, u16 addr, const u8 *buf, int len) |
249 | { |
250 | struct ssbi *ssbi = dev_get_drvdata(dev); |
251 | unsigned long flags; |
252 | int ret; |
253 | |
254 | spin_lock_irqsave(&ssbi->lock, flags); |
255 | ret = ssbi->write(ssbi, addr, buf, len); |
256 | spin_unlock_irqrestore(lock: &ssbi->lock, flags); |
257 | |
258 | return ret; |
259 | } |
260 | EXPORT_SYMBOL_GPL(ssbi_write); |
261 | |
262 | static int ssbi_probe(struct platform_device *pdev) |
263 | { |
264 | struct device_node *np = pdev->dev.of_node; |
265 | struct ssbi *ssbi; |
266 | const char *type; |
267 | |
268 | ssbi = devm_kzalloc(dev: &pdev->dev, size: sizeof(*ssbi), GFP_KERNEL); |
269 | if (!ssbi) |
270 | return -ENOMEM; |
271 | |
272 | ssbi->base = devm_platform_get_and_ioremap_resource(pdev, index: 0, NULL); |
273 | if (IS_ERR(ptr: ssbi->base)) |
274 | return PTR_ERR(ptr: ssbi->base); |
275 | |
276 | platform_set_drvdata(pdev, data: ssbi); |
277 | |
278 | type = of_get_property(node: np, name: "qcom,controller-type" , NULL); |
279 | if (type == NULL) { |
280 | dev_err(&pdev->dev, "Missing qcom,controller-type property\n" ); |
281 | return -EINVAL; |
282 | } |
283 | dev_info(&pdev->dev, "SSBI controller type: '%s'\n" , type); |
284 | if (strcmp(type, "ssbi" ) == 0) |
285 | ssbi->controller_type = MSM_SBI_CTRL_SSBI; |
286 | else if (strcmp(type, "ssbi2" ) == 0) |
287 | ssbi->controller_type = MSM_SBI_CTRL_SSBI2; |
288 | else if (strcmp(type, "pmic-arbiter" ) == 0) |
289 | ssbi->controller_type = MSM_SBI_CTRL_PMIC_ARBITER; |
290 | else { |
291 | dev_err(&pdev->dev, "Unknown qcom,controller-type\n" ); |
292 | return -EINVAL; |
293 | } |
294 | |
295 | if (ssbi->controller_type == MSM_SBI_CTRL_PMIC_ARBITER) { |
296 | ssbi->read = ssbi_pa_read_bytes; |
297 | ssbi->write = ssbi_pa_write_bytes; |
298 | } else { |
299 | ssbi->read = ssbi_read_bytes; |
300 | ssbi->write = ssbi_write_bytes; |
301 | } |
302 | |
303 | spin_lock_init(&ssbi->lock); |
304 | |
305 | return devm_of_platform_populate(dev: &pdev->dev); |
306 | } |
307 | |
308 | static const struct of_device_id ssbi_match_table[] = { |
309 | { .compatible = "qcom,ssbi" }, |
310 | {} |
311 | }; |
312 | MODULE_DEVICE_TABLE(of, ssbi_match_table); |
313 | |
314 | static struct platform_driver ssbi_driver = { |
315 | .probe = ssbi_probe, |
316 | .driver = { |
317 | .name = "ssbi" , |
318 | .of_match_table = ssbi_match_table, |
319 | }, |
320 | }; |
321 | module_platform_driver(ssbi_driver); |
322 | |
323 | MODULE_LICENSE("GPL v2" ); |
324 | MODULE_VERSION("1.0" ); |
325 | MODULE_ALIAS("platform:ssbi" ); |
326 | MODULE_AUTHOR("Dima Zavin <dima@android.com>" ); |
327 | |