Re: [PATCH] SPI: Add driver for Cadence SPI controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Mar 17, 2014 at 05:35:36PM +0530, Harini Katakam wrote:

> +	bits_per_word = transfer ?
> +			transfer->bits_per_word : spi->bits_per_word;

This would be a lot more legible without the ternery operator.

> +	if (bits_per_word != 8) {
> +		dev_err(&spi->dev, "%s, unsupported bits per word %x\n",
> +			__func__, spi->bits_per_word);
> +		return -EINVAL;
> +	}

The core will check this for you.

> +static int cdns_spi_setup(struct spi_device *spi)
> +{
> +	if (!spi->max_speed_hz)
> +		return -EINVAL;
> +
> +	if (!spi->bits_per_word)
> +		spi->bits_per_word = 8;

The core does this for you.

> +	return cdns_spi_setup_transfer(spi, NULL);
> +}

It's not clear to me why this has been split into a separate function
and the function will write to the hardware which you're not allowed to
do in setup() if it might affect an ongoing transfer.

> +	intr_status = cdns_spi_read(xspi->regs + CDNS_SPI_ISR_OFFSET);
> +	cdns_spi_write(xspi->regs + CDNS_SPI_ISR_OFFSET, intr_status);
> +
> +	if (intr_status & CDNS_SPI_IXR_MODF_MASK) {
> +		/* Indicate that transfer is completed, the SPI subsystem will
> +		 * identify the error as the remaining bytes to be
> +		 * transferred is non-zero
> +		 */
> +		cdns_spi_write(xspi->regs + CDNS_SPI_IDR_OFFSET,
> +			       CDNS_SPI_IXR_DEFAULT_MASK);
> +		complete(&xspi->done);
> +	} else if (intr_status & CDNS_SPI_IXR_TXOW_MASK) {

What happens if both interrupts are flagged at the same time?

> +		if (xspi->remaining_bytes) {
> +			/* There is more data to send */
> +			cdns_spi_fill_tx_fifo(xspi);
> +		} else {
> +			/* Transfer is completed */
> +			cdns_spi_write(xspi->regs + CDNS_SPI_IDR_OFFSET,
> +				       CDNS_SPI_IXR_DEFAULT_MASK);
> +			complete(&xspi->done);
> +		}
> +	}

I see from further up the file that there are error interrupt states
which might be flagged but these are just being ignored.

> +
> +	return IRQ_HANDLED;

This should only be returned if an interrupt was actually handled - if
the line is shared in some system this will break.

> +	cdns_spi_write(xspi->regs + CDNS_SPI_IER_OFFSET,
> +		       CDNS_SPI_IXR_DEFAULT_MASK);
> +
> +	ret = wait_for_completion_interruptible_timeout(&xspi->done,
> +							CDNS_SPI_TIMEOUT);
> +	if (ret < 1) {

If you return a positive value from transfer_one() the core will wait
for you.

> +static int cdns_prepare_transfer_hardware(struct spi_master *master)
> +{
> +	struct cdns_spi *xspi = spi_master_get_devdata(master);
> +
> +	if (xspi->driver_state != CDNS_SPI_DRIVER_STATE_READY)
> +		return -EINVAL;
> +
> +	cdns_spi_write(xspi->regs + CDNS_SPI_ER_OFFSET,
> +		       CDNS_SPI_ER_ENABLE_MASK);
> +
> +	return 0;
> +}

You probably want to enable the clocks here (and disable them when
unpreparing too).

> +static int cdns_transfer_one_message(struct spi_master *master,
> +				     struct spi_message *msg)
> +{

Just implement transfer_one() and provide a set_cs() operation and most
of this code will go away.

> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		ret = -ENXIO;
> +		dev_err(&pdev->dev, "irq number is negative\n");
> +		goto remove_master;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, irq, cdns_spi_irq,
> +			       0, pdev->name, xspi);
> +	if (ret != 0) {
> +		ret = -ENXIO;
> +		dev_err(&pdev->dev, "request_irq failed\n");
> +		goto remove_master;
> +	}

I'd expect to see something that initialises the hardware prior to
requesting the interrupt, you don't know what state the hardware is in
when the driver takes control.

> +	init_completion(&xspi->done);

This needs to be done prior to the interrupt as well.

> +	ret = of_property_read_u16(pdev->dev.of_node, "num-chip-select",
> +				   &master->num_chipselect);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "couldn't determine num-chip-select\n");
> +		goto clk_dis_all;
> +	}

Is there no reasonable default?

> +	/* Set to default valid value */
> +	xspi->speed_hz = clk_get_rate(xspi->ref_clk) / 4;

The driver should set max_speed_hz as well.

> +	dev_info(&pdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n",
> +		 res->start, (u32 __force)xspi->regs, irq);

No need for this, it's just noisy.

> +static int __maybe_unused cdns_spi_suspend(struct device *dev)
> +{

This needs to call spi_master_suspend() as well (and similarly on
resume).

> +	cdns_spi_write(xspi->regs + CDNS_SPI_IDR_OFFSET,
> +		       CDNS_SPI_IXR_DEFAULT_MASK);
> +	complete(&xspi->done);
> +
> +	ctrl_reg = cdns_spi_read(xspi->regs + CDNS_SPI_CR_OFFSET);
> +	ctrl_reg |= CDNS_SPI_CR_SSCTRL_MASK;
> +	cdns_spi_write(xspi->regs + CDNS_SPI_CR_OFFSET, ctrl_reg);

Don't do this, the spi_master_suspend() call will quiesce transfers (or
if open coding it should be doing something similar rather than just
breaking any ongoing transfer).

> +	clk_disable(xspi->ref_clk);
> +	clk_disable(xspi->pclk);

Why not unprepare?

Attachment: signature.asc
Description: Digital signature


[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux