[PATCH] spi: i.MX: Add driver for DSPI

Andrey Smirnov andrew.smirnov at gmail.com
Thu Jan 26 14:24:44 PST 2017


On Tue, Jan 24, 2017 at 12:26 AM, Sascha Hauer <s.hauer at pengutronix.de> wrote:
> On Sun, Jan 22, 2017 at 09:57:35PM -0800, Andrey Smirnov wrote:
>> Add driver for DSPI - SPI IP core found on various Freescale/NXP
>> products (including Vybrid/VF610).
>>
>> Signed-off-by: Andrey Smirnov <andrew.smirnov at gmail.com>
>> ---
>>  drivers/spi/Kconfig    |   8 +
>>  drivers/spi/Makefile   |   1 +
>>  drivers/spi/dspi_spi.c | 413 +++++++++++++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 422 insertions(+)
>>  create mode 100644 drivers/spi/dspi_spi.c
>>
>> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
>> index 43ba8f4..dd1b214 100644
>> --- a/drivers/spi/Kconfig
>> +++ b/drivers/spi/Kconfig
>> @@ -54,6 +54,14 @@ config DRIVER_SPI_OMAP3
>>       bool "OMAP3 McSPI Master driver"
>>       depends on ARCH_OMAP3 || ARCH_AM33XX
>>
>> +config DRIVER_SPI_DSPI
>> +     tristate "Freescale DSPI SPI Master driver"
>> +     depends on ARCH_VF610
>> +     default y
>
> Please no default y for optional features.

OK, will fix in v2.

