Re: [PATCH v9 2/2] spi: loongson: add bus driver for the loongson spi controller

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

 



Wed, Apr 26, 2023 at 03:10:45PM +0800, Yinbo Zhu kirjoitti:
> This bus driver supports the Loongson spi hardware controller in the
> Loongson platforms and supports to use DTS and PCI framework to
> register spi device resources.

SPI

...

> +config SPI_LOONGSON_CORE
> +	tristate "Loongson SPI Controller Core Driver Support"

Does it need to be visible to the user?

> +	depends on LOONGARCH || COMPILE_TEST
> +	help
> +	  This core driver supports the Loongson spi hardware controller in
> +	  the Loongson platforms.
> +	  Say Y or M here if you want to use the SPI controller on
> +	  Loongson platform.

...

> +config SPI_LOONGSON_PLATFORM
> +	tristate "Loongson SPI Controller Platform Driver Support"
> +	select SPI_LOONGSON_CORE
> +	depends on OF && (LOONGARCH || COMPILE_TEST)

Is it really dependent to OF? Why?

> +	help
> +	  This bus driver supports the Loongson spi hardware controller in
> +	  the Loongson platforms and supports to use DTS framework to
> +	  register spi device resources.
> +	  Say Y or M here if you want to use the SPI controller on
> +	  Loongson platform.

...

> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/spi/spi.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>

Ordered?

...

> +	if (loongson_spi->mode & SPI_NO_CS)
> +		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, 0);

Missing {}

> +	else {
> +		cs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG)
> +					   & ~(0x11 << spi->chip_select);
> +		loongson_spi_write_reg(loongson_spi,
> +				       LOONGSON_SPI_SFCS_REG,
> +				       (val ? (0x11 << spi->chip_select) :
> +				       (0x1 << spi->chip_select)) | cs);

Too many parentheses.

> +	}

...

> +	const char rdiv[12] = {0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10, 11};

Oh, why?!

...

> +	if ((hz && loongson_spi->hz != hz) ||
> +	    ((spi->mode ^ loongson_spi->mode) & (SPI_CPOL | SPI_CPHA))) {
> +		div = DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz);

> +		if (div < 2)
> +			div = 2;
> +		if (div > 4096)
> +			div = 4096;

NIH clamp_val()

> +		bit = fls(div) - 1;
> +		if ((1<<bit) == div)
> +			bit--;
> +		div_tmp = rdiv[bit];

I believe this can be optimized.

> +		dev_dbg(&spi->dev, "clk_rate = %llu hz = %d div_tmp = %d bit = %d\n",
> +			loongson_spi->clk_rate, hz, div_tmp, bit);
> +
> +		loongson_spi->hz = hz;
> +		loongson_spi->spcr = div_tmp & 3;
> +		loongson_spi->sper = (div_tmp >> 2) & 3;
> +		val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
> +		val &= ~0xc;

GENMASK()

> +		if (spi->mode & SPI_CPOL)
> +			val |= 8;

BIT()

> +		if (spi->mode & SPI_CPHA)
> +			val |= 4;

> +		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, (val & ~3) |
> +				       loongson_spi->spcr);
> +		val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
> +		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, (val & ~3) |
> +				       loongson_spi->sper);
> +		loongson_spi->mode &= SPI_NO_CS;
> +		loongson_spi->mode |= spi->mode;
> +	}

...

> +		while ((loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG) & 0x1) == 1 &&
> +			time_after(timeout, jiffies))
> +			cpu_relax();

iopoll.h has a suitable macro for this.

...

> +		while ((loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG) & 0x1) == 1 &&
> +			time_after(timeout, jiffies))
> +			cpu_relax();

Ditto.

...

> +	master = devm_spi_alloc_master(dev, sizeof(struct loongson_spi));
> +	if (master == NULL) {

> +		dev_info(dev, "master allocation failed\n");

We do not issue a message for ENOMEM

> +		return -ENOMEM;
> +	}

...

> +	master->dev.of_node = of_node_get(dev->of_node);

device_set_node()

...

> +	spi->base = devm_ioremap(dev, res->start, resource_size(res));

Why not devm_ioremap_resource()?


> +	if (spi->base == NULL) {
> +		dev_err(dev, "cannot map io\n");
> +		return -ENXIO;

	return dev_err_probe();

> +	}

...

> +	clk = devm_clk_get(dev, NULL);

Can we hav

> +	if (!IS_ERR(clk))

Use _optional variant above instead of this.
Do not forget about deferred probe.

> +		spi->clk_rate = clk_get_rate(clk);

...

