[PATCH v2 2/2] spi: add driver for Rockchip RK3xxx SoCs integrated SPI
addy ke
addy.ke at rock-chips.com
Sun Jul 6 18:42:52 PDT 2014
> On Tue, Jul 01, 2014 at 09:03:59AM +0800, addy ke wrote:
>> In order to facilitate understanding, rockchip SPI controller IP design
>> looks similar in its registers to designware. But IC implementation
>> is different from designware, So we need a dedicated driver for Rockchip
>> RK3XXX SoCs integrated SPI. The main differences:
>
> This looks good overall, a nice clean driver. I've applied it but there
> are a few small issues that need fixing up which I've noted below, can
> you please send followup patches dealing with these?
>
>> + * static void spi_set_cs(struct spi_device *spi, bool enable)
>> + * {
>> + * if (spi->mode & SPI_CS_HIGH)
>> + * enable = !enable;
>> + *
>> + * if (spi->cs_gpio >= 0)
>> + * gpio_set_value(spi->cs_gpio, !enable);
>> + * else if (spi->master->set_cs)
>> + * spi->master->set_cs(spi, !enable);
>> + * }
>> + *
>> + * Note: enable(rockchip_spi_set_cs) = !enable(spi_set_cs)
>> + */
>
> So, the point here is that chip select is an active low signal by
> default which means that if chip select is asserted we have a low logic
> level and the parameter means "asserted" not "logic level for the
> output". It doesn't really matter but it might be clearer to say so
> directly.
>
>> + if (spi->mode & SPI_CS_HIGH) {
>> + dev_err(rs->dev, "spi_cs_hign: not support\n");
>> + return -EINVAL;
>
> Typo here (high).
>
>> +static int rockchip_spi_unprepare_message(struct spi_master *master,
>> + struct spi_message *msg)
>> +{
>> + unsigned long flags;
>> + struct rockchip_spi *rs = spi_master_get_devdata(master);
>> +
>> + spin_lock_irqsave(&rs->lock, flags);
>> +
>> + if (rs->use_dma) {
>> + if (rs->state & RXBUSY) {
>> + dmaengine_terminate_all(rs->dma_rx.ch);
>> + flush_fifo(rs);
>> + }
>> +
>> + if (rs->state & TXBUSY)
>> + dmaengine_terminate_all(rs->dma_tx.ch);
>> + }
>
> This initially looks wrong - the DMA should all be quiesced by the time
> that we get to unpreparing the hardware, otherwise the transfer might be
> ongoing while the chip select is deasserted. However this is really
> just error handling in case something went wrong which is sensible and
> reasonable, a comment explaining this would help so can you please send
> a followup patch adding one.
>
> The error handling here is actually a good point - we should probably
> add a callback for the core to use when it times out since the issue
> also applies if there are further transactions queued with the hardware.
> I'll look into that later unless someone does it first.
>
>> + /* Delay until the FIFO data completely */
>> + if (xfer->tx_buf)
>> + xfer->delay_usecs
>> + = rs->fifo_len * rs->bpw * 1000000 / rs->speed;
>
> The driver shouldn't be doing this, if it needs a delay it needs to
> implement it itself. delay_usecs can be set by devices if they need a
> delay between transfers, it should be in addition to the time taken for
> the transfer to complete.
>
> Please send a followup patch fixing this.
>
Are the following modifications reasonable?
+static inline void wait_for_idle(struct rockchip_spi *rs)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(5);
+
+ while (time_before(jiffies, timeout)) {
+ if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY))
+ return;
+ }
+
+ dev_warn(rs->dev, "spi controller is in busy state!\n");
+}
static int rockchip_spi_pio_transfer(struct rockchip_spi *rs)
{
int remain = 0;
do {
if (rs->tx) {
remain = rs->tx_end - rs->tx;
rockchip_spi_pio_writer(rs);
}
if (rs->rx) {
remain = rs->rx_end - rs->rx;
rockchip_spi_pio_reader(rs);
}
cpu_relax();
} while (remain);
+ /* If tx, wait until the FIFO data completely. */
+ if (rs->tx)
+ wait_for_idle(rs);
return 0;
}
static void rockchip_spi_dma_txcb(void *data)
{
unsigned long flags;
struct rockchip_spi *rs = data;
+ /* Wait until the FIFO data completely. */
+ wait_for_idle(rs);
spin_lock_irqsave(&rs->lock, flags);
rs->state &= ~TXBUSY;
if (!(rs->state & RXBUSY))
spi_finalize_current_transfer(rs->master);
spin_unlock_irqrestore(&rs->lock, flags);
}
>> +static bool rockchip_spi_can_dma(struct spi_master *master,
>> + struct spi_device *spi,
>> + struct spi_transfer *xfer)
>> +{
>> + struct rockchip_spi *rs = spi_master_get_devdata(master);
>> +
>> + return (xfer->len > rs->fifo_len);
>> +}
>
> We should factor this out into the core as well, just let the driver set
> the minimum size for DMA since it's such a common pattern. I'll look
> into this as well.
>
>> + master = spi_alloc_master(&pdev->dev, sizeof(struct rockchip_spi));
>> + if (!master) {
>> + dev_err(&pdev->dev, "No memory for spi_master\n");
>> + return -ENOMEM;
>> + }
>
> No need to print an error message - OOM messags from the memory
> management subsystem are already noisy enough as it is.
>
>> + dev_info(&pdev->dev, "Rockchip SPI controller initialized\n");
>
> Please send a followup patch removing this, it's not really adding
> anything and there's core debug messages that can be enabled - usually
> these prints are done when there is some information that has been read
> back from the hardware (eg, IP revisions).
>
>> +static const struct of_device_id rockchip_spi_dt_match[] = {
>> + { .compatible = "rockchip,rk3066-spi", },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
>
> Your DT binding defined some additional compatible strings, please add
> those to the driver.
>
So which is better to describe DT binding for rockchip spi driver as follow:
1. Only add "rockchip,rk3066-spi" for all rockchip socs:
In Documentation/devicetree/bindings/spi/spi-rockchip.txt
- compatible: should be one of the following.
"rockchip,rk3066-spi" for rk3066 and following.
In drivers/spi/spi-rockchip.c
static const struct of_device_id rockchip_spi_dt_match[] = {
{ .compatible = "rockchip,rk3066-spi", },
{ },
};
------
2. Add "rockchip,rk3066-spi", "rockchip,rk3066-spi", "rockchip,rk3066-spi" for each soc:
In Documentation/devicetree/bindings/spi/spi-rockchip.txt
- compatible: should be one of the following.
"rockchip,rk3066-spi" for rk3066.
"rockchip,rk3188-spi", "rockchip,rk3066-spi" for rk3188.
"rockchip,rk3288-spi", "rockchip,rk3066-spi" for rk3288.
In drivers/spi/spi-rockchip.c
static const struct of_device_id rockchip_spi_dt_match[] = {
{ .compatible = "rockchip,rk3066-spi", },
{ .compatible = "rockchip,rk3188-spi", },
{ .compatible = "rockchip,rk3288-spi", },
{ },
};
More information about the linux-arm-kernel
mailing list