diff options
author | Mark Brown <broonie@kernel.org> | 2021-04-20 19:30:54 +0100 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2021-04-20 19:30:54 +0100 |
commit | 1799bb1065ba6e9be0a1562400cd0b9afdcf65e7 (patch) | |
tree | a333807dfd022a0ef80e828d2b67ce819f6486b3 /drivers/spi | |
parent | 3a1634daf8cbf7d1e76b120fea9665325557be8c (diff) | |
parent | ba2fc167e9447596a812e828842d0130ea9cd0e4 (diff) | |
download | linux-1799bb1065ba6e9be0a1562400cd0b9afdcf65e7.tar.bz2 |
Merge series "spi: altera: Add DFL bus support for Altera SPI" from matthew.gerlach@linux.intel.com
Matthew Gerlach <matthew.gerlach@linux.intel.com>:
From: Matthew Gerlach <matthew.gerlach@linux.intel.com>
This patch set adds Device Feature List (DFL) bus support for
the Altera SPI Master controller.
Patch 1 separates spi-altera.c into spi-altera-core.c and
spi-altera-platform.c.
Patch 2 adds spi-altera-dfl.c.
Matthew Gerlach (2):
spi: altera: separate core code from platform code
spi: altera: Add DFL bus driver for Altera API Controller
drivers/spi/Kconfig | 18 +-
drivers/spi/Makefile | 4 +-
drivers/spi/spi-altera-core.c | 222 ++++++++++++++++++++++
drivers/spi/spi-altera-dfl.c | 204 ++++++++++++++++++++
drivers/spi/spi-altera-platform.c | 172 +++++++++++++++++
drivers/spi/spi-altera.c | 378 --------------------------------------
include/linux/spi/altera.h | 21 +++
7 files changed, 639 insertions(+), 380 deletions(-)
create mode 100644 drivers/spi/spi-altera-core.c
create mode 100644 drivers/spi/spi-altera-dfl.c
create mode 100644 drivers/spi/spi-altera-platform.c
delete mode 100644 drivers/spi/spi-altera.c
--
1.8.3.1
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 18 | ||||
-rw-r--r-- | drivers/spi/Makefile | 4 | ||||
-rw-r--r-- | drivers/spi/spi-altera-core.c (renamed from drivers/spi/spi-altera.c) | 166 | ||||
-rw-r--r-- | drivers/spi/spi-altera-dfl.c | 204 | ||||
-rw-r--r-- | drivers/spi/spi-altera-platform.c | 172 |
5 files changed, 401 insertions, 163 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 853cf4c46e5e..8b161ec4943b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -58,11 +58,27 @@ config SPI_MEM comment "SPI Master Controller Drivers" config SPI_ALTERA - tristate "Altera SPI Controller" + tristate "Altera SPI Controller platform driver" + select SPI_ALTERA_CORE select REGMAP_MMIO help This is the driver for the Altera SPI Controller. +config SPI_ALTERA_CORE + tristate "Altera SPI Controller core code" + select REGMAP + help + "The core code for the Altera SPI Controller" + +config SPI_ALTERA_DFL + tristate "DFL bus driver for Altera SPI Controller" + depends on FPGA_DFL + select SPI_ALTERA_CORE + help + This is a Device Feature List (DFL) bus driver for the + Altera SPI master controller. The SPI master is connected + to a SPI slave to Avalon bridge in a Intel MAX BMC. + config SPI_AR934X tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver" depends on ATH79 || COMPILE_TEST diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 29fee71022b4..13e54c45e9df 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -14,7 +14,9 @@ obj-$(CONFIG_SPI_SPIDEV) += spidev.o obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o # SPI master controller drivers (bus) -obj-$(CONFIG_SPI_ALTERA) += spi-altera.o +obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o +obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o +obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera-core.c index 62ea0c9e321b..de4d31c530d9 100644 --- a/drivers/spi/spi-altera.c +++ b/drivers/spi/spi-altera-core.c @@ -10,7 +10,6 @@ * Ben Dooks <ben@simtec.co.uk> */ -#include <linux/interrupt.h> #include <linux/errno.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -41,36 +40,6 @@ #define ALTERA_SPI_CONTROL_IE_MSK 0x100 #define ALTERA_SPI_CONTROL_SSO_MSK 0x400 -#define ALTERA_SPI_MAX_CS 32 - -enum altera_spi_type { - ALTERA_SPI_TYPE_UNKNOWN, - ALTERA_SPI_TYPE_SUBDEV, -}; - -struct altera_spi { - int irq; - int len; - int count; - int bytes_per_word; - u32 imr; - - /* data buffers */ - const unsigned char *tx; - unsigned char *rx; - - struct regmap *regmap; - u32 regoff; - struct device *dev; -}; - -static const struct regmap_config spi_altera_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .fast_io = true, -}; - static int altr_spi_writel(struct altera_spi *hw, unsigned int reg, unsigned int val) { @@ -211,7 +180,7 @@ static int altera_spi_txrx(struct spi_master *master, return 0; } -static irqreturn_t altera_spi_irq(int irq, void *dev) +irqreturn_t altera_spi_irq(int irq, void *dev) { struct spi_master *master = dev; struct altera_spi *hw = spi_master_get_devdata(master); @@ -230,84 +199,16 @@ static irqreturn_t altera_spi_irq(int irq, void *dev) return IRQ_HANDLED; } +EXPORT_SYMBOL_GPL(altera_spi_irq); -static int altera_spi_probe(struct platform_device *pdev) +void altera_spi_init_master(struct spi_master *master) { - const struct platform_device_id *platid = platform_get_device_id(pdev); - struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev); - enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; - struct altera_spi *hw; - struct spi_master *master; - int err = -ENODEV; + struct altera_spi *hw = spi_master_get_devdata(master); u32 val; - u16 i; - - master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); - if (!master) - return err; - - /* setup the master state. */ - master->bus_num = pdev->id; - - if (pdata) { - if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) { - dev_err(&pdev->dev, - "Invalid number of chipselect: %hu\n", - pdata->num_chipselect); - err = -EINVAL; - goto exit; - } - master->num_chipselect = pdata->num_chipselect; - master->mode_bits = pdata->mode_bits; - master->bits_per_word_mask = pdata->bits_per_word_mask; - } else { - master->num_chipselect = 16; - master->mode_bits = SPI_CS_HIGH; - master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); - } - - master->dev.of_node = pdev->dev.of_node; master->transfer_one = altera_spi_txrx; master->set_cs = altera_spi_set_cs; - hw = spi_master_get_devdata(master); - hw->dev = &pdev->dev; - - if (platid) - type = platid->driver_data; - - /* find and map our resources */ - if (type == ALTERA_SPI_TYPE_SUBDEV) { - struct resource *regoff; - - hw->regmap = dev_get_regmap(pdev->dev.parent, NULL); - if (!hw->regmap) { - dev_err(&pdev->dev, "get regmap failed\n"); - goto exit; - } - - regoff = platform_get_resource(pdev, IORESOURCE_REG, 0); - if (regoff) - hw->regoff = regoff->start; - } else { - void __iomem *res; - - res = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(res)) { - err = PTR_ERR(res); - goto exit; - } - - hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, - &spi_altera_config); - if (IS_ERR(hw->regmap)) { - dev_err(&pdev->dev, "regmap mmio init failed\n"); - err = PTR_ERR(hw->regmap); - goto exit; - } - } - /* program defaults into the registers */ hw->imr = 0; /* disable spi interrupts */ altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); @@ -315,64 +216,7 @@ static int altera_spi_probe(struct platform_device *pdev) altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); if (val & ALTERA_SPI_STATUS_RRDY_MSK) altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */ - /* irq is optional */ - hw->irq = platform_get_irq(pdev, 0); - if (hw->irq >= 0) { - err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, - pdev->name, master); - if (err) - goto exit; - } - - err = devm_spi_register_master(&pdev->dev, master); - if (err) - goto exit; - - if (pdata) { - for (i = 0; i < pdata->num_devices; i++) { - if (!spi_new_device(master, pdata->devices + i)) - dev_warn(&pdev->dev, - "unable to create SPI device: %s\n", - pdata->devices[i].modalias); - } - } - - dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq); - - return 0; -exit: - spi_master_put(master); - return err; } +EXPORT_SYMBOL_GPL(altera_spi_init_master); -#ifdef CONFIG_OF -static const struct of_device_id altera_spi_match[] = { - { .compatible = "ALTR,spi-1.0", }, - { .compatible = "altr,spi-1.0", }, - {}, -}; -MODULE_DEVICE_TABLE(of, altera_spi_match); -#endif /* CONFIG_OF */ - -static const struct platform_device_id altera_spi_ids[] = { - { DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN }, - { "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV }, - { } -}; -MODULE_DEVICE_TABLE(platform, altera_spi_ids); - -static struct platform_driver altera_spi_driver = { - .probe = altera_spi_probe, - .driver = { - .name = DRV_NAME, - .pm = NULL, - .of_match_table = of_match_ptr(altera_spi_match), - }, - .id_table = altera_spi_ids, -}; -module_platform_driver(altera_spi_driver); - -MODULE_DESCRIPTION("Altera SPI driver"); -MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/spi/spi-altera-dfl.c b/drivers/spi/spi-altera-dfl.c new file mode 100644 index 000000000000..3e32e4fe5895 --- /dev/null +++ b/drivers/spi/spi-altera-dfl.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// DFL bus driver for Altera SPI Master +// +// Copyright (C) 2020 Intel Corporation, Inc. +// +// Authors: +// Matthew Gerlach <matthew.gerlach@linux.intel.com> +// + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/errno.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/bitfield.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/spi/altera.h> +#include <linux/dfl.h> + +#define FME_FEATURE_ID_MAX10_SPI 0xe +#define FME_FEATURE_REV_MAX10_SPI_N5010 0x1 + +#define SPI_CORE_PARAMETER 0x8 +#define SHIFT_MODE BIT_ULL(1) +#define SHIFT_MODE_MSB 0 +#define SHIFT_MODE_LSB 1 +#define DATA_WIDTH GENMASK_ULL(7, 2) +#define NUM_CHIPSELECT GENMASK_ULL(13, 8) +#define CLK_POLARITY BIT_ULL(14) +#define CLK_PHASE BIT_ULL(15) +#define PERIPHERAL_ID GENMASK_ULL(47, 32) +#define SPI_CLK GENMASK_ULL(31, 22) +#define SPI_INDIRECT_ACC_OFST 0x10 + +#define INDIRECT_ADDR (SPI_INDIRECT_ACC_OFST+0x0) +#define INDIRECT_WR BIT_ULL(8) +#define INDIRECT_RD BIT_ULL(9) +#define INDIRECT_RD_DATA (SPI_INDIRECT_ACC_OFST+0x8) +#define INDIRECT_DATA_MASK GENMASK_ULL(31, 0) +#define INDIRECT_DEBUG BIT_ULL(32) +#define INDIRECT_WR_DATA (SPI_INDIRECT_ACC_OFST+0x10) +#define INDIRECT_TIMEOUT 10000 + +static int indirect_bus_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + void __iomem *base = context; + int loops; + u64 v; + + writeq((reg >> 2) | INDIRECT_RD, base + INDIRECT_ADDR); + + loops = 0; + while ((readq(base + INDIRECT_ADDR) & INDIRECT_RD) && + (loops++ < INDIRECT_TIMEOUT)) + cpu_relax(); + + if (loops >= INDIRECT_TIMEOUT) { + pr_err("%s timed out %d\n", __func__, loops); + return -ETIME; + } + + v = readq(base + INDIRECT_RD_DATA); + + *val = v & INDIRECT_DATA_MASK; + + return 0; +} + +static int indirect_bus_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + void __iomem *base = context; + int loops; + + writeq(val, base + INDIRECT_WR_DATA); + writeq((reg >> 2) | INDIRECT_WR, base + INDIRECT_ADDR); + + loops = 0; + while ((readq(base + INDIRECT_ADDR) & INDIRECT_WR) && + (loops++ < INDIRECT_TIMEOUT)) + cpu_relax(); + + if (loops >= INDIRECT_TIMEOUT) { + pr_err("%s timed out %d\n", __func__, loops); + return -ETIME; + } + return 0; +} + +static const struct regmap_config indirect_regbus_cfg = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .max_register = 24, + + .reg_write = indirect_bus_reg_write, + .reg_read = indirect_bus_reg_read, +}; + +static struct spi_board_info m10_bmc_info = { + .modalias = "m10-d5005", + .max_speed_hz = 12500000, + .bus_num = 0, + .chip_select = 0, +}; + +static void config_spi_master(void __iomem *base, struct spi_master *master) +{ + u64 v; + + v = readq(base + SPI_CORE_PARAMETER); + + master->mode_bits = SPI_CS_HIGH; + if (FIELD_GET(CLK_POLARITY, v)) + master->mode_bits |= SPI_CPOL; + if (FIELD_GET(CLK_PHASE, v)) + master->mode_bits |= SPI_CPHA; + + master->num_chipselect = FIELD_GET(NUM_CHIPSELECT, v); + master->bits_per_word_mask = + SPI_BPW_RANGE_MASK(1, FIELD_GET(DATA_WIDTH, v)); +} + +static int dfl_spi_altera_probe(struct dfl_device *dfl_dev) +{ + struct device *dev = &dfl_dev->dev; + struct spi_master *master; + struct altera_spi *hw; + void __iomem *base; + int err = -ENODEV; + + master = spi_alloc_master(dev, sizeof(struct altera_spi)); + if (!master) + return -ENOMEM; + + master->bus_num = dfl_dev->id; + + hw = spi_master_get_devdata(master); + + hw->dev = dev; + + base = devm_ioremap_resource(dev, &dfl_dev->mmio_res); + + if (IS_ERR(base)) { + dev_err(dev, "%s get mem resource fail!\n", __func__); + return PTR_ERR(base); + } + + config_spi_master(base, master); + dev_dbg(dev, "%s cs %u bpm 0x%x mode 0x%x\n", __func__, + master->num_chipselect, master->bits_per_word_mask, + master->mode_bits); + + hw->regmap = devm_regmap_init(dev, NULL, base, &indirect_regbus_cfg); + if (IS_ERR(hw->regmap)) + return PTR_ERR(hw->regmap); + + hw->irq = -EINVAL; + + altera_spi_init_master(master); + + err = devm_spi_register_master(dev, master); + if (err) { + dev_err(dev, "%s failed to register spi master %d\n", __func__, err); + goto exit; + } + + if (!spi_new_device(master, &m10_bmc_info)) { + dev_err(dev, "%s failed to create SPI device: %s\n", + __func__, m10_bmc_info.modalias); + } + + return 0; +exit: + spi_master_put(master); + return err; +} + +static const struct dfl_device_id dfl_spi_altera_ids[] = { + { FME_ID, FME_FEATURE_ID_MAX10_SPI }, + { } +}; + +static struct dfl_driver dfl_spi_altera_driver = { + .drv = { + .name = "dfl-spi-altera", + }, + .id_table = dfl_spi_altera_ids, + .probe = dfl_spi_altera_probe, +}; + +module_dfl_driver(dfl_spi_altera_driver); + +MODULE_DEVICE_TABLE(dfl, dfl_spi_altera_ids); +MODULE_DESCRIPTION("DFL spi altera driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-altera-platform.c b/drivers/spi/spi-altera-platform.c new file mode 100644 index 000000000000..f7a7c14e3679 --- /dev/null +++ b/drivers/spi/spi-altera-platform.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Altera SPI driver + * + * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw> + * + * Based on spi_s3c24xx.c, which is: + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + */ + +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spi/altera.h> +#include <linux/spi/spi.h> +#include <linux/io.h> +#include <linux/of.h> + +#define DRV_NAME "spi_altera" + +enum altera_spi_type { + ALTERA_SPI_TYPE_UNKNOWN, + ALTERA_SPI_TYPE_SUBDEV, +}; + +static const struct regmap_config spi_altera_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + +static int altera_spi_probe(struct platform_device *pdev) +{ + const struct platform_device_id *platid = platform_get_device_id(pdev); + struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev); + enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; + struct altera_spi *hw; + struct spi_master *master; + int err = -ENODEV; + u16 i; + + master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); + if (!master) + return err; + + /* setup the master state. */ + master->bus_num = pdev->id; + + if (pdata) { + if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) { + dev_err(&pdev->dev, + "Invalid number of chipselect: %u\n", + pdata->num_chipselect); + err = -EINVAL; + goto exit; + } + + master->num_chipselect = pdata->num_chipselect; + master->mode_bits = pdata->mode_bits; + master->bits_per_word_mask = pdata->bits_per_word_mask; + } else { + master->num_chipselect = 16; + master->mode_bits = SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); + } + + master->dev.of_node = pdev->dev.of_node; + + hw = spi_master_get_devdata(master); + hw->dev = &pdev->dev; + + if (platid) + type = platid->driver_data; + + /* find and map our resources */ + if (type == ALTERA_SPI_TYPE_SUBDEV) { + struct resource *regoff; + + hw->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!hw->regmap) { + dev_err(&pdev->dev, "get regmap failed\n"); + goto exit; + } + + regoff = platform_get_resource(pdev, IORESOURCE_REG, 0); + if (regoff) + hw->regoff = regoff->start; + } else { + void __iomem *res; + + res = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(res)) { + err = PTR_ERR(res); + goto exit; + } + + hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, + &spi_altera_config); + if (IS_ERR(hw->regmap)) { + dev_err(&pdev->dev, "regmap mmio init failed\n"); + err = PTR_ERR(hw->regmap); + goto exit; + } + } + + altera_spi_init_master(master); + + /* irq is optional */ + hw->irq = platform_get_irq(pdev, 0); + if (hw->irq >= 0) { + err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, + pdev->name, master); + if (err) + goto exit; + } + + err = devm_spi_register_master(&pdev->dev, master); + if (err) + goto exit; + + if (pdata) { + for (i = 0; i < pdata->num_devices; i++) { + if (!spi_new_device(master, pdata->devices + i)) + dev_warn(&pdev->dev, + "unable to create SPI device: %s\n", + pdata->devices[i].modalias); + } + } + + dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq); + + return 0; +exit: + spi_master_put(master); + return err; +} + +#ifdef CONFIG_OF +static const struct of_device_id altera_spi_match[] = { + { .compatible = "ALTR,spi-1.0", }, + { .compatible = "altr,spi-1.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, altera_spi_match); +#endif /* CONFIG_OF */ + +static const struct platform_device_id altera_spi_ids[] = { + { DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN }, + { "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV }, + { } +}; +MODULE_DEVICE_TABLE(platform, altera_spi_ids); + +static struct platform_driver altera_spi_driver = { + .probe = altera_spi_probe, + .driver = { + .name = DRV_NAME, + .pm = NULL, + .of_match_table = of_match_ptr(altera_spi_match), + }, + .id_table = altera_spi_ids, +}; +module_platform_driver(altera_spi_driver); + +MODULE_DESCRIPTION("Altera SPI driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); |