]>
Commit | Line | Data |
---|---|---|
bbb6b2f9 EJ |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | // Copyright (C) IBM Corporation 2020 | |
3 | ||
4 | #include <linux/bitfield.h> | |
5 | #include <linux/bits.h> | |
6 | #include <linux/fsi.h> | |
7 | #include <linux/jiffies.h> | |
8 | #include <linux/kernel.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/of.h> | |
11 | #include <linux/spi/spi.h> | |
12 | ||
13 | #define FSI_ENGID_SPI 0x23 | |
14 | #define FSI_MBOX_ROOT_CTRL_8 0x2860 | |
15 | ||
16 | #define FSI2SPI_DATA0 0x00 | |
17 | #define FSI2SPI_DATA1 0x04 | |
18 | #define FSI2SPI_CMD 0x08 | |
19 | #define FSI2SPI_CMD_WRITE BIT(31) | |
20 | #define FSI2SPI_RESET 0x18 | |
21 | #define FSI2SPI_STATUS 0x1c | |
22 | #define FSI2SPI_STATUS_ANY_ERROR BIT(31) | |
23 | #define FSI2SPI_IRQ 0x20 | |
24 | ||
25 | #define SPI_FSI_BASE 0x70000 | |
26 | #define SPI_FSI_INIT_TIMEOUT_MS 1000 | |
27 | #define SPI_FSI_MAX_TRANSFER_SIZE 2048 | |
28 | ||
29 | #define SPI_FSI_ERROR 0x0 | |
30 | #define SPI_FSI_COUNTER_CFG 0x1 | |
31 | #define SPI_FSI_COUNTER_CFG_LOOPS(x) (((u64)(x) & 0xffULL) << 32) | |
32 | #define SPI_FSI_CFG1 0x2 | |
33 | #define SPI_FSI_CLOCK_CFG 0x3 | |
34 | #define SPI_FSI_CLOCK_CFG_MM_ENABLE BIT_ULL(32) | |
35 | #define SPI_FSI_CLOCK_CFG_ECC_DISABLE (BIT_ULL(35) | BIT_ULL(33)) | |
36 | #define SPI_FSI_CLOCK_CFG_RESET1 (BIT_ULL(36) | BIT_ULL(38)) | |
37 | #define SPI_FSI_CLOCK_CFG_RESET2 (BIT_ULL(37) | BIT_ULL(39)) | |
38 | #define SPI_FSI_CLOCK_CFG_MODE (BIT_ULL(41) | BIT_ULL(42)) | |
39 | #define SPI_FSI_CLOCK_CFG_SCK_RECV_DEL GENMASK_ULL(51, 44) | |
40 | #define SPI_FSI_CLOCK_CFG_SCK_NO_DEL BIT_ULL(51) | |
41 | #define SPI_FSI_CLOCK_CFG_SCK_DIV GENMASK_ULL(63, 52) | |
42 | #define SPI_FSI_MMAP 0x4 | |
43 | #define SPI_FSI_DATA_TX 0x5 | |
44 | #define SPI_FSI_DATA_RX 0x6 | |
45 | #define SPI_FSI_SEQUENCE 0x7 | |
46 | #define SPI_FSI_SEQUENCE_STOP 0x00 | |
47 | #define SPI_FSI_SEQUENCE_SEL_SLAVE(x) (0x10 | ((x) & 0xf)) | |
48 | #define SPI_FSI_SEQUENCE_SHIFT_OUT(x) (0x30 | ((x) & 0xf)) | |
49 | #define SPI_FSI_SEQUENCE_SHIFT_IN(x) (0x40 | ((x) & 0xf)) | |
50 | #define SPI_FSI_SEQUENCE_COPY_DATA_TX 0xc0 | |
51 | #define SPI_FSI_SEQUENCE_BRANCH(x) (0xe0 | ((x) & 0xf)) | |
52 | #define SPI_FSI_STATUS 0x8 | |
53 | #define SPI_FSI_STATUS_ERROR \ | |
54 | (GENMASK_ULL(31, 21) | GENMASK_ULL(15, 12)) | |
55 | #define SPI_FSI_STATUS_SEQ_STATE GENMASK_ULL(55, 48) | |
56 | #define SPI_FSI_STATUS_SEQ_STATE_IDLE BIT_ULL(48) | |
57 | #define SPI_FSI_STATUS_TDR_UNDERRUN BIT_ULL(57) | |
58 | #define SPI_FSI_STATUS_TDR_OVERRUN BIT_ULL(58) | |
59 | #define SPI_FSI_STATUS_TDR_FULL BIT_ULL(59) | |
60 | #define SPI_FSI_STATUS_RDR_UNDERRUN BIT_ULL(61) | |
61 | #define SPI_FSI_STATUS_RDR_OVERRUN BIT_ULL(62) | |
62 | #define SPI_FSI_STATUS_RDR_FULL BIT_ULL(63) | |
63 | #define SPI_FSI_STATUS_ANY_ERROR \ | |
64 | (SPI_FSI_STATUS_ERROR | SPI_FSI_STATUS_TDR_UNDERRUN | \ | |
65 | SPI_FSI_STATUS_TDR_OVERRUN | SPI_FSI_STATUS_RDR_UNDERRUN | \ | |
66 | SPI_FSI_STATUS_RDR_OVERRUN) | |
67 | #define SPI_FSI_PORT_CTRL 0x9 | |
68 | ||
69 | struct fsi_spi { | |
70 | struct device *dev; /* SPI controller device */ | |
71 | struct fsi_device *fsi; /* FSI2SPI CFAM engine device */ | |
72 | u32 base; | |
73 | }; | |
74 | ||
75 | struct fsi_spi_sequence { | |
76 | int bit; | |
77 | u64 data; | |
78 | }; | |
79 | ||
80 | static int fsi_spi_check_status(struct fsi_spi *ctx) | |
81 | { | |
82 | int rc; | |
83 | u32 sts; | |
84 | __be32 sts_be; | |
85 | ||
86 | rc = fsi_device_read(ctx->fsi, FSI2SPI_STATUS, &sts_be, | |
87 | sizeof(sts_be)); | |
88 | if (rc) | |
89 | return rc; | |
90 | ||
91 | sts = be32_to_cpu(sts_be); | |
92 | if (sts & FSI2SPI_STATUS_ANY_ERROR) { | |
93 | dev_err(ctx->dev, "Error with FSI2SPI interface: %08x.\n", sts); | |
94 | return -EIO; | |
95 | } | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | static int fsi_spi_read_reg(struct fsi_spi *ctx, u32 offset, u64 *value) | |
101 | { | |
102 | int rc; | |
103 | __be32 cmd_be; | |
104 | __be32 data_be; | |
105 | u32 cmd = offset + ctx->base; | |
106 | ||
107 | *value = 0ULL; | |
108 | ||
109 | if (cmd & FSI2SPI_CMD_WRITE) | |
110 | return -EINVAL; | |
111 | ||
112 | cmd_be = cpu_to_be32(cmd); | |
113 | rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be)); | |
114 | if (rc) | |
115 | return rc; | |
116 | ||
117 | rc = fsi_spi_check_status(ctx); | |
118 | if (rc) | |
119 | return rc; | |
120 | ||
121 | rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA0, &data_be, | |
122 | sizeof(data_be)); | |
123 | if (rc) | |
124 | return rc; | |
125 | ||
126 | *value |= (u64)be32_to_cpu(data_be) << 32; | |
127 | ||
128 | rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA1, &data_be, | |
129 | sizeof(data_be)); | |
130 | if (rc) | |
131 | return rc; | |
132 | ||
133 | *value |= (u64)be32_to_cpu(data_be); | |
134 | dev_dbg(ctx->dev, "Read %02x[%016llx].\n", offset, *value); | |
135 | ||
136 | return 0; | |
137 | } | |
138 | ||
139 | static int fsi_spi_write_reg(struct fsi_spi *ctx, u32 offset, u64 value) | |
140 | { | |
141 | int rc; | |
142 | __be32 cmd_be; | |
143 | __be32 data_be; | |
144 | u32 cmd = offset + ctx->base; | |
145 | ||
146 | if (cmd & FSI2SPI_CMD_WRITE) | |
147 | return -EINVAL; | |
148 | ||
149 | dev_dbg(ctx->dev, "Write %02x[%016llx].\n", offset, value); | |
150 | ||
151 | data_be = cpu_to_be32(upper_32_bits(value)); | |
152 | rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA0, &data_be, | |
153 | sizeof(data_be)); | |
154 | if (rc) | |
155 | return rc; | |
156 | ||
157 | data_be = cpu_to_be32(lower_32_bits(value)); | |
158 | rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA1, &data_be, | |
159 | sizeof(data_be)); | |
160 | if (rc) | |
161 | return rc; | |
162 | ||
163 | cmd_be = cpu_to_be32(cmd | FSI2SPI_CMD_WRITE); | |
164 | rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be)); | |
165 | if (rc) | |
166 | return rc; | |
167 | ||
168 | return fsi_spi_check_status(ctx); | |
169 | } | |
170 | ||
171 | static int fsi_spi_data_in(u64 in, u8 *rx, int len) | |
172 | { | |
173 | int i; | |
174 | int num_bytes = min(len, 8); | |
175 | ||
176 | for (i = 0; i < num_bytes; ++i) | |
177 | rx[i] = (u8)(in >> (8 * ((num_bytes - 1) - i))); | |
178 | ||
179 | return num_bytes; | |
180 | } | |
181 | ||
182 | static int fsi_spi_data_out(u64 *out, const u8 *tx, int len) | |
183 | { | |
184 | int i; | |
185 | int num_bytes = min(len, 8); | |
186 | u8 *out_bytes = (u8 *)out; | |
187 | ||
188 | /* Unused bytes of the tx data should be 0. */ | |
189 | *out = 0ULL; | |
190 | ||
191 | for (i = 0; i < num_bytes; ++i) | |
192 | out_bytes[8 - (i + 1)] = tx[i]; | |
193 | ||
194 | return num_bytes; | |
195 | } | |
196 | ||
197 | static int fsi_spi_reset(struct fsi_spi *ctx) | |
198 | { | |
199 | int rc; | |
200 | ||
201 | dev_dbg(ctx->dev, "Resetting SPI controller.\n"); | |
202 | ||
203 | rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG, | |
204 | SPI_FSI_CLOCK_CFG_RESET1); | |
205 | if (rc) | |
206 | return rc; | |
207 | ||
208 | return fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG, | |
209 | SPI_FSI_CLOCK_CFG_RESET2); | |
210 | } | |
211 | ||
212 | static int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val) | |
213 | { | |
214 | /* | |
215 | * Add the next byte of instruction to the 8-byte sequence register. | |
216 | * Then decrement the counter so that the next instruction will go in | |
217 | * the right place. Return the number of "slots" left in the sequence | |
218 | * register. | |
219 | */ | |
220 | seq->data |= (u64)val << seq->bit; | |
221 | seq->bit -= 8; | |
222 | ||
223 | return ((64 - seq->bit) / 8) - 2; | |
224 | } | |
225 | ||
226 | static void fsi_spi_sequence_init(struct fsi_spi_sequence *seq) | |
227 | { | |
228 | seq->bit = 56; | |
229 | seq->data = 0ULL; | |
230 | } | |
231 | ||
232 | static int fsi_spi_sequence_transfer(struct fsi_spi *ctx, | |
233 | struct fsi_spi_sequence *seq, | |
234 | struct spi_transfer *transfer) | |
235 | { | |
236 | int loops; | |
237 | int idx; | |
238 | int rc; | |
239 | u8 len = min(transfer->len, 8U); | |
240 | u8 rem = transfer->len % len; | |
241 | ||
242 | loops = transfer->len / len; | |
243 | ||
244 | if (transfer->tx_buf) { | |
245 | idx = fsi_spi_sequence_add(seq, | |
246 | SPI_FSI_SEQUENCE_SHIFT_OUT(len)); | |
247 | if (rem) | |
248 | rem = SPI_FSI_SEQUENCE_SHIFT_OUT(rem); | |
249 | } else if (transfer->rx_buf) { | |
250 | idx = fsi_spi_sequence_add(seq, | |
251 | SPI_FSI_SEQUENCE_SHIFT_IN(len)); | |
252 | if (rem) | |
253 | rem = SPI_FSI_SEQUENCE_SHIFT_IN(rem); | |
254 | } else { | |
255 | return -EINVAL; | |
256 | } | |
257 | ||
258 | if (loops > 1) { | |
259 | fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx)); | |
260 | ||
261 | if (rem) | |
262 | fsi_spi_sequence_add(seq, rem); | |
263 | ||
264 | rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, | |
265 | SPI_FSI_COUNTER_CFG_LOOPS(loops - 1)); | |
266 | if (rc) | |
267 | return rc; | |
268 | } | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | static int fsi_spi_transfer_data(struct fsi_spi *ctx, | |
274 | struct spi_transfer *transfer) | |
275 | { | |
276 | int rc = 0; | |
277 | u64 status = 0ULL; | |
278 | ||
279 | if (transfer->tx_buf) { | |
280 | int nb; | |
281 | int sent = 0; | |
282 | u64 out = 0ULL; | |
283 | const u8 *tx = transfer->tx_buf; | |
284 | ||
285 | while (transfer->len > sent) { | |
286 | nb = fsi_spi_data_out(&out, &tx[sent], | |
287 | (int)transfer->len - sent); | |
288 | ||
289 | rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, out); | |
290 | if (rc) | |
291 | return rc; | |
292 | ||
293 | do { | |
294 | rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, | |
295 | &status); | |
296 | if (rc) | |
297 | return rc; | |
298 | ||
299 | if (status & SPI_FSI_STATUS_ANY_ERROR) { | |
300 | rc = fsi_spi_reset(ctx); | |
301 | if (rc) | |
302 | return rc; | |
303 | ||
304 | return -EREMOTEIO; | |
305 | } | |
306 | } while (status & SPI_FSI_STATUS_TDR_FULL); | |
307 | ||
308 | sent += nb; | |
309 | } | |
310 | } else if (transfer->rx_buf) { | |
311 | int recv = 0; | |
312 | u64 in = 0ULL; | |
313 | u8 *rx = transfer->rx_buf; | |
314 | ||
315 | while (transfer->len > recv) { | |
316 | do { | |
317 | rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, | |
318 | &status); | |
319 | if (rc) | |
320 | return rc; | |
321 | ||
322 | if (status & SPI_FSI_STATUS_ANY_ERROR) { | |
323 | rc = fsi_spi_reset(ctx); | |
324 | if (rc) | |
325 | return rc; | |
326 | ||
327 | return -EREMOTEIO; | |
328 | } | |
329 | } while (!(status & SPI_FSI_STATUS_RDR_FULL)); | |
330 | ||
331 | rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in); | |
332 | if (rc) | |
333 | return rc; | |
334 | ||
335 | recv += fsi_spi_data_in(in, &rx[recv], | |
336 | (int)transfer->len - recv); | |
337 | } | |
338 | } | |
339 | ||
340 | return 0; | |
341 | } | |
342 | ||
343 | static int fsi_spi_transfer_init(struct fsi_spi *ctx) | |
344 | { | |
345 | int rc; | |
346 | bool reset = false; | |
347 | unsigned long end; | |
348 | u64 seq_state; | |
349 | u64 clock_cfg = 0ULL; | |
350 | u64 status = 0ULL; | |
351 | u64 wanted_clock_cfg = SPI_FSI_CLOCK_CFG_ECC_DISABLE | | |
352 | SPI_FSI_CLOCK_CFG_SCK_NO_DEL | | |
353 | FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 4); | |
354 | ||
355 | end = jiffies + msecs_to_jiffies(SPI_FSI_INIT_TIMEOUT_MS); | |
356 | do { | |
357 | if (time_after(jiffies, end)) | |
358 | return -ETIMEDOUT; | |
359 | ||
360 | rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, &status); | |
361 | if (rc) | |
362 | return rc; | |
363 | ||
364 | seq_state = status & SPI_FSI_STATUS_SEQ_STATE; | |
365 | ||
366 | if (status & (SPI_FSI_STATUS_ANY_ERROR | | |
367 | SPI_FSI_STATUS_TDR_FULL | | |
368 | SPI_FSI_STATUS_RDR_FULL)) { | |
369 | if (reset) | |
370 | return -EIO; | |
371 | ||
372 | rc = fsi_spi_reset(ctx); | |
373 | if (rc) | |
374 | return rc; | |
375 | ||
376 | reset = true; | |
377 | continue; | |
378 | } | |
379 | } while (seq_state && (seq_state != SPI_FSI_STATUS_SEQ_STATE_IDLE)); | |
380 | ||
381 | rc = fsi_spi_read_reg(ctx, SPI_FSI_CLOCK_CFG, &clock_cfg); | |
382 | if (rc) | |
383 | return rc; | |
384 | ||
385 | if ((clock_cfg & (SPI_FSI_CLOCK_CFG_MM_ENABLE | | |
386 | SPI_FSI_CLOCK_CFG_ECC_DISABLE | | |
387 | SPI_FSI_CLOCK_CFG_MODE | | |
388 | SPI_FSI_CLOCK_CFG_SCK_RECV_DEL | | |
389 | SPI_FSI_CLOCK_CFG_SCK_DIV)) != wanted_clock_cfg) | |
390 | rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG, | |
391 | wanted_clock_cfg); | |
392 | ||
393 | return rc; | |
394 | } | |
395 | ||
396 | static int fsi_spi_transfer_one_message(struct spi_controller *ctlr, | |
397 | struct spi_message *mesg) | |
398 | { | |
399 | int rc = 0; | |
400 | u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(mesg->spi->chip_select + 1); | |
401 | struct spi_transfer *transfer; | |
402 | struct fsi_spi *ctx = spi_controller_get_devdata(ctlr); | |
403 | ||
404 | list_for_each_entry(transfer, &mesg->transfers, transfer_list) { | |
405 | struct fsi_spi_sequence seq; | |
406 | struct spi_transfer *next = NULL; | |
407 | ||
408 | /* Sequencer must do shift out (tx) first. */ | |
409 | if (!transfer->tx_buf || | |
410 | transfer->len > SPI_FSI_MAX_TRANSFER_SIZE) { | |
411 | rc = -EINVAL; | |
412 | goto error; | |
413 | } | |
414 | ||
415 | dev_dbg(ctx->dev, "Start tx of %d bytes.\n", transfer->len); | |
416 | ||
417 | rc = fsi_spi_transfer_init(ctx); | |
418 | if (rc < 0) | |
419 | goto error; | |
420 | ||
421 | fsi_spi_sequence_init(&seq); | |
422 | fsi_spi_sequence_add(&seq, seq_slave); | |
423 | ||
424 | rc = fsi_spi_sequence_transfer(ctx, &seq, transfer); | |
425 | if (rc) | |
426 | goto error; | |
427 | ||
428 | if (!list_is_last(&transfer->transfer_list, | |
429 | &mesg->transfers)) { | |
430 | next = list_next_entry(transfer, transfer_list); | |
431 | ||
432 | /* Sequencer can only do shift in (rx) after tx. */ | |
433 | if (next->rx_buf) { | |
434 | if (next->len > SPI_FSI_MAX_TRANSFER_SIZE) { | |
435 | rc = -EINVAL; | |
436 | goto error; | |
437 | } | |
438 | ||
439 | dev_dbg(ctx->dev, "Sequence rx of %d bytes.\n", | |
440 | next->len); | |
441 | ||
442 | rc = fsi_spi_sequence_transfer(ctx, &seq, | |
443 | next); | |
444 | if (rc) | |
445 | goto error; | |
446 | } else { | |
447 | next = NULL; | |
448 | } | |
449 | } | |
450 | ||
451 | fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SEL_SLAVE(0)); | |
452 | ||
453 | rc = fsi_spi_write_reg(ctx, SPI_FSI_SEQUENCE, seq.data); | |
454 | if (rc) | |
455 | goto error; | |
456 | ||
457 | rc = fsi_spi_transfer_data(ctx, transfer); | |
458 | if (rc) | |
459 | goto error; | |
460 | ||
461 | if (next) { | |
462 | rc = fsi_spi_transfer_data(ctx, next); | |
463 | if (rc) | |
464 | goto error; | |
465 | ||
466 | transfer = next; | |
467 | } | |
468 | } | |
469 | ||
470 | error: | |
471 | mesg->status = rc; | |
472 | spi_finalize_current_message(ctlr); | |
473 | ||
474 | return rc; | |
475 | } | |
476 | ||
477 | static size_t fsi_spi_max_transfer_size(struct spi_device *spi) | |
478 | { | |
479 | return SPI_FSI_MAX_TRANSFER_SIZE; | |
480 | } | |
481 | ||
482 | static int fsi_spi_probe(struct device *dev) | |
483 | { | |
484 | int rc; | |
485 | u32 root_ctrl_8; | |
486 | struct device_node *np; | |
487 | int num_controllers_registered = 0; | |
488 | struct fsi_device *fsi = to_fsi_dev(dev); | |
489 | ||
490 | /* | |
491 | * Check the SPI mux before attempting to probe. If the mux isn't set | |
492 | * then the SPI controllers can't access their slave devices. | |
493 | */ | |
494 | rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8, | |
495 | sizeof(root_ctrl_8)); | |
496 | if (rc) | |
497 | return rc; | |
498 | ||
499 | if (!root_ctrl_8) { | |
500 | dev_dbg(dev, "SPI mux not set, aborting probe.\n"); | |
501 | return -ENODEV; | |
502 | } | |
503 | ||
504 | for_each_available_child_of_node(dev->of_node, np) { | |
505 | u32 base; | |
506 | struct fsi_spi *ctx; | |
507 | struct spi_controller *ctlr; | |
508 | ||
509 | if (of_property_read_u32(np, "reg", &base)) | |
510 | continue; | |
511 | ||
512 | ctlr = spi_alloc_master(dev, sizeof(*ctx)); | |
513 | if (!ctlr) | |
514 | break; | |
515 | ||
516 | ctlr->dev.of_node = np; | |
517 | ctlr->num_chipselect = of_get_available_child_count(np) ?: 1; | |
518 | ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX; | |
519 | ctlr->max_transfer_size = fsi_spi_max_transfer_size; | |
520 | ctlr->transfer_one_message = fsi_spi_transfer_one_message; | |
521 | ||
522 | ctx = spi_controller_get_devdata(ctlr); | |
523 | ctx->dev = &ctlr->dev; | |
524 | ctx->fsi = fsi; | |
525 | ctx->base = base + SPI_FSI_BASE; | |
526 | ||
527 | rc = devm_spi_register_controller(dev, ctlr); | |
528 | if (rc) | |
529 | spi_controller_put(ctlr); | |
530 | else | |
531 | num_controllers_registered++; | |
532 | } | |
533 | ||
534 | if (!num_controllers_registered) | |
535 | return -ENODEV; | |
536 | ||
537 | return 0; | |
538 | } | |
539 | ||
540 | static const struct fsi_device_id fsi_spi_ids[] = { | |
541 | { FSI_ENGID_SPI, FSI_VERSION_ANY }, | |
542 | { } | |
543 | }; | |
544 | MODULE_DEVICE_TABLE(fsi, fsi_spi_ids); | |
545 | ||
546 | static struct fsi_driver fsi_spi_driver = { | |
547 | .id_table = fsi_spi_ids, | |
548 | .drv = { | |
549 | .name = "spi-fsi", | |
550 | .bus = &fsi_bus_type, | |
551 | .probe = fsi_spi_probe, | |
552 | }, | |
553 | }; | |
554 | module_fsi_driver(fsi_spi_driver); | |
555 | ||
556 | MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>"); | |
557 | MODULE_DESCRIPTION("FSI attached SPI controller"); | |
558 | MODULE_LICENSE("GPL"); |