Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:22:49

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (c) 2017 embedded brains GmbH & Co. KG
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0025  * POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 #include <bsp.h>
0029 #include <bsp/fdt.h>
0030 #include <bsp/imx-gpio.h>
0031 #include <bsp/imx-iomux.h>
0032 #include <libfdt.h>
0033 #include <arm/freescale/imx/imx_ccmvar.h>
0034 #include <arm/freescale/imx/imx_ecspireg.h>
0035 #include <dev/spi/spi.h>
0036 #include <rtems/irq-extension.h>
0037 #include <sys/param.h>
0038 #include <sys/endian.h>
0039 
0040 #define IMX_ECSPI_FIFO_SIZE 64
0041 #define IMX_ECSPI_MAX_CHIPSELECTS 4
0042 #define IMX_ECSPI_CS_NONE IMX_ECSPI_MAX_CHIPSELECTS
0043 
0044 typedef struct imx_ecspi_bus imx_ecspi_bus;
0045 
0046 struct imx_ecspi_bus {
0047   spi_bus base;
0048   volatile imx_ecspi *regs;
0049   uint32_t conreg;
0050   uint32_t speed_hz;
0051   uint32_t mode;
0052   uint8_t bits_per_word;
0053   uint8_t cs;
0054   uint32_t msg_todo;
0055   const spi_ioc_transfer *msg;
0056   uint32_t todo;
0057   uint32_t in_transfer;
0058   uint8_t *rx_buf;
0059   const uint8_t *tx_buf;
0060   void (*push)(imx_ecspi_bus *, volatile imx_ecspi *);
0061   void (*pop)(imx_ecspi_bus *, volatile imx_ecspi *);
0062   rtems_id task_id;
0063   rtems_vector_number irq;
0064   struct {
0065     struct imx_gpio_pin pin;
0066     bool valid;
0067   } cspins[IMX_ECSPI_MAX_CHIPSELECTS];
0068 };
0069 
0070 static bool imx_ecspi_is_rx_fifo_not_empty(volatile imx_ecspi *regs)
0071 {
0072   return (regs->statreg & IMX_ECSPI_RR) != 0;
0073 }
0074 
0075 static void imx_ecspi_reset(volatile imx_ecspi *regs)
0076 {
0077   while (imx_ecspi_is_rx_fifo_not_empty(regs)) {
0078     regs->rxdata;
0079   }
0080 }
0081 
0082 static void imx_ecspi_done(imx_ecspi_bus *bus)
0083 {
0084   rtems_event_transient_send(bus->task_id);
0085 }
0086 
0087 #define IMC_ECSPI_PUSH(type) \
0088 static void imx_ecspi_push_##type(imx_ecspi_bus *bus, volatile imx_ecspi *regs) \
0089 { \
0090   type val = 0; \
0091   if (bus->tx_buf != NULL) { \
0092     val = *(type *)bus->tx_buf; \
0093     bus->tx_buf += sizeof(type); \
0094   } \
0095   bus->todo -= sizeof(type); \
0096   regs->txdata = val; \
0097 }
0098 
0099 #define IMX_ECSPI_POP(type) \
0100 static void imx_ecspi_pop_##type(imx_ecspi_bus *bus, volatile imx_ecspi *regs) \
0101 { \
0102   uint32_t val = regs->rxdata; \
0103   if (bus->rx_buf != NULL) { \
0104     *(type *)bus->rx_buf = val; \
0105     bus->rx_buf += sizeof(type); \
0106   } \
0107 }
0108 
0109 IMC_ECSPI_PUSH(uint8_t)
0110 IMX_ECSPI_POP(uint8_t)
0111 IMC_ECSPI_PUSH(uint16_t)
0112 IMX_ECSPI_POP(uint16_t)
0113 IMC_ECSPI_PUSH(uint32_t)
0114 IMX_ECSPI_POP(uint32_t)
0115 
0116 static void imx_ecspi_push_uint32_t_swap(
0117   imx_ecspi_bus *bus,
0118   volatile imx_ecspi *regs
0119 )
0120 {
0121   uint32_t val = 0;
0122 
0123   if (bus->tx_buf != NULL) {
0124     val = bswap32(*(uint32_t *)bus->tx_buf);
0125     bus->tx_buf += sizeof(uint32_t);
0126   }
0127 
0128   bus->todo -= sizeof(uint32_t);
0129   regs->txdata = val;
0130 }
0131 
0132 static void imx_ecspi_pop_uint32_t_swap(
0133   imx_ecspi_bus *bus,
0134   volatile imx_ecspi *regs
0135 )
0136 {
0137   uint32_t val = regs->rxdata;
0138 
0139   if (bus->rx_buf != NULL) {
0140     *(uint32_t *)bus->rx_buf = bswap32(val);
0141     bus->rx_buf += sizeof(uint32_t);
0142   }
0143 }
0144 
0145 static void imx_ecspi_push(imx_ecspi_bus *bus, volatile imx_ecspi *regs)
0146 {
0147   while (bus->todo > 0 && bus->in_transfer < IMX_ECSPI_FIFO_SIZE) {
0148     (*bus->push)(bus, regs);
0149     ++bus->in_transfer;
0150   }
0151 }
0152 
0153 /* Call with IMX_ECSPI_CS_NONE for @a cs to set all to idle */
0154 static void
0155 imx_ecspi_set_chipsel(imx_ecspi_bus *bus, uint32_t cs)
0156 {
0157   size_t i;
0158 
0159   /* Currently this is fixed active low */
0160   static const uint32_t idle = 1;
0161   static const uint32_t select = 0;
0162 
0163   for (i = 0; i < IMX_ECSPI_MAX_CHIPSELECTS; ++i) {
0164     if (bus->cspins[i].valid) {
0165       if (i != cs) {
0166         imx_gpio_set_output(&bus->cspins[i].pin, idle);
0167       } else {
0168         imx_gpio_set_output(&bus->cspins[cs].pin, select);
0169       }
0170     }
0171   }
0172 }
0173 
0174 static uint32_t imx_ecspi_conreg_divider(imx_ecspi_bus *bus, uint32_t speed_hz)
0175 {
0176   uint32_t post;
0177   uint32_t pre;
0178   uint32_t clk_in;
0179 
0180   clk_in = bus->base.max_speed_hz;
0181 
0182   if (clk_in > speed_hz) {
0183     post = fls((int) clk_in) - fls((int) speed_hz);
0184 
0185     if (clk_in > (speed_hz << post)) {
0186       ++post;
0187     }
0188 
0189     /* We have 2^4 == 16, use the pre-divider for this factor */
0190     post = MAX(4, post) - 4;
0191 
0192     if (post <= 0xf) {
0193       pre = howmany(clk_in, speed_hz << post) - 1;
0194     } else {
0195       post = 0xf;
0196       pre = 0xf;
0197     }
0198   } else {
0199     post = 0;
0200     pre = 0;
0201   }
0202 
0203   return IMX_ECSPI_CONREG_POST_DIVIDER(post)
0204     | IMX_ECSPI_CONREG_PRE_DIVIDER(pre);
0205 }
0206 
0207 static void imx_ecspi_config(
0208   imx_ecspi_bus *bus,
0209   volatile imx_ecspi *regs,
0210   uint32_t speed_hz,
0211   uint8_t bits_per_word,
0212   uint32_t mode,
0213   uint8_t cs
0214 )
0215 {
0216   uint32_t conreg;
0217   uint32_t testreg;
0218   uint32_t configreg;
0219   uint32_t dmareg;
0220   uint32_t cs_bit;
0221 
0222   conreg = IMX_ECSPI_CONREG_CHANNEL_MODE(0xf)
0223     | IMX_ECSPI_CONREG_SMC | IMX_ECSPI_CONREG_EN;
0224   testreg = regs->testreg;
0225   configreg = regs->configreg;
0226   dmareg = regs->dmareg;
0227   cs_bit = 1U << cs;
0228 
0229   conreg |= imx_ecspi_conreg_divider(bus, speed_hz);
0230   conreg |= IMX_ECSPI_CONREG_CHANNEL_SELECT(cs);
0231 
0232   configreg |= IMX_ECSPI_CONFIGREG_SS_CTL(cs_bit);
0233 
0234   if ((mode & SPI_CPHA) != 0) {
0235     configreg |= IMX_ECSPI_CONFIGREG_SCLK_PHA(cs_bit);
0236   } else {
0237     configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_PHA(cs_bit);
0238   }
0239 
0240   if ((mode & SPI_CPOL) != 0) {
0241     configreg |= IMX_ECSPI_CONFIGREG_SCLK_POL(cs_bit);
0242     configreg |= IMX_ECSPI_CONFIGREG_SCLK_CTL(cs_bit);
0243   } else {
0244     configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_POL(cs_bit);
0245     configreg &= ~IMX_ECSPI_CONFIGREG_SCLK_CTL(cs_bit);
0246   }
0247 
0248   if ((mode & SPI_CS_HIGH) != 0) {
0249     configreg |= IMX_ECSPI_CONFIGREG_SS_POL(cs_bit);
0250   } else {
0251     configreg &= ~IMX_ECSPI_CONFIGREG_SS_POL(cs_bit);
0252   }
0253 
0254   if ((mode & SPI_LOOP) != 0) {
0255     testreg |= IMX_ECSPI_TESTREG_LBC;
0256   } else {
0257     testreg &= ~IMX_ECSPI_TESTREG_LBC;
0258   }
0259 
0260   dmareg = IMX_ECSPI_DMAREG_TX_THRESHOLD_SET(dmareg, IMX_ECSPI_FIFO_SIZE/2);
0261 
0262   regs->conreg = conreg;
0263   regs->testreg = testreg;
0264   regs->dmareg = dmareg;
0265   regs->configreg = configreg;
0266 
0267   bus->conreg = conreg;
0268   bus->speed_hz = speed_hz;
0269   bus->bits_per_word = bits_per_word;
0270   bus->mode = mode;
0271   bus->cs = cs;
0272 
0273   /* FIXME: Clock change delay */
0274 }
0275 
0276 static void imx_ecspi_set_push_pop(
0277   imx_ecspi_bus *bus,
0278   uint32_t len,
0279   uint8_t bits_per_word
0280 )
0281 {
0282   uint32_t conreg;
0283 
0284   conreg = bus->conreg;
0285 
0286   if (len % 4 == 0 && len <= IMX_ECSPI_FIFO_SIZE) {
0287     conreg |= IMX_ECSPI_CONREG_BURST_LENGTH((len * 8) - 1);
0288 
0289     bus->push = imx_ecspi_push_uint32_t_swap;
0290     bus->pop = imx_ecspi_pop_uint32_t_swap;
0291   } else {
0292     conreg |= IMX_ECSPI_CONREG_BURST_LENGTH(bits_per_word - 1);
0293 
0294     if (bits_per_word <= 8) {
0295       bus->push = imx_ecspi_push_uint8_t;
0296       bus->pop = imx_ecspi_pop_uint8_t;
0297     } else if (bits_per_word <= 16) {
0298       bus->push = imx_ecspi_push_uint16_t;
0299       bus->pop = imx_ecspi_pop_uint16_t;
0300     } else {
0301       bus->push = imx_ecspi_push_uint32_t;
0302       bus->pop = imx_ecspi_pop_uint32_t;
0303     }
0304   }
0305 
0306   bus->regs->conreg = conreg;
0307 }
0308 
0309 static void imx_ecspi_next_msg(imx_ecspi_bus *bus, volatile imx_ecspi *regs)
0310 {
0311   if (bus->msg_todo > 0) {
0312     const spi_ioc_transfer *msg;
0313 
0314     msg = bus->msg;
0315 
0316     if (
0317       msg->speed_hz != bus->speed_hz
0318         || msg->bits_per_word != bus->bits_per_word
0319         || msg->mode != bus->mode
0320         || msg->cs != bus->cs
0321     ) {
0322       imx_ecspi_config(
0323         bus,
0324         regs,
0325         msg->speed_hz,
0326         msg->bits_per_word,
0327         msg->mode,
0328         msg->cs
0329       );
0330     }
0331     if ((msg->mode & SPI_NO_CS) != 0) {
0332       imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
0333     } else {
0334       imx_ecspi_set_chipsel(bus, msg->cs);
0335     }
0336 
0337     bus->todo = msg->len;
0338     bus->rx_buf = msg->rx_buf;
0339     bus->tx_buf = msg->tx_buf;
0340     imx_ecspi_set_push_pop(bus, msg->len, msg->bits_per_word);
0341     imx_ecspi_push(bus, regs);
0342     regs->intreg = IMX_ECSPI_TE | IMX_ECSPI_TDR;
0343   } else {
0344     regs->intreg = 0;
0345     imx_ecspi_done(bus);
0346   }
0347 }
0348 
0349 static void imx_ecspi_interrupt(void *arg)
0350 {
0351   imx_ecspi_bus *bus;
0352   volatile imx_ecspi *regs;
0353 
0354   bus = arg;
0355   regs = bus->regs;
0356 
0357   while (imx_ecspi_is_rx_fifo_not_empty(regs)) {
0358     (*bus->pop)(bus, regs);
0359     --bus->in_transfer;
0360   }
0361 
0362   if (bus->todo > 0) {
0363     imx_ecspi_push(bus, regs);
0364   } else if (bus->in_transfer > 0) {
0365     regs->intreg = IMX_ECSPI_RR;
0366   } else {
0367     if (bus->msg->cs_change) {
0368       imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
0369     }
0370     --bus->msg_todo;
0371     ++bus->msg;
0372     imx_ecspi_next_msg(bus, regs);
0373   }
0374 }
0375 
0376 static int imx_ecspi_check_messages(
0377   imx_ecspi_bus *bus,
0378   const spi_ioc_transfer *msg,
0379   uint32_t size)
0380 {
0381   while(size > 0) {
0382     if (msg->delay_usecs != 0) {
0383       return -EINVAL;
0384     }
0385     if (msg->bits_per_word > 32) {
0386       return -EINVAL;
0387     }
0388     if ((msg->mode &
0389         ~(SPI_CPHA | SPI_CPOL | SPI_LOOP | SPI_NO_CS)) != 0) {
0390       return -EINVAL;
0391     }
0392     if ((msg->mode & SPI_NO_CS) == 0 &&
0393         (msg->cs > IMX_ECSPI_MAX_CHIPSELECTS || !bus->cspins[msg->cs].valid)) {
0394       return -EINVAL;
0395     }
0396 
0397     ++msg;
0398     --size;
0399   }
0400 
0401   return 0;
0402 }
0403 
0404 static int imx_ecspi_transfer(
0405   spi_bus *base,
0406   const spi_ioc_transfer *msgs,
0407   uint32_t n
0408 )
0409 {
0410   imx_ecspi_bus *bus;
0411   int rv;
0412 
0413   bus = (imx_ecspi_bus *) base;
0414 
0415   rv = imx_ecspi_check_messages(bus, msgs, n);
0416 
0417   if (rv == 0) {
0418     bus->msg_todo = n;
0419     bus->msg = &msgs[0];
0420     bus->task_id = rtems_task_self();
0421 
0422     imx_ecspi_next_msg(bus, bus->regs);
0423     rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
0424     if (msgs[n-1].cs_change) {
0425       imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
0426     }
0427   }
0428   return rv;
0429 }
0430 
0431 static void imx_ecspi_destroy(spi_bus *base)
0432 {
0433   imx_ecspi_bus *bus;
0434 
0435   bus = (imx_ecspi_bus *) base;
0436   rtems_interrupt_handler_remove(bus->irq, imx_ecspi_interrupt, bus);
0437   spi_bus_destroy_and_free(&bus->base);
0438 }
0439 
0440 static int imx_ecspi_init(imx_ecspi_bus *bus, const void *fdt, int node)
0441 {
0442   rtems_status_code sc;
0443   int len;
0444   const uint32_t *val;
0445   size_t i;
0446 
0447   for (i = 0; i < IMX_ECSPI_MAX_CHIPSELECTS; ++i) {
0448     rtems_status_code sc_gpio = imx_gpio_init_from_fdt_property(
0449         &bus->cspins[i].pin, node, "cs-gpios", IMX_GPIO_MODE_OUTPUT, i);
0450     bus->cspins[i].valid = (sc_gpio == RTEMS_SUCCESSFUL);
0451   }
0452   imx_ecspi_set_chipsel(bus, IMX_ECSPI_CS_NONE);
0453 
0454   imx_ecspi_config(
0455     bus,
0456     bus->regs,
0457     bus->base.max_speed_hz,
0458     8,
0459     0,
0460     0
0461   );
0462   imx_ecspi_reset(bus->regs);
0463 
0464   sc = rtems_interrupt_handler_install(
0465     bus->irq,
0466     "ECSPI",
0467     RTEMS_INTERRUPT_UNIQUE,
0468     imx_ecspi_interrupt,
0469     bus
0470   );
0471   if (sc != RTEMS_SUCCESSFUL) {
0472     return EAGAIN;
0473   }
0474 
0475   val = fdt_getprop(fdt, node, "pinctrl-0", &len);
0476   if (len == 4) {
0477     imx_iomux_configure_pins(fdt, fdt32_to_cpu(val[0]));
0478   }
0479 
0480   return 0;
0481 }
0482 
0483 static int imx_ecspi_setup(spi_bus *base)
0484 {
0485   imx_ecspi_bus *bus;
0486 
0487   bus = (imx_ecspi_bus *) base;
0488 
0489   if (
0490     bus->base.speed_hz > imx_ccm_ipg_hz()
0491       || bus->base.bits_per_word > 32
0492   ) {
0493     return -EINVAL;
0494   }
0495 
0496   imx_ecspi_config(
0497     bus,
0498     bus->regs,
0499     bus->base.speed_hz,
0500     bus->base.bits_per_word,
0501     bus->base.mode,
0502     bus->base.cs
0503   );
0504   return 0;
0505 }
0506 
0507 int spi_bus_register_imx(const char *bus_path, const char *alias_or_path)
0508 {
0509   const void *fdt;
0510   const char *path;
0511   int node;
0512   imx_ecspi_bus *bus;
0513   int eno;
0514 
0515   fdt = bsp_fdt_get();
0516   path = fdt_get_alias(fdt, alias_or_path);
0517 
0518   if (path == NULL) {
0519     path = alias_or_path;
0520   }
0521 
0522   node = fdt_path_offset(fdt, path);
0523   if (node < 0) {
0524     rtems_set_errno_and_return_minus_one(ENXIO);
0525   }
0526 
0527   bus = (imx_ecspi_bus *) spi_bus_alloc_and_init(sizeof(*bus));
0528   if (bus == NULL){
0529     return -1;
0530   }
0531 
0532   bus->base.max_speed_hz = imx_ccm_ecspi_hz();
0533   bus->base.delay_usecs = 0;
0534   bus->regs = imx_get_reg_of_node(fdt, node);
0535   bus->irq = imx_get_irq_of_node(fdt, node, 0);
0536 
0537   eno = imx_ecspi_init(bus, fdt, node);
0538   if (eno != 0) {
0539     (*bus->base.destroy)(&bus->base);
0540     rtems_set_errno_and_return_minus_one(eno);
0541   }
0542 
0543   bus->base.transfer = imx_ecspi_transfer;
0544   bus->base.destroy = imx_ecspi_destroy;
0545   bus->base.setup = imx_ecspi_setup;
0546 
0547   return spi_bus_register(&bus->base, bus_path);
0548 }