Merge tag 'spi-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi updates from Mark Brown:
 "This is quite a quiet release for SPI, there's been a bit of cleanup
  to the core from Uwe but nothing functionality wise.

  We have added several new drivers, Cadence XSPI, Ingenic JZ47xx,
  Qualcomm SC7280 and SC7180 and Xilinx Versal OSPI"

* tag 'spi-v5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (41 commits)
  spi: Convert NXP flexspi to json schema
  spi: spi-geni-qcom: Add support for GPI dma
  spi: fsi: Fix contention in the FSI2SPI engine
  spi: spi-rpc-if: Check return value of rpcif_sw_init()
  spi: tegra210-quad: Put device into suspend on driver removal
  spi: tegra20-slink: Put device into suspend on driver removal
  spi: bcm-qspi: Fix missing clk_disable_unprepare() on error in bcm_qspi_probe()
  spi: at91-usart: replacing legacy gpio interface for gpiod
  spi: replace snprintf in show functions with sysfs_emit
  spi: cadence: Add of_node_put() before return
  spi: orion: Add of_node_put() before goto
  spi: cadence-quadspi: fix dma_unmap_single() call
  spi: tegra20: fix build with CONFIG_PM_SLEEP=n
  spi: bcm-qspi: add support for 3-wire mode for half duplex transfer
  spi: bcm-qspi: Add mspi spcr3 32/64-bits xfer mode
  spi: Make several public functions private to spi.c
  spi: Reorder functions to simplify the next commit
  spi: Remove unused function spi_busnum_to_master()
  spi: Move comment about chipselect check to the right place
  spi: fsi: Print status on error
  ...
This commit is contained in:
Linus Torvalds
2021-11-01 19:09:04 -07:00
34 changed files with 2325 additions and 453 deletions

View File

@@ -11,6 +11,14 @@ maintainers:
allOf:
- $ref: spi-controller.yaml#
- if:
properties:
compatible:
contains:
const: xlnx,versal-ospi-1.0
then:
required:
- power-domains
properties:
compatible:
@@ -20,6 +28,7 @@ properties:
- ti,k2g-qspi
- ti,am654-ospi
- intel,lgm-qspi
- xlnx,versal-ospi-1.0
- const: cdns,qspi-nor
- const: cdns,qspi-nor
@@ -65,6 +74,9 @@ properties:
data rather than the QSPI clock. Make sure that QSPI return clock
is populated on the board before using this property.
power-domains:
maxItems: 1
resets:
maxItems: 2

View File

@@ -0,0 +1,77 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2020-21 Cadence
%YAML 1.2
---
$id: "http://devicetree.org/schemas/spi/cdns,xspi.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Cadence XSPI Controller
maintainers:
- Parshuram Thombare <pthombar@cadence.com>
description: |
The XSPI controller allows SPI protocol communication in
single, dual, quad or octal wire transmission modes for
read/write access to slaves such as SPI-NOR flash.
allOf:
- $ref: "spi-controller.yaml#"
properties:
compatible:
const: cdns,xspi-nor
reg:
items:
- description: address and length of the controller register set
- description: address and length of the Slave DMA data port
- description: address and length of the auxiliary registers
reg-names:
items:
- const: io
- const: sdma
- const: aux
interrupts:
maxItems: 1
required:
- compatible
- reg
- interrupts
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
bus {
#address-cells = <2>;
#size-cells = <2>;
xspi: spi@a0010000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cdns,xspi-nor";
reg = <0x0 0xa0010000 0x0 0x1040>,
<0x0 0xb0000000 0x0 0x1000>,
<0x0 0xa0020000 0x0 0x100>;
reg-names = "io", "sdma", "aux";
interrupts = <0 90 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&gic>;
flash@0 {
compatible = "jedec,spi-nor";
spi-max-frequency = <75000000>;
reg = <0>;
};
flash@1 {
compatible = "jedec,spi-nor";
spi-max-frequency = <75000000>;
reg = <1>;
};
};
};

View File

@@ -0,0 +1,72 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/spi/ingenic,spi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ingenic SoCs SPI controller devicetree bindings
maintainers:
- Artur Rojek <contact@artur-rojek.eu>
- Paul Cercueil <paul@crapouillou.net>
allOf:
- $ref: /schemas/spi/spi-controller.yaml#
properties:
compatible:
oneOf:
- enum:
- ingenic,jz4750-spi
- ingenic,jz4780-spi
- items:
- enum:
- ingenic,jz4760-spi
- ingenic,jz4770-spi
- const: ingenic,jz4750-spi
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
dmas:
maxItems: 2
minItems: 2
dma-names:
items:
- const: rx
- const: tx
required:
- compatible
- reg
- interrupts
- clocks
- dmas
- dma-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/jz4770-cgu.h>
spi@10043000 {
compatible = "ingenic,jz4770-spi", "ingenic,jz4750-spi";
reg = <0x10043000 0x1c>;
#address-cells = <1>;
#size-cells = <0>;
interrupt-parent = <&intc>;
interrupts = <8>;
clocks = <&cgu JZ4770_CLK_SSI0>;
dmas = <&dmac1 23 0xffffffff>, <&dmac1 22 0xffffffff>;
dma-names = "rx", "tx";
};

View File

@@ -21,7 +21,11 @@ allOf:
properties:
compatible:
items:
- const: qcom,sdm845-qspi
- enum:
- qcom,sc7180-qspi
- qcom,sc7280-qspi
- qcom,sdm845-qspi
- const: qcom,qspi-v1
reg:

View File

@@ -1,44 +0,0 @@
* NXP Flex Serial Peripheral Interface (FSPI)
Required properties:
- compatible : Should be "nxp,lx2160a-fspi"
"nxp,imx8qxp-fspi"
"nxp,imx8mm-fspi"
"nxp,imx8mp-fspi"
"nxp,imx8dxl-fspi"
- reg : First contains the register location and length,
Second contains the memory mapping address and length
- reg-names : Should contain the resource reg names:
- fspi_base: configuration register address space
- fspi_mmap: memory mapped address space
- interrupts : Should contain the interrupt for the device
Required SPI slave node properties:
- reg : There are two buses (A and B) with two chip selects each.
This encodes to which bus and CS the flash is connected:
- <0>: Bus A, CS 0
- <1>: Bus A, CS 1
- <2>: Bus B, CS 0
- <3>: Bus B, CS 1
Example showing the usage of two SPI NOR slave devices on bus A:
fspi0: spi@20c0000 {
compatible = "nxp,lx2160a-fspi";
reg = <0x0 0x20c0000 0x0 0x10000>, <0x0 0x20000000 0x0 0x10000000>;
reg-names = "fspi_base", "fspi_mmap";
interrupts = <0 25 0x4>; /* Level high type */
clocks = <&clockgen 4 3>, <&clockgen 4 3>;
clock-names = "fspi_en", "fspi";
mt35xu512aba0: flash@0 {
reg = <0>;
....
};
mt35xu512aba1: flash@1 {
reg = <1>;
....
};
};

View File

