[PATCH v2] spi: add spi_tegra driver
Erik Gilling
konkers at android.com
Wed Aug 25 17:22:53 EDT 2010
On Thu, Aug 12, 2010 at 12:54 PM, Grant Likely
<grant.likely at secretlab.ca> wrote:
> Missing patch description. Give reviewers and future readers some
> more details about what hardware this patch adds support for.
I'll add one
>> +#undef DEBUG
>
> Still need to trim this #undef
Done.
> It's a good idea to prefix all these register #defines with something
> driver specific to avoid the possibility of collisions with the global
> namespace.
I'll add SLINK_ to all the bit definitions.
>> +static inline unsigned bytes_per_word(u8 bits)
>> +{
>> + WARN_ON((bits < 1) || bits > 32);
>> + if (bits <= 8)
>> + return 1;
>> + else if (bits <= 16)
>> + return 2;
>> + else if (bits <= 24)
>> + return 3;
>> + else
>> + return 4;
>> +}
>
> No longer used, can be removed.
done
>> +static unsigned long spi_tegra_readl(struct spi_tegra_data *tspi,
>> + unsigned long reg)
>> +{
>> + return readl(tspi->base + reg);
>> +}
>
> static inline
done
>> +
>> +static void spi_tegra_writel(struct spi_tegra_data *tspi, unsigned long val,
>> + unsigned long reg)
>> +{
>> + writel(val, tspi->base + reg);
>> +}
>
> ditto
done
>> + val = spi_tegra_readl(tspi, SLINK_COMMAND2);
>> + if (t->rx_buf)
>> + val |= RXEN;
>> + else
>> + val &= ~RXEN;
>> +
>> + if (t->tx_buf)
>> + val |= TXEN;
>> + else
>> + val &= ~TXEN;
>> +
>> + val &= ~SS_EN_CS(~0);
>> + val |= SS_EN_CS(spi->chip_select);
>> + val |= SPIE;
>
> Nit: This would be more concise:
>
> + val &= ~(SS_EN_CS(~0) | RXEN | TXEN);
> + if (t->rx_buf)
> + val |= RXEN;
> + if (t->tx_buf)
> + val |= TXEN;
> + val |= SS_EN_CS(spi->chip_select);
> + val |= SPIE;
done
>> + if (spi->mode & SPI_CPOL)
>> + val |= IDLE_SCLK_DRIVE_HIGH;
>> + else
>> + val |= IDLE_SCLK_DRIVE_LOW;
>
> nit: IDLE_SCLK_DRIVE_LOW == 0. The driver will be more concise if the
> above 2 lines are dropped.
I did it this way because it's not obvious from this code which sets
the bit and which does not without decoding the #defines. I expect
the complier optimizes this out.
>> +
>> + val |= M_S;
>> +
>> + spi_tegra_writel(tspi, val, SLINK_COMMAND);
>
> The read/modify/write register blocks are quite common. Would it be
> clearer and more concise to have a spi_tegra_clrsetbits() static
> inline?
More concise, yes. Clearer? I don't think so. It seems that
clrsetbits is mostly used in ppc code.
>> + /* the SPI controller may come back with both the BSY and RDY bits
>> + * set. In this case we need to wait for the BSY bit to clear so
>> + * that we are sure the DMA is finished. 1000 reads was empirically
>> + * determined to be long enough.
>> + */
>> + while (timeout++ < 1000) {
>> + if (!(spi_tegra_readl(tspi, SLINK_STATUS) & BSY))
>> + break;
>> + }
>
> What happens if the BSY bit is still set when the loop exits?
Given how I've seen the hardware act, I don't think it's possible for
BSY to still be set after 1000 reads. I forget the average number of
loops but 1000 was very conservative. Worst case some of the data
might be corrupt. I'll put an error message in and set status to
-EIO.
>> +
>> + val = spi_tegra_readl(tspi, SLINK_STATUS);
>> + val |= RDY;
>> + spi_tegra_writel(tspi, val, SLINK_STATUS);
>
> The spin lock isn't held at this point. Will that make this
> read-modify-write operation risky?
move spinlock up.
>> +
>> + spin_lock_irqsave(&tspi->lock, flags);
>
> Is there any condition/event which could cause the spi controller to
> go busy before obtaining the spinlock?
No. The controller only goes busy when a transaction is started by
calling spi_tegra_go().
>> +
>> + m = list_first_entry(&tspi->queue, struct spi_message, queue);
>> + spi = m->state;
>> +
>> + tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur);
>> + m->actual_length += tspi->cur_pos;
>> +
>> + if (tspi->cur_pos < tspi->cur->len) {
>> + tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, tspi->cur);
>> + spi_tegra_go(tspi);
>> + } else if (!list_is_last(&tspi->cur->transfer_list,
>> + &m->transfers)) {
>> + tspi->cur = list_first_entry(&tspi->cur->transfer_list,
>> + struct spi_transfer,
>> + transfer_list);
>> + spi_tegra_start_transfer(spi, tspi->cur);
>> + } else {
>> + complete = 1;
>> + }
>> +
>> + if (complete) {
>
> Looks like the above else clause is the only thing that can set
> complete. You could delete the above three lines to get the same
> behaviour.
deleted
>> + list_del(&m->queue);
>> +
>> + m->status = 0;
>> + m->complete(m->context);
>> + switch (spi->chip_select) {
>> + case 0:
>> + cs_bit = CS_POLARITY;
>> + break;
>> +
>> + case 1:
>> + cs_bit = CS_POLARITY1;
>> + break;
>> +
>> + case 2:
>> + cs_bit = CS_POLARITY2;
>> + break;
>> +
>> + case 4:
>> + cs_bit = CS_POLARITY3;
>> + break;
>
> CS_POLARITY is named a little oddly as it seems to indicate the bit,
> not the polarity of the bit. Would this better be named
> CS_BIT0..CS_BIT3?
That's how Nvidia named them in the TRM.
>> + tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
>> + if (IS_ERR(tspi->rx_dma)) {
>
> As an aside, and commenting on the tegra DMA api... Is the IS_ERR()
> pattern in the tegra_dma_allocate_channel() return really a good idea
> here? Personally I find that pattern isn't very useful, especially
> when the results is either "yes, allocated" or "no, not-alocated", and
> it is very easy to write code that tests the wrong thing on the return
> value because the compiler cannot catch it. In other words, if it
> isn't vital to return a specific error code, the using the PTR_ERR()
> pattern adds unnecessary risk to the code because developers are used
> to seeing and writing this pattern:
>
> tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
> if (!tspi->rx_dma)
> return -ENOMEM;
>
> The compiler won't catch it if the return value is an PTR_ERR.
>
> (I know that some subsystems make heavy use of it, particularly
> filesystems and the block layer which do need to return specific error
> codes to userspace. My argument is that the specific error code is
> probably irrelevant when allocating DMA channels)
I don't have a strong opinion either way but that's the way Colin's
code is written. I'll let him speak to that.
>> +static int __init spi_tegra_init(void)
>> +{
>> + return platform_driver_probe(&spi_tegra_driver, spi_tegra_probe);
>> +}
>> +subsys_initcall(spi_tegra_init);
>
> Unless there is a specific reason that this driver needs to be
> initialized earlier, it should use the module_init() call. In which
> case the reason it is initialized early should be documented with a
> comment. (for reference: 4 of the mainlined spi bus drivers use
> subsys_initcall() whereas 30 of them use module_init().. 1 uses
> device_initcall() which is the same as module_init. None of the ones
> using subsys_initcall() give a reason why.)
heh.. I missed the module_inits because i grepped for initcall.
changed to module_init.
patch incoming.
-Erik
More information about the linux-arm-kernel
mailing list