> +	if (of_get_property(dev->of_node, "spi-nocs", NULL))
> +		spi->mode |= SPI_NO_CS;

Don't we have something in the SPI core to handle this in a generic way?

...


> +EXPORT_SYMBOL_GPL(loongson_spi_init_master);

Please, use _NS variant.

...

> +MODULE_DESCRIPTION("Loongson spi core driver");

SPI

...

> +	struct resource res[2];
> +	struct device *dev = &pdev->dev;
> +
> +	ret = pci_enable_device(pdev);

pcim_enable_device()

> +	if (ret < 0) {
> +		dev_err(dev, "cannot enable pci device\n");
> +		goto err_out;

	return dev_err_probe();

> +	}
> +
> +	ret = pci_request_region(pdev, 0, "loongson-spi io");
> +	if (ret < 0) {
> +		dev_err(dev, "cannot request region 0.\n");
> +		goto err_out;
> +	}
> +
> +	res[0].start = pci_resource_start(pdev, 0);
> +	res[0].end = pci_resource_end(pdev, 0);

What's wrong with pcim_iomap_regions()?

...

> +	ret = pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &v8);

What?!

What's wrong with pci_alloc_irq_vectors()?

> +
> +	if (ret == PCIBIOS_SUCCESSFUL) {
> +		res[1].start = v8;
> +		res[1].end = v8;
> +	}
> +
> +	ret = loongson_spi_init_master(dev, res);

Why not passing the remapped address and IRQ number instead?

> +	if (ret)
> +		dev_err(dev, "failed to initialize master\n");

	return dev_err_probe();

> +
> +err_out:

Completely useless. Return in-line.

> +	return ret;
> +}

...

> +static struct pci_device_id loongson_spi_devices[] = {
> +	{PCI_DEVICE(0x14, 0x7a0b)},
> +	{PCI_DEVICE(0x14, 0x7a1b)},

Can you define vendor ID in pci_ids.h?


> +	{0, 0, 0, 0, 0, 0, 0}

What is this? Why {} is not working for you?

> +};

> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (res == NULL) {

Why not using devm_platform_ioremap_resource()?

> +		dev_err(dev, "cannot get io resource memory\n");
> +		return -ENOENT;

	return dev_err_probe();

> +	}
> +
> +	ret = loongson_spi_init_master(dev, res);
> +	if (ret)
> +		dev_err(dev, "failed to initialize master\n");

Ditto.

...

> +static const struct of_device_id loongson_spi_id_table[] = {
> +	{ .compatible = "loongson,ls2k-spi", },

Inned comma is redundant.

> +	{ }
> +};

...

> +#ifndef __LINUX_SPI_LOONGSON_H
> +#define __LINUX_SPI_LOONGSON_H

Missing bits.h
Missing types.h
Missing declaration for msecs_to_jiffies()
Missing forward declarations for struct spi_master and struct device.
MIssing declaration for dev_pm_ops.


> +#define	LOONGSON_SPI_SPCR_REG	0x00
> +#define	LOONGSON_SPI_SPSR_REG	0x01
> +#define	LOONGSON_SPI_FIFO_REG	0x02
> +#define	LOONGSON_SPI_SPER_REG	0x03
> +#define	LOONGSON_SPI_PARA_REG	0x04
> +#define	LOONGSON_SPI_SFCS_REG	0x05
> +#define	LOONGSON_SPI_TIMI_REG	0x06
> +
> +/* Bits definition for Loongson SPI register */
> +#define	LOONGSON_SPI_PARA_MEM_EN	BIT(0)
> +#define	LOONGSON_SPI_SPSR_SPIF	BIT(7)
> +#define	LOONGSON_SPI_SPSR_WCOL	BIT(6)
> +#define	LOONGSON_SPI_SPCR_SPE	BIT(6)
> +
> +#define SPI_COMPLETION_TIMEOUT	msecs_to_jiffies(2000)
> +
> +struct loongson_spi {
> +	struct	spi_master	*master;
> +	void __iomem		*base;
> +	int			cs_active;
> +	unsigned int		hz;
> +	unsigned char		spcr;
> +	unsigned char		sper;
> +	unsigned char		spsr;
> +	unsigned char		para;
> +	unsigned char		sfcs;
> +	unsigned char		timi;
> +	unsigned int		mode;
> +	u64			clk_rate;
> +};
> +
> +extern int loongson_spi_init_master(struct device *dev, struct resource *res);

No extern for the function declarations.

> +extern const struct dev_pm_ops loongson_spi_dev_pm_ops;

> +#endif /* __LINUX_SPI_LOONGSON_H */

-- 
With Best Regards,
Andy Shevchenko





[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux