1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Intel La Jolla Cove Adapter USB-SPI driver |
4 | * |
5 | * Copyright (c) 2023, Intel Corporation. |
6 | */ |
7 | |
8 | #include <linux/auxiliary_bus.h> |
9 | #include <linux/bitfield.h> |
10 | #include <linux/bits.h> |
11 | #include <linux/dev_printk.h> |
12 | #include <linux/module.h> |
13 | #include <linux/spi/spi.h> |
14 | #include <linux/usb/ljca.h> |
15 | |
16 | #define LJCA_SPI_BUS_MAX_HZ 48000000 |
17 | |
18 | #define LJCA_SPI_BUF_SIZE 60u |
19 | #define LJCA_SPI_MAX_XFER_SIZE \ |
20 | (LJCA_SPI_BUF_SIZE - sizeof(struct ljca_spi_xfer_packet)) |
21 | |
22 | #define LJCA_SPI_CLK_MODE_POLARITY BIT(0) |
23 | #define LJCA_SPI_CLK_MODE_PHASE BIT(1) |
24 | |
25 | #define LJCA_SPI_XFER_INDICATOR_ID GENMASK(5, 0) |
26 | #define LJCA_SPI_XFER_INDICATOR_CMPL BIT(6) |
27 | #define LJCA_SPI_XFER_INDICATOR_INDEX BIT(7) |
28 | |
29 | /* SPI commands */ |
30 | enum ljca_spi_cmd { |
31 | LJCA_SPI_INIT = 1, |
32 | LJCA_SPI_READ, |
33 | LJCA_SPI_WRITE, |
34 | LJCA_SPI_WRITEREAD, |
35 | LJCA_SPI_DEINIT, |
36 | }; |
37 | |
38 | enum { |
39 | LJCA_SPI_BUS_SPEED_24M, |
40 | LJCA_SPI_BUS_SPEED_12M, |
41 | LJCA_SPI_BUS_SPEED_8M, |
42 | LJCA_SPI_BUS_SPEED_6M, |
43 | LJCA_SPI_BUS_SPEED_4_8M, /*4.8MHz*/ |
44 | LJCA_SPI_BUS_SPEED_MIN = LJCA_SPI_BUS_SPEED_4_8M, |
45 | }; |
46 | |
47 | enum { |
48 | LJCA_SPI_CLOCK_LOW_POLARITY, |
49 | LJCA_SPI_CLOCK_HIGH_POLARITY, |
50 | }; |
51 | |
52 | enum { |
53 | LJCA_SPI_CLOCK_FIRST_PHASE, |
54 | LJCA_SPI_CLOCK_SECOND_PHASE, |
55 | }; |
56 | |
57 | struct ljca_spi_init_packet { |
58 | u8 index; |
59 | u8 speed; |
60 | u8 mode; |
61 | } __packed; |
62 | |
63 | struct ljca_spi_xfer_packet { |
64 | u8 indicator; |
65 | u8 len; |
66 | u8 data[] __counted_by(len); |
67 | } __packed; |
68 | |
69 | struct ljca_spi_dev { |
70 | struct ljca_client *ljca; |
71 | struct spi_controller *controller; |
72 | struct ljca_spi_info *spi_info; |
73 | u8 speed; |
74 | u8 mode; |
75 | |
76 | u8 obuf[LJCA_SPI_BUF_SIZE]; |
77 | u8 ibuf[LJCA_SPI_BUF_SIZE]; |
78 | }; |
79 | |
80 | static int ljca_spi_read_write(struct ljca_spi_dev *ljca_spi, const u8 *w_data, |
81 | u8 *r_data, int len, int id, int complete, |
82 | int cmd) |
83 | { |
84 | struct ljca_spi_xfer_packet *w_packet = |
85 | (struct ljca_spi_xfer_packet *)ljca_spi->obuf; |
86 | struct ljca_spi_xfer_packet *r_packet = |
87 | (struct ljca_spi_xfer_packet *)ljca_spi->ibuf; |
88 | int ret; |
89 | |
90 | w_packet->indicator = FIELD_PREP(LJCA_SPI_XFER_INDICATOR_ID, id) | |
91 | FIELD_PREP(LJCA_SPI_XFER_INDICATOR_CMPL, complete) | |
92 | FIELD_PREP(LJCA_SPI_XFER_INDICATOR_INDEX, |
93 | ljca_spi->spi_info->id); |
94 | |
95 | if (cmd == LJCA_SPI_READ) { |
96 | w_packet->len = sizeof(u16); |
97 | *(__le16 *)&w_packet->data[0] = cpu_to_le16(len); |
98 | } else { |
99 | w_packet->len = len; |
100 | memcpy(w_packet->data, w_data, len); |
101 | } |
102 | |
103 | ret = ljca_transfer(client: ljca_spi->ljca, cmd, obuf: (u8 *)w_packet, |
104 | struct_size(w_packet, data, w_packet->len), |
105 | ibuf: (u8 *)r_packet, LJCA_SPI_BUF_SIZE); |
106 | if (ret < 0) |
107 | return ret; |
108 | else if (ret < sizeof(*r_packet) || r_packet->len <= 0) |
109 | return -EIO; |
110 | |
111 | if (r_data) |
112 | memcpy(r_data, r_packet->data, r_packet->len); |
113 | |
114 | return 0; |
115 | } |
116 | |
117 | static int ljca_spi_init(struct ljca_spi_dev *ljca_spi, u8 div, u8 mode) |
118 | { |
119 | struct ljca_spi_init_packet w_packet = {}; |
120 | int ret; |
121 | |
122 | if (ljca_spi->mode == mode && ljca_spi->speed == div) |
123 | return 0; |
124 | |
125 | w_packet.index = ljca_spi->spi_info->id; |
126 | w_packet.speed = div; |
127 | w_packet.mode = FIELD_PREP(LJCA_SPI_CLK_MODE_POLARITY, |
128 | (mode & SPI_CPOL) ? LJCA_SPI_CLOCK_HIGH_POLARITY : |
129 | LJCA_SPI_CLOCK_LOW_POLARITY) | |
130 | FIELD_PREP(LJCA_SPI_CLK_MODE_PHASE, |
131 | (mode & SPI_CPHA) ? LJCA_SPI_CLOCK_SECOND_PHASE : |
132 | LJCA_SPI_CLOCK_FIRST_PHASE); |
133 | |
134 | ret = ljca_transfer(client: ljca_spi->ljca, cmd: LJCA_SPI_INIT, obuf: (u8 *)&w_packet, |
135 | obuf_len: sizeof(w_packet), NULL, ibuf_len: 0); |
136 | if (ret < 0) |
137 | return ret; |
138 | |
139 | ljca_spi->mode = mode; |
140 | ljca_spi->speed = div; |
141 | |
142 | return 0; |
143 | } |
144 | |
145 | static int ljca_spi_deinit(struct ljca_spi_dev *ljca_spi) |
146 | { |
147 | struct ljca_spi_init_packet w_packet = {}; |
148 | int ret; |
149 | |
150 | w_packet.index = ljca_spi->spi_info->id; |
151 | |
152 | ret = ljca_transfer(client: ljca_spi->ljca, cmd: LJCA_SPI_DEINIT, obuf: (u8 *)&w_packet, |
153 | obuf_len: sizeof(w_packet), NULL, ibuf_len: 0); |
154 | |
155 | return ret < 0 ? ret : 0; |
156 | } |
157 | |
158 | static inline int ljca_spi_transfer(struct ljca_spi_dev *ljca_spi, |
159 | const u8 *tx_data, u8 *rx_data, u16 len) |
160 | { |
161 | int complete, cur_len; |
162 | int remaining = len; |
163 | int cmd, ret, i; |
164 | int offset = 0; |
165 | |
166 | if (tx_data && rx_data) |
167 | cmd = LJCA_SPI_WRITEREAD; |
168 | else if (tx_data) |
169 | cmd = LJCA_SPI_WRITE; |
170 | else if (rx_data) |
171 | cmd = LJCA_SPI_READ; |
172 | else |
173 | return -EINVAL; |
174 | |
175 | for (i = 0; remaining > 0; i++) { |
176 | cur_len = min_t(unsigned int, remaining, LJCA_SPI_MAX_XFER_SIZE); |
177 | complete = (cur_len == remaining); |
178 | |
179 | ret = ljca_spi_read_write(ljca_spi, |
180 | w_data: tx_data ? tx_data + offset : NULL, |
181 | r_data: rx_data ? rx_data + offset : NULL, |
182 | len: cur_len, id: i, complete, cmd); |
183 | if (ret) |
184 | return ret; |
185 | |
186 | offset += cur_len; |
187 | remaining -= cur_len; |
188 | } |
189 | |
190 | return 0; |
191 | } |
192 | |
193 | static int ljca_spi_transfer_one(struct spi_controller *controller, |
194 | struct spi_device *spi, |
195 | struct spi_transfer *xfer) |
196 | { |
197 | u8 div = DIV_ROUND_UP(controller->max_speed_hz, xfer->speed_hz) / 2 - 1; |
198 | struct ljca_spi_dev *ljca_spi = spi_controller_get_devdata(ctlr: controller); |
199 | int ret; |
200 | |
201 | div = min_t(u8, LJCA_SPI_BUS_SPEED_MIN, div); |
202 | |
203 | ret = ljca_spi_init(ljca_spi, div, mode: spi->mode); |
204 | if (ret) { |
205 | dev_err(&ljca_spi->ljca->auxdev.dev, |
206 | "cannot initialize transfer ret %d\n" , ret); |
207 | return ret; |
208 | } |
209 | |
210 | ret = ljca_spi_transfer(ljca_spi, tx_data: xfer->tx_buf, rx_data: xfer->rx_buf, len: xfer->len); |
211 | if (ret) |
212 | dev_err(&ljca_spi->ljca->auxdev.dev, |
213 | "transfer failed len: %d\n" , xfer->len); |
214 | |
215 | return ret; |
216 | } |
217 | |
218 | static int ljca_spi_probe(struct auxiliary_device *auxdev, |
219 | const struct auxiliary_device_id *aux_dev_id) |
220 | { |
221 | struct ljca_client *ljca = auxiliary_dev_to_ljca_client(auxdev); |
222 | struct spi_controller *controller; |
223 | struct ljca_spi_dev *ljca_spi; |
224 | int ret; |
225 | |
226 | controller = devm_spi_alloc_host(dev: &auxdev->dev, size: sizeof(*ljca_spi)); |
227 | if (!controller) |
228 | return -ENOMEM; |
229 | |
230 | ljca_spi = spi_controller_get_devdata(ctlr: controller); |
231 | ljca_spi->ljca = ljca; |
232 | ljca_spi->spi_info = dev_get_platdata(dev: &auxdev->dev); |
233 | ljca_spi->controller = controller; |
234 | |
235 | controller->bus_num = -1; |
236 | controller->mode_bits = SPI_CPHA | SPI_CPOL; |
237 | controller->transfer_one = ljca_spi_transfer_one; |
238 | controller->auto_runtime_pm = false; |
239 | controller->max_speed_hz = LJCA_SPI_BUS_MAX_HZ; |
240 | |
241 | device_set_node(dev: &ljca_spi->controller->dev, dev_fwnode(&auxdev->dev)); |
242 | auxiliary_set_drvdata(auxdev, data: controller); |
243 | |
244 | ret = spi_register_controller(ctlr: controller); |
245 | if (ret) |
246 | dev_err(&auxdev->dev, "Failed to register controller\n" ); |
247 | |
248 | return ret; |
249 | } |
250 | |
251 | static void ljca_spi_dev_remove(struct auxiliary_device *auxdev) |
252 | { |
253 | struct spi_controller *controller = auxiliary_get_drvdata(auxdev); |
254 | struct ljca_spi_dev *ljca_spi = spi_controller_get_devdata(ctlr: controller); |
255 | |
256 | spi_unregister_controller(ctlr: controller); |
257 | ljca_spi_deinit(ljca_spi); |
258 | } |
259 | |
260 | static int ljca_spi_dev_suspend(struct device *dev) |
261 | { |
262 | struct spi_controller *controller = dev_get_drvdata(dev); |
263 | |
264 | return spi_controller_suspend(ctlr: controller); |
265 | } |
266 | |
267 | static int ljca_spi_dev_resume(struct device *dev) |
268 | { |
269 | struct spi_controller *controller = dev_get_drvdata(dev); |
270 | |
271 | return spi_controller_resume(ctlr: controller); |
272 | } |
273 | |
274 | static const struct dev_pm_ops ljca_spi_pm = { |
275 | SYSTEM_SLEEP_PM_OPS(ljca_spi_dev_suspend, ljca_spi_dev_resume) |
276 | }; |
277 | |
278 | static const struct auxiliary_device_id ljca_spi_id_table[] = { |
279 | { "usb_ljca.ljca-spi" , 0 }, |
280 | { /* sentinel */ } |
281 | }; |
282 | MODULE_DEVICE_TABLE(auxiliary, ljca_spi_id_table); |
283 | |
284 | static struct auxiliary_driver ljca_spi_driver = { |
285 | .driver.pm = &ljca_spi_pm, |
286 | .probe = ljca_spi_probe, |
287 | .remove = ljca_spi_dev_remove, |
288 | .id_table = ljca_spi_id_table, |
289 | }; |
290 | module_auxiliary_driver(ljca_spi_driver); |
291 | |
292 | MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>" ); |
293 | MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>" ); |
294 | MODULE_AUTHOR("Lixu Zhang <lixu.zhang@intel.com>" ); |
295 | MODULE_DESCRIPTION("Intel La Jolla Cove Adapter USB-SPI driver" ); |
296 | MODULE_LICENSE("GPL" ); |
297 | MODULE_IMPORT_NS(LJCA); |
298 | |