From patchwork Fri Apr 16 16:57:19 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Gerlach X-Patchwork-Id: 12208243 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CEFC8C43461 for ; Fri, 16 Apr 2021 16:55:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AF6846121F for ; Fri, 16 Apr 2021 16:55:51 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236515AbhDPQ4P (ORCPT ); Fri, 16 Apr 2021 12:56:15 -0400 Received: from mga01.intel.com ([192.55.52.88]:11721 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236466AbhDPQ4P (ORCPT ); Fri, 16 Apr 2021 12:56:15 -0400 IronPort-SDR: ftxq8cMrONI0oFAYKL9jW552ZejFjItkUEITgxc7FwJyzL5HKD7FckuD0/dQ/WzcTCRlIMZ7AD /ujcj859rKvg== X-IronPort-AV: E=McAfee;i="6200,9189,9956"; a="215606434" X-IronPort-AV: E=Sophos;i="5.82,226,1613462400"; d="scan'208";a="215606434" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 Apr 2021 09:55:50 -0700 IronPort-SDR: Np3jfFkpz4IaRsvwZ8P64MVe1L5dWja+HhV7P/jmFdaQ9D7HBKGYOphQbLGy49mHTKJ3Fd6xwW aoGXa29uFNUw== X-IronPort-AV: E=Sophos;i="5.82,226,1613462400"; d="scan'208";a="425655936" Received: from rhweight-wrk1.ra.intel.com ([137.102.106.42]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 Apr 2021 09:55:49 -0700 From: matthew.gerlach@linux.intel.com To: hao.wu@intel.com, trix@redhat.com, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org, yilun.xu@intel.com, russell.h.weight@intel.com, broonie@kernel.org, linux-spi@vger.kernel.org Cc: Matthew Gerlach Subject: [PATCH v3 1/2] spi: altera: separate core code from platform code Date: Fri, 16 Apr 2021 09:57:19 -0700 Message-Id: <20210416165720.554144-2-matthew.gerlach@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210416165720.554144-1-matthew.gerlach@linux.intel.com> References: <20210416165720.554144-1-matthew.gerlach@linux.intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org From: Matthew Gerlach In preparation of adding support for a new bus type, separate the core spi-altera code from the platform driver code. Signed-off-by: Matthew Gerlach --- drivers/spi/Kconfig | 9 +- drivers/spi/Makefile | 3 +- drivers/spi/spi-altera-core.c | 222 ++++++++++++++++++++++ drivers/spi/spi-altera-platform.c | 172 +++++++++++++++++ drivers/spi/spi-altera.c | 378 -------------------------------------- include/linux/spi/altera.h | 21 +++ 6 files changed, 425 insertions(+), 380 deletions(-) create mode 100644 drivers/spi/spi-altera-core.c create mode 100644 drivers/spi/spi-altera-platform.c delete mode 100644 drivers/spi/spi-altera.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 853cf4c..e59f548 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -58,11 +58,18 @@ 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_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 29fee71..e6be348 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -14,7 +14,8 @@ 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_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-core.c b/drivers/spi/spi-altera-core.c new file mode 100644 index 0000000..de4d31c --- /dev/null +++ b/drivers/spi/spi-altera-core.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Altera SPI driver + * + * Copyright (C) 2008 Thomas Chou + * + * Based on spi_s3c24xx.c, which is: + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "spi_altera" + +#define ALTERA_SPI_RXDATA 0 +#define ALTERA_SPI_TXDATA 4 +#define ALTERA_SPI_STATUS 8 +#define ALTERA_SPI_CONTROL 12 +#define ALTERA_SPI_SLAVE_SEL 20 + +#define ALTERA_SPI_STATUS_ROE_MSK 0x8 +#define ALTERA_SPI_STATUS_TOE_MSK 0x10 +#define ALTERA_SPI_STATUS_TMT_MSK 0x20 +#define ALTERA_SPI_STATUS_TRDY_MSK 0x40 +#define ALTERA_SPI_STATUS_RRDY_MSK 0x80 +#define ALTERA_SPI_STATUS_E_MSK 0x100 + +#define ALTERA_SPI_CONTROL_IROE_MSK 0x8 +#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 +#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 +#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 +#define ALTERA_SPI_CONTROL_IE_MSK 0x100 +#define ALTERA_SPI_CONTROL_SSO_MSK 0x400 + +static int altr_spi_writel(struct altera_spi *hw, unsigned int reg, + unsigned int val) +{ + int ret; + + ret = regmap_write(hw->regmap, hw->regoff + reg, val); + if (ret) + dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d\n", + reg, val, ret); + + return ret; +} + +static int altr_spi_readl(struct altera_spi *hw, unsigned int reg, + unsigned int *val) +{ + int ret; + + ret = regmap_read(hw->regmap, hw->regoff + reg, val); + if (ret) + dev_err(hw->dev, "fail to read reg 0x%x: %d\n", reg, ret); + + return ret; +} + +static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) +{ + return spi_master_get_devdata(sdev->master); +} + +static void altera_spi_set_cs(struct spi_device *spi, bool is_high) +{ + struct altera_spi *hw = altera_spi_to_hw(spi); + + if (is_high) { + hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); + altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 0); + } else { + altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, + BIT(spi->chip_select)); + hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); + } +} + +static void altera_spi_tx_word(struct altera_spi *hw) +{ + unsigned int txd = 0; + + if (hw->tx) { + switch (hw->bytes_per_word) { + case 1: + txd = hw->tx[hw->count]; + break; + case 2: + txd = (hw->tx[hw->count * 2] + | (hw->tx[hw->count * 2 + 1] << 8)); + break; + case 4: + txd = (hw->tx[hw->count * 4] + | (hw->tx[hw->count * 4 + 1] << 8) + | (hw->tx[hw->count * 4 + 2] << 16) + | (hw->tx[hw->count * 4 + 3] << 24)); + break; + + } + } + + altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd); +} + +static void altera_spi_rx_word(struct altera_spi *hw) +{ + unsigned int rxd; + + altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd); + if (hw->rx) { + switch (hw->bytes_per_word) { + case 1: + hw->rx[hw->count] = rxd; + break; + case 2: + hw->rx[hw->count * 2] = rxd; + hw->rx[hw->count * 2 + 1] = rxd >> 8; + break; + case 4: + hw->rx[hw->count * 4] = rxd; + hw->rx[hw->count * 4 + 1] = rxd >> 8; + hw->rx[hw->count * 4 + 2] = rxd >> 16; + hw->rx[hw->count * 4 + 3] = rxd >> 24; + break; + + } + } + + hw->count++; +} + +static int altera_spi_txrx(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *t) +{ + struct altera_spi *hw = spi_master_get_devdata(master); + u32 val; + + hw->tx = t->tx_buf; + hw->rx = t->rx_buf; + hw->count = 0; + hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); + hw->len = t->len / hw->bytes_per_word; + + if (hw->irq >= 0) { + /* enable receive interrupt */ + hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK; + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); + + /* send the first byte */ + altera_spi_tx_word(hw); + + return 1; + } + + while (hw->count < hw->len) { + altera_spi_tx_word(hw); + + for (;;) { + altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); + if (val & ALTERA_SPI_STATUS_RRDY_MSK) + break; + + cpu_relax(); + } + + altera_spi_rx_word(hw); + } + spi_finalize_current_transfer(master); + + return 0; +} + +irqreturn_t altera_spi_irq(int irq, void *dev) +{ + struct spi_master *master = dev; + struct altera_spi *hw = spi_master_get_devdata(master); + + altera_spi_rx_word(hw); + + if (hw->count < hw->len) { + altera_spi_tx_word(hw); + } else { + /* disable receive interrupt */ + hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); + + spi_finalize_current_transfer(master); + } + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(altera_spi_irq); + +void altera_spi_init_master(struct spi_master *master) +{ + struct altera_spi *hw = spi_master_get_devdata(master); + u32 val; + + master->transfer_one = altera_spi_txrx; + master->set_cs = altera_spi_set_cs; + + /* program defaults into the registers */ + hw->imr = 0; /* disable spi interrupts */ + altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); + altr_spi_writel(hw, ALTERA_SPI_STATUS, 0); /* clear status reg */ + 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 */ +} +EXPORT_SYMBOL_GPL(altera_spi_init_master); + +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-altera-platform.c b/drivers/spi/spi-altera-platform.c new file mode 100644 index 0000000..f7a7c14 --- /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 + * + * Based on spi_s3c24xx.c, which is: + * Copyright (c) 2006 Ben Dooks + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/spi/spi-altera.c b/drivers/spi/spi-altera.c deleted file mode 100644 index 62ea0c9..0000000 --- a/drivers/spi/spi-altera.c +++ /dev/null @@ -1,378 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Altera SPI driver - * - * Copyright (C) 2008 Thomas Chou - * - * Based on spi_s3c24xx.c, which is: - * Copyright (c) 2006 Ben Dooks - * Copyright (c) 2006 Simtec Electronics - * Ben Dooks - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define DRV_NAME "spi_altera" - -#define ALTERA_SPI_RXDATA 0 -#define ALTERA_SPI_TXDATA 4 -#define ALTERA_SPI_STATUS 8 -#define ALTERA_SPI_CONTROL 12 -#define ALTERA_SPI_SLAVE_SEL 20 - -#define ALTERA_SPI_STATUS_ROE_MSK 0x8 -#define ALTERA_SPI_STATUS_TOE_MSK 0x10 -#define ALTERA_SPI_STATUS_TMT_MSK 0x20 -#define ALTERA_SPI_STATUS_TRDY_MSK 0x40 -#define ALTERA_SPI_STATUS_RRDY_MSK 0x80 -#define ALTERA_SPI_STATUS_E_MSK 0x100 - -#define ALTERA_SPI_CONTROL_IROE_MSK 0x8 -#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 -#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 -#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 -#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) -{ - int ret; - - ret = regmap_write(hw->regmap, hw->regoff + reg, val); - if (ret) - dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d\n", - reg, val, ret); - - return ret; -} - -static int altr_spi_readl(struct altera_spi *hw, unsigned int reg, - unsigned int *val) -{ - int ret; - - ret = regmap_read(hw->regmap, hw->regoff + reg, val); - if (ret) - dev_err(hw->dev, "fail to read reg 0x%x: %d\n", reg, ret); - - return ret; -} - -static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) -{ - return spi_master_get_devdata(sdev->master); -} - -static void altera_spi_set_cs(struct spi_device *spi, bool is_high) -{ - struct altera_spi *hw = altera_spi_to_hw(spi); - - if (is_high) { - hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; - altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); - altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 0); - } else { - altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, - BIT(spi->chip_select)); - hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; - altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); - } -} - -static void altera_spi_tx_word(struct altera_spi *hw) -{ - unsigned int txd = 0; - - if (hw->tx) { - switch (hw->bytes_per_word) { - case 1: - txd = hw->tx[hw->count]; - break; - case 2: - txd = (hw->tx[hw->count * 2] - | (hw->tx[hw->count * 2 + 1] << 8)); - break; - case 4: - txd = (hw->tx[hw->count * 4] - | (hw->tx[hw->count * 4 + 1] << 8) - | (hw->tx[hw->count * 4 + 2] << 16) - | (hw->tx[hw->count * 4 + 3] << 24)); - break; - - } - } - - altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd); -} - -static void altera_spi_rx_word(struct altera_spi *hw) -{ - unsigned int rxd; - - altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd); - if (hw->rx) { - switch (hw->bytes_per_word) { - case 1: - hw->rx[hw->count] = rxd; - break; - case 2: - hw->rx[hw->count * 2] = rxd; - hw->rx[hw->count * 2 + 1] = rxd >> 8; - break; - case 4: - hw->rx[hw->count * 4] = rxd; - hw->rx[hw->count * 4 + 1] = rxd >> 8; - hw->rx[hw->count * 4 + 2] = rxd >> 16; - hw->rx[hw->count * 4 + 3] = rxd >> 24; - break; - - } - } - - hw->count++; -} - -static int altera_spi_txrx(struct spi_master *master, - struct spi_device *spi, struct spi_transfer *t) -{ - struct altera_spi *hw = spi_master_get_devdata(master); - u32 val; - - hw->tx = t->tx_buf; - hw->rx = t->rx_buf; - hw->count = 0; - hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); - hw->len = t->len / hw->bytes_per_word; - - if (hw->irq >= 0) { - /* enable receive interrupt */ - hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK; - altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); - - /* send the first byte */ - altera_spi_tx_word(hw); - - return 1; - } - - while (hw->count < hw->len) { - altera_spi_tx_word(hw); - - for (;;) { - altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); - if (val & ALTERA_SPI_STATUS_RRDY_MSK) - break; - - cpu_relax(); - } - - altera_spi_rx_word(hw); - } - spi_finalize_current_transfer(master); - - return 0; -} - -static irqreturn_t altera_spi_irq(int irq, void *dev) -{ - struct spi_master *master = dev; - struct altera_spi *hw = spi_master_get_devdata(master); - - altera_spi_rx_word(hw); - - if (hw->count < hw->len) { - altera_spi_tx_word(hw); - } else { - /* disable receive interrupt */ - hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; - altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); - - spi_finalize_current_transfer(master); - } - - return IRQ_HANDLED; -} - -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; - 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); - altr_spi_writel(hw, ALTERA_SPI_STATUS, 0); /* clear status reg */ - 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; -} - -#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 "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/spi/altera.h b/include/linux/spi/altera.h index 2d42641..2e2a622 100644 --- a/include/linux/spi/altera.h +++ b/include/linux/spi/altera.h @@ -5,10 +5,13 @@ #ifndef __LINUX_SPI_ALTERA_H #define __LINUX_SPI_ALTERA_H +#include #include #include #include +#define ALTERA_SPI_MAX_CS 32 + /** * struct altera_spi_platform_data - Platform data of the Altera SPI driver * @mode_bits: Mode bits of SPI master. @@ -26,4 +29,22 @@ struct altera_spi_platform_data { struct spi_board_info *devices; }; +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; +}; + +extern irqreturn_t altera_spi_irq(int irq, void *dev); +extern void altera_spi_init_master(struct spi_master *master); #endif /* __LINUX_SPI_ALTERA_H */ From patchwork Fri Apr 16 16:57:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matthew Gerlach X-Patchwork-Id: 12208241 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5D196C43460 for ; Fri, 16 Apr 2021 16:55:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 42D0A613C0 for ; Fri, 16 Apr 2021 16:55:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237713AbhDPQ4Q (ORCPT ); Fri, 16 Apr 2021 12:56:16 -0400 Received: from mga01.intel.com ([192.55.52.88]:11721 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236504AbhDPQ4P (ORCPT ); Fri, 16 Apr 2021 12:56:15 -0400 IronPort-SDR: gQwN+Jv028jRFmeTn74CivghbJfq5aALZul+jhsuR+u1d7kCx8SXXfkgUakWVyn/Ld1D249mVc h5FSEiPlIWDg== X-IronPort-AV: E=McAfee;i="6200,9189,9956"; a="215606435" X-IronPort-AV: E=Sophos;i="5.82,226,1613462400"; d="scan'208";a="215606435" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga101.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 Apr 2021 09:55:50 -0700 IronPort-SDR: SoiJjiSyL9QI8NgTY1I0ojJTmGG0VntA2BvzNCh4v+qzyeJXoq/EeeQDKJi57E4iN7+0pKehl3 kWmSyR5ugsIw== X-IronPort-AV: E=Sophos;i="5.82,226,1613462400"; d="scan'208";a="425655938" Received: from rhweight-wrk1.ra.intel.com ([137.102.106.42]) by orsmga008-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 16 Apr 2021 09:55:49 -0700 From: matthew.gerlach@linux.intel.com To: hao.wu@intel.com, trix@redhat.com, linux-fpga@vger.kernel.org, linux-kernel@vger.kernel.org, yilun.xu@intel.com, russell.h.weight@intel.com, broonie@kernel.org, linux-spi@vger.kernel.org Cc: Matthew Gerlach Subject: [PATCH v3 2/2] spi: altera: Add DFL bus driver for Altera API Controller Date: Fri, 16 Apr 2021 09:57:20 -0700 Message-Id: <20210416165720.554144-3-matthew.gerlach@linux.intel.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210416165720.554144-1-matthew.gerlach@linux.intel.com> References: <20210416165720.554144-1-matthew.gerlach@linux.intel.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fpga@vger.kernel.org From: Matthew Gerlach This patch adds a Device Feature List (DFL) bus driver for the Altera SPI Master controller. The SPI master is connected to an Intel SPI Slave to Avalon Bridge inside an Intel MAX10 BMC Chip. Signed-off-by: Matthew Gerlach --- v3: Instantiate SPI device instead of creating a sub-platform device. Changed comment style as per Mark Brown. v2: moved drivers/fpga/dfl-spi-altera.c to drivers/spi/spi-altera-dfl.c --- drivers/spi/Kconfig | 9 ++ drivers/spi/Makefile | 1 + drivers/spi/spi-altera-dfl.c | 204 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 drivers/spi/spi-altera-dfl.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index e59f548..8b161ec 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -70,6 +70,15 @@ config SPI_ALTERA_CORE 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 e6be348..13e54c4 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o # SPI master controller drivers (bus) 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-dfl.c b/drivers/spi/spi-altera-dfl.c new file mode 100644 index 0000000..3e32e4f --- /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 +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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");