ath79: add new ar934x spi driver
A new shift mode was introduced since ar934x which has a way better performance than current bitbang driver and can handle higher spi clock properly. This commit adds a new driver to make use of this new feature. This new driver has chipselect properly configured and we don't need cs-gpios hack in dts anymore. Remove them. Signed-off-by: Chuanhong Guo <gch981213@gmail.com>
This commit is contained in:
		| @@ -211,6 +211,7 @@ CONFIG_SERIAL_AR933X_CONSOLE=y | ||||
| CONFIG_SERIAL_AR933X_NR_UARTS=2 | ||||
| CONFIG_SERIAL_OF_PLATFORM=y | ||||
| CONFIG_SPI=y | ||||
| CONFIG_SPI_AR934X=y | ||||
| CONFIG_SPI_ATH79=y | ||||
| CONFIG_SPI_BITBANG=y | ||||
| CONFIG_SPI_GPIO=y | ||||
|   | ||||
| @@ -110,7 +110,6 @@ | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	num-cs = <2>; | ||||
| 	cs-gpios= <0>, <0>; | ||||
|  | ||||
| 	flash@0 { | ||||
| 		#address-cells = <1>; | ||||
|   | ||||
| @@ -183,11 +183,10 @@ | ||||
| 		}; | ||||
|  | ||||
| 		spi: spi@1f000000 { | ||||
| 			compatible = "qca,ar9340-spi", "qca,ar7100-spi"; | ||||
| 			compatible = "qca,ar934x-spi"; | ||||
| 			reg = <0x1f000000 0x1c>; | ||||
|  | ||||
| 			clocks = <&pll ATH79_CLK_AHB>; | ||||
| 			clock-names = "ahb"; | ||||
|  | ||||
| 			#address-cells = <1>; | ||||
| 			#size-cells = <0>; | ||||
|   | ||||
| @@ -76,7 +76,6 @@ | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	num-cs = <2>; | ||||
| 	cs-gpios = <0>, <0>; | ||||
|  | ||||
| 	flash@0 { | ||||
| 		compatible = "jedec,spi-nor"; | ||||
|   | ||||
| @@ -201,11 +201,10 @@ | ||||
| 		}; | ||||
|  | ||||
| 		spi: spi@1f000000 { | ||||
| 			compatible = "qca,ar9530-spi", "qca,ar7100-spi"; | ||||
| 			reg = <0x1f000000 0x10>; | ||||
| 			compatible = "qca,ar934x-spi"; | ||||
| 			reg = <0x1f000000 0x1c>; | ||||
|  | ||||
| 			clocks = <&pll ATH79_CLK_AHB>; | ||||
| 			clock-names = "ahb"; | ||||
|  | ||||
| 			status = "disabled"; | ||||
|  | ||||
|   | ||||
| @@ -292,11 +292,10 @@ | ||||
| 		}; | ||||
|  | ||||
| 		spi: spi@1f000000 { | ||||
| 			compatible = "qca,ar9557-spi", "qca,ar7100-spi"; | ||||
| 			reg = <0x1f000000 0x10>; | ||||
| 			compatible = "qca,ar934x-spi"; | ||||
| 			reg = <0x1f000000 0x1c>; | ||||
|  | ||||
| 			clocks = <&pll ATH79_CLK_AHB>; | ||||
| 			clock-names = "ahb"; | ||||
|  | ||||
| 			status = "disabled"; | ||||
|  | ||||
|   | ||||
| @@ -75,7 +75,6 @@ | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	num-cs = <2>; | ||||
| 	cs-gpios = <0>, <0>; | ||||
|  | ||||
| 	flash_nor: flash@0 { | ||||
| 		compatible = "jedec,spi-nor"; | ||||
|   | ||||
| @@ -100,7 +100,6 @@ | ||||
| 	status = "okay"; | ||||
|  | ||||
| 	num-cs = <2>; | ||||
| 	cs-gpios = <0>, <0>; | ||||
|  | ||||
| 	flash@0 { | ||||
| 		compatible = "jedec,spi-nor"; | ||||
|   | ||||
| @@ -215,11 +215,10 @@ | ||||
| 		}; | ||||
|  | ||||
| 		spi: spi@1f000000 { | ||||
| 			compatible = "qca,qca9560-spi", "qca,ar7100-spi"; | ||||
| 			reg = <0x1f000000 0x10>; | ||||
| 			compatible = "qca,ar934x-spi"; | ||||
| 			reg = <0x1f000000 0x1c>; | ||||
|  | ||||
| 			clocks = <&pll ATH79_CLK_AHB>; | ||||
| 			clock-names = "ahb"; | ||||
|  | ||||
| 			status = "disabled"; | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,61 @@ | ||||
| From 7945f929f1a77a1c8887a97ca07f87626858ff42 Mon Sep 17 00:00:00 2001 | ||||
| From: Bartosz Golaszewski <bgolaszewski@baylibre.com> | ||||
| Date: Wed, 20 Feb 2019 11:12:39 +0000 | ||||
| Subject: [PATCH] drivers: provide devm_platform_ioremap_resource() | ||||
|  | ||||
| There are currently 1200+ instances of using platform_get_resource() | ||||
| and devm_ioremap_resource() together in the kernel tree. | ||||
|  | ||||
| This patch wraps these two calls in a single helper. Thanks to that | ||||
| we don't have to declare a local variable for struct resource * and can | ||||
| omit the redundant argument for resource type. We also have one | ||||
| function call less. | ||||
|  | ||||
| Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com> | ||||
| Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||||
| Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> | ||||
| Signed-off-by: Linus Walleij <linus.walleij@linaro.org> | ||||
| --- | ||||
|  drivers/base/platform.c         | 18 ++++++++++++++++++ | ||||
|  include/linux/platform_device.h |  3 +++ | ||||
|  2 files changed, 21 insertions(+) | ||||
|  | ||||
| --- a/drivers/base/platform.c | ||||
| +++ b/drivers/base/platform.c | ||||
| @@ -80,6 +80,24 @@ struct resource *platform_get_resource(s | ||||
|  EXPORT_SYMBOL_GPL(platform_get_resource); | ||||
|   | ||||
|  /** | ||||
| + * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform | ||||
| + *				    device | ||||
| + * | ||||
| + * @pdev: platform device to use both for memory resource lookup as well as | ||||
| + *        resource managemend | ||||
| + * @index: resource index | ||||
| + */ | ||||
| +void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, | ||||
| +					     unsigned int index) | ||||
| +{ | ||||
| +	struct resource *res; | ||||
| + | ||||
| +	res = platform_get_resource(pdev, IORESOURCE_MEM, index); | ||||
| +	return devm_ioremap_resource(&pdev->dev, res); | ||||
| +} | ||||
| +EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); | ||||
| + | ||||
| +/** | ||||
|   * platform_get_irq - get an IRQ for a device | ||||
|   * @dev: platform device | ||||
|   * @num: IRQ number index | ||||
| --- a/include/linux/platform_device.h | ||||
| +++ b/include/linux/platform_device.h | ||||
| @@ -51,6 +51,9 @@ extern struct device platform_bus; | ||||
|  extern void arch_setup_pdev_archdata(struct platform_device *); | ||||
|  extern struct resource *platform_get_resource(struct platform_device *, | ||||
|  					      unsigned int, unsigned int); | ||||
| +extern void __iomem * | ||||
| +devm_platform_ioremap_resource(struct platform_device *pdev, | ||||
| +			       unsigned int index); | ||||
|  extern int platform_get_irq(struct platform_device *, unsigned int); | ||||
|  extern int platform_irq_count(struct platform_device *); | ||||
|  extern struct resource *platform_get_resource_byname(struct platform_device *, | ||||
| @@ -0,0 +1,277 @@ | ||||
| From b518f18f89dbd49fe9403a8c92230f1af59219bc Mon Sep 17 00:00:00 2001 | ||||
| From: Chuanhong Guo <gch981213@gmail.com> | ||||
| Date: Wed, 5 Feb 2020 18:25:37 +0800 | ||||
| Subject: [PATCH 1/2] spi: add driver for ar934x spi controller | ||||
|  | ||||
| This patch adds driver for SPI controller found in Qualcomm Atheros | ||||
| AR934x/QCA95xx SoCs. | ||||
| This controller is a superset of the already supported qca,ar7100-spi. | ||||
| Besides the bit-bang mode in spi-ath79.c, this new controller added | ||||
| a new "shift register" mode, allowing faster spi operations. | ||||
|  | ||||
| Signed-off-by: Chuanhong Guo <gch981213@gmail.com> | ||||
| --- | ||||
|  drivers/spi/Kconfig      |   7 ++ | ||||
|  drivers/spi/Makefile     |   1 + | ||||
|  drivers/spi/spi-ar934x.c | 229 +++++++++++++++++++++++++++++++++++++++ | ||||
|  3 files changed, 237 insertions(+) | ||||
|  create mode 100644 drivers/spi/spi-ar934x.c | ||||
|  | ||||
| --- a/drivers/spi/Kconfig | ||||
| +++ b/drivers/spi/Kconfig | ||||
| @@ -61,6 +61,13 @@ config SPI_ALTERA | ||||
|  	help | ||||
|  	  This is the driver for the Altera SPI Controller. | ||||
|   | ||||
| +config SPI_AR934X | ||||
| +	tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver" | ||||
| +	depends on ATH79 || COMPILE_TEST | ||||
| +	help | ||||
| +	  This enables support for the SPI controller present on the | ||||
| +	  Qualcomm Atheros AR934X/QCA95XX SoCs. | ||||
| + | ||||
|  config SPI_ATH79 | ||||
|  	tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver" | ||||
|  	depends on ATH79 && GPIOLIB | ||||
| --- a/drivers/spi/Makefile | ||||
| +++ b/drivers/spi/Makefile | ||||
| @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST)		+= spi- | ||||
|   | ||||
|  # SPI master controller drivers (bus) | ||||
|  obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o | ||||
| +obj-$(CONFIG_SPI_AR934X)		+= spi-ar934x.o | ||||
|  obj-$(CONFIG_SPI_ARMADA_3700)		+= spi-armada-3700.o | ||||
|  obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o | ||||
|  obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o | ||||
| --- /dev/null | ||||
| +++ b/drivers/spi/spi-ar934x.c | ||||
| @@ -0,0 +1,229 @@ | ||||
| +// SPDX-License-Identifier: GPL-2.0 | ||||
| +// | ||||
| +// SPI controller driver for Qualcomm Atheros AR934x/QCA95xx SoCs | ||||
| +// | ||||
| +// Copyright (C) 2020 Chuanhong Guo <gch981213@gmail.com> | ||||
| +// | ||||
| +// Based on spi-mt7621.c: | ||||
| +// Copyright (C) 2011 Sergiy <piratfm@gmail.com> | ||||
| +// Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org> | ||||
| +// Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name> | ||||
| + | ||||
| +#include <linux/clk.h> | ||||
| +#include <linux/io.h> | ||||
| +#include <linux/iopoll.h> | ||||
| +#include <linux/kernel.h> | ||||
| +#include <linux/module.h> | ||||
| +#include <linux/of_device.h> | ||||
| +#include <linux/spi/spi.h> | ||||
| + | ||||
| +#define DRIVER_NAME "spi-ar934x" | ||||
| + | ||||
| +#define AR934X_SPI_REG_FS		0x00 | ||||
| +#define AR934X_SPI_ENABLE		BIT(0) | ||||
| + | ||||
| +#define AR934X_SPI_REG_CTRL		0x04 | ||||
| +#define AR934X_SPI_CLK_MASK		GENMASK(5, 0) | ||||
| + | ||||
| +#define AR934X_SPI_DATAOUT		0x10 | ||||
| + | ||||
| +#define AR934X_SPI_REG_SHIFT_CTRL	0x14 | ||||
| +#define AR934X_SPI_SHIFT_EN		BIT(31) | ||||
| +#define AR934X_SPI_SHIFT_CS(n)		BIT(28 + (n)) | ||||
| +#define AR934X_SPI_SHIFT_TERM		26 | ||||
| +#define AR934X_SPI_SHIFT_VAL(cs, term, count)			\ | ||||
| +	(AR934X_SPI_SHIFT_EN | AR934X_SPI_SHIFT_CS(cs) |	\ | ||||
| +	(term) << AR934X_SPI_SHIFT_TERM | (count)) | ||||
| + | ||||
| +#define AR934X_SPI_DATAIN 0x18 | ||||
| + | ||||
| +struct ar934x_spi { | ||||
| +	struct spi_controller *ctlr; | ||||
| +	void __iomem *base; | ||||
| +	struct clk *clk; | ||||
| +	unsigned int clk_freq; | ||||
| +}; | ||||
| + | ||||
| +static inline int ar934x_spi_clk_div(struct ar934x_spi *sp, unsigned int freq) | ||||
| +{ | ||||
| +	int div = DIV_ROUND_UP(sp->clk_freq, freq * 2) - 1; | ||||
| + | ||||
| +	if (div < 0) | ||||
| +		return 0; | ||||
| +	else if (div > AR934X_SPI_CLK_MASK) | ||||
| +		return -EINVAL; | ||||
| +	else | ||||
| +		return div; | ||||
| +} | ||||
| + | ||||
| +static int ar934x_spi_setup(struct spi_device *spi) | ||||
| +{ | ||||
| +	struct ar934x_spi *sp = spi_controller_get_devdata(spi->master); | ||||
| + | ||||
| +	if ((spi->max_speed_hz == 0) || | ||||
| +	    (spi->max_speed_hz > (sp->clk_freq / 2))) { | ||||
| +		spi->max_speed_hz = sp->clk_freq / 2; | ||||
| +	} else if (spi->max_speed_hz < (sp->clk_freq / 128)) { | ||||
| +		dev_err(&spi->dev, "spi clock is too low\n"); | ||||
| +		return -EINVAL; | ||||
| +	} | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static int ar934x_spi_transfer_one_message(struct spi_controller *master, | ||||
| +					   struct spi_message *m) | ||||
| +{ | ||||
| +	struct ar934x_spi *sp = spi_controller_get_devdata(master); | ||||
| +	struct spi_transfer *t = NULL; | ||||
| +	struct spi_device *spi = m->spi; | ||||
| +	unsigned long trx_done, trx_cur; | ||||
| +	int stat = 0; | ||||
| +	u8 term = 0; | ||||
| +	int div, i; | ||||
| +	u32 reg; | ||||
| +	const u8 *tx_buf; | ||||
| +	u8 *buf; | ||||
| + | ||||
| +	m->actual_length = 0; | ||||
| +	list_for_each_entry(t, &m->transfers, transfer_list) { | ||||
| +		if (t->speed_hz) | ||||
| +			div = ar934x_spi_clk_div(sp, t->speed_hz); | ||||
| +		else | ||||
| +			div = ar934x_spi_clk_div(sp, spi->max_speed_hz); | ||||
| +		if (div < 0) { | ||||
| +			stat = -EIO; | ||||
| +			goto msg_done; | ||||
| +		} | ||||
| + | ||||
| +		reg = ioread32(sp->base + AR934X_SPI_REG_CTRL); | ||||
| +		reg &= ~AR934X_SPI_CLK_MASK; | ||||
| +		reg |= div; | ||||
| +		iowrite32(reg, sp->base + AR934X_SPI_REG_CTRL); | ||||
| +		iowrite32(0, sp->base + AR934X_SPI_DATAOUT); | ||||
| + | ||||
| +		for (trx_done = 0; trx_done < t->len; trx_done += 4) { | ||||
| +			trx_cur = t->len - trx_done; | ||||
| +			if (trx_cur > 4) | ||||
| +				trx_cur = 4; | ||||
| +			else if (list_is_last(&t->transfer_list, &m->transfers)) | ||||
| +				term = 1; | ||||
| + | ||||
| +			if (t->tx_buf) { | ||||
| +				tx_buf = t->tx_buf + trx_done; | ||||
| +				reg = tx_buf[0]; | ||||
| +				for (i = 1; i < trx_cur; i++) | ||||
| +					reg = reg << 8 | tx_buf[i]; | ||||
| +				iowrite32(reg, sp->base + AR934X_SPI_DATAOUT); | ||||
| +			} | ||||
| + | ||||
| +			reg = AR934X_SPI_SHIFT_VAL(spi->chip_select, term, | ||||
| +						   trx_cur * 8); | ||||
| +			iowrite32(reg, sp->base + AR934X_SPI_REG_SHIFT_CTRL); | ||||
| +			stat = readl_poll_timeout( | ||||
| +				sp->base + AR934X_SPI_REG_SHIFT_CTRL, reg, | ||||
| +				!(reg & AR934X_SPI_SHIFT_EN), 0, 5); | ||||
| +			if (stat < 0) | ||||
| +				goto msg_done; | ||||
| + | ||||
| +			if (t->rx_buf) { | ||||
| +				reg = ioread32(sp->base + AR934X_SPI_DATAIN); | ||||
| +				buf = t->rx_buf + trx_done; | ||||
| +				for (i = 0; i < trx_cur; i++) { | ||||
| +					buf[trx_cur - i - 1] = reg & 0xff; | ||||
| +					reg >>= 8; | ||||
| +				} | ||||
| +			} | ||||
| +		} | ||||
| +		m->actual_length += t->len; | ||||
| +	} | ||||
| + | ||||
| +msg_done: | ||||
| +	m->status = stat; | ||||
| +	spi_finalize_current_message(master); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static const struct of_device_id ar934x_spi_match[] = { | ||||
| +	{ .compatible = "qca,ar934x-spi" }, | ||||
| +	{}, | ||||
| +}; | ||||
| +MODULE_DEVICE_TABLE(of, ar934x_spi_match); | ||||
| + | ||||
| +static int ar934x_spi_probe(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct spi_controller *ctlr; | ||||
| +	struct ar934x_spi *sp; | ||||
| +	void __iomem *base; | ||||
| +	struct clk *clk; | ||||
| +	int ret; | ||||
| + | ||||
| +	base = devm_platform_ioremap_resource(pdev, 0); | ||||
| +	if (IS_ERR(base)) | ||||
| +		return PTR_ERR(base); | ||||
| + | ||||
| +	clk = devm_clk_get(&pdev->dev, NULL); | ||||
| +	if (IS_ERR(clk)) { | ||||
| +		dev_err(&pdev->dev, "failed to get clock\n"); | ||||
| +		return PTR_ERR(clk); | ||||
| +	} | ||||
| + | ||||
| +	ret = clk_prepare_enable(clk); | ||||
| +	if (ret) | ||||
| +		return ret; | ||||
| + | ||||
| +	ctlr = spi_alloc_master(&pdev->dev, sizeof(*sp)); | ||||
| +	if (!ctlr) { | ||||
| +		dev_info(&pdev->dev, "failed to allocate spi controller\n"); | ||||
| +		return -ENOMEM; | ||||
| +	} | ||||
| + | ||||
| +	iowrite32(AR934X_SPI_ENABLE, base + AR934X_SPI_REG_FS); | ||||
| + | ||||
| +	ctlr->mode_bits = SPI_LSB_FIRST; | ||||
| +	ctlr->setup = ar934x_spi_setup; | ||||
| +	ctlr->transfer_one_message = ar934x_spi_transfer_one_message; | ||||
| +	ctlr->bits_per_word_mask = SPI_BPW_MASK(8); | ||||
| +	ctlr->dev.of_node = pdev->dev.of_node; | ||||
| +	ctlr->num_chipselect = 3; | ||||
| + | ||||
| +	dev_set_drvdata(&pdev->dev, ctlr); | ||||
| + | ||||
| +	sp = spi_controller_get_devdata(ctlr); | ||||
| +	sp->base = base; | ||||
| +	sp->clk = clk; | ||||
| +	sp->clk_freq = clk_get_rate(clk); | ||||
| +	sp->ctlr = ctlr; | ||||
| + | ||||
| +	return devm_spi_register_controller(&pdev->dev, ctlr); | ||||
| +} | ||||
| + | ||||
| +static int ar934x_spi_remove(struct platform_device *pdev) | ||||
| +{ | ||||
| +	struct spi_controller *ctlr; | ||||
| +	struct ar934x_spi *sp; | ||||
| + | ||||
| +	ctlr = dev_get_drvdata(&pdev->dev); | ||||
| +	sp = spi_controller_get_devdata(ctlr); | ||||
| + | ||||
| +	clk_disable_unprepare(sp->clk); | ||||
| + | ||||
| +	return 0; | ||||
| +} | ||||
| + | ||||
| +static struct platform_driver ar934x_spi_driver = { | ||||
| +	.driver = { | ||||
| +		.name = DRIVER_NAME, | ||||
| +		.of_match_table = ar934x_spi_match, | ||||
| +	}, | ||||
| +	.probe = ar934x_spi_probe, | ||||
| +	.remove = ar934x_spi_remove, | ||||
| +}; | ||||
| + | ||||
| +module_platform_driver(ar934x_spi_driver); | ||||
| + | ||||
| +MODULE_DESCRIPTION("SPI controller driver for Qualcomm Atheros AR934x/QCA95xx"); | ||||
| +MODULE_AUTHOR("Chuanhong Guo <gch981213@gmail.com>"); | ||||
| +MODULE_LICENSE("GPL v2"); | ||||
| +MODULE_ALIAS("platform:" DRIVER_NAME); | ||||
		Reference in New Issue
	
	Block a user
	 Chuanhong Guo
					Chuanhong Guo