1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * NBUS driver for TS-4600 based boards |
4 | * |
5 | * Copyright (c) 2016 - Savoir-faire Linux |
6 | * Author: Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com> |
7 | * |
8 | * This driver implements a GPIOs bit-banged bus, called the NBUS by Technologic |
9 | * Systems. It is used to communicate with the peripherals in the FPGA on the |
10 | * TS-4600 SoM. |
11 | */ |
12 | |
13 | #include <linux/bitops.h> |
14 | #include <linux/gpio/consumer.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> |
17 | #include <linux/mutex.h> |
18 | #include <linux/of_platform.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/pwm.h> |
21 | #include <linux/ts-nbus.h> |
22 | |
23 | #define TS_NBUS_DIRECTION_IN 0 |
24 | #define TS_NBUS_DIRECTION_OUT 1 |
25 | #define TS_NBUS_WRITE_ADR 0 |
26 | #define TS_NBUS_WRITE_VAL 1 |
27 | |
28 | struct ts_nbus { |
29 | struct pwm_device *pwm; |
30 | struct gpio_descs *data; |
31 | struct gpio_desc *csn; |
32 | struct gpio_desc *txrx; |
33 | struct gpio_desc *strobe; |
34 | struct gpio_desc *ale; |
35 | struct gpio_desc *rdy; |
36 | struct mutex lock; |
37 | }; |
38 | |
39 | /* |
40 | * request all gpios required by the bus. |
41 | */ |
42 | static int ts_nbus_init_pdata(struct platform_device *pdev, struct ts_nbus |
43 | *ts_nbus) |
44 | { |
45 | ts_nbus->data = devm_gpiod_get_array(dev: &pdev->dev, con_id: "ts,data" , |
46 | flags: GPIOD_OUT_HIGH); |
47 | if (IS_ERR(ptr: ts_nbus->data)) { |
48 | dev_err(&pdev->dev, "failed to retrieve ts,data-gpio from dts\n" ); |
49 | return PTR_ERR(ptr: ts_nbus->data); |
50 | } |
51 | |
52 | ts_nbus->csn = devm_gpiod_get(dev: &pdev->dev, con_id: "ts,csn" , flags: GPIOD_OUT_HIGH); |
53 | if (IS_ERR(ptr: ts_nbus->csn)) { |
54 | dev_err(&pdev->dev, "failed to retrieve ts,csn-gpio from dts\n" ); |
55 | return PTR_ERR(ptr: ts_nbus->csn); |
56 | } |
57 | |
58 | ts_nbus->txrx = devm_gpiod_get(dev: &pdev->dev, con_id: "ts,txrx" , flags: GPIOD_OUT_HIGH); |
59 | if (IS_ERR(ptr: ts_nbus->txrx)) { |
60 | dev_err(&pdev->dev, "failed to retrieve ts,txrx-gpio from dts\n" ); |
61 | return PTR_ERR(ptr: ts_nbus->txrx); |
62 | } |
63 | |
64 | ts_nbus->strobe = devm_gpiod_get(dev: &pdev->dev, con_id: "ts,strobe" , flags: GPIOD_OUT_HIGH); |
65 | if (IS_ERR(ptr: ts_nbus->strobe)) { |
66 | dev_err(&pdev->dev, "failed to retrieve ts,strobe-gpio from dts\n" ); |
67 | return PTR_ERR(ptr: ts_nbus->strobe); |
68 | } |
69 | |
70 | ts_nbus->ale = devm_gpiod_get(dev: &pdev->dev, con_id: "ts,ale" , flags: GPIOD_OUT_HIGH); |
71 | if (IS_ERR(ptr: ts_nbus->ale)) { |
72 | dev_err(&pdev->dev, "failed to retrieve ts,ale-gpio from dts\n" ); |
73 | return PTR_ERR(ptr: ts_nbus->ale); |
74 | } |
75 | |
76 | ts_nbus->rdy = devm_gpiod_get(dev: &pdev->dev, con_id: "ts,rdy" , flags: GPIOD_IN); |
77 | if (IS_ERR(ptr: ts_nbus->rdy)) { |
78 | dev_err(&pdev->dev, "failed to retrieve ts,rdy-gpio from dts\n" ); |
79 | return PTR_ERR(ptr: ts_nbus->rdy); |
80 | } |
81 | |
82 | return 0; |
83 | } |
84 | |
85 | /* |
86 | * the data gpios are used for reading and writing values, their directions |
87 | * should be adjusted accordingly. |
88 | */ |
89 | static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction) |
90 | { |
91 | int i; |
92 | |
93 | for (i = 0; i < 8; i++) { |
94 | if (direction == TS_NBUS_DIRECTION_IN) |
95 | gpiod_direction_input(desc: ts_nbus->data->desc[i]); |
96 | else |
97 | /* when used as output the default state of the data |
98 | * lines are set to high */ |
99 | gpiod_direction_output(desc: ts_nbus->data->desc[i], value: 1); |
100 | } |
101 | } |
102 | |
103 | /* |
104 | * reset the bus in its initial state. |
105 | * The data, csn, strobe and ale lines must be zero'ed to let the FPGA knows a |
106 | * new transaction can be process. |
107 | */ |
108 | static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus) |
109 | { |
110 | DECLARE_BITMAP(values, 8); |
111 | |
112 | values[0] = 0; |
113 | |
114 | gpiod_set_array_value_cansleep(array_size: 8, desc_array: ts_nbus->data->desc, |
115 | array_info: ts_nbus->data->info, value_bitmap: values); |
116 | gpiod_set_value_cansleep(desc: ts_nbus->csn, value: 0); |
117 | gpiod_set_value_cansleep(desc: ts_nbus->strobe, value: 0); |
118 | gpiod_set_value_cansleep(desc: ts_nbus->ale, value: 0); |
119 | } |
120 | |
121 | /* |
122 | * let the FPGA knows it can process. |
123 | */ |
124 | static void ts_nbus_start_transaction(struct ts_nbus *ts_nbus) |
125 | { |
126 | gpiod_set_value_cansleep(desc: ts_nbus->strobe, value: 1); |
127 | } |
128 | |
129 | /* |
130 | * read a byte value from the data gpios. |
131 | * return 0 on success or negative errno on failure. |
132 | */ |
133 | static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val) |
134 | { |
135 | struct gpio_descs *gpios = ts_nbus->data; |
136 | int ret, i; |
137 | |
138 | *val = 0; |
139 | for (i = 0; i < 8; i++) { |
140 | ret = gpiod_get_value_cansleep(desc: gpios->desc[i]); |
141 | if (ret < 0) |
142 | return ret; |
143 | if (ret) |
144 | *val |= BIT(i); |
145 | } |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | /* |
151 | * set the data gpios accordingly to the byte value. |
152 | */ |
153 | static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte) |
154 | { |
155 | struct gpio_descs *gpios = ts_nbus->data; |
156 | DECLARE_BITMAP(values, 8); |
157 | |
158 | values[0] = byte; |
159 | |
160 | gpiod_set_array_value_cansleep(array_size: 8, desc_array: gpios->desc, array_info: gpios->info, value_bitmap: values); |
161 | } |
162 | |
163 | /* |
164 | * reading the bus consists of resetting the bus, then notifying the FPGA to |
165 | * send the data in the data gpios and return the read value. |
166 | * return 0 on success or negative errno on failure. |
167 | */ |
168 | static int ts_nbus_read_bus(struct ts_nbus *ts_nbus, u8 *val) |
169 | { |
170 | ts_nbus_reset_bus(ts_nbus); |
171 | ts_nbus_start_transaction(ts_nbus); |
172 | |
173 | return ts_nbus_read_byte(ts_nbus, val); |
174 | } |
175 | |
176 | /* |
177 | * writing to the bus consists of resetting the bus, then define the type of |
178 | * command (address/value), write the data and notify the FPGA to retrieve the |
179 | * value in the data gpios. |
180 | */ |
181 | static void ts_nbus_write_bus(struct ts_nbus *ts_nbus, int cmd, u8 val) |
182 | { |
183 | ts_nbus_reset_bus(ts_nbus); |
184 | |
185 | if (cmd == TS_NBUS_WRITE_ADR) |
186 | gpiod_set_value_cansleep(desc: ts_nbus->ale, value: 1); |
187 | |
188 | ts_nbus_write_byte(ts_nbus, byte: val); |
189 | ts_nbus_start_transaction(ts_nbus); |
190 | } |
191 | |
192 | /* |
193 | * read the value in the FPGA register at the given address. |
194 | * return 0 on success or negative errno on failure. |
195 | */ |
196 | int ts_nbus_read(struct ts_nbus *ts_nbus, u8 adr, u16 *val) |
197 | { |
198 | int ret, i; |
199 | u8 byte; |
200 | |
201 | /* bus access must be atomic */ |
202 | mutex_lock(&ts_nbus->lock); |
203 | |
204 | /* set the bus in read mode */ |
205 | gpiod_set_value_cansleep(desc: ts_nbus->txrx, value: 0); |
206 | |
207 | /* write address */ |
208 | ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, val: adr); |
209 | |
210 | /* set the data gpios direction as input before reading */ |
211 | ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_IN); |
212 | |
213 | /* reading value MSB first */ |
214 | do { |
215 | *val = 0; |
216 | byte = 0; |
217 | for (i = 1; i >= 0; i--) { |
218 | /* read a byte from the bus, leave on error */ |
219 | ret = ts_nbus_read_bus(ts_nbus, val: &byte); |
220 | if (ret < 0) |
221 | goto err; |
222 | |
223 | /* append the byte read to the final value */ |
224 | *val |= byte << (i * 8); |
225 | } |
226 | gpiod_set_value_cansleep(desc: ts_nbus->csn, value: 1); |
227 | ret = gpiod_get_value_cansleep(desc: ts_nbus->rdy); |
228 | } while (ret); |
229 | |
230 | err: |
231 | /* restore the data gpios direction as output after reading */ |
232 | ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_OUT); |
233 | |
234 | mutex_unlock(lock: &ts_nbus->lock); |
235 | |
236 | return ret; |
237 | } |
238 | EXPORT_SYMBOL_GPL(ts_nbus_read); |
239 | |
240 | /* |
241 | * write the desired value in the FPGA register at the given address. |
242 | */ |
243 | int ts_nbus_write(struct ts_nbus *ts_nbus, u8 adr, u16 val) |
244 | { |
245 | int i; |
246 | |
247 | /* bus access must be atomic */ |
248 | mutex_lock(&ts_nbus->lock); |
249 | |
250 | /* set the bus in write mode */ |
251 | gpiod_set_value_cansleep(desc: ts_nbus->txrx, value: 1); |
252 | |
253 | /* write address */ |
254 | ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, val: adr); |
255 | |
256 | /* writing value MSB first */ |
257 | for (i = 1; i >= 0; i--) |
258 | ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_VAL, val: (u8)(val >> (i * 8))); |
259 | |
260 | /* wait for completion */ |
261 | gpiod_set_value_cansleep(desc: ts_nbus->csn, value: 1); |
262 | while (gpiod_get_value_cansleep(desc: ts_nbus->rdy) != 0) { |
263 | gpiod_set_value_cansleep(desc: ts_nbus->csn, value: 0); |
264 | gpiod_set_value_cansleep(desc: ts_nbus->csn, value: 1); |
265 | } |
266 | |
267 | mutex_unlock(lock: &ts_nbus->lock); |
268 | |
269 | return 0; |
270 | } |
271 | EXPORT_SYMBOL_GPL(ts_nbus_write); |
272 | |
273 | static int ts_nbus_probe(struct platform_device *pdev) |
274 | { |
275 | struct pwm_device *pwm; |
276 | struct pwm_args pargs; |
277 | struct device *dev = &pdev->dev; |
278 | struct ts_nbus *ts_nbus; |
279 | int ret; |
280 | |
281 | ts_nbus = devm_kzalloc(dev, size: sizeof(*ts_nbus), GFP_KERNEL); |
282 | if (!ts_nbus) |
283 | return -ENOMEM; |
284 | |
285 | mutex_init(&ts_nbus->lock); |
286 | |
287 | ret = ts_nbus_init_pdata(pdev, ts_nbus); |
288 | if (ret < 0) |
289 | return ret; |
290 | |
291 | pwm = devm_pwm_get(dev, NULL); |
292 | if (IS_ERR(ptr: pwm)) { |
293 | ret = PTR_ERR(ptr: pwm); |
294 | if (ret != -EPROBE_DEFER) |
295 | dev_err(dev, "unable to request PWM\n" ); |
296 | return ret; |
297 | } |
298 | |
299 | pwm_get_args(pwm, args: &pargs); |
300 | if (!pargs.period) { |
301 | dev_err(&pdev->dev, "invalid PWM period\n" ); |
302 | return -EINVAL; |
303 | } |
304 | |
305 | /* |
306 | * FIXME: pwm_apply_args() should be removed when switching to |
307 | * the atomic PWM API. |
308 | */ |
309 | pwm_apply_args(pwm); |
310 | ret = pwm_config(pwm, duty_ns: pargs.period, period_ns: pargs.period); |
311 | if (ret < 0) |
312 | return ret; |
313 | |
314 | /* |
315 | * we can now start the FPGA and populate the peripherals. |
316 | */ |
317 | pwm_enable(pwm); |
318 | ts_nbus->pwm = pwm; |
319 | |
320 | /* |
321 | * let the child nodes retrieve this instance of the ts-nbus. |
322 | */ |
323 | dev_set_drvdata(dev, data: ts_nbus); |
324 | |
325 | ret = of_platform_populate(root: dev->of_node, NULL, NULL, parent: dev); |
326 | if (ret < 0) |
327 | return ret; |
328 | |
329 | dev_info(dev, "initialized\n" ); |
330 | |
331 | return 0; |
332 | } |
333 | |
334 | static int ts_nbus_remove(struct platform_device *pdev) |
335 | { |
336 | struct ts_nbus *ts_nbus = dev_get_drvdata(dev: &pdev->dev); |
337 | |
338 | /* shutdown the FPGA */ |
339 | mutex_lock(&ts_nbus->lock); |
340 | pwm_disable(pwm: ts_nbus->pwm); |
341 | mutex_unlock(lock: &ts_nbus->lock); |
342 | |
343 | return 0; |
344 | } |
345 | |
346 | static const struct of_device_id ts_nbus_of_match[] = { |
347 | { .compatible = "technologic,ts-nbus" , }, |
348 | { }, |
349 | }; |
350 | MODULE_DEVICE_TABLE(of, ts_nbus_of_match); |
351 | |
352 | static struct platform_driver ts_nbus_driver = { |
353 | .probe = ts_nbus_probe, |
354 | .remove = ts_nbus_remove, |
355 | .driver = { |
356 | .name = "ts_nbus" , |
357 | .of_match_table = ts_nbus_of_match, |
358 | }, |
359 | }; |
360 | |
361 | module_platform_driver(ts_nbus_driver); |
362 | |
363 | MODULE_ALIAS("platform:ts_nbus" ); |
364 | MODULE_AUTHOR("Sebastien Bourdelin <sebastien.bourdelin@savoirfairelinux.com>" ); |
365 | MODULE_DESCRIPTION("Technologic Systems NBUS" ); |
366 | MODULE_LICENSE("GPL v2" ); |
367 | |