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