>
>> +     help
>> +       This enables support for the Freescale DSPI controller in master
>> +       mode. VF610 platform uses the controller.
>> +
>>  endif
>>
>>  endmenu
>
>> +static void hz_to_spi_baud(char *pbr, char *br, int speed_hz,
>> +                        unsigned long clkrate)
>> +{
>> +     /* Valid baud rate pre-scaler values */
>> +     int pbr_tbl[4] = {2, 3, 5, 7};
>> +     int brs[16] = { 2,      4,      6,      8,
>> +             16,     32,     64,     128,
>> +             256,    512,    1024,   2048,
>> +             4096,   8192,   16384,  32768 };
>> +     int scale_needed, scale, minscale = INT_MAX;
>> +     int i, j;
>> +
>> +     scale_needed = clkrate / speed_hz;
>> +     if (clkrate % speed_hz)
>> +             scale_needed++;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(brs); i++)
>> +             for (j = 0; j < ARRAY_SIZE(pbr_tbl); j++) {
>> +                     scale = brs[i] * pbr_tbl[j];
>> +                     if (scale >= scale_needed) {
>> +                             if (scale < minscale) {
>> +                                     minscale = scale;
>> +                                     *br = i;
>> +                                     *pbr = j;
>> +                             }
>> +                             break;
>> +                     }
>> +             }
>> +
>> +     if (minscale == INT_MAX) {
>> +             pr_warn("Can not find valid baud rate,speed_hz is %d,"
>> +                     "clkrate is %ld, we use the max prescaler value.\n",
>> +                     speed_hz, clkrate);
>
> Use dev_* for drivers please.

WIll do in v2.

>
>> +             *pbr = ARRAY_SIZE(pbr_tbl) - 1;
>> +             *br =  ARRAY_SIZE(brs) - 1;
>> +     }
>> +}
>> +
>> +static void ns_delay_scale(char *psc, char *sc, int delay_ns,
>> +             unsigned long clkrate)
>> +{
>> +     int pscale_tbl[4] = {1, 3, 5, 7};
>> +     int scale_needed, scale, minscale = INT_MAX;
>> +     int i, j;
>> +     u32 remainder;
>> +
>> +     scale_needed = div_u64_rem((u64)delay_ns * clkrate, NSEC_PER_SEC,
>> +                     &remainder);
>> +     if (remainder)
>> +             scale_needed++;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(pscale_tbl); i++)
>> +             for (j = 0; j <= SPI_CTAR_SCALE_BITS; j++) {
>> +                     scale = pscale_tbl[i] * (2 << j);
>> +                     if (scale >= scale_needed) {
>> +                             if (scale < minscale) {
>> +                                     minscale = scale;
>> +                                     *psc = i;
>> +                                     *sc = j;
>> +                             }
>> +                             break;
>> +                     }
>> +             }
>> +
>> +     if (minscale == INT_MAX) {
>> +             pr_warn("Cannot find correct scale values for %dns "
>> +                     "delay at clkrate %ld, using max prescaler value",
>> +                     delay_ns, clkrate);
>> +             *psc = ARRAY_SIZE(pscale_tbl) - 1;
>> +             *sc = SPI_CTAR_SCALE_BITS;
>> +     }
>> +}
>> +
>> +static u32 dspi_xchg_single(struct fsl_dspi *dspi, u32 in)
>> +{
>> +     const uint32_t sr_ready = (SPI_SR_EOQF | SPI_SR_RFDF);
>> +
>> +     writel(in, dspi->base + SPI_PUSHR);
>> +
>> +     while ((readl(dspi->base + SPI_SR) & sr_ready) != sr_ready)
>> +             ;
>> +
>> +     writel(sr_ready, dspi->base + SPI_SR);
>> +
>> +     return readl(dspi->base + SPI_POPR);
>> +}
>> +
>> +static const void *dspi_load_and_advance(const void *buffer,
>> +                                      size_t word_size, uint32_t *word)
>> +{
>> +     if (!buffer) {
>> +             *word = 0;
>> +             return NULL;
>> +     }
>> +
>> +     switch (word_size) {
>> +     case 2:
>> +             *word = *(const uint16_t *)buffer;
>> +             break;
>> +     case 1:
>> +             *word = *(const uint8_t *)buffer;
>> +             break;
>> +     }
>> +
>> +     return buffer + word_size;
>> +}
>> +
>> +static void *dspi_store_and_advance(void *buffer,
>> +                                 size_t word_size, uint32_t word)
>> +{
>> +     if (!buffer)
>> +             return NULL;
>> +
>> +     switch (word_size) {
>> +     case 2:
>> +             *(uint16_t *)buffer = word;
>> +             break;
>> +     case 1:
>> +             *(uint8_t *)buffer  = word;
>> +             break;
>> +     }
>> +
>> +     return buffer + word_size;
>> +}
>> +
>> +
>> +static void dspi_do_transfer(struct spi_device *spi,
>> +                          struct spi_transfer *transfer,
>> +                          bool last)
>> +{
>> +     size_t i;
>> +     void *rx;
>> +     const void *tx;
>> +     unsigned int flags;
>> +     struct fsl_dspi *dspi = to_dspi(spi->master);
>> +     const size_t word_size = spi->bits_per_word <= 8 ? 1 : 2;
>> +     const size_t count = transfer->len / word_size;
>> +
>> +     flags = SPI_PUSHR_PCS(spi->chip_select)
>> +             | SPI_PUSHR_CTAS(spi->chip_select)
>> +             | SPI_PUSHR_EOQ
>> +             | SPI_PUSHR_CONT;
>> +
>> +     tx = transfer->tx_buf;
>> +     rx = transfer->rx_buf;
>> +
>> +     for (i = 0; i < count; i++) {
>> +             uint32_t in, out;
>> +
>> +             if (i == count - 1 &&
>> +                 (last || transfer->cs_change))
>> +                     flags &= ~SPI_PUSHR_CONT;
>> +
>> +             tx = dspi_load_and_advance(tx, word_size, &out);
>> +             in = dspi_xchg_single(dspi, flags | out);
>> +             rx = dspi_store_and_advance(rx, word_size, in);
>> +     }
>> +}
>> +
>> +static int dspi_transfer(struct spi_device *spi,
>> +                      struct spi_message *message)
>> +{
>> +     struct spi_transfer *transfer;
>> +
>> +     message->actual_length = 0;
>> +
>> +     list_for_each_entry(transfer, &message->transfers, transfer_list) {
>> +
>> +             if (spi->bits_per_word > 8 &&
>> +                 transfer->len % 2)
>> +                     dev_err(spi->master->dev,
>> +                             "Transfer doesn't contain exact number of SPI "
>> +                             "words. Last partial word will be truncated");
>> +
>> +             dspi_do_transfer(spi, transfer,
>> +                              list_is_last(&transfer->transfer_list,
>> +                                           &message->transfers));
>> +             message->actual_length += transfer->len;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int dspi_setup(struct spi_device *spi)
>> +{
>> +     struct fsl_dspi *dspi = to_dspi(spi->master);
>> +     unsigned char br = 0, pbr = 0, pcssck = 0, cssck = 0;
>> +     unsigned char pasc = 0, asc = 0, fmsz = 0;
>> +     unsigned long clkrate;
>> +
>> +     if (4 <= spi->bits_per_word && spi->bits_per_word <= 16)
>> +             fmsz = spi->bits_per_word - 1;
>> +     else
>> +             return -ENODEV;
>> +
>> +     clkrate = clk_get_rate(dspi->clk);
>> +     hz_to_spi_baud(&pbr, &br, spi->max_speed_hz, clkrate);
>> +
>> +     /* Set PCS to SCK delay scale values */
>> +     ns_delay_scale(&pcssck, &cssck, dspi->cs_sck_delay, clkrate);
>> +
>> +     /* Set After SCK delay scale values */
>> +     ns_delay_scale(&pasc, &asc, dspi->sck_cs_delay, clkrate);
>> +
>> +     writel(SPI_CTAR_FMSZ(fmsz)
>> +            | SPI_CTAR_CPOL(spi->mode & SPI_CPOL ? 1 : 0)
>> +            | SPI_CTAR_CPHA(spi->mode & SPI_CPHA ? 1 : 0)
>> +            | SPI_CTAR_LSBFE(spi->mode & SPI_LSB_FIRST ? 1 : 0)
>> +            | SPI_CTAR_PCSSCK(pcssck)
>> +            | SPI_CTAR_CSSCK(cssck)
>> +            | SPI_CTAR_PASC(pasc)
>> +            | SPI_CTAR_ASC(asc)
>> +            | SPI_CTAR_PBR(pbr)
>> +            | SPI_CTAR_BR(br),
>> +            dspi->base + SPI_CTAR(spi->chip_select));
>> +
>> +     return 0;
>> +}
>> +
>> +static int dspi_probe(struct device_d *dev)
>> +{
>> +     struct resource *io;
>> +     struct fsl_dspi *dspi;
>> +     struct spi_master *master;
>> +     struct device_node *np = dev->device_node;
>> +
>> +     int ret = 0;
>> +     uint32_t bus_num = 0;
>> +     uint32_t cs_num;
>> +
>> +     dspi = xzalloc(sizeof(*dspi));
>> +
>> +     dspi->devtype_data = of_device_get_match_data(dev);
>> +     if (!dspi->devtype_data) {
>> +             dev_err(dev, "can't get devtype_data\n");
>> +             ret = -EFAULT;
>> +             goto free_memory;
>> +     }
>> +
>> +     master = &dspi->master;
>> +     master->dev = dev;
>> +     master->bus_num = dev->id;
>> +     master->setup = dspi_setup;
>> +     master->transfer = dspi_transfer;
>> +
>> +     ret = of_property_read_u32(np, "spi-num-chipselects", &cs_num);
>> +     if (ret < 0) {
>> +             dev_err(dev, "can't get spi-num-chipselects\n");
>> +             goto free_memory;
>> +     }
>> +     master->num_chipselect = cs_num;
>> +
>> +     ret = of_property_read_u32(np, "bus-num", &bus_num);
>> +     if (ret < 0) {
>> +             dev_err(dev, "can't get bus-num\n");
>> +             goto free_memory;
>> +     }
>> +     master->bus_num = bus_num;
>
> I don't think the presence of a bus-num property should be enforced. It
> seems nobody has reviewed the binding since normally such a property
> shouldn't even exist. You can honor it if it's present, but shouldn't
> enforce it.

Good point. Will fix in v2.

>
>> +
>> +     of_property_read_u32(dev->device_node, "fsl,spi-cs-sck-delay",
>> +                          &dspi->cs_sck_delay);
>> +     of_property_read_u32(dev->device_node, "fsl,spi-sck-cs-delay",
>> +                          &dspi->sck_cs_delay);
>> +
>> +     io = dev_request_mem_resource(dev, 0);
>> +     if (IS_ERR(io))
>> +             return PTR_ERR(io);
>
> I don't care much about cleaning up properly in the probe error pathes
> in barebox, but now that you started with "goto free_memory" in the
> driver, you can use it here aswell ;)

True :-) Will fix in v2.

Thanks,
Andrey Smirnov



More information about the barebox mailing list