source: rtems/bsps/arm/imx/spi/imx-ecspi.c @ 1fc3f171

Last change on this file since 1fc3f171 was 1fc3f171, checked in by Christian Mauderer <christian.mauderer@…>, on 09/28/21 at 12:19:47

bsp/imx: Add cs_change support to SPI

  • Property mode set to 100644
File size: 11.6 KB
Line 
1/*
2 * Copyright (c) 2017 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <info@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#include <bsp.h>
16#include <bsp/fdt.h>
17#include <bsp/imx-gpio.h>
18#include <bsp/imx-iomux.h>
19#include <libfdt.h>
20#include <arm/freescale/imx/imx_ccmvar.h>
21#include <arm/freescale/imx/imx_ecspireg.h>
22#include <dev/spi/spi.h>
23#include <rtems/irq-extension.h>
24#include <sys/param.h>
25#include <sys/endian.h>
26
27#define IMX_ECSPI_FIFO_SIZE 64
28#define IMX_ECSPI_MAX_CHIPSELECTS 4
29#define IMX_ECSPI_CS_NONE IMX_ECSPI_MAX_CHIPSELECTS
30
31typedef struct imx_ecspi_bus imx_ecspi_bus;
32
33struct imx_ecspi_bus {
34  spi_bus base;
35  volatile imx_ecspi *regs;
36  uint32_t conreg;
37  uint32_t speed_hz;
38  uint32_t mode;
39  uint8_t bits_per_word;
40  uint8_t cs;
41  uint32_t msg_todo;
42  const spi_ioc_transfer *msg;
43  uint32_t todo;
44  uint32_t in_transfer;
45  uint8_t *rx_buf;
46  const uint8_t *tx_buf;
47  void (*push)(imx_ecspi_bus *, volatile imx_ecspi *);
48  void (*pop)(imx_ecspi_bus *, volatile imx_ecspi *);
49  rtems_id task_id;
50  rtems_vector_number irq;
51  struct {
52    struct imx_gpio_pin pin;
53    bool valid;
54  } cspins[IMX_ECSPI_MAX_CHIPSELECTS];
55};
56
57static bool imx_ecspi_is_rx_fifo_not_empty(volatile imx_ecspi *regs)
58{
59  return (regs->statreg & IMX_ECSPI_RR) != 0;
60}
61
62static void imx_ecspi_reset(volatile imx_ecspi *regs)
63{
64  while (imx_ecspi_is_rx_fifo_not_empty(regs)) {
65    regs->rxdata;
66  }
67}
68
69static void imx_ecspi_done(imx_ecspi_bus *bus)
70{
71  rtems_event_transient_send(bus->task_id);
72}
73
74#define IMC_ECSPI_PUSH(type) \
75static void imx_ecspi_push_##type(imx_ecspi_bus *bus, volatile imx_ecspi *regs) \
76{ \
77  type val = 0; \
78  if (bus->tx_buf != NULL) { \
79    val = *(type *)bus->tx_buf; \
80    bus->tx_buf += sizeof(type); \
81  } \
82  bus->todo -= sizeof(type); \
83  regs->txdata = val; \
84}
85
86#define IMX_ECSPI_POP(type) \
87static void imx_ecspi_pop_##type(imx_ecspi_bus *bus, volatile imx_ecspi *regs) \
88{ \
89  uint32_t val = regs->rxdata; \
90  if (bus->rx_buf != NULL) { \
91    *(type *)bus->rx_buf = val; \
92    bus->rx_buf += sizeof(type); \
93  } \
94}
95
96IMC_ECSPI_PUSH(uint8_t)
97IMX_ECSPI_POP(uint8_t)
98IMC_ECSPI_PUSH(uint16_t)
99IMX_ECSPI_POP(uint16_t)
100IMC_ECSPI_PUSH(uint32_t)
101IMX_ECSPI_POP(uint32_t)
102
103static void imx_ecspi_push_uint32_t_swap(
104  imx_ecspi_bus *bus,
105  volatile imx_ecspi *regs
106)
107{
108  uint32_t val = 0;
109
110  if (bus->tx_buf != NULL) {
111    val = bswap32(*(uint32_t *)bus->tx_buf);
112    bus->tx_buf += sizeof(uint32_t);
113  }
114
115  bus->todo -= sizeof(uint32_t);
116  regs->txdata = val;
117}
118
119static void imx_ecspi_pop_uint32_t_swap(
120  imx_ecspi_bus *bus,
121  volatile imx_ecspi *regs
122)
123{
124  uint32_t val = regs->rxdata;
125
126  if (bus->rx_buf != NULL) {
127    *(uint32_t *)bus->rx_buf = bswap32(val);
128    bus->rx_buf += sizeof(uint32_t);
129  }
130}
131
132static void imx_ecspi_push(imx_ecspi_bus *bus, volatile imx_ecspi *regs)
133{
134  while (bus->todo > 0 && bus->in_transfer < IMX_ECSPI_FIFO_SIZE) {
135    (*bus->push)(bus, regs);
136    ++bus->in_transfer;
137  }
138}
139
140/* Call with IMX_ECSPI_CS_NONE for @a cs to set all to idle */
141static void
142imx_ecspi_set_chipsel(imx_ecspi_bus *bus, uint32_t cs)
143{
144  size_t i;
145
146  /* Currently this is fixed active low */
147  static const uint32_t idle = 1;
148  static const uint32_t select = 0;
149
150  for (i = 0; i < IMX_ECSPI_MAX_CHIPSELECTS; ++i) {
151    if (bus->cspins[i].valid) {
152      if (i != cs) {
153        imx_gpio_set_output(&bus->cspins[i].pin, idle);
154      } else {
155        imx_gpio_set_output(&bus->cspins[cs].pin, select);
156      }
157    }
158  }
159}
160
161static uint32_t imx_ecspi_conreg_divider(imx_ecspi_bus *bus, uint32_t speed_hz)
162{
163  uint32_t post;
164  uint32_t pre;
165  uint32_t clk_in;
166
167  clk_in = bus->base.max_speed_hz;
168
169  if (clk_in > speed_hz) {
170    post = fls((int) clk_in) - fls((int) speed_hz);
171
172    if (clk_in > (speed_hz << post)) {
173      ++post;
174    }
175
176    /* We have 2^4 == 16, use the pre-divider for this factor */
177    post = MAX(4, post) - 4;
178
179    if (post <= 0xf) {
180      pre = howmany(clk_in, speed_hz << post) - 1;
181    } else {
182      post = 0xf;
183      pre = 0xf;
184    }
185  } else {
186    post = 0;
187    pre = 0;
188  }
189
190  return IMX_ECSPI_CONREG_POST_DIVIDER(post)
191    | IMX_ECSPI_CONREG_PRE_DIVIDER(pre);
192}
193
194static void imx_ecspi_config(
195  imx_ecspi_bus *bus,
196  volatile imx_ecspi *regs,
197  uint32_t speed_hz,
198  uint8_t bits_per_word,
199  uint32_t mode,
200  uint8_t cs
201)
202{
203  uint32_t conreg;
204  uint32_t testreg;
205  uint32_t configreg;
206  uint32_t dmareg;
207  uint32_t cs_bit;
208
209  conreg = IMX_ECSPI_CONREG_CHANNEL_MODE(0xf)
210    | IMX_ECSPI_CONREG_SMC | IMX_ECSPI_CONREG_EN;
211  testreg = regs->testreg;
212  configreg = regs->configreg;
213  dmareg = regs->dmareg;
214  cs_bit = 1U << cs;
215
216  conreg |= imx_ecspi_conreg_divider(bus, speed_hz);
217  conreg |= IMX_ECSPI_CONREG_CHANNEL_SELECT(cs);
218
219  configreg |= IMX_ECSPI_CONFIGREG_SS_CTL(cs_bit);
220
221  if ((mode & SPI_CPHA) != 0) {
222    configreg |= IMX_ECSPI_CONFIGREG_SCLK_PHA(cs_bit);
223  } else {
224    configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_PHA(cs_bit);
225  }
226
227  if ((mode & SPI_CPOL) != 0) {
228    configreg |= IMX_ECSPI_CONFIGREG_SCLK_POL(cs_bit);
229    configreg |= IMX_ECSPI_CONFIGREG_SCLK_CTL(cs_bit);
230  } else {
231    configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_POL(cs_bit);
232    configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_CTL(cs_bit);
233  }
234
235  if ((mode & SPI_CS_HIGH) != 0) {
236    configreg |= IMX_ECSPI_CONFIGREG_SS_POL(cs_bit);
237  } else {
238    configreg &= ~IMX_ECSPI_CONFIGREG_SS_POL(cs_bit);
239  }
240
241  if ((mode & SPI_LOOP) != 0) {
242    testreg |= IMX_ECSPI_TESTREG_LBC;
243  } else {
244    testreg &= ~IMX_ECSPI_TESTREG_LBC;
245  }
246
247  dmareg = IMX_ECSPI_DMAREG_TX_THRESHOLD_SET(dmareg, IMX_ECSPI_FIFO_SIZE/2);
248
249  regs->conreg = conreg;
250  regs->testreg = testreg;
251  regs->dmareg = dmareg;
252  regs->configreg = configreg;
253
254  bus->conreg = conreg;
255  bus->speed_hz = speed_hz;
256  bus->bits_per_word = bits_per_word;
257  bus->mode = mode;
258  bus->cs = cs;
259
260  /* FIXME: Clock change delay */
261}
262
263static void imx_ecspi_set_push_pop(
264  imx_ecspi_bus *bus,
265  uint32_t len,
266  uint8_t bits_per_word
267)
268{
269  uint32_t conreg;
270
271  conreg = bus->conreg;
272
273  if (len % 4 == 0 && len <= IMX_ECSPI_FIFO_SIZE) {
274    conreg |= IMX_ECSPI_CONREG_BURST_LENGTH((len * 8) - 1);
275
276    bus->push = imx_ecspi_push_uint32_t_swap;
277    bus->pop = imx_ecspi_pop_uint32_t_swap;
278  } else {
279    conreg |= IMX_ECSPI_CONREG_BURST_LENGTH(bits_per_word - 1);
280
281    if (bits_per_word <= 8) {
282      bus->push = imx_ecspi_push_uint8_t;
283      bus->pop = imx_ecspi_pop_uint8_t;
284    } else if (bits_per_word <= 16) {
285      bus->push = imx_ecspi_push_uint16_t;
286      bus->pop = imx_ecspi_pop_uint16_t;
287    } else {
288      bus->push = imx_ecspi_push_uint32_t;
289      bus->pop = imx_ecspi_pop_uint32_t;
290    }
291  }
292
293  bus->regs->conreg = conreg;
294}
295
296static void imx_ecspi_next_msg(imx_ecspi_bus *bus, volatile imx_ecspi *regs)
297{
298  if (bus->msg_todo > 0) {
299    const spi_ioc_transfer *msg;
300
301    msg = bus->msg;
302
303    if (
304      msg->speed_hz != bus->speed_hz
305        || msg->bits_per_word != bus->bits_per_word
306        || msg->mode != bus->mode
307        || msg->cs != bus->cs
308    ) {
309      imx_ecspi_config(
310        bus,
311        regs,
312        msg->speed_hz,
313        msg->bits_per_word,
314        msg->mode,
315        msg->cs
316      );
317    }
318    if ((msg->mode & SPI_NO_CS) != 0) {
319      imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
320    } else {
321      imx_ecspi_set_chipsel(bus, msg->cs);
322    }
323
324    bus->todo = msg->len;
325    bus->rx_buf = msg->rx_buf;
326    bus->tx_buf = msg->tx_buf;
327    imx_ecspi_set_push_pop(bus, msg->len, msg->bits_per_word);
328    imx_ecspi_push(bus, regs);
329    regs->intreg = IMX_ECSPI_TE | IMX_ECSPI_TDR;
330  } else {
331    regs->intreg = 0;
332    imx_ecspi_done(bus);
333  }
334}
335
336static void imx_ecspi_interrupt(void *arg)
337{
338  imx_ecspi_bus *bus;
339  volatile imx_ecspi *regs;
340
341  bus = arg;
342  regs = bus->regs;
343
344  while (imx_ecspi_is_rx_fifo_not_empty(regs)) {
345    (*bus->pop)(bus, regs);
346    --bus->in_transfer;
347  }
348
349  if (bus->todo > 0) {
350    imx_ecspi_push(bus, regs);
351  } else if (bus->in_transfer > 0) {
352    regs->intreg = IMX_ECSPI_RR;
353  } else {
354    if (bus->msg->cs_change) {
355      imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
356    }
357    --bus->msg_todo;
358    ++bus->msg;
359    imx_ecspi_next_msg(bus, regs);
360  }
361}
362
363static int imx_ecspi_check_messages(
364  imx_ecspi_bus *bus,
365  const spi_ioc_transfer *msg,
366  uint32_t size)
367{
368  while(size > 0) {
369    if (msg->delay_usecs != 0) {
370      return -EINVAL;
371    }
372    if (msg->bits_per_word > 32) {
373      return -EINVAL;
374    }
375    if ((msg->mode &
376        ~(SPI_CPHA | SPI_CPOL | SPI_LOOP | SPI_NO_CS)) != 0) {
377      return -EINVAL;
378    }
379    if ((msg->mode & SPI_NO_CS) == 0 &&
380        (msg->cs > IMX_ECSPI_MAX_CHIPSELECTS || !bus->cspins[msg->cs].valid)) {
381      return -EINVAL;
382    }
383
384    ++msg;
385    --size;
386  }
387
388  return 0;
389}
390
391static int imx_ecspi_transfer(
392  spi_bus *base,
393  const spi_ioc_transfer *msgs,
394  uint32_t n
395)
396{
397  imx_ecspi_bus *bus;
398  int rv;
399
400  bus = (imx_ecspi_bus *) base;
401
402  rv = imx_ecspi_check_messages(bus, msgs, n);
403
404  if (rv == 0) {
405    bus->msg_todo = n;
406    bus->msg = &msgs[0];
407    bus->task_id = rtems_task_self();
408
409    imx_ecspi_next_msg(bus, bus->regs);
410    rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
411    if (msgs[n-1].cs_change) {
412      imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
413    }
414  }
415  return rv;
416}
417
418static void imx_ecspi_destroy(spi_bus *base)
419{
420  imx_ecspi_bus *bus;
421
422  bus = (imx_ecspi_bus *) base;
423  rtems_interrupt_handler_remove(bus->irq, imx_ecspi_interrupt, bus);
424  spi_bus_destroy_and_free(&bus->base);
425}
426
427static int imx_ecspi_init(imx_ecspi_bus *bus, const void *fdt, int node)
428{
429  rtems_status_code sc;
430  int len;
431  const uint32_t *val;
432  size_t i;
433
434  for (i = 0; i < IMX_ECSPI_MAX_CHIPSELECTS; ++i) {
435    rtems_status_code sc_gpio = imx_gpio_init_from_fdt_property(
436        &bus->cspins[i].pin, node, "cs-gpios", IMX_GPIO_MODE_OUTPUT, i);
437    bus->cspins[i].valid = (sc_gpio == RTEMS_SUCCESSFUL);
438  }
439  imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
440
441  imx_ecspi_config(
442    bus,
443    bus->regs,
444    bus->base.max_speed_hz,
445    8,
446    0,
447    0
448  );
449  imx_ecspi_reset(bus->regs);
450
451  sc = rtems_interrupt_handler_install(
452    bus->irq,
453    "ECSPI",
454    RTEMS_INTERRUPT_UNIQUE,
455    imx_ecspi_interrupt,
456    bus
457  );
458  if (sc != RTEMS_SUCCESSFUL) {
459    return EAGAIN;
460  }
461
462  val = fdt_getprop(fdt, node, "pinctrl-0", &len);
463  if (len == 4) {
464    imx_iomux_configure_pins(fdt, fdt32_to_cpu(val[0]));
465  }
466
467  return 0;
468}
469
470static int imx_ecspi_setup(spi_bus *base)
471{
472  imx_ecspi_bus *bus;
473
474  bus = (imx_ecspi_bus *) base;
475
476  if (
477    bus->base.speed_hz > imx_ccm_ipg_hz()
478      || bus->base.bits_per_word > 32
479  ) {
480    return -EINVAL;
481  }
482
483  imx_ecspi_config(
484    bus,
485    bus->regs,
486    bus->base.speed_hz,
487    bus->base.bits_per_word,
488    bus->base.mode,
489    bus->base.cs
490  );
491  return 0;
492}
493
494int spi_bus_register_imx(const char *bus_path, const char *alias_or_path)
495{
496  const void *fdt;
497  const char *path;
498  int node;
499  imx_ecspi_bus *bus;
500  int eno;
501
502  fdt = bsp_fdt_get();
503  path = fdt_get_alias(fdt, alias_or_path);
504
505  if (path == NULL) {
506    path = alias_or_path;
507  }
508
509  node = fdt_path_offset(fdt, path);
510  if (node < 0) {
511    rtems_set_errno_and_return_minus_one(ENXIO);
512  }
513
514  bus = (imx_ecspi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
515  if (bus == NULL){
516    return -1;
517  }
518
519  bus->base.max_speed_hz = imx_ccm_ecspi_hz();
520  bus->base.delay_usecs = 0;
521  bus->regs = imx_get_reg_of_node(fdt, node);
522  bus->irq = imx_get_irq_of_node(fdt, node, 0);
523
524  eno = imx_ecspi_init(bus, fdt, node);
525  if (eno != 0) {
526    (*bus->base.destroy)(&bus->base);
527    rtems_set_errno_and_return_minus_one(eno);
528  }
529
530  bus->base.transfer = imx_ecspi_transfer;
531  bus->base.destroy = imx_ecspi_destroy;
532  bus->base.setup = imx_ecspi_setup;
533
534  return spi_bus_register(&bus->base, bus_path);
535}
Note: See TracBrowser for help on using the repository browser.