@@ -0,0 +1,86 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/spi/spi-nxp-fspi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP Flex Serial Peripheral Interface (FSPI)
maintainers:
- Kuldeep Singh <kuldeep.singh@nxp.com>
allOf:
- $ref: "spi-controller.yaml#"
properties:
compatible:
enum:
- nxp,imx8dxl-fspi
- nxp,imx8mm-fspi
- nxp,imx8mp-fspi
- nxp,imx8qxp-fspi
- nxp,lx2160a-fspi
reg:
items:
- description: registers address space
- description: memory mapped address space
reg-names:
items:
- const: fspi_base
- const: fspi_mmap
interrupts:
maxItems: 1
clocks:
items:
- description: SPI bus clock
- description: SPI serial clock
clock-names:
items:
- const: fspi_en
- const: fspi
required:
- compatible
- reg
- reg-names
- interrupts
- clocks
- clock-names
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/fsl,qoriq-clockgen.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
spi@20c0000 {
compatible = "nxp,lx2160a-fspi";
reg = <0x0 0x20c0000 0x0 0x100000>,
<0x0 0x20000000 0x0 0x10000000>;
reg-names = "fspi_base", "fspi_mmap";
interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(4)>,
<&clockgen QORIQ_CLK_PLATFORM_PLL QORIQ_CLK_PLL_DIV(4)>;
clock-names = "fspi_en", "fspi";
#address-cells = <1>;
#size-cells = <0>;
flash@0 {
compatible = "jedec,spi-nor";
spi-max-frequency = <50000000>;
reg = <0>;
spi-rx-bus-width = <8>;
spi-tx-bus-width = <8>;
};
};
};

View File

@@ -336,14 +336,6 @@ certainly includes SPI devices hooked up through the card connectors!
Non-static Configurations
^^^^^^^^^^^^^^^^^^^^^^^^^
Developer boards often play by different rules than product boards, and one
example is the potential need to hotplug SPI devices and/or controllers.
For those cases you might need to use spi_busnum_to_master() to look
up the spi bus master, and will likely need spi_new_device() to provide the
board info based on the board that was hotplugged. Of course, you'd later
call at least spi_unregister_device() when that board is removed.
When Linux includes support for MMC/SD/SDIO/DataFlash cards through SPI, those
configurations will also be dynamic. Fortunately, such devices all support
basic device identification probes, so they should hotplug normally.

View File

@@ -13488,7 +13488,7 @@ M: Ashish Kumar <ashish.kumar@nxp.com>
R: Yogesh Gaur <yogeshgaur.83@gmail.com>
L: linux-spi@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/spi/spi-nxp-fspi.txt
F: Documentation/devicetree/bindings/spi/spi-nxp-fspi.yaml
F: drivers/spi/spi-nxp-fspi.c
NXP FXAS21002C DRIVER

View File

