* [PATCH 0/3] spi: Add support for Amlogic Meson SPIFC @ 2014-11-09 9:25 Beniamino Galvani 2014-11-09 9:25 ` [PATCH 1/3] spi: meson: Add device tree bindings documentation for SPIFC Beniamino Galvani ` (2 more replies) 0 siblings, 3 replies; 9+ messages in thread From: Beniamino Galvani @ 2014-11-09 9:25 UTC (permalink / raw) To: Mark Brown Cc: linux-spi, Carlo Caione, linux-kernel, linux-arm-kernel, devicetree, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jerry Cao, Victor Wan, Beniamino Galvani Hi, this patchset adds a driver for the SPIFC (SPI flash controller) available in Amlogic Meson6 and Meson8 SoCs. It doesn't support DMA and has a 64-byte unified transmit/receive buffer. The driver has been tested on a Meson8 based device to communicate with a Macronix mx25l1606e serial flash. Beniamino Galvani (3): spi: meson: Add device tree bindings documentation for SPIFC spi: meson: Add support for Amlogic Meson SPIFC ARM: dts: meson: add node for SPIFC .../devicetree/bindings/spi/spi-meson.txt | 22 ++ arch/arm/boot/dts/meson.dtsi | 9 + drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-meson-spifc.c | 411 +++++++++++++++++++++ 5 files changed, 450 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-meson.txt create mode 100644 drivers/spi/spi-meson-spifc.c -- 1.9.1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 1/3] spi: meson: Add device tree bindings documentation for SPIFC 2014-11-09 9:25 [PATCH 0/3] spi: Add support for Amlogic Meson SPIFC Beniamino Galvani @ 2014-11-09 9:25 ` Beniamino Galvani 2014-11-09 9:25 ` [PATCH 2/3] spi: meson: Add support for Amlogic Meson SPIFC Beniamino Galvani 2014-11-09 9:25 ` [PATCH 3/3] ARM: dts: meson: add node for SPIFC Beniamino Galvani 2 siblings, 0 replies; 9+ messages in thread From: Beniamino Galvani @ 2014-11-09 9:25 UTC (permalink / raw) To: Mark Brown Cc: Mark Rutland, devicetree, Victor Wan, Pawel Moll, Ian Campbell, linux-kernel, linux-spi, Beniamino Galvani, Rob Herring, Kumar Gala, Carlo Caione, Jerry Cao, linux-arm-kernel This adds documentation of device tree bindings for the Amlogic Meson SPIFC (SPI Flash Controller). Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> --- .../devicetree/bindings/spi/spi-meson.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/spi/spi-meson.txt diff --git a/Documentation/devicetree/bindings/spi/spi-meson.txt b/Documentation/devicetree/bindings/spi/spi-meson.txt new file mode 100644 index 0000000..bb52a86 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-meson.txt @@ -0,0 +1,22 @@ +Amlogic Meson SPI controllers + +* SPIFC (SPI Flash Controller) + +The Meson SPIFC is a controller optimized for communication with SPI +NOR memories, without DMA support and a 64-byte unified transmit / +receive buffer. + +Required properties: + - compatible: should be "amlogic,meson6-spifc" + - reg: physical base address and length of the controller registers + - clocks: phandle of the input clock for the baud rate generator + - #address-cells: should be 1 + - #size-cells: should be 0 + + spi@c1108c80 { + compatible = "amlogic,meson6-spifc"; + reg = <0xc1108c80 0x80>; + clocks = <&clk81>; + #address-cells = <1>; + #size-cells = <0>; + }; -- 1.9.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/3] spi: meson: Add support for Amlogic Meson SPIFC 2014-11-09 9:25 [PATCH 0/3] spi: Add support for Amlogic Meson SPIFC Beniamino Galvani 2014-11-09 9:25 ` [PATCH 1/3] spi: meson: Add device tree bindings documentation for SPIFC Beniamino Galvani @ 2014-11-09 9:25 ` Beniamino Galvani [not found] ` <1415525113-25598-3-git-send-email-b.galvani-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2014-11-09 9:25 ` [PATCH 3/3] ARM: dts: meson: add node for SPIFC Beniamino Galvani 2 siblings, 1 reply; 9+ messages in thread From: Beniamino Galvani @ 2014-11-09 9:25 UTC (permalink / raw) To: Mark Brown Cc: linux-spi, Carlo Caione, linux-kernel, linux-arm-kernel, devicetree, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jerry Cao, Victor Wan, Beniamino Galvani This is a driver for the Amlogic Meson SPIFC (SPI flash controller), which is one of the two SPI controllers available on the SoC. It doesn't support DMA and has a 64-byte unified transmit/receive buffer. The device is optimized for interfacing with SPI NOR memories and allows the execution of standard operations such as read, page program, sector erase, etc. in a simplified way, basically toggling a bit in a dedicated register. The driver doesn't use those predefined commands and relies only on custom transfers. Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/spi-meson-spifc.c | 411 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 419 insertions(+) create mode 100644 drivers/spi/spi-meson-spifc.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 84e7c9e..3c98a9d 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -301,6 +301,13 @@ config SPI_FSL_ESPI From MPC8536, 85xx platform uses the controller, and all P10xx, P20xx, P30xx,P40xx, P50xx uses this controller. +config SPI_MESON_SPIFC + bool "Amlogic Meson SPIFC controller" + depends on ARCH_MESON + help + This enables master mode support for the SPIFC (SPI flash + controller) available in Amlogic Meson SoCs. + config SPI_OC_TINY tristate "OpenCores tiny SPI" depends on GPIOLIB diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 78f24ca..9b8a747 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o obj-$(CONFIG_SPI_GPIO) += spi-gpio.o obj-$(CONFIG_SPI_IMX) += spi-imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o +obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c new file mode 100644 index 0000000..a0ba5d8 --- /dev/null +++ b/drivers/spi/spi-meson-spifc.c @@ -0,0 +1,411 @@ +/* + * Driver for Amlogic Meson SPI flash controller (SPIFC) + * + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/types.h> + +/* register map */ +#define REG_CMD 0x00 +#define REG_ADDR 0x04 +#define REG_CTRL 0x08 +#define REG_CTRL1 0x0c +#define REG_STATUS 0x10 +#define REG_CTRL2 0x14 +#define REG_CLOCK 0x18 +#define REG_USER 0x1c +#define REG_USER1 0x20 +#define REG_USER2 0x24 +#define REG_USER3 0x28 +#define REG_USER4 0x2c +#define REG_SLAVE 0x30 +#define REG_SLAVE1 0x34 +#define REG_SLAVE2 0x38 +#define REG_SLAVE3 0x3c +#define REG_C0 0x40 +#define REG_B8 0x60 + +/* register fields */ +#define CMD_USER BIT(18) +#define CTRL_ENABLE_AHB BIT(17) +#define CLOCK_SOURCE BIT(31) +#define CLOCK_DIV_SHIFT 12 +#define CLOCK_DIV_MASK (0x3f << CLOCK_DIV_SHIFT) +#define CLOCK_CNT_HIGH_SHIFT 6 +#define CLOCK_CNT_HIGH_MASK (0x3f << CLOCK_CNT_HIGH_SHIFT) +#define CLOCK_CNT_LOW_SHIFT 0 +#define CLOCK_CNT_LOW_MASK (0x3f << CLOCK_CNT_LOW_SHIFT) +#define USER_DIN_EN_MS BIT(0) +#define USER_CMP_MODE BIT(2) +#define USER_UC_DOUT_SEL BIT(27) +#define USER_UC_DIN_SEL BIT(28) +#define USER_UC_MASK ((BIT(5) - 1) << 27) +#define USER1_BN_UC_DOUT_SHIFT 17 +#define USER1_BN_UC_DOUT_MASK (0xff << 16) +#define USER1_BN_UC_DIN_SHIFT 8 +#define USER1_BN_UC_DIN_MASK (0xff << 8) +#define USER4_CS_ACT BIT(30) +#define SLAVE_TRST_DONE BIT(4) +#define SLAVE_OP_MODE BIT(30) +#define SLAVE_SW_RST BIT(31) + +#define SPIFC_BUFFER_SIZE 64 + +/** + * struct meson_spifc + * @master: the SPI master + * @regmap: regmap for device registers + * @clk: input clock of the built-in baud rate generator + * @device: the device structure + */ +struct meson_spifc { + struct spi_master *master; + struct regmap *regmap; + struct clk *clk; + struct device *dev; +}; + +static struct regmap_config spifc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +/** + * meson_spifc_wait_ready() - wait for the current operation to terminate + * @spifc: the Meson SPI device + * Return: 0 on success, a negative value on error + */ +static int meson_spifc_wait_ready(struct meson_spifc *spifc) +{ + unsigned long deadline = jiffies + msecs_to_jiffies(1000); + u32 data; + + do { + regmap_read(spifc->regmap, REG_SLAVE, &data); + if (data & SLAVE_TRST_DONE) + return 0; + cond_resched(); + } while (time_before(jiffies, deadline)); + + return -ETIMEDOUT; +} + +/** + * meson_spifc_drain_buffer() - copy data from device buffer to memory + * @spifc: the Meson SPI device + * @buf: the destination buffer + * @len: number of bytes to copy + */ +static void meson_spifc_drain_buffer(struct meson_spifc *spifc, u8 *buf, + int len) +{ + u32 data; + int i = 0; + + while (i < len) { + regmap_read(spifc->regmap, REG_C0 + i, &data); + + if (len - i >= 4) { + *((u32 *)buf) = data; + buf += 4; + } else { + memcpy(buf, &data, len - i); + break; + } + i += 4; + } +} + +/** + * meson_spifc_fill_buffer() - copy data from memory to device buffer + * @spifc: the Meson SPI device + * @buf: the source buffer + * @len: number of bytes to copy + */ +static void meson_spifc_fill_buffer(struct meson_spifc *spifc, const u8 *buf, + int len) +{ + u32 data; + int i = 0; + + while (i < len) { + if (len - i >= 4) + data = *(u32 *)buf; + else + memcpy(&data, buf, len - i); + + regmap_write(spifc->regmap, REG_C0 + i, data); + + buf += 4; + i += 4; + } +} + +/** + * meson_spifc_setup_speed() - program the clock divider + * @spifc: the Meson SPI device + * @speed: desired speed in Hz + */ +void meson_spifc_setup_speed(struct meson_spifc *spifc, u32 speed) +{ + unsigned long parent, value; + int n; + + parent = clk_get_rate(spifc->clk); + n = max_t(int, parent / speed - 1, 1); + + dev_dbg(spifc->dev, "parent %lu, speed %u, n %d\n", parent, + speed, n); + + value = (n << CLOCK_DIV_SHIFT) & CLOCK_DIV_MASK; + value |= (n << CLOCK_CNT_LOW_SHIFT) & CLOCK_CNT_LOW_MASK; + value |= (((n + 1) / 2 - 1) << CLOCK_CNT_HIGH_SHIFT) & + CLOCK_CNT_HIGH_MASK; + + regmap_write(spifc->regmap, REG_CLOCK, value); +} + +/** + * meson_spifc_txrx() - transfer a chunk of data + * @spifc: the Meson SPI device + * @xfer: the current SPI transfer + * @offset: offset of the data to transfer + * @len: length of the data to transfer + * @last_xfer: whether this is the last transfer of the message + * @last_chunk: whether this is the last chunk of the transfer + * Return: 0 on success, a negative value on error + */ +static int meson_spifc_txrx(struct meson_spifc *spifc, + struct spi_transfer *xfer, + int offset, int len, bool last_xfer, + bool last_chunk) +{ + bool keep_cs = true; + int ret; + + if (xfer->tx_buf) + meson_spifc_fill_buffer(spifc, xfer->tx_buf + offset, len); + + /* enable DOUT stage */ + regmap_update_bits(spifc->regmap, REG_USER, USER_UC_MASK, + USER_UC_DOUT_SEL); + regmap_write(spifc->regmap, REG_USER1, + (8 * len - 1) << USER1_BN_UC_DOUT_SHIFT); + + /* enable data input during DOUT */ + regmap_update_bits(spifc->regmap, REG_USER, USER_DIN_EN_MS, + USER_DIN_EN_MS); + + if (last_chunk) { + if (last_xfer) + keep_cs = xfer->cs_change; + else + keep_cs = !xfer->cs_change; + } + + regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT, + keep_cs ? USER4_CS_ACT : 0); + + /* clear transition done bit */ + regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_TRST_DONE, 0); + /* start transfer */ + regmap_update_bits(spifc->regmap, REG_CMD, CMD_USER, CMD_USER); + + ret = meson_spifc_wait_ready(spifc); + + if (!ret && xfer->rx_buf) + meson_spifc_drain_buffer(spifc, xfer->rx_buf + offset, len); + + return ret; +} + +/** + * meson_spifc_transfer_one() - perform a single transfer + * @spifc: the Meson SPI device + * @xfer: the current SPI transfer + * @last_xfer: whether this is the last transfer of the message + * Return: 0 on success, a negative value on error + */ +static int meson_spifc_transfer_one(struct spi_device *spi, + struct spi_transfer *xfer, + bool last_xfer) +{ + struct meson_spifc *spifc = spi_master_get_devdata(spi->master); + int len, done = 0, ret = 0; + + meson_spifc_setup_speed(spifc, xfer->speed_hz ? xfer->speed_hz : + spi->max_speed_hz); + + regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, 0); + + while (done < xfer->len && !ret) { + len = min_t(int, xfer->len - done, SPIFC_BUFFER_SIZE); + ret = meson_spifc_txrx(spifc, xfer, done, len, + last_xfer, done + len >= xfer->len); + done += len; + } + + regmap_update_bits(spifc->regmap, REG_CTRL, CTRL_ENABLE_AHB, + CTRL_ENABLE_AHB); + + if (!ret && xfer->delay_usecs) + udelay(xfer->delay_usecs); + + return ret; +} + +/** + * meson_spifc_transfer_one_message() - transfer a single SPI message + * @master: the SPI master + * @msg: the message to transfer + * Return: 0 on success, a negative value on error + */ +static int meson_spifc_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct spi_transfer *xfer; + int ret = 0; + bool last; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + last = list_is_last(&xfer->transfer_list, &msg->transfers); + ret = meson_spifc_transfer_one(msg->spi, xfer, last); + if (ret) + break; + msg->actual_length += xfer->len; + } + + msg->status = ret; + spi_finalize_current_message(master); + + return ret; +} + +static int meson_spifc_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct meson_spifc *spifc; + struct resource *res; + void __iomem *base; + unsigned int rate; + int ret = 0; + + master = spi_alloc_master(&pdev->dev, sizeof(struct meson_spifc)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + spifc = spi_master_get_devdata(master); + memset(spifc, 0, sizeof(struct meson_spifc)); + spifc->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(spifc->dev, res); + if (IS_ERR(base)) { + ret = PTR_ERR(base); + goto out_err; + } + + spifc_regmap_config.max_register = resource_size(res) - 4; + spifc_regmap_config.name = "amlogic,meson-spifc"; + spifc->regmap = devm_regmap_init_mmio(spifc->dev, base, + &spifc_regmap_config); + if (IS_ERR(spifc->regmap)) { + ret = PTR_ERR(spifc->regmap); + goto out_err; + } + + spifc->clk = devm_clk_get(spifc->dev, NULL); + if (IS_ERR(spifc->clk)) { + dev_err(spifc->dev, "missing clock\n"); + ret = PTR_ERR(spifc->clk); + goto out_err; + } + + ret = clk_prepare_enable(spifc->clk); + if (ret) { + dev_err(spifc->dev, "can't prepare clock\n"); + goto out_err; + } + + rate = clk_get_rate(spifc->clk); + + master->bus_num = pdev->id; + master->num_chipselect = 1; + master->dev.of_node = pdev->dev.of_node; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->transfer_one_message = meson_spifc_transfer_one_message; + master->min_speed_hz = rate >> 6; + master->max_speed_hz = rate >> 1; + + /* reset device */ + regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_SW_RST, + SLAVE_SW_RST); + /* disable compatible mode */ + regmap_update_bits(spifc->regmap, REG_USER, USER_CMP_MODE, 0); + /* set master mode */ + regmap_update_bits(spifc->regmap, REG_SLAVE, SLAVE_OP_MODE, 0); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(spifc->dev, "failed to register spi master\n"); + goto out_clk; + } + + return 0; +out_clk: + clk_disable_unprepare(spifc->clk); +out_err: + spi_master_put(master); + return ret; +} + +static int meson_spifc_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct meson_spifc *spifc = spi_master_get_devdata(master); + + clk_disable_unprepare(spifc->clk); + + return 0; +} + +static const struct of_device_id meson_spifc_dt_match[] = { + { .compatible = "amlogic,meson6-spifc", }, + { }, +}; + +static struct platform_driver meson_spifc_driver = { + .probe = meson_spifc_probe, + .remove = meson_spifc_remove, + .driver = { + .name = "meson-spifc", + .of_match_table = of_match_ptr(meson_spifc_dt_match), + }, +}; + +module_platform_driver(meson_spifc_driver); + +MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>"); +MODULE_DESCRIPTION("Amlogic Meson SPIFC driver"); +MODULE_LICENSE("GPL v2"); -- 1.9.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
[parent not found: <1415525113-25598-3-git-send-email-b.galvani-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH 2/3] spi: meson: Add support for Amlogic Meson SPIFC [not found] ` <1415525113-25598-3-git-send-email-b.galvani-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2014-11-09 10:17 ` Mark Brown [not found] ` <20141109101712.GM2722-GFdadSzt00ze9xe1eoZjHA@public.gmane.org> 0 siblings, 1 reply; 9+ messages in thread From: Mark Brown @ 2014-11-09 10:17 UTC (permalink / raw) To: Beniamino Galvani Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA, Carlo Caione, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jerry Cao, Victor Wan [-- Attachment #1: Type: text/plain, Size: 2647 bytes --] On Sun, Nov 09, 2014 at 10:25:12AM +0100, Beniamino Galvani wrote: > +static int meson_spifc_wait_ready(struct meson_spifc *spifc) > +{ > + unsigned long deadline = jiffies + msecs_to_jiffies(1000); > + u32 data; > + > + do { > + regmap_read(spifc->regmap, REG_SLAVE, &data); > + if (data & SLAVE_TRST_DONE) > + return 0; > + cond_resched(); > + } while (time_before(jiffies, deadline)); This will busy wait for up to a second, that seems like a long time to busy wait. We also appear to be using this for the entire duration of the transfer which could be a fairly long time even during normal operation if doing a large transfer such as a firmware download, or if the bus speed is low. > + meson_spifc_setup_speed(spifc, xfer->speed_hz ? xfer->speed_hz : > + spi->max_speed_hz); > + Please avoid the ternery operator, it does nothing for legibility and in this case it's not needed as the core will always ensure that there is a per-transfer speed set. > + while (done < xfer->len && !ret) { > + len = min_t(int, xfer->len - done, SPIFC_BUFFER_SIZE); > + ret = meson_spifc_txrx(spifc, xfer, done, len, > + last_xfer, done + len >= xfer->len); > + done += len; > + } I noticed that the handling of /CS was done in the spifc_txrx() function - will this do the right thing if the transfer needs to be split for the buffer size? > + if (!ret && xfer->delay_usecs) > + udelay(xfer->delay_usecs); The core will do this for you if you implement this as transfer_one(). > +static int meson_spifc_transfer_one_message(struct spi_master *master, > + struct spi_message *msg) This appears to do nothing that the core won't do - just implement transfer_one() and remove this. > + spifc = spi_master_get_devdata(master); > + memset(spifc, 0, sizeof(struct meson_spifc)); There should be no need for this memset. > + spifc_regmap_config.max_register = resource_size(res) - 4; > + spifc_regmap_config.name = "amlogic,meson-spifc"; If you're dynamically initializing the structure you need to work with a copy of it rather than directly since there may be multiple instances. I'm not seeing a reason to override the regmap name here, this is only really intended for devices with multiple register maps. > + ret = clk_prepare_enable(spifc->clk); > + if (ret) { > + dev_err(spifc->dev, "can't prepare clock\n"); > + goto out_err; > + } You should really implement runtime PM operations to disable this when not in use and use auto_runtime_pm to make sure this happens. > + master->bus_num = pdev->id; Leave this blank for DT only devices (and for non-DT devices this won't work if you get two different buses). [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 473 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
[parent not found: <20141109101712.GM2722-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>]
* Re: [PATCH 2/3] spi: meson: Add support for Amlogic Meson SPIFC [not found] ` <20141109101712.GM2722-GFdadSzt00ze9xe1eoZjHA@public.gmane.org> @ 2014-11-09 22:56 ` Beniamino Galvani [not found] ` <20141109225650.GA27950-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 9+ messages in thread From: Beniamino Galvani @ 2014-11-09 22:56 UTC (permalink / raw) To: Mark Brown Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA, Carlo Caione, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jerry Cao, Victor Wan On Sun, Nov 09, 2014 at 10:17:12AM +0000, Mark Brown wrote: > On Sun, Nov 09, 2014 at 10:25:12AM +0100, Beniamino Galvani wrote: > > > +static int meson_spifc_wait_ready(struct meson_spifc *spifc) > > +{ > > + unsigned long deadline = jiffies + msecs_to_jiffies(1000); > > + u32 data; > > + > > + do { > > + regmap_read(spifc->regmap, REG_SLAVE, &data); > > + if (data & SLAVE_TRST_DONE) > > + return 0; > > + cond_resched(); > > + } while (time_before(jiffies, deadline)); > > This will busy wait for up to a second, that seems like a long time to > busy wait. We also appear to be using this for the entire duration of > the transfer which could be a fairly long time even during normal > operation if doing a large transfer such as a firmware download, or if > the bus speed is low. Yes, probably the timeout value is too long since the maximum length of a basic transfer is 64 bytes. Can you suggest a reasonable value? > > > + meson_spifc_setup_speed(spifc, xfer->speed_hz ? xfer->speed_hz : > > + spi->max_speed_hz); > > + > > Please avoid the ternery operator, it does nothing for legibility and in > this case it's not needed as the core will always ensure that there is a > per-transfer speed set. Ok. > > + while (done < xfer->len && !ret) { > > + len = min_t(int, xfer->len - done, SPIFC_BUFFER_SIZE); > > + ret = meson_spifc_txrx(spifc, xfer, done, len, > > + last_xfer, done + len >= xfer->len); > > + done += len; > > + } > > I noticed that the handling of /CS was done in the spifc_txrx() function > - will this do the right thing if the transfer needs to be split for the > buffer size? It should. When the transfer gets split, CS is kept active for all the chunks and the value of CS after that depends on the value of cs_change. > > > + if (!ret && xfer->delay_usecs) > > + udelay(xfer->delay_usecs); > > The core will do this for you if you implement this as transfer_one(). Please correct me if I'm wrong, but I think that transfer_one() can't be used in this case. The hardware doesn't support direct manipulation of CS and allows only to specify if CS must be kept active after the current transfer. So I need to know for each transfer if it's the last and this can be achieved only implementing transfer_one_message(). > > > +static int meson_spifc_transfer_one_message(struct spi_master *master, > > + struct spi_message *msg) > > This appears to do nothing that the core won't do - just implement > transfer_one() and remove this. > > > + spifc = spi_master_get_devdata(master); > > + memset(spifc, 0, sizeof(struct meson_spifc)); > > There should be no need for this memset. > > > + spifc_regmap_config.max_register = resource_size(res) - 4; > > + spifc_regmap_config.name = "amlogic,meson-spifc"; > > If you're dynamically initializing the structure you need to work with a > copy of it rather than directly since there may be multiple instances. > I'm not seeing a reason to override the regmap name here, this is only > really intended for devices with multiple register maps. > > > + ret = clk_prepare_enable(spifc->clk); > > + if (ret) { > > + dev_err(spifc->dev, "can't prepare clock\n"); > > + goto out_err; > > + } > > You should really implement runtime PM operations to disable this when > not in use and use auto_runtime_pm to make sure this happens. > > > + master->bus_num = pdev->id; > > Leave this blank for DT only devices (and for non-DT devices this won't > work if you get two different buses). Ok, will do. Thanks for the review. Beniamino -- To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 9+ messages in thread
[parent not found: <20141109225650.GA27950-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH 2/3] spi: meson: Add support for Amlogic Meson SPIFC [not found] ` <20141109225650.GA27950-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2014-11-10 15:11 ` Mark Brown 2014-11-11 19:52 ` Beniamino Galvani 0 siblings, 1 reply; 9+ messages in thread From: Mark Brown @ 2014-11-10 15:11 UTC (permalink / raw) To: Beniamino Galvani Cc: linux-spi-u79uwXL29TY76Z2rM5mHXA, Carlo Caione, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jerry Cao, Victor Wan [-- Attachment #1: Type: text/plain, Size: 2100 bytes --] On Sun, Nov 09, 2014 at 11:56:50PM +0100, Beniamino Galvani wrote: > On Sun, Nov 09, 2014 at 10:17:12AM +0000, Mark Brown wrote: > > This will busy wait for up to a second, that seems like a long time to > > busy wait. We also appear to be using this for the entire duration of > > the transfer which could be a fairly long time even during normal > > operation if doing a large transfer such as a firmware download, or if > > the bus speed is low. > Yes, probably the timeout value is too long since the maximum length > of a basic transfer is 64 bytes. Can you suggest a reasonable value? 10ms? It depends somewhat > > > + while (done < xfer->len && !ret) { > > > + len = min_t(int, xfer->len - done, SPIFC_BUFFER_SIZE); > > > + ret = meson_spifc_txrx(spifc, xfer, done, len, > > > + last_xfer, done + len >= xfer->len); > > > + done += len; > > > + } > > I noticed that the handling of /CS was done in the spifc_txrx() function > > - will this do the right thing if the transfer needs to be split for the > > buffer size? > It should. When the transfer gets split, CS is kept active for all the > chunks and the value of CS after that depends on the value of > cs_change. Can you be more specific about how that works? I'm just not seeing the code that handles this. > > > + if (!ret && xfer->delay_usecs) > > > + udelay(xfer->delay_usecs); > > The core will do this for you if you implement this as transfer_one(). > Please correct me if I'm wrong, but I think that transfer_one() can't > be used in this case. The hardware doesn't support direct manipulation > of CS and allows only to specify if CS must be kept active after the > current transfer. So I need to know for each transfer if it's the last > and this can be achieved only implementing transfer_one_message(). This is already in a function that's operating at the transfer_one() level, the function is even called transfer_one() and besides it's clearly not something specific to this hardware so should be factored out into the core instead of open coded. [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 473 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/3] spi: meson: Add support for Amlogic Meson SPIFC 2014-11-10 15:11 ` Mark Brown @ 2014-11-11 19:52 ` Beniamino Galvani 0 siblings, 0 replies; 9+ messages in thread From: Beniamino Galvani @ 2014-11-11 19:52 UTC (permalink / raw) To: Mark Brown Cc: linux-spi, Carlo Caione, linux-kernel, linux-arm-kernel, devicetree, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jerry Cao, Victor Wan On Mon, Nov 10, 2014 at 03:11:40PM +0000, Mark Brown wrote: > On Sun, Nov 09, 2014 at 11:56:50PM +0100, Beniamino Galvani wrote: > > On Sun, Nov 09, 2014 at 10:17:12AM +0000, Mark Brown wrote: > > > > I noticed that the handling of /CS was done in the spifc_txrx() function > > > - will this do the right thing if the transfer needs to be split for the > > > buffer size? > > > It should. When the transfer gets split, CS is kept active for all the > > chunks and the value of CS after that depends on the value of > > cs_change. > > Can you be more specific about how that works? I'm just not seeing the > code that handles this. It's this: static int meson_spifc_txrx(struct meson_spifc *spifc, struct spi_transfer *xfer, int offset, int len, bool last_xfer, bool last_chunk) { bool keep_cs = true; [...] if (last_chunk) { if (last_xfer) keep_cs = xfer->cs_change; else keep_cs = !xfer->cs_change; } regmap_update_bits(spifc->regmap, REG_USER4, USER4_CS_ACT, keep_cs ? USER4_CS_ACT : 0); /* start transfer */ [...] } The USER4_CS_ACT bit specifies if CS must be kept active after the transfer. > > > > + if (!ret && xfer->delay_usecs) > > > > + udelay(xfer->delay_usecs); > > > > The core will do this for you if you implement this as transfer_one(). > > > Please correct me if I'm wrong, but I think that transfer_one() can't > > be used in this case. The hardware doesn't support direct manipulation > > of CS and allows only to specify if CS must be kept active after the > > current transfer. So I need to know for each transfer if it's the last > > and this can be achieved only implementing transfer_one_message(). > > This is already in a function that's operating at the transfer_one() > level, the function is even called transfer_one() and besides it's > clearly not something specific to this hardware so should be factored > out into the core instead of open coded. A way to simplify this at core level could be to add a 'last' flag to the spi_transfer structure and populate it before calling transfer_one_message(); in this way, drivers that need to know the position of the transfer in the message in order to properly handle CS can use the generic version of transfer_one_message() instead of reimplementing it. It seems that other existing drivers probably can benefit from this. Beniamino ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH 3/3] ARM: dts: meson: add node for SPIFC 2014-11-09 9:25 [PATCH 0/3] spi: Add support for Amlogic Meson SPIFC Beniamino Galvani 2014-11-09 9:25 ` [PATCH 1/3] spi: meson: Add device tree bindings documentation for SPIFC Beniamino Galvani 2014-11-09 9:25 ` [PATCH 2/3] spi: meson: Add support for Amlogic Meson SPIFC Beniamino Galvani @ 2014-11-09 9:25 ` Beniamino Galvani 2014-11-09 10:17 ` Mark Brown 2 siblings, 1 reply; 9+ messages in thread From: Beniamino Galvani @ 2014-11-09 9:25 UTC (permalink / raw) To: Mark Brown Cc: linux-spi, Carlo Caione, linux-kernel, linux-arm-kernel, devicetree, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jerry Cao, Victor Wan, Beniamino Galvani This adds a node for the SPI Flash Controller to the Amlogic Meson DTS. Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> --- arch/arm/boot/dts/meson.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/boot/dts/meson.dtsi b/arch/arm/boot/dts/meson.dtsi index e6539ea..d28b16e 100644 --- a/arch/arm/boot/dts/meson.dtsi +++ b/arch/arm/boot/dts/meson.dtsi @@ -106,5 +106,14 @@ clocks = <&clk81>; status = "disabled"; }; + + spifc: spi@c1108c80 { + compatible = "amlogic,meson6-spifc"; + reg = <0xc1108c80 0x80>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clk81>; + status = "disabled"; + }; }; }; /* end of / */ -- 1.9.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 3/3] ARM: dts: meson: add node for SPIFC 2014-11-09 9:25 ` [PATCH 3/3] ARM: dts: meson: add node for SPIFC Beniamino Galvani @ 2014-11-09 10:17 ` Mark Brown 0 siblings, 0 replies; 9+ messages in thread From: Mark Brown @ 2014-11-09 10:17 UTC (permalink / raw) To: Beniamino Galvani Cc: linux-spi, Carlo Caione, linux-kernel, linux-arm-kernel, devicetree, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jerry Cao, Victor Wan [-- Attachment #1: Type: text/plain, Size: 186 bytes --] On Sun, Nov 09, 2014 at 10:25:13AM +0100, Beniamino Galvani wrote: > This adds a node for the SPI Flash Controller to the Amlogic Meson > DTS. Acked-by: Mark Brown <broonie@kernel.org> [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 473 bytes --] ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2014-11-11 19:52 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-11-09 9:25 [PATCH 0/3] spi: Add support for Amlogic Meson SPIFC Beniamino Galvani 2014-11-09 9:25 ` [PATCH 1/3] spi: meson: Add device tree bindings documentation for SPIFC Beniamino Galvani 2014-11-09 9:25 ` [PATCH 2/3] spi: meson: Add support for Amlogic Meson SPIFC Beniamino Galvani [not found] ` <1415525113-25598-3-git-send-email-b.galvani-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2014-11-09 10:17 ` Mark Brown [not found] ` <20141109101712.GM2722-GFdadSzt00ze9xe1eoZjHA@public.gmane.org> 2014-11-09 22:56 ` Beniamino Galvani [not found] ` <20141109225650.GA27950-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2014-11-10 15:11 ` Mark Brown 2014-11-11 19:52 ` Beniamino Galvani 2014-11-09 9:25 ` [PATCH 3/3] ARM: dts: meson: add node for SPIFC Beniamino Galvani 2014-11-09 10:17 ` Mark Brown
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).