Re: [PATCH v2 1/2] SPI: Add SPI driver for Sunplus SP7021
From: Mark Brown
Date: Tue Nov 09 2021 - 09:56:52 EST
On Tue, Nov 09, 2021 at 05:01:27PM +0800, LH.Kuo wrote:
A lot of my previous feedback on this driver still applies, while some
of the issues have been addressed most of the major structural issues
continue to apply here. A lot of the code is replicating code from the
core or is really hard to explain, it's hard to see anything super
unusual with the hardware here that would require such unusual code.
Please don't ignore review comments, people are generally making them
for a reason and are likely to have the same concerns if issues remain
unaddressed. Having to repeat the same comments can get repetitive and
make people question the value of time spent reviewing. If you disagree
with the review comments that's fine but you need to reply and discuss
your concerns so that the reviewer can understand your decisions.
> +static void sp7021_spi_set_cs(struct spi_device *_s, bool _on)
> +{
> + if (_s->mode & SPI_NO_CS)
> + return;
> + if (!(_s->cs_gpiod))
> + return;
> + dev_dbg(&(_s->dev), "%d gpiod:%d", _on, desc_to_gpio(_s->cs_gpiod));
> + if (_s->mode & SPI_CS_HIGH)
> + _on = !_on;
> + gpiod_set_value_cansleep(_s->cs_gpiod, _on);
> +}
This is *still* open coding a GPIO chip select, to repeat what I said
last time this is not OK - use the core facilities to avoid introducing
bugs like double application of SPI_CS_HIGH you have here.
> +// spi slave irq handler
> +static irqreturn_t sp7021_spi_sla_irq(int _irq, void *_dev)
> +{
> + struct sp7021_spi_ctlr *pspim = (struct sp7021_spi_ctlr *)_dev;
If you need this cast something is very wrong, do you need it?
> +int sp7021_spi_sla_rw(struct spi_device *_s, struct spi_transfer *_t, int RW_phase)
Please use the normal kernel coding style, using _s for parameter names
or mixed case symbol names isn't normal for the kernel. There's also
issues with line lengths over 80 columns all over, while it's not a
strict limit it's still good try to keep things to a reasonable length.
> + if (RW_phase == SP7021_SLA_WRITE) {
This looks like a switch statement, though given how little code is
shared it's not clear why this is all in one function.
> + if (_t->tx_dma == pspim->tx_dma_phy_base)
> + memcpy(pspim->tx_dma_vir_base, _t->tx_buf, _t->len);
Why are we copying data into a DMA transfer buffer, doesn't this defeat
the point of DMA? I'd expect to DMA data directly. I'd also expect
some synchronisation operations to ensure that everything has a
consistent view of the memory.
> +// spi master irq handler
> +static irqreturn_t sp7021_spi_mas_irq_dma(int _irq, void *_dev)
> +{
> + struct sp7021_spi_ctlr *pspim = (struct sp7021_spi_ctlr *)_dev;
> +
> + spin_lock_irq(&pspim->lock);
Why are we using spin_lock_irq() when we're already in an interrupt
handler?
> + }
> +}
> +void sp7021spi_wb(struct sp7021_spi_ctlr *_m, u8 _len)
Blank lines between functions.
> +fin_irq:
> + if (isrdone)
> + complete(&pspim->isr_done);
> + spin_unlock_irqrestore(&pspim->lock, flags);
> + return IRQ_HANDLED;
> +}
This unconditionally reports IRQ_HANDLED even if we didn't actually see
any interrupt status flagged, this will break shared interrupts and
reduce the ability of the interrupt core to handle errors.
> + for (i = 0; i < transfers_cnt; i++) {
> + if (t->tx_buf)
> + memcpy(&pspim->tx_data_buf[data_len], t->tx_buf, t->len);
> + if (t->rx_buf)
> + xfer_rx = true;
> + data_len += t->len;
> + t = list_entry(t->transfer_list.next, struct spi_transfer, transfer_list);
> + }
This is still copying all data for no apparent reason as highlighted
last time.
> + dev_dbg(&(_c->dev), "data_len %d xfer_rx %d", data_len, xfer_rx);
> +
> + // set SPI FIFO data for full duplex (SPI_FD fifo_data) 91.13
> + if (pspim->tx_cur_len < data_len) {
> + len_temp = min(pspim->data_unit, data_len);
> + sp7021spi_wb(pspim, len_temp);
> + }
Is the device full duplex or half duplex? The code immediately above
this treats both transmit and recieve buffers as optional. If the
device does actually need to be full duplex then the driver should flag
it as such.
> +// called when child device is registering on the bus
> +static int sp7021_spi_dev_setup(struct spi_device *_s)
> +{
> + struct sp7021_spi_ctlr *pspim = spi_controller_get_devdata(_s->controller);
> + int ret;
> +
> + ret = pm_runtime_get_sync(pspim->dev);
> + if (ret < 0)
> + return 0;
> +
> + pm_runtime_put(pspim->dev);
> +
> + return 0;
> +
> +}
This function does nothing except bounce the power on the device, this
is obviously not useful and should be removed.
> +static int sp7021_spi_controller_unprepare_message(struct spi_controller *_c,
> + struct spi_message *msg)
> +{
> + return 0;
> +}
Remove empty functions.
> +static size_t sp7021_spi_max_length(struct spi_device *spi)
> +{
> + return SP7021_SPI_MSG_SIZE;
> +}
Is there any actual limit in the hardware? This looks very much like
it's a completely artificial limit in the driver for no obvious reason.
> +static int sp7021_spi_mas_transfer_one_message(struct spi_controller *_c, struct spi_message *m)
> +{
> + struct sp7021_spi_ctlr *pspim = spi_master_get_devdata(_c);
> + struct spi_device *spi = m->spi;
> + unsigned int xfer_cnt = 0, total_len = 0;
> + bool start_xfer = false;
> + struct spi_transfer *xfer, *first_xfer = NULL;
> + int ret;
> + bool keep_cs = false;
> +
> + pm_runtime_get_sync(pspim->dev);
To repeat the feedback from last time do not open code runtime PM, use
the core support.
> + sp7021_spi_set_cs(spi, true);
> +
> + list_for_each_entry(xfer, &m->transfers, transfer_list) {
> + if (!first_xfer)
> + first_xfer = xfer;
> + total_len += xfer->len;
To further repeat my prior feedback I can't see any reason why this
driver doesn't just use transfer_one().
Attachment:
signature.asc
Description: PGP signature