source: rtems/c/src/lib/libbsp/arm/imx/spi/imx-ecspi.c @ 170df3d

5
Last change on this file since 170df3d was 170df3d, checked in by Sebastian Huber <sebastian.huber@…>, on 10/04/17 at 05:24:19

bsp/imx: Add SPI bus driver

Update #3090.

  • Property mode set to 100644
File size: 9.4 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 <libfdt.h>
18#include <arm/freescale/imx/imx_ccmvar.h>
19#include <arm/freescale/imx/imx_ecspireg.h>
20#include <dev/spi/spi.h>
21#include <rtems/irq-extension.h>
22#include <sys/param.h>
23#include <sys/endian.h>
24
25#define IMX_ECSPI_FIFO_SIZE 64
26
27typedef struct imx_ecspi_bus imx_ecspi_bus;
28
29struct imx_ecspi_bus {
30  spi_bus base;
31  volatile imx_ecspi *regs;
32  uint32_t conreg;
33  uint32_t speed_hz;
34  uint32_t mode;
35  uint8_t bits_per_word;
36  uint8_t cs;
37  uint32_t msg_todo;
38  const spi_ioc_transfer *msg;
39  uint32_t todo;
40  uint32_t in_transfer;
41  uint8_t *rx_buf;
42  const uint8_t *tx_buf;
43  void (*push)(imx_ecspi_bus *, volatile imx_ecspi *);
44  void (*pop)(imx_ecspi_bus *, volatile imx_ecspi *);
45  rtems_id task_id;
46  rtems_vector_number irq;
47};
48
49static bool imx_ecspi_is_rx_fifo_not_empty(volatile imx_ecspi *regs)
50{
51  return (regs->statreg & IMX_ECSPI_RR) != 0;
52}
53
54static void imx_ecspi_reset(volatile imx_ecspi *regs)
55{
56  while (imx_ecspi_is_rx_fifo_not_empty(regs)) {
57    regs->rxdata;
58  }
59}
60
61static void imx_ecspi_done(imx_ecspi_bus *bus)
62{
63  rtems_event_transient_send(bus->task_id);
64}
65
66#define IMC_ECSPI_PUSH(type) \
67static void imx_ecspi_push_##type(imx_ecspi_bus *bus, volatile imx_ecspi *regs) \
68{ \
69  type val = 0; \
70  if (bus->tx_buf != NULL) { \
71    val = *(type *)bus->tx_buf; \
72    bus->tx_buf += sizeof(type); \
73  } \
74  bus->todo -= sizeof(type); \
75  regs->txdata = val; \
76}
77
78#define IMX_ECSPI_POP(type) \
79static void imx_ecspi_pop_##type(imx_ecspi_bus *bus, volatile imx_ecspi *regs) \
80{ \
81  uint32_t val = regs->rxdata; \
82  if (bus->rx_buf != NULL) { \
83    *(type *)bus->rx_buf = val; \
84    bus->rx_buf += sizeof(type); \
85  } \
86}
87
88IMC_ECSPI_PUSH(uint8_t)
89IMX_ECSPI_POP(uint8_t)
90IMC_ECSPI_PUSH(uint16_t)
91IMX_ECSPI_POP(uint16_t)
92IMC_ECSPI_PUSH(uint32_t)
93IMX_ECSPI_POP(uint32_t)
94
95static void imx_ecspi_push_uint32_t_swap(
96  imx_ecspi_bus *bus,
97  volatile imx_ecspi *regs
98)
99{
100  uint32_t val = 0;
101
102  if (bus->tx_buf != NULL) {
103    val = bswap32(*(uint32_t *)bus->tx_buf);
104    bus->tx_buf += sizeof(uint32_t);
105  }
106
107  bus->todo -= sizeof(uint32_t);
108  regs->txdata = val;
109}
110
111static void imx_ecspi_pop_uint32_t_swap(
112  imx_ecspi_bus *bus,
113  volatile imx_ecspi *regs
114)
115{
116  uint32_t val = regs->rxdata;
117
118  if (bus->rx_buf != NULL) {
119    *(uint32_t *)bus->rx_buf = bswap32(val);
120    bus->rx_buf += sizeof(uint32_t);
121  }
122}
123
124static void imx_ecspi_push(imx_ecspi_bus *bus, volatile imx_ecspi *regs)
125{
126  while (bus->todo > 0 && bus->in_transfer < IMX_ECSPI_FIFO_SIZE) {
127    (*bus->push)(bus, regs);
128    ++bus->in_transfer;
129  }
130}
131
132static uint32_t imx_ecspi_conreg_divider(imx_ecspi_bus *bus, uint32_t speed_hz)
133{
134  uint32_t post;
135  uint32_t pre;
136  uint32_t clk_in;
137
138  clk_in = bus->base.max_speed_hz;
139
140  if (clk_in > speed_hz) {
141    post = fls((int) clk_in) - fls((int) speed_hz);
142
143    if (clk_in > (speed_hz << post)) {
144      ++post;
145    }
146
147    /* We have 2^4 == 16, use the pre-divider for this factor */
148    post = MAX(4, post) - 4;
149
150    if (post <= 0xf) {
151      pre = howmany(clk_in, speed_hz << post) - 1;
152    } else {
153      post = 0xf;
154      pre = 0xf;
155    }
156  } else {
157    post = 0;
158    pre = 0;
159  }
160
161  return IMX_ECSPI_CONREG_POST_DIVIDER(post)
162    | IMX_ECSPI_CONREG_PRE_DIVIDER(pre);
163}
164
165static void imx_ecspi_config(
166  imx_ecspi_bus *bus,
167  volatile imx_ecspi *regs,
168  uint32_t speed_hz,
169  uint8_t bits_per_word,
170  uint32_t mode,
171  uint8_t cs
172)
173{
174  uint32_t conreg;
175  uint32_t testreg;
176  uint32_t configreg;
177  uint32_t cs_bit;
178
179  conreg = IMX_ECSPI_CONREG_CHANNEL_MODE(0xf)
180    | IMX_ECSPI_CONREG_SMC | IMX_ECSPI_CONREG_EN;
181  testreg = regs->testreg;
182  configreg = regs->configreg;
183  cs_bit = 1U << cs;
184
185  conreg |= imx_ecspi_conreg_divider(bus, speed_hz);
186  conreg |= IMX_ECSPI_CONREG_CHANNEL_SELECT(cs);
187
188  configreg |= IMX_ECSPI_CONFIGREG_SS_CTL(cs_bit);
189
190  if ((mode & SPI_CPHA) != 0) {
191    configreg |= IMX_ECSPI_CONFIGREG_SCLK_PHA(cs_bit);
192  } else {
193    configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_PHA(cs_bit);
194  }
195
196  if ((mode & SPI_CPOL) != 0) {
197    configreg |= IMX_ECSPI_CONFIGREG_SCLK_POL(cs_bit);
198    configreg |= IMX_ECSPI_CONFIGREG_SCLK_CTL(cs_bit);
199  } else {
200    configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_POL(cs_bit);
201    configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_CTL(cs_bit);
202  }
203
204  if ((mode & SPI_CS_HIGH) != 0) {
205    configreg |= IMX_ECSPI_CONFIGREG_SS_POL(cs_bit);
206  } else {
207    configreg &= ~IMX_ECSPI_CONFIGREG_SS_POL(cs_bit);
208  }
209
210  if ((mode & SPI_LOOP) != 0) {
211    testreg |= IMX_ECSPI_TESTREG_LBC;
212  } else {
213    testreg &= ~IMX_ECSPI_TESTREG_LBC;
214  }
215
216  regs->conreg = conreg;
217  regs->testreg = testreg;
218  regs->configreg = configreg;
219
220  bus->conreg = conreg;
221  bus->speed_hz = speed_hz;
222  bus->bits_per_word = bits_per_word;
223  bus->mode = mode;
224  bus->cs = cs;
225
226  /* FIXME: Clock change delay */
227}
228
229static void imx_ecspi_set_push_pop(
230  imx_ecspi_bus *bus,
231  uint32_t len,
232  uint8_t bits_per_word
233)
234{
235  uint32_t conreg;
236
237  conreg = bus->conreg;
238
239  if (len % 4 == 0 && len <= IMX_ECSPI_FIFO_SIZE) {
240    conreg |= IMX_ECSPI_CONREG_BURST_LENGTH((len * 8) - 1);
241
242    bus->push = imx_ecspi_push_uint32_t_swap;
243    bus->pop = imx_ecspi_pop_uint32_t_swap;
244  } else {
245    conreg |= IMX_ECSPI_CONREG_BURST_LENGTH(bits_per_word - 1);
246
247    if (bits_per_word <= 8) {
248      bus->push = imx_ecspi_push_uint8_t;
249      bus->pop = imx_ecspi_pop_uint8_t;
250    } else if (bits_per_word <= 16) {
251      bus->push = imx_ecspi_push_uint16_t;
252      bus->pop = imx_ecspi_pop_uint16_t;
253    } else {
254      bus->push = imx_ecspi_push_uint32_t;
255      bus->pop = imx_ecspi_pop_uint32_t;
256    }
257  }
258
259  bus->regs->conreg = conreg;
260}
261
262static void imx_ecspi_next_msg(imx_ecspi_bus *bus, volatile imx_ecspi *regs)
263{
264  if (bus->msg_todo > 0) {
265    const spi_ioc_transfer *msg;
266
267    msg = bus->msg;
268
269    if (
270      msg->speed_hz != bus->speed_hz
271        || msg->bits_per_word != bus->bits_per_word
272        || msg->mode != bus->mode
273        || msg->cs != bus->cs
274    ) {
275      imx_ecspi_config(
276        bus,
277        regs,
278        msg->speed_hz,
279        msg->bits_per_word,
280        msg->mode,
281        msg->cs
282      );
283    }
284
285    bus->todo = msg->len;
286    bus->rx_buf = msg->rx_buf;
287    bus->tx_buf = msg->tx_buf;
288    imx_ecspi_set_push_pop(bus, msg->len, msg->bits_per_word);
289    imx_ecspi_push(bus, regs);
290    regs->intreg = IMX_ECSPI_TE;
291  } else {
292    regs->intreg = 0;
293    imx_ecspi_done(bus);
294  }
295}
296
297static void imx_ecspi_interrupt(void *arg)
298{
299  imx_ecspi_bus *bus;
300  volatile imx_ecspi *regs;
301
302  bus = arg;
303  regs = bus->regs;
304
305  while (imx_ecspi_is_rx_fifo_not_empty(regs)) {
306    (*bus->pop)(bus, regs);
307    --bus->in_transfer;
308  }
309
310  if (bus->todo > 0) {
311    imx_ecspi_push(bus, regs);
312  } else if (bus->in_transfer > 0) {
313    regs->intreg = IMX_ECSPI_RR;
314  } else {
315    --bus->msg_todo;
316    ++bus->msg;
317    imx_ecspi_next_msg(bus, regs);
318  }
319}
320
321static int imx_ecspi_transfer(
322  spi_bus *base,
323  const spi_ioc_transfer *msgs,
324  uint32_t n
325)
326{
327  imx_ecspi_bus *bus;
328
329  bus = (imx_ecspi_bus *) base;
330
331  bus->msg_todo = n;
332  bus->msg = &msgs[0];
333  bus->task_id = rtems_task_self();
334
335  imx_ecspi_next_msg(bus, bus->regs);
336  rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
337  return 0;
338}
339
340static void imx_ecspi_destroy(spi_bus *base)
341{
342  imx_ecspi_bus *bus;
343
344  bus = (imx_ecspi_bus *) base;
345  rtems_interrupt_handler_remove(bus->irq, imx_ecspi_interrupt, bus);
346  spi_bus_destroy_and_free(&bus->base);
347}
348
349static int imx_ecspi_init(imx_ecspi_bus *bus, const void *fdt, int node)
350{
351  rtems_status_code sc;
352  int len;
353  const uint32_t *val;
354
355  imx_ecspi_config(
356    bus,
357    bus->regs,
358    bus->base.max_speed_hz,
359    8,
360    0,
361    0
362  );
363  imx_ecspi_reset(bus->regs);
364
365  sc = rtems_interrupt_handler_install(
366    bus->irq,
367    "ECSPI",
368    RTEMS_INTERRUPT_UNIQUE,
369    imx_ecspi_interrupt,
370    bus
371  );
372  if (sc != RTEMS_SUCCESSFUL) {
373    return EAGAIN;
374  }
375
376  val = fdt_getprop(fdt, node, "pinctrl-0", &len);
377  if (len == 4) {
378    imx_iomux_configure_pins(fdt, fdt32_to_cpu(val[0]));
379  }
380
381  return 0;
382}
383
384static int imx_ecspi_setup(spi_bus *base)
385{
386  imx_ecspi_bus *bus;
387
388  bus = (imx_ecspi_bus *) base;
389
390  if (
391    bus->base.speed_hz > imx_ccm_ipg_hz()
392      || bus->base.bits_per_word > 32
393  ) {
394    return -EINVAL;
395  }
396
397  imx_ecspi_config(
398    bus,
399    bus->regs,
400    bus->base.speed_hz,
401    bus->base.bits_per_word,
402    bus->base.mode,
403    bus->base.cs
404  );
405  return 0;
406}
407
408int spi_bus_register_imx(const char *bus_path, const char *alias_or_path)
409{
410  const void *fdt;
411  const char *path;
412  int node;
413  imx_ecspi_bus *bus;
414  int eno;
415
416  fdt = bsp_fdt_get();
417  path = fdt_get_alias(fdt, alias_or_path);
418
419  if (path == NULL) {
420    path = alias_or_path;
421  }
422
423  node = fdt_path_offset(fdt, path);
424  if (node < 0) {
425    rtems_set_errno_and_return_minus_one(ENXIO);
426  }
427
428  bus = (imx_ecspi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
429  if (bus == NULL){
430    return -1;
431  }
432
433  bus->base.max_speed_hz = imx_ccm_ipg_hz();
434  bus->base.delay_usecs = 1;
435  bus->regs = imx_get_reg_of_node(fdt, node);
436  bus->irq = imx_get_irq_of_node(fdt, node, 0);
437
438  eno = imx_ecspi_init(bus, fdt, node);
439  if (eno != 0) {
440    (*bus->base.destroy)(&bus->base);
441    rtems_set_errno_and_return_minus_one(eno);
442  }
443
444  bus->base.transfer = imx_ecspi_transfer;
445  bus->base.destroy = imx_ecspi_destroy;
446  bus->base.setup = imx_ecspi_setup;
447
448  return spi_bus_register(&bus->base, bus_path);
449}
Note: See TracBrowser for help on using the repository browser.