@@ -113,9 +113,12 @@
* Use the 32.768 kHz oscillator as the parent of the RTC for a higher
* precision.
*/
assigned-clocks = <&cgu JZ4780_CLK_OTGPHY>, <&cgu JZ4780_CLK_RTC>;
assigned-clock-parents = <0>, <&cgu JZ4780_CLK_RTCLK>;
assigned-clock-rates = <48000000>;
assigned-clocks = <&cgu JZ4780_CLK_OTGPHY>, <&cgu JZ4780_CLK_RTC>,
<&cgu JZ4780_CLK_SSIPLL>, <&cgu JZ4780_CLK_SSI>;
assigned-clock-parents = <0>, <&cgu JZ4780_CLK_RTCLK>,
<&cgu JZ4780_CLK_MPLL>,
<&cgu JZ4780_CLK_SSIPLL>;
assigned-clock-rates = <48000000>, <0>, <54000000>;
};
&tcu {

View File

@@ -255,22 +255,23 @@
};
};
spi_gpio {
compatible = "spi-gpio";
spi0: spi@10043000 {
compatible = "ingenic,jz4780-spi";
reg = <0x10043000 0x1c>;
#address-cells = <1>;
#size-cells = <0>;
num-chipselects = <2>;
gpio-miso = <&gpe 14 0>;
gpio-sck = <&gpe 15 0>;
gpio-mosi = <&gpe 17 0>;
cs-gpios = <&gpe 16 0>, <&gpe 18 0>;
interrupt-parent = <&intc>;
interrupts = <8>;
spidev@0 {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <1000000>;
};
clocks = <&cgu JZ4780_CLK_SSI0>;
clock-names = "spi";
dmas = <&dma JZ4780_DMA_SSI0_RX 0xffffffff>,
<&dma JZ4780_DMA_SSI0_TX 0xffffffff>;
dma-names = "rx", "tx";
status = "disabled";
};
uart0: serial@10030000 {
@@ -338,6 +339,25 @@
status = "disabled";
};
spi1: spi@10044000 {
compatible = "ingenic,jz4780-spi";
reg = <0x10044000 0x1c>;
#address-cells = <1>;
#size-sells = <0>;
interrupt-parent = <&intc>;
interrupts = <7>;
clocks = <&cgu JZ4780_CLK_SSI1>;
clock-names = "spi";
dmas = <&dma JZ4780_DMA_SSI1_RX 0xffffffff>,
<&dma JZ4780_DMA_SSI1_TX 0xffffffff>;
dma-names = "rx", "tx";
status = "disabled";
};
i2c0: i2c@10050000 {
compatible = "ingenic,jz4780-i2c", "ingenic,jz4770-i2c";
#address-cells = <1>;

View File

@@ -647,6 +647,23 @@ int zynqmp_pm_sd_dll_reset(u32 node_id, u32 type)
}
EXPORT_SYMBOL_GPL(zynqmp_pm_sd_dll_reset);
/**
* zynqmp_pm_ospi_mux_select() - OSPI Mux selection
*
* @dev_id: Device Id of the OSPI device.
* @select: OSPI Mux select value.
*
* This function select the OSPI Mux.
*
* Return: Returns status, either success or error+reason
*/
int zynqmp_pm_ospi_mux_select(u32 dev_id, u32 select)
{
return zynqmp_pm_invoke_fn(PM_IOCTL, dev_id, IOCTL_OSPI_MUX_SELECT,
select, 0, NULL);
}
EXPORT_SYMBOL_GPL(zynqmp_pm_ospi_mux_select);
/**
* zynqmp_pm_write_ggs() - PM API for writing global general storage (ggs)
* @index: GGS register index

View File

@@ -228,6 +228,18 @@ config SPI_CADENCE_QUADSPI
device with a Cadence QSPI controller and want to access the
Flash as an MTD device.
config SPI_CADENCE_XSPI
tristate "Cadence XSPI controller"
depends on (OF || COMPILE_TEST) && HAS_IOMEM
depends on SPI_MEM
help
Enable support for the Cadence XSPI Flash controller.
Cadence XSPI is a specialized controller for connecting an SPI
Flash over upto 8bit wide bus. Enable this option if you have a
device with a Cadence XSPI controller and want to access the
Flash as an MTD device.
config SPI_CLPS711X
tristate "CLPS711X host SPI controller"
depends on ARCH_CLPS711X || COMPILE_TEST
@@ -406,6 +418,15 @@ config SPI_IMX
help
This enables support for the Freescale i.MX SPI controllers.
config SPI_INGENIC
tristate "Ingenic JZ47xx SoCs SPI controller"
depends on MACH_INGENIC || COMPILE_TEST
help
This enables support for the Ingenic JZ47xx SoCs SPI controller.
To compile this driver as a module, choose M here: the module
will be called spi-ingenic.
config SPI_JCORE
tristate "J-Core SPI Master"
depends on OF && (SUPERH || COMPILE_TEST)
@@ -738,10 +759,11 @@ config SPI_S3C24XX_FIQ
TX and RX data paths.
config SPI_S3C64XX
tristate "Samsung S3C64XX series type SPI"
tristate "Samsung S3C64XX/Exynos SoC series type SPI"
depends on (PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST)
help
SPI driver for Samsung S3C64XX and newer SoCs.
SPI driver for Samsung S3C64XX, S5Pv210 and Exynos SoCs.
Choose Y/M here only if you build for such Samsung SoC.
config SPI_SC18IS602
tristate "NXP SC18IS602/602B/603 I2C to SPI bridge"

View File

@@ -34,6 +34,7 @@ obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi-butterfly.o
obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += spi-cadence-quadspi.o
obj-$(CONFIG_SPI_CADENCE_XSPI) += spi-cadence-xspi.o
obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o
obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o
obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o
@@ -59,6 +60,7 @@ obj-$(CONFIG_SPI_HISI_KUNPENG) += spi-hisi-kunpeng.o
obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
obj-$(CONFIG_SPI_IMX) += spi-imx.o
obj-$(CONFIG_SPI_INGENIC) += spi-ingenic.o
obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o
obj-$(CONFIG_SPI_JCORE) += spi-jcore.o
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o

View File

@@ -310,7 +310,7 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
return mode;
ifr |= atmel_qspi_modes[mode].config;
if (op->dummy.buswidth && op->dummy.nbytes)
if (op->dummy.nbytes)
dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
/*

View File

@@ -38,126 +38,102 @@ struct amd_spi {
void __iomem *io_remap_addr;
unsigned long io_base_addr;
u32 rom_addr;
u8 chip_select;
};
static inline u8 amd_spi_readreg8(struct spi_master *master, int idx)
static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx)
{
struct amd_spi *amd_spi = spi_master_get_devdata(master);
return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx);
}
static inline void amd_spi_writereg8(struct spi_master *master, int idx,
u8 val)
static inline void amd_spi_writereg8(struct amd_spi *amd_spi, int idx, u8 val)
{
struct amd_spi *amd_spi = spi_master_get_devdata(master);
iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
}
static inline void amd_spi_setclear_reg8(struct spi_master *master, int idx,
u8 set, u8 clear)
static void amd_spi_setclear_reg8(struct amd_spi *amd_spi, int idx, u8 set, u8 clear)
{
u8 tmp = amd_spi_readreg8(master, idx);
u8 tmp = amd_spi_readreg8(amd_spi, idx);
tmp = (tmp & ~clear) | set;
amd_spi_writereg8(master, idx, tmp);
amd_spi_writereg8(amd_spi, idx, tmp);
}
static inline u32 amd_spi_readreg32(struct spi_master *master, int idx)
static inline u32 amd_spi_readreg32(struct amd_spi *amd_spi, int idx)
{
struct amd_spi *amd_spi = spi_master_get_devdata(master);
return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx);
}
static inline void amd_spi_writereg32(struct spi_master *master, int idx,
u32 val)
static inline void amd_spi_writereg32(struct amd_spi *amd_spi, int idx, u32 val)
{
struct amd_spi *amd_spi = spi_master_get_devdata(master);
iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
}
static inline void amd_spi_setclear_reg32(struct spi_master *master, int idx,
u32 set, u32 clear)
static inline void amd_spi_setclear_reg32(struct amd_spi *amd_spi, int idx, u32 set, u32 clear)
{
u32 tmp = amd_spi_readreg32(master, idx);
u32 tmp = amd_spi_readreg32(amd_spi, idx);
tmp = (tmp & ~clear) | set;
amd_spi_writereg32(master, idx, tmp);
amd_spi_writereg32(amd_spi, idx, tmp);
}
static void amd_spi_select_chip(struct spi_master *master)
static void amd_spi_select_chip(struct amd_spi *amd_spi, u8 cs)
{
struct amd_spi *amd_spi = spi_master_get_devdata(master);
u8 chip_select = amd_spi->chip_select;
amd_spi_setclear_reg8(master, AMD_SPI_ALT_CS_REG, chip_select,
AMD_SPI_ALT_CS_MASK);
amd_spi_setclear_reg8(amd_spi, AMD_SPI_ALT_CS_REG, cs, AMD_SPI_ALT_CS_MASK);
}
static void amd_spi_clear_fifo_ptr(struct spi_master *master)
static void amd_spi_clear_fifo_ptr(struct amd_spi *amd_spi)
{
amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR,
AMD_SPI_FIFO_CLEAR);
amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, AMD_SPI_FIFO_CLEAR);
}
static void amd_spi_set_opcode(struct spi_master *master, u8 cmd_opcode)
static void amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode)
{
amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, cmd_opcode,
AMD_SPI_OPCODE_MASK);
amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, cmd_opcode, AMD_SPI_OPCODE_MASK);
}
static inline void amd_spi_set_rx_count(struct spi_master *master,
u8 rx_count)
static inline void amd_spi_set_rx_count(struct amd_spi *amd_spi, u8 rx_count)
{
amd_spi_setclear_reg8(master, AMD_SPI_RX_COUNT_REG, rx_count, 0xff);
amd_spi_setclear_reg8(amd_spi, AMD_SPI_RX_COUNT_REG, rx_count, 0xff);
}
static inline void amd_spi_set_tx_count(struct spi_master *master,
u8 tx_count)
static inline void amd_spi_set_tx_count(struct amd_spi *amd_spi, u8 tx_count)
{
amd_spi_setclear_reg8(master, AMD_SPI_TX_COUNT_REG, tx_count, 0xff);
amd_spi_setclear_reg8(amd_spi, AMD_SPI_TX_COUNT_REG, tx_count, 0xff);
}
static inline int amd_spi_busy_wait(struct amd_spi *amd_spi)
static int amd_spi_busy_wait(struct amd_spi *amd_spi)
{
bool spi_busy;
int timeout = 100000;
/* poll for SPI bus to become idle */
spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr +
AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY;
while (spi_busy) {
while (amd_spi_readreg32(amd_spi, AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) {
usleep_range(10, 20);
if (timeout-- < 0)
return -ETIMEDOUT;
spi_busy = (ioread32((u8 __iomem *)amd_spi->io_remap_addr +
AMD_SPI_CTRL0_REG) & AMD_SPI_BUSY) == AMD_SPI_BUSY;
}
return 0;
}
static void amd_spi_execute_opcode(struct spi_master *master)
static int amd_spi_execute_opcode(struct amd_spi *amd_spi)
{
struct amd_spi *amd_spi = spi_master_get_devdata(master);
int ret;
ret = amd_spi_busy_wait(amd_spi);
if (ret)
return ret;
/* Set ExecuteOpCode bit in the CTRL0 register */
amd_spi_setclear_reg32(master, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD,
AMD_SPI_EXEC_CMD);
amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD, AMD_SPI_EXEC_CMD);
amd_spi_busy_wait(amd_spi);
return 0;
}
static int amd_spi_master_setup(struct spi_device *spi)
{
struct spi_master *master = spi->master;
struct amd_spi *amd_spi = spi_master_get_devdata(spi->master);
amd_spi_clear_fifo_ptr(master);
amd_spi_clear_fifo_ptr(amd_spi);
return 0;
}
@@ -185,19 +161,18 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
tx_len = xfer->len - 1;
cmd_opcode = *(u8 *)xfer->tx_buf;
buf++;
amd_spi_set_opcode(master, cmd_opcode);
amd_spi_set_opcode(amd_spi, cmd_opcode);
/* Write data into the FIFO. */
for (i = 0; i < tx_len; i++) {
iowrite8(buf[i],
((u8 __iomem *)amd_spi->io_remap_addr +
iowrite8(buf[i], ((u8 __iomem *)amd_spi->io_remap_addr +
AMD_SPI_FIFO_BASE + i));
}
amd_spi_set_tx_count(master, tx_len);
amd_spi_clear_fifo_ptr(master);
amd_spi_set_tx_count(amd_spi, tx_len);
amd_spi_clear_fifo_ptr(amd_spi);
/* Execute command */
amd_spi_execute_opcode(master);
amd_spi_execute_opcode(amd_spi);
}
if (m_cmd & AMD_SPI_XFER_RX) {
/*
@@ -206,15 +181,14 @@ static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
*/
rx_len = xfer->len;
buf = (u8 *)xfer->rx_buf;
amd_spi_set_rx_count(master, rx_len);
amd_spi_clear_fifo_ptr(master);
amd_spi_set_rx_count(amd_spi, rx_len);
amd_spi_clear_fifo_ptr(amd_spi);
/* Execute command */
amd_spi_execute_opcode(master);
amd_spi_execute_opcode(amd_spi);
amd_spi_busy_wait(amd_spi);
/* Read data from FIFO to receive buffer */
for (i = 0; i < rx_len; i++)
buf[i] = amd_spi_readreg8(master,
AMD_SPI_FIFO_BASE +
tx_len + i);
buf[i] = amd_spi_readreg8(amd_spi, AMD_SPI_FIFO_BASE + tx_len + i);
}
}
@@ -233,8 +207,7 @@ static int amd_spi_master_transfer(struct spi_master *master,
struct amd_spi *amd_spi = spi_master_get_devdata(master);
struct spi_device *spi = msg->spi;
amd_spi->chip_select = spi->chip_select;
amd_spi_select_chip(master);
amd_spi_select_chip(amd_spi, spi->chip_select);
/*
* Extract spi_transfers from the spi message and

View File

@@ -14,7 +14,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -482,29 +482,12 @@ static void at91_usart_spi_init(struct at91_usart_spi *aus)
static int at91_usart_gpio_setup(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.parent->of_node;
int i;
int ret;
int nb;
struct gpio_descs *cs_gpios;
if (!np)
return -EINVAL;
cs_gpios = devm_gpiod_get_array_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
nb = of_gpio_named_count(np, "cs-gpios");
for (i = 0; i < nb; i++) {
int cs_gpio = of_get_named_gpio(np, "cs-gpios", i);
if (cs_gpio < 0)
return cs_gpio;
if (gpio_is_valid(cs_gpio)) {
ret = devm_gpio_request_one(&pdev->dev, cs_gpio,
GPIOF_DIR_OUT,
dev_name(&pdev->dev));
if (ret)
return ret;
}
}
if (IS_ERR(cs_gpios))
return PTR_ERR(cs_gpios);
return 0;
}

View File

@@ -83,6 +83,9 @@
/* MSPI register offsets */
#define MSPI_SPCR0_LSB 0x000
#define MSPI_SPCR0_MSB 0x004
#define MSPI_SPCR0_MSB_CPHA BIT(0)
#define MSPI_SPCR0_MSB_CPOL BIT(1)
#define MSPI_SPCR0_MSB_BITS_SHIFT 0x2
#define MSPI_SPCR1_LSB 0x008
#define MSPI_SPCR1_MSB 0x00c
#define MSPI_NEWQP 0x010
@@ -100,8 +103,10 @@
#define MSPI_MASTER_BIT BIT(7)
#define MSPI_NUM_CDRAM 16
#define MSPI_CDRAM_OUTP BIT(8)
#define MSPI_CDRAM_CONT_BIT BIT(7)
#define MSPI_CDRAM_BITSE_BIT BIT(6)
#define MSPI_CDRAM_DT_BIT BIT(5)
#define MSPI_CDRAM_PCS 0xf
#define MSPI_SPCR2_SPE BIT(6)
@@ -114,6 +119,14 @@
~(BIT(10) | BIT(11)))
#define MSPI_SPCR3_SYSCLKSEL_108 (MSPI_SPCR3_SYSCLKSEL_MASK & \
BIT(11))
#define MSPI_SPCR3_TXRXDAM_MASK GENMASK(4, 2)
#define MSPI_SPCR3_DAM_8BYTE 0
#define MSPI_SPCR3_DAM_16BYTE (BIT(2) | BIT(4))
#define MSPI_SPCR3_DAM_32BYTE (BIT(3) | BIT(5))
#define MSPI_SPCR3_HALFDUPLEX BIT(6)
#define MSPI_SPCR3_HDOUTTYPE BIT(7)
#define MSPI_SPCR3_DATA_REG_SZ BIT(8)
#define MSPI_SPCR3_CPHARX BIT(9)
#define MSPI_MSPI_STATUS_SPIF BIT(0)
@@ -153,6 +166,14 @@
#define TRANS_STATUS_BREAK_DESELECT (TRANS_STATUS_BREAK_EOM | \
TRANS_STATUS_BREAK_CS_CHANGE)
/*
* Used for writing and reading data in the right order
* to TXRAM and RXRAM when used as 32-bit registers respectively
*/
#define swap4bytes(__val) \
((((__val) >> 24) & 0x000000FF) | (((__val) >> 8) & 0x0000FF00) | \
(((__val) << 8) & 0x00FF0000) | (((__val) << 24) & 0xFF000000))
struct bcm_qspi_parms {
u32 speed_hz;
u8 mode;
@@ -261,7 +282,7 @@ static inline bool bcm_qspi_has_sysclk_108(struct bcm_qspi *qspi)
static inline int bcm_qspi_spbr_min(struct bcm_qspi *qspi)
{
if (bcm_qspi_has_fastbr(qspi))
return 1;
return (bcm_qspi_has_sysclk_108(qspi) ? 4 : 1);
else
return 8;
}
@@ -395,7 +416,8 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
if (addrlen == BSPI_ADDRLEN_4BYTES)
bpp = BSPI_BPP_ADDR_SELECT_MASK;
bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth;
if (op->dummy.nbytes)
bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth;
switch (width) {
case SPI_NBITS_SINGLE:
@@ -570,23 +592,23 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi,
{
u32 spcr, spbr = 0;
if (xp->speed_hz)
spbr = qspi->base_clk / (2 * xp->speed_hz);
spcr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX);
bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spcr);
if (!qspi->mspi_maj_rev)
/* legacy controller */
spcr = MSPI_MASTER_BIT;
else
spcr = 0;
/* for 16 bit the data should be zero */
if (xp->bits_per_word != 16)
spcr |= xp->bits_per_word << 2;
spcr |= xp->mode & 3;
/*
* Bits per transfer. BITS determines the number of data bits
* transferred if the command control bit (BITSE of a
* CDRAM Register) is equal to 1.
* If CDRAM BITSE is equal to 0, 8 data bits are transferred
* regardless
*/
if (xp->bits_per_word != 16 && xp->bits_per_word != 64)
spcr |= xp->bits_per_word << MSPI_SPCR0_MSB_BITS_SHIFT;
spcr |= xp->mode & (MSPI_SPCR0_MSB_CPHA | MSPI_SPCR0_MSB_CPOL);
bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_MSB, spcr);
if (bcm_qspi_has_fastbr(qspi)) {
@@ -595,17 +617,44 @@ static void bcm_qspi_hw_set_parms(struct bcm_qspi *qspi,
/* enable fastbr */
spcr |= MSPI_SPCR3_FASTBR;
if (xp->mode & SPI_3WIRE)
spcr |= MSPI_SPCR3_HALFDUPLEX | MSPI_SPCR3_HDOUTTYPE;
if (bcm_qspi_has_sysclk_108(qspi)) {
/* SYSCLK_108 */
spcr |= MSPI_SPCR3_SYSCLKSEL_108;
qspi->base_clk = MSPI_BASE_FREQ * 4;
/* Change spbr as we changed sysclk */
bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, 4);
}
if (xp->bits_per_word > 16) {
/* data_reg_size 1 (64bit) */
spcr |= MSPI_SPCR3_DATA_REG_SZ;
/* TxRx RAM data access mode 2 for 32B and set fastdt */
spcr |= MSPI_SPCR3_DAM_32BYTE | MSPI_SPCR3_FASTDT;
/*
* Set length of delay after transfer
* DTL from 0(256) to 1
*/
bcm_qspi_write(qspi, MSPI, MSPI_SPCR1_LSB, 1);
} else {
/* data_reg_size[8] = 0 */
spcr &= ~(MSPI_SPCR3_DATA_REG_SZ);
/*
* TxRx RAM access mode 8B
* and disable fastdt
*/
spcr &= ~(MSPI_SPCR3_DAM_32BYTE);
}
bcm_qspi_write(qspi, MSPI, MSPI_SPCR3, spcr);
}
if (xp->speed_hz)
spbr = qspi->base_clk / (2 * xp->speed_hz);
spbr = clamp_val(spbr, bcm_qspi_spbr_min(qspi), QSPI_SPBR_MAX);
bcm_qspi_write(qspi, MSPI, MSPI_SPCR0_LSB, spbr);
qspi->last_parms = *xp;
}
@@ -626,7 +675,7 @@ static int bcm_qspi_setup(struct spi_device *spi)
{
struct bcm_qspi_parms *xp;
if (spi->bits_per_word > 16)
if (spi->bits_per_word > 64)
return -EINVAL;
xp = spi_get_ctldata(spi);
@@ -665,8 +714,12 @@ static int update_qspi_trans_byte_count(struct bcm_qspi *qspi,
/* count the last transferred bytes */
if (qt->trans->bits_per_word <= 8)
qt->byte++;
else
else if (qt->trans->bits_per_word <= 16)
qt->byte += 2;
else if (qt->trans->bits_per_word <= 32)
qt->byte += 4;
else if (qt->trans->bits_per_word <= 64)
qt->byte += 8;
if (qt->byte >= qt->trans->len) {
/* we're at the end of the spi_transfer */
@@ -709,6 +762,33 @@ static inline u16 read_rxram_slot_u16(struct bcm_qspi *qspi, int slot)
((bcm_qspi_read(qspi, MSPI, msb_offset) & 0xff) << 8);
}
static inline u32 read_rxram_slot_u32(struct bcm_qspi *qspi, int slot)
{
u32 reg_offset = MSPI_RXRAM;
u32 offset = reg_offset + (slot << 3);
u32 val;
val = bcm_qspi_read(qspi, MSPI, offset);
val = swap4bytes(val);
return val;
}
static inline u64 read_rxram_slot_u64(struct bcm_qspi *qspi, int slot)
{
u32 reg_offset = MSPI_RXRAM;
u32 lsb_offset = reg_offset + (slot << 3) + 0x4;
u32 msb_offset = reg_offset + (slot << 3);
u32 msb, lsb;
msb = bcm_qspi_read(qspi, MSPI, msb_offset);
msb = swap4bytes(msb);
lsb = bcm_qspi_read(qspi, MSPI, lsb_offset);
lsb = swap4bytes(lsb);
return ((u64)msb << 32 | lsb);
}
static void read_from_hw(struct bcm_qspi *qspi, int slots)
{
struct qspi_trans tp;
@@ -732,7 +812,7 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots)
buf[tp.byte] = read_rxram_slot_u8(qspi, slot);
dev_dbg(&qspi->pdev->dev, "RD %02x\n",
buf ? buf[tp.byte] : 0x0);
} else {
} else if (tp.trans->bits_per_word <= 16) {
u16 *buf = tp.trans->rx_buf;
if (buf)
@@ -740,6 +820,25 @@ static void read_from_hw(struct bcm_qspi *qspi, int slots)
slot);
dev_dbg(&qspi->pdev->dev, "RD %04x\n",
buf ? buf[tp.byte / 2] : 0x0);
} else if (tp.trans->bits_per_word <= 32) {
u32 *buf = tp.trans->rx_buf;
if (buf)
buf[tp.byte / 4] = read_rxram_slot_u32(qspi,
slot);
dev_dbg(&qspi->pdev->dev, "RD %08x\n",
buf ? buf[tp.byte / 4] : 0x0);
} else if (tp.trans->bits_per_word <= 64) {
u64 *buf = tp.trans->rx_buf;
if (buf)
buf[tp.byte / 8] = read_rxram_slot_u64(qspi,
slot);
dev_dbg(&qspi->pdev->dev, "RD %llx\n",
buf ? buf[tp.byte / 8] : 0x0);
}
update_qspi_trans_byte_count(qspi, &tp,
@@ -769,6 +868,28 @@ static inline void write_txram_slot_u16(struct bcm_qspi *qspi, int slot,
bcm_qspi_write(qspi, MSPI, lsb_offset, (val & 0xff));
}
static inline void write_txram_slot_u32(struct bcm_qspi *qspi, int slot,
u32 val)
{
u32 reg_offset = MSPI_TXRAM;
u32 msb_offset = reg_offset + (slot << 3);
bcm_qspi_write(qspi, MSPI, msb_offset, swap4bytes(val));
}
static inline void write_txram_slot_u64(struct bcm_qspi *qspi, int slot,
u64 val)
{
u32 reg_offset = MSPI_TXRAM;
u32 msb_offset = reg_offset + (slot << 3);
u32 lsb_offset = reg_offset + (slot << 3) + 0x4;
u32 msb = upper_32_bits(val);
u32 lsb = lower_32_bits(val);
bcm_qspi_write(qspi, MSPI, msb_offset, swap4bytes(msb));
bcm_qspi_write(qspi, MSPI, lsb_offset, swap4bytes(lsb));
}
static inline u32 read_cdram_slot(struct bcm_qspi *qspi, int slot)
{
return bcm_qspi_read(qspi, MSPI, MSPI_CDRAM + (slot << 2));
@@ -792,20 +913,43 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
/* Run until end of transfer or reached the max data */
while (!tstatus && slot < MSPI_NUM_CDRAM) {
mspi_cdram = MSPI_CDRAM_CONT_BIT;
if (tp.trans->bits_per_word <= 8) {
const u8 *buf = tp.trans->tx_buf;
u8 val = buf ? buf[tp.byte] : 0x00;
write_txram_slot_u8(qspi, slot, val);
dev_dbg(&qspi->pdev->dev, "WR %02x\n", val);
} else {
} else if (tp.trans->bits_per_word <= 16) {
const u16 *buf = tp.trans->tx_buf;
u16 val = buf ? buf[tp.byte / 2] : 0x0000;
write_txram_slot_u16(qspi, slot, val);
dev_dbg(&qspi->pdev->dev, "WR %04x\n", val);
} else if (tp.trans->bits_per_word <= 32) {
const u32 *buf = tp.trans->tx_buf;
u32 val = buf ? buf[tp.byte/4] : 0x0;
write_txram_slot_u32(qspi, slot, val);
dev_dbg(&qspi->pdev->dev, "WR %08x\n", val);
} else if (tp.trans->bits_per_word <= 64) {
const u64 *buf = tp.trans->tx_buf;
u64 val = (buf ? buf[tp.byte/8] : 0x0);
/* use the length of delay from SPCR1_LSB */
if (bcm_qspi_has_fastbr(qspi))
mspi_cdram |= MSPI_CDRAM_DT_BIT;
write_txram_slot_u64(qspi, slot, val);
dev_dbg(&qspi->pdev->dev, "WR %llx\n", val);
}
mspi_cdram = MSPI_CDRAM_CONT_BIT;
mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 :
MSPI_CDRAM_BITSE_BIT);
/* set 3wrire halfduplex mode data from master to slave */
if ((spi->mode & SPI_3WIRE) && tp.trans->tx_buf)
mspi_cdram |= MSPI_CDRAM_OUTP;
if (has_bspi(qspi))
mspi_cdram &= ~1;
@@ -813,9 +957,6 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
mspi_cdram |= (~(1 << spi->chip_select) &
MSPI_CDRAM_PCS);
mspi_cdram |= ((tp.trans->bits_per_word <= 8) ? 0 :
MSPI_CDRAM_BITSE_BIT);
write_cdram_slot(qspi, slot, mspi_cdram);
tstatus = update_qspi_trans_byte_count(qspi, &tp,
@@ -1350,7 +1491,8 @@ int bcm_qspi_probe(struct platform_device *pdev,
qspi->master = master;
master->bus_num = -1;
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD;
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD |
SPI_3WIRE;
master->setup = bcm_qspi_setup;
master->transfer_one = bcm_qspi_transfer_one;
master->mem_ops = &bcm_qspi_mem_ops;
@@ -1460,7 +1602,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
&qspi->dev_ids[val]);
if (ret < 0) {
dev_err(&pdev->dev, "IRQ %s not found\n", name);
goto qspi_probe_err;
goto qspi_unprepare_err;
}
qspi->dev_ids[val].dev = qspi;
@@ -1475,7 +1617,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
if (!num_ints) {
dev_err(&pdev->dev, "no IRQs registered, cannot init driver\n");
ret = -EINVAL;
goto qspi_probe_err;
goto qspi_unprepare_err;
}
bcm_qspi_hw_init(qspi);
@@ -1499,6 +1641,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
qspi_reg_err:
bcm_qspi_hw_uninit(qspi);
qspi_unprepare_err:
clk_disable_unprepare(qspi->clk);
qspi_probe_err:
kfree(qspi->dev_ids);

View File

@@ -13,6 +13,7 @@
#include <linux/dmaengine.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/firmware/xlnx-zynqmp.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -35,6 +36,7 @@
/* Quirks */
#define CQSPI_NEEDS_WR_DELAY BIT(0)
#define CQSPI_DISABLE_DAC_MODE BIT(1)
#define CQSPI_SUPPORT_EXTERNAL_DMA BIT(2)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
@@ -82,11 +84,16 @@ struct cqspi_st {
u32 wr_delay;
bool use_direct_mode;
struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT];
bool use_dma_read;
u32 pd_dev_id;
};
struct cqspi_driver_platdata {
u32 hwcaps_mask;
u8 quirks;
int (*indirect_read_dma)(struct cqspi_flash_pdata *f_pdata,
u_char *rxbuf, loff_t from_addr, size_t n_rx);
u32 (*get_dma_status)(struct cqspi_st *cqspi);
};
/* Operation timeout value */
@@ -217,6 +224,8 @@ struct cqspi_driver_platdata {
#define CQSPI_REG_INDIRECTWRSTARTADDR 0x78
#define CQSPI_REG_INDIRECTWRBYTES 0x7C
#define CQSPI_REG_INDTRIG_ADDRRANGE 0x80
#define CQSPI_REG_CMDADDRESS 0x94
#define CQSPI_REG_CMDREADDATALOWER 0xA0
#define CQSPI_REG_CMDREADDATAUPPER 0xA4
@@ -231,6 +240,23 @@ struct cqspi_driver_platdata {
#define CQSPI_REG_OP_EXT_WRITE_LSB 16
#define CQSPI_REG_OP_EXT_STIG_LSB 0
#define CQSPI_REG_VERSAL_DMA_SRC_ADDR 0x1000
#define CQSPI_REG_VERSAL_DMA_DST_ADDR 0x1800
#define CQSPI_REG_VERSAL_DMA_DST_SIZE 0x1804
#define CQSPI_REG_VERSAL_DMA_DST_CTRL 0x180C
#define CQSPI_REG_VERSAL_DMA_DST_I_STS 0x1814
#define CQSPI_REG_VERSAL_DMA_DST_I_EN 0x1818
#define CQSPI_REG_VERSAL_DMA_DST_I_DIS 0x181C
#define CQSPI_REG_VERSAL_DMA_DST_DONE_MASK BIT(1)
#define CQSPI_REG_VERSAL_DMA_DST_ADDR_MSB 0x1828
#define CQSPI_REG_VERSAL_DMA_DST_CTRL_VAL 0xF43FFA00
#define CQSPI_REG_VERSAL_ADDRRANGE_WIDTH_VAL 0x6
/* Interrupt status bits */
#define CQSPI_REG_IRQ_MODE_ERR BIT(0)
#define CQSPI_REG_IRQ_UNDERFLOW BIT(1)
@@ -250,6 +276,9 @@ struct cqspi_driver_platdata {
CQSPI_REG_IRQ_UNDERFLOW)
#define CQSPI_IRQ_STATUS_MASK 0x1FFFF
#define CQSPI_DMA_UNALIGN 0x3
#define CQSPI_REG_VERSAL_DMA_VAL 0x602
static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr)
{
@@ -275,10 +304,26 @@ static u32 cqspi_get_rd_sram_level(struct cqspi_st *cqspi)
return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK;
}
static u32 cqspi_get_versal_dma_status(struct cqspi_st *cqspi)
{
u32 dma_status;
dma_status = readl(cqspi->iobase +
CQSPI_REG_VERSAL_DMA_DST_I_STS);
writel(dma_status, cqspi->iobase +
CQSPI_REG_VERSAL_DMA_DST_I_STS);
return dma_status & CQSPI_REG_VERSAL_DMA_DST_DONE_MASK;
}
static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)
{
struct cqspi_st *cqspi = dev;
unsigned int irq_status;
struct device *device = &cqspi->pdev->dev;
const struct cqspi_driver_platdata *ddata;
ddata = of_device_get_match_data(device);
/* Read interrupt status */
irq_status = readl(cqspi->iobase + CQSPI_REG_IRQSTATUS);
@@ -286,6 +331,13 @@ static irqreturn_t cqspi_irq_handler(int this_irq, void *dev)
/* Clear interrupt */
writel(irq_status, cqspi->iobase + CQSPI_REG_IRQSTATUS);
if (cqspi->use_dma_read && ddata && ddata->get_dma_status) {
if (ddata->get_dma_status(cqspi)) {
complete(&cqspi->transfer_complete);
return IRQ_HANDLED;
}
}
irq_status &= CQSPI_IRQ_MASK_RD | CQSPI_IRQ_MASK_WR;
if (irq_status)
@@ -781,6 +833,131 @@ failrd:
return ret;
}
static int cqspi_versal_indirect_read_dma(struct cqspi_flash_pdata *f_pdata,
u_char *rxbuf, loff_t from_addr,
size_t n_rx)
{
struct cqspi_st *cqspi = f_pdata->cqspi;
struct device *dev = &cqspi->pdev->dev;
void __iomem *reg_base = cqspi->iobase;
u32 reg, bytes_to_dma;
loff_t addr = from_addr;
void *buf = rxbuf;
dma_addr_t dma_addr;
u8 bytes_rem;
int ret = 0;
bytes_rem = n_rx % 4;
bytes_to_dma = (n_rx - bytes_rem);
if (!bytes_to_dma)
goto nondmard;
ret = zynqmp_pm_ospi_mux_select(cqspi->pd_dev_id, PM_OSPI_MUX_SEL_DMA);
if (ret)
return ret;
reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
reg |= CQSPI_REG_CONFIG_DMA_MASK;
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
dma_addr = dma_map_single(dev, rxbuf, bytes_to_dma, DMA_FROM_DEVICE);
if (dma_mapping_error(dev, dma_addr)) {
dev_err(dev, "dma mapping failed\n");
return -ENOMEM;
}
writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
writel(bytes_to_dma, reg_base + CQSPI_REG_INDIRECTRDBYTES);
writel(CQSPI_REG_VERSAL_ADDRRANGE_WIDTH_VAL,
reg_base + CQSPI_REG_INDTRIG_ADDRRANGE);
/* Clear all interrupts. */
writel(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS);
/* Enable DMA done interrupt */
writel(CQSPI_REG_VERSAL_DMA_DST_DONE_MASK,
reg_base + CQSPI_REG_VERSAL_DMA_DST_I_EN);
/* Default DMA periph configuration */
writel(CQSPI_REG_VERSAL_DMA_VAL, reg_base + CQSPI_REG_DMA);
/* Configure DMA Dst address */
writel(lower_32_bits(dma_addr),
reg_base + CQSPI_REG_VERSAL_DMA_DST_ADDR);
writel(upper_32_bits(dma_addr),
reg_base + CQSPI_REG_VERSAL_DMA_DST_ADDR_MSB);
/* Configure DMA Src address */
writel(cqspi->trigger_address, reg_base +
CQSPI_REG_VERSAL_DMA_SRC_ADDR);
/* Set DMA destination size */
writel(bytes_to_dma, reg_base + CQSPI_REG_VERSAL_DMA_DST_SIZE);
/* Set DMA destination control */
writel(CQSPI_REG_VERSAL_DMA_DST_CTRL_VAL,
reg_base + CQSPI_REG_VERSAL_DMA_DST_CTRL);
writel(CQSPI_REG_INDIRECTRD_START_MASK,
reg_base + CQSPI_REG_INDIRECTRD);
reinit_completion(&cqspi->transfer_complete);
if (!wait_for_completion_timeout(&cqspi->transfer_complete,
msecs_to_jiffies(CQSPI_READ_TIMEOUT_MS))) {
ret = -ETIMEDOUT;
goto failrd;
}
/* Disable DMA interrupt */
writel(0x0, cqspi->iobase + CQSPI_REG_VERSAL_DMA_DST_I_DIS);
/* Clear indirect completion status */
writel(CQSPI_REG_INDIRECTRD_DONE_MASK,
cqspi->iobase + CQSPI_REG_INDIRECTRD);
dma_unmap_single(dev, dma_addr, bytes_to_dma, DMA_FROM_DEVICE);
reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
reg &= ~CQSPI_REG_CONFIG_DMA_MASK;
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
ret = zynqmp_pm_ospi_mux_select(cqspi->pd_dev_id,
PM_OSPI_MUX_SEL_LINEAR);
if (ret)
return ret;
nondmard:
if (bytes_rem) {
addr += bytes_to_dma;
buf += bytes_to_dma;
ret = cqspi_indirect_read_execute(f_pdata, buf, addr,
bytes_rem);
if (ret)
return ret;
}
return 0;
failrd:
/* Disable DMA interrupt */
writel(0x0, reg_base + CQSPI_REG_VERSAL_DMA_DST_I_DIS);
/* Cancel the indirect read */
writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK,
reg_base + CQSPI_REG_INDIRECTRD);
dma_unmap_single(dev, dma_addr, bytes_to_dma, DMA_FROM_DEVICE);
reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
reg &= ~CQSPI_REG_CONFIG_DMA_MASK;
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
zynqmp_pm_ospi_mux_select(cqspi->pd_dev_id, PM_OSPI_MUX_SEL_LINEAR);
return ret;
}
static int cqspi_write_setup(struct cqspi_flash_pdata *f_pdata,
const struct spi_mem_op *op)
{
@@ -1180,11 +1357,15 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata,
const struct spi_mem_op *op)
{
struct cqspi_st *cqspi = f_pdata->cqspi;
struct device *dev = &cqspi->pdev->dev;
const struct cqspi_driver_platdata *ddata;
loff_t from = op->addr.val;
size_t len = op->data.nbytes;
u_char *buf = op->data.buf.in;
u64 dma_align = (u64)(uintptr_t)buf;
int ret;
ddata = of_device_get_match_data(dev);
ret = cqspi_set_protocol(f_pdata, op);
if (ret)
return ret;
@@ -1196,6 +1377,10 @@ static ssize_t cqspi_read(struct cqspi_flash_pdata *f_pdata,
if (cqspi->use_direct_mode && ((from + len) <= cqspi->ahb_size))
return cqspi_direct_read_execute(f_pdata, buf, from, len);
if (cqspi->use_dma_read && ddata && ddata->indirect_read_dma &&
virt_addr_valid(buf) && ((dma_align & CQSPI_DMA_UNALIGN) == 0))
return ddata->indirect_read_dma(f_pdata, buf, from, len);
return cqspi_indirect_read_execute(f_pdata, buf, from, len);
}
@@ -1299,6 +1484,7 @@ static int cqspi_of_get_pdata(struct cqspi_st *cqspi)
{
struct device *dev = &cqspi->pdev->dev;
struct device_node *np = dev->of_node;
u32 id[2];
cqspi->is_decoded_cs = of_property_read_bool(np, "cdns,is-decoded-cs");
@@ -1323,6 +1509,10 @@ static int cqspi_of_get_pdata(struct cqspi_st *cqspi)
cqspi->rclk_en = of_property_read_bool(np, "cdns,rclk-en");
if (!of_property_read_u32_array(np, "power-domains", id,
ARRAY_SIZE(id)))
cqspi->pd_dev_id = id[1];
return 0;
}
@@ -1359,6 +1549,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
}
/* Enable DMA interface */
if (cqspi->use_dma_read) {
reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
reg |= CQSPI_REG_CONFIG_DMA_MASK;
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
}
cqspi_controller_enable(cqspi, 1);
}
@@ -1548,6 +1745,12 @@ static int cqspi_probe(struct platform_device *pdev)
master->mode_bits |= SPI_RX_OCTAL | SPI_TX_OCTAL;
if (!(ddata->quirks & CQSPI_DISABLE_DAC_MODE))
cqspi->use_direct_mode = true;
if (ddata->quirks & CQSPI_SUPPORT_EXTERNAL_DMA)
cqspi->use_dma_read = true;
if (of_device_is_compatible(pdev->dev.of_node,
"xlnx,versal-ospi-1.0"))
dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
}
ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0,
@@ -1656,6 +1859,13 @@ static const struct cqspi_driver_platdata intel_lgm_qspi = {
.quirks = CQSPI_DISABLE_DAC_MODE,
};
static const struct cqspi_driver_platdata versal_ospi = {
.hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
.quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA,
.indirect_read_dma = cqspi_versal_indirect_read_dma,
.get_dma_status = cqspi_get_versal_dma_status,
};
static const struct of_device_id cqspi_dt_ids[] = {
{
.compatible = "cdns,qspi-nor",
@@ -1673,6 +1883,10 @@ static const struct of_device_id cqspi_dt_ids[] = {
.compatible = "intel,lgm-qspi",
.data = &intel_lgm_qspi,
},
{
.compatible = "xlnx,versal-ospi-1.0",
.data = (void *)&versal_ospi,
},
{ /* end of table */ }
};

File diff suppressed because it is too large Load Diff

View File

@@ -67,9 +67,14 @@
SPI_FSI_STATUS_RDR_OVERRUN)
#define SPI_FSI_PORT_CTRL 0x9
struct fsi2spi {
struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
struct mutex lock; /* lock access to the device */
};
struct fsi_spi {
struct device *dev; /* SPI controller device */
struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
struct fsi2spi *bridge; /* FSI2SPI device */
u32 base;
};
@@ -104,7 +109,7 @@ static int fsi_spi_check_status(struct fsi_spi *ctx)
u32 sts;
__be32 sts_be;
rc = fsi_device_read(ctx->fsi, FSI2SPI_STATUS, &sts_be,
rc = fsi_device_read(ctx->bridge->fsi, FSI2SPI_STATUS, &sts_be,
sizeof(sts_be));
if (rc)
return rc;
@@ -120,73 +125,91 @@ static int fsi_spi_check_status(struct fsi_spi *ctx)
static int fsi_spi_read_reg(struct fsi_spi *ctx, u32 offset, u64 *value)
{
int rc;
int rc = 0;
__be32 cmd_be;
__be32 data_be;
u32 cmd = offset + ctx->base;
struct fsi2spi *bridge = ctx->bridge;
*value = 0ULL;
if (cmd & FSI2SPI_CMD_WRITE)
return -EINVAL;
cmd_be = cpu_to_be32(cmd);
rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be));
rc = mutex_lock_interruptible(&bridge->lock);
if (rc)
return rc;
cmd_be = cpu_to_be32(cmd);
rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be,
sizeof(cmd_be));
if (rc)
goto unlock;
rc = fsi_spi_check_status(ctx);
if (rc)
return rc;
goto unlock;
rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA0, &data_be,
rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA0, &data_be,
sizeof(data_be));
if (rc)
return rc;
goto unlock;
*value |= (u64)be32_to_cpu(data_be) << 32;
rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA1, &data_be,
rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA1, &data_be,
sizeof(data_be));
if (rc)
return rc;
goto unlock;
*value |= (u64)be32_to_cpu(data_be);
dev_dbg(ctx->dev, "Read %02x[%016llx].\n", offset, *value);
return 0;
unlock:
mutex_unlock(&bridge->lock);
return rc;
}
static int fsi_spi_write_reg(struct fsi_spi *ctx, u32 offset, u64 value)
{
int rc;
int rc = 0;
__be32 cmd_be;
__be32 data_be;
u32 cmd = offset + ctx->base;
struct fsi2spi *bridge = ctx->bridge;
if (cmd & FSI2SPI_CMD_WRITE)
return -EINVAL;
rc = mutex_lock_interruptible(&bridge->lock);
if (rc)
return rc;
dev_dbg(ctx->dev, "Write %02x[%016llx].\n", offset, value);
data_be = cpu_to_be32(upper_32_bits(value));
rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA0, &data_be,
rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA0, &data_be,
sizeof(data_be));
if (rc)
return rc;
goto unlock;
data_be = cpu_to_be32(lower_32_bits(value));
rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA1, &data_be,
rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA1, &data_be,
sizeof(data_be));
if (rc)
return rc;
goto unlock;
cmd_be = cpu_to_be32(cmd | FSI2SPI_CMD_WRITE);
rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be));
rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be,
sizeof(cmd_be));
if (rc)
return rc;
goto unlock;
return fsi_spi_check_status(ctx);
rc = fsi_spi_check_status(ctx);
unlock:
mutex_unlock(&bridge->lock);
return rc;
}
static int fsi_spi_data_in(u64 in, u8 *rx, int len)
@@ -234,6 +257,26 @@ static int fsi_spi_reset(struct fsi_spi *ctx)
return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL);
}
static int fsi_spi_status(struct fsi_spi *ctx, u64 *status, const char *dir)
{
int rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, status);
if (rc)
return rc;
if (*status & SPI_FSI_STATUS_ANY_ERROR) {
dev_err(ctx->dev, "%s error: %016llx\n", dir, *status);
rc = fsi_spi_reset(ctx);
if (rc)
return rc;
return -EREMOTEIO;
}
return 0;
}
static void fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
{
/*
@@ -273,18 +316,9 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
return rc;
do {
rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
&status);
rc = fsi_spi_status(ctx, &status, "TX");
if (rc)
return rc;
if (status & SPI_FSI_STATUS_ANY_ERROR) {
rc = fsi_spi_reset(ctx);
if (rc)
return rc;
return -EREMOTEIO;
}
} while (status & SPI_FSI_STATUS_TDR_FULL);
sent += nb;
@@ -296,18 +330,9 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
while (transfer->len > recv) {
do {
rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
&status);
rc = fsi_spi_status(ctx, &status, "RX");
if (rc)
return rc;
if (status & SPI_FSI_STATUS_ANY_ERROR) {
rc = fsi_spi_reset(ctx);
if (rc)
return rc;
return -EREMOTEIO;
}
} while (!(status & SPI_FSI_STATUS_RDR_FULL));
rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in);
@@ -348,8 +373,12 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx)
if (status & (SPI_FSI_STATUS_ANY_ERROR |
SPI_FSI_STATUS_TDR_FULL |
SPI_FSI_STATUS_RDR_FULL)) {
if (reset)
if (reset) {
dev_err(ctx->dev,
"Initialization error: %08llx\n",
status);
return -EIO;
}
rc = fsi_spi_reset(ctx);
if (rc)
@@ -388,7 +417,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
struct spi_transfer *transfer;
struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
rc = fsi_spi_check_mux(ctx->fsi, ctx->dev);
rc = fsi_spi_check_mux(ctx->bridge->fsi, ctx->dev);
if (rc)
goto error;
@@ -478,12 +507,20 @@ static int fsi_spi_probe(struct device *dev)
int rc;
struct device_node *np;
int num_controllers_registered = 0;
struct fsi2spi *bridge;
struct fsi_device *fsi = to_fsi_dev(dev);
rc = fsi_spi_check_mux(fsi, dev);
if (rc)
return -ENODEV;
bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
if (!bridge)
return -ENOMEM;
bridge->fsi = fsi;
mutex_init(&bridge->lock);
for_each_available_child_of_node(dev->of_node, np) {
u32 base;
struct fsi_spi *ctx;
@@ -506,7 +543,7 @@ static int fsi_spi_probe(struct device *dev)
ctx = spi_controller_get_devdata(ctlr);
ctx->dev = &ctlr->dev;
ctx->fsi = fsi;
ctx->bridge = bridge;
ctx->base = base + SPI_FSI_BASE;
rc = devm_spi_register_controller(dev, ctlr);

Some files were not shown because too many files have changed in this diff Show More