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

Pull spi updates from Mark Brown:
 "This release is almost entirely new drivers, with a couple of small
  changes in generic code.

  The biggest individual update is a rename of the existing Microchip
  driver and the addition of a new driver for the silicon SPI controller
  in their PolarFire SoCs. The overlap between the soft IP supported by
  the current driver and this new one is regrettably all in the IP and
  not in the register interface offered to software.

   - Add a time offset parameter for offloads, allowing them to be
     defined in relation to each other. This is useful for IIO type
     applcations where you trigger an operation then read the result
     after a delay.

   - Add a tracepoint for flash exec_ops, bringing the flash support
     more in line with the debuggability of vanilla SPI.

   - Support for Airoha EN7523, Arduino MCUs, Aspeed AST2700, Microchip
     PolarFire SPI controllers, NXP i.MX51 ECSPI target mode, Qualcomm
     IPQ5414 and IPQ5332, Renesas RZ/T2H, RZ/V2N and RZ/2NH and SpacemiT
     K1 QuadSPI.

  There's also a small set of ASoC cleanups that I mistakenly applied to
  the SPI tree and then put more stuff on top of before it was brought
  to my attention, sorry about that"

* tag 'spi-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (80 commits)
  spi: microchip-core: Refactor FIFO read and write handlers
  spi: ch341: fix out-of-bounds memory access in ch341_transfer_one
  spi: microchip-core: Remove unneeded PM related macro
  spi: microchip-core: Use SPI_MODE_X_MASK
  spi: microchip-core: Utilise temporary variable for struct device
  spi: microchip-core: Replace dead code (-ENOMEM error message)
  spi: microchip-core: use min() instead of min_t()
  spi: dt-bindings: airoha: add compatible for EN7523
  spi: airoha-snfi: en7523: workaround flash damaging if UART_TXD was short to GND
  spi: dt-bindings: renesas,rzv2h-rspi: Document RZ/V2N SoC support
  spi: dt-bindings: renesas,rzv2h-rspi: Document RZ/V2N SoC support
  spi: microchip: Enable compile-testing for FPGA SPI controllers
  spi: Fix potential uninitialized variable in probe()
  spi: rzv2h-rspi: add support for RZ/T2H and RZ/N2H
  spi: dt-bindings: renesas,rzv2h-rspi: document RZ/T2H and RZ/N2H
  spi: rzv2h-rspi: add support for loopback mode
  spi: rzv2h-rspi: add support for variable transfer clock
  spi: rzv2h-rspi: add support for using PCLK for transfer clock
  spi: rzv2h-rspi: make transfer clock rate finding chip-specific
  spi: rzv2h-rspi: avoid recomputing transfer frequency
  ...
This commit is contained in:
Linus Torvalds
2025-12-04 11:24:24 -08:00
42 changed files with 2496 additions and 908 deletions

View File

@@ -14,7 +14,12 @@ allOf:
properties:
compatible:
const: airoha,en7581-snand
oneOf:
- const: airoha,en7581-snand
- items:
- enum:
- airoha,en7523-snand
- const: airoha,en7581-snand
reg:
items:

View File

@@ -12,7 +12,7 @@ maintainers:
description: |
This binding describes the Aspeed Static Memory Controllers (FMC and
SPI) of the AST2400, AST2500 and AST2600 SOCs.
SPI) of the AST2400, AST2500, AST2600 and AST2700 SOCs.
allOf:
- $ref: spi-controller.yaml#
@@ -20,6 +20,8 @@ allOf:
properties:
compatible:
enum:
- aspeed,ast2700-fmc
- aspeed,ast2700-spi
- aspeed,ast2600-fmc
- aspeed,ast2600-spi
- aspeed,ast2500-fmc

View File

@@ -9,9 +9,6 @@ title: Freescale Quad Serial Peripheral Interface (QuadSPI)
maintainers:
- Han Xu <han.xu@nxp.com>
allOf:
- $ref: spi-controller.yaml#
properties:
compatible:
oneOf:
@@ -22,6 +19,7 @@ properties:
- fsl,imx6ul-qspi
- fsl,ls1021a-qspi
- fsl,ls2080a-qspi
- spacemit,k1-qspi
- items:
- enum:
- fsl,ls1043a-qspi
@@ -54,6 +52,11 @@ properties:
- const: qspi_en
- const: qspi
resets:
items:
- description: SoC QSPI reset
- description: SoC QSPI bus reset
required:
- compatible
- reg
@@ -62,6 +65,18 @@ required:
- clocks
- clock-names
allOf:
- $ref: spi-controller.yaml#
- if:
properties:
compatible:
not:
contains:
const: spacemit,k1-qspi
then:
properties:
resets: false
unevaluatedProperties: false
examples:

View File

@@ -21,11 +21,13 @@ properties:
- microchip,mpfs-qspi
- microchip,pic64gx-qspi
- const: microchip,coreqspi-rtl-v2
- const: microchip,coreqspi-rtl-v2 # FPGA QSPI
- enum:
- microchip,coreqspi-rtl-v2 # FPGA QSPI
- microchip,corespi-rtl-v5 # FPGA CoreSPI
- microchip,mpfs-spi
- items:
- const: microchip,pic64gx-spi
- const: microchip,mpfs-spi
- const: microchip,mpfs-spi
reg:
maxItems: 1
@@ -39,6 +41,45 @@ properties:
clocks:
maxItems: 1
microchip,apb-datawidth:
description: APB bus data width in bits.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [8, 16, 32]
default: 8
microchip,frame-size:
description: |
Number of bits per SPI frame, as configured in Libero.
In Motorola and TI modes, this corresponds directly
to the requested frame size. For NSC mode this is set
to 9 + the required data frame size.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 4
maximum: 32
default: 8
microchip,protocol-configuration:
description: CoreSPI protocol selection. Determines operating mode
$ref: /schemas/types.yaml#/definitions/string
enum:
- motorola
- ti
- nsc
default: motorola
microchip,motorola-mode:
description: Motorola SPI mode selection
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 3
microchip,ssel-active:
description: |
Keep SSEL asserted between frames when using the Motorola protocol.
When present, the controller keeps SSEL active across contiguous
transfers and deasserts only when the overall transfer completes.
type: boolean
required:
- compatible
- reg
@@ -71,6 +112,31 @@ allOf:
num-cs:
maximum: 1
- if:
properties:
compatible:
contains:
const: microchip,corespi-rtl-v5
then:
properties:
num-cs:
minimum: 1
maximum: 8
default: 8
fifo-depth:
minimum: 1
maximum: 32
default: 4
else:
properties:
microchip,apb-datawidth: false
microchip,frame-size: false
microchip,protocol-configuration: false
microchip,motorola-mode: false
microchip,ssel-active: false
unevaluatedProperties: false
examples:

View File

@@ -1,36 +0,0 @@
Nuvoton NPCM Peripheral Serial Peripheral Interface(PSPI) controller driver
Nuvoton NPCM7xx SOC support two PSPI channels.
Required properties:
- compatible : "nuvoton,npcm750-pspi" for Poleg NPCM7XX.
"nuvoton,npcm845-pspi" for Arbel NPCM8XX.
- #address-cells : should be 1. see spi-bus.txt
- #size-cells : should be 0. see spi-bus.txt
- specifies physical base address and size of the register.
- interrupts : contain PSPI interrupt.
- clocks : phandle of PSPI reference clock.
- clock-names: Should be "clk_apb5".
- pinctrl-names : a pinctrl state named "default" must be defined.
- pinctrl-0 : phandle referencing pin configuration of the device.
- resets : phandle to the reset control for this device.
- cs-gpios: Specifies the gpio pins to be used for chipselects.
See: Documentation/devicetree/bindings/spi/spi-bus.txt
Optional properties:
- clock-frequency : Input clock frequency to the PSPI block in Hz.
Default is 25000000 Hz.
spi0: spi@f0200000 {
compatible = "nuvoton,npcm750-pspi";
reg = <0xf0200000 0x1000>;
pinctrl-names = "default";
pinctrl-0 = <&pspi1_pins>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM7XX_CLK_APB5>;
clock-names = "clk_apb5";
resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>
cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
};

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/nuvoton,npcm-pspi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Nuvoton NPCM Peripheral SPI (PSPI) Controller
maintainers:
- Tomer Maimon <tmaimon77@gmail.com>
allOf:
- $ref: spi-controller.yaml#
description:
Nuvoton NPCM Peripheral Serial Peripheral Interface (PSPI) controller.
Nuvoton NPCM7xx SOC supports two PSPI channels.
Nuvoton NPCM8xx SOC support one PSPI channel.
properties:
compatible:
enum:
- nuvoton,npcm750-pspi # Poleg NPCM7XX
- nuvoton,npcm845-pspi # Arbel NPCM8XX
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
description: PSPI reference clock.
clock-names:
items:
- const: clk_apb5
resets:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/reset/nuvoton,npcm7xx-reset.h>
#include "dt-bindings/gpio/gpio.h"
spi0: spi@f0200000 {
compatible = "nuvoton,npcm750-pspi";
reg = <0xf0200000 0x1000>;
pinctrl-names = "default";
pinctrl-0 = <&pspi1_pins>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM7XX_CLK_APB5>;
clock-names = "clk_apb5";
resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>;
cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
};

View File

@@ -25,6 +25,8 @@ properties:
- items:
- enum:
- qcom,ipq5018-snand
- qcom,ipq5332-snand
- qcom,ipq5424-snand
- const: qcom,ipq9574-snand
- const: qcom,ipq9574-snand

View File

@@ -9,12 +9,18 @@ title: Renesas RZ/V2H(P) Renesas Serial Peripheral Interface (RSPI)
maintainers:
- Fabrizio Castro <fabrizio.castro.jz@renesas.com>
allOf:
- $ref: spi-controller.yaml#
properties:
compatible:
const: renesas,r9a09g057-rspi # RZ/V2H(P)
oneOf:
- enum:
- renesas,r9a09g057-rspi # RZ/V2H(P)
- renesas,r9a09g077-rspi # RZ/T2H
- items:
- const: renesas,r9a09g056-rspi # RZ/V2N
- const: renesas,r9a09g057-rspi
- items:
- const: renesas,r9a09g087-rspi # RZ/N2H
- const: renesas,r9a09g077-rspi # RZ/T2H
reg:
maxItems: 1
@@ -36,13 +42,12 @@ properties:
- const: tx
clocks:
minItems: 2
maxItems: 3
clock-names:
items:
- const: pclk
- const: pclk_sfr
- const: tclk
minItems: 2
maxItems: 3
resets:
maxItems: 2
@@ -62,12 +67,52 @@ required:
- interrupt-names
- clocks
- clock-names
- resets
- reset-names
- power-domains
- '#address-cells'
- '#size-cells'
allOf:
- $ref: spi-controller.yaml#
- if:
properties:
compatible:
contains:
enum:
- renesas,r9a09g057-rspi
then:
properties:
clocks:
minItems: 3
clock-names:
items:
- const: pclk
- const: pclk_sfr
- const: tclk
required:
- resets
- reset-names
- if:
properties:
compatible:
contains:
enum:
- renesas,r9a09g077-rspi
then:
properties:
clocks:
maxItems: 2
clock-names:
items:
- const: pclk
- const: pclkspi
resets: false
reset-names: false
unevaluatedProperties: false
examples:

View File

@@ -153,7 +153,7 @@ properties:
provides an interface to override the native DWC SSI CS control.
patternProperties:
"^.*@[0-9a-f]+$":
"@[0-9a-f]+$":
type: object
additionalProperties: true

View File

@@ -21,6 +21,7 @@ properties:
- enum:
- xlnx,zynqmp-spi-r1p6
- xlnx,versal-net-spi-r1p6
- cix,sky1-spi-r1p6
- const: cdns,spi-r1p6
reg:

View File

@@ -111,7 +111,7 @@ properties:
- compatible
patternProperties:
"^.*@[0-9a-f]+$":
"@[0-9a-f]+$":
type: object
$ref: spi-peripheral-props.yaml
additionalProperties: true

View File

@@ -53,6 +53,8 @@ properties:
- adi,lt7182s
# AMS iAQ-Core VOC Sensor
- ams,iaq-core
# Arduino microcontroller interface over SPI on UnoQ board
- arduino,unoq-mcu
# Temperature monitoring of Astera Labs PT5161L PCIe retimer
- asteralabs,pt5161l
# i2c h/w elliptic curve crypto module

View File

@@ -22247,7 +22247,7 @@ F: drivers/reset/reset-mpfs.c
F: drivers/rtc/rtc-mpfs.c
F: drivers/soc/microchip/mpfs-sys-controller.c
F: drivers/spi/spi-microchip-core-qspi.c
F: drivers/spi/spi-microchip-core.c
F: drivers/spi/spi-mpfs.c
F: drivers/usb/musb/mpfs.c
F: include/soc/microchip/mpfs.h
@@ -24385,6 +24385,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
F: Documentation/devicetree/bindings/spi/
F: Documentation/spi/
F: drivers/spi/
F: include/trace/events/spi*
F: include/linux/spi/
F: include/uapi/linux/spi/
F: tools/spi/

View File

@@ -435,7 +435,8 @@ config SPI_FSL_LPSPI
config SPI_FSL_QUADSPI
tristate "Freescale QSPI controller"
depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || \
ARCH_SPACEMIT || COMPILE_TEST
depends on HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
@@ -706,15 +707,6 @@ config SPI_MESON_SPIFC
This enables master mode support for the SPIFC (SPI flash
controller) available in Amlogic Meson SoCs.
config SPI_MICROCHIP_CORE
tristate "Microchip FPGA SPI controllers"
depends on SPI_MASTER
help
This enables the SPI driver for Microchip FPGA SPI controllers.
Say Y or M here if you want to use the "hard" controllers on
PolarFire SoC.
If built as a module, it will be called spi-microchip-core.
config SPI_MICROCHIP_CORE_QSPI
tristate "Microchip FPGA QSPI controllers"
depends on SPI_MASTER
@@ -724,6 +716,15 @@ config SPI_MICROCHIP_CORE_QSPI
PolarFire SoC.
If built as a module, it will be called spi-microchip-core-qspi.
config SPI_MICROCHIP_CORE_SPI
tristate "Microchip FPGA CoreSPI controller"
depends on SPI_MASTER
help
This enables the SPI driver for Microchip FPGA CoreSPI controller.
Say Y or M here if you want to use the "soft" controllers on
PolarFire SoC.
If built as a module, it will be called spi-microchip-core-spi.
config SPI_MT65XX
tristate "MediaTek SPI controller"
depends on ARCH_MEDIATEK || COMPILE_TEST
@@ -871,6 +872,16 @@ config SPI_PL022
controller. If you have an embedded system with an AMBA(R)
bus and a PL022 controller, say Y or M here.
config SPI_POLARFIRE_SOC
tristate "Microchip FPGA SPI controllers"
depends on SPI_MASTER
depends on ARCH_MICROCHIP || COMPILE_TEST
help
This enables the SPI driver for Microchip FPGA SPI controllers.
Say Y or M here if you want to use the "hard" controllers on
PolarFire SoC.
If built as a module, it will be called spi-mpfs.
config SPI_PPC4xx
tristate "PPC4xx SPI Controller"
depends on PPC32 && 4xx

View File

@@ -86,8 +86,8 @@ obj-$(CONFIG_SPI_LOONGSON_PLATFORM) += spi-loongson-plat.o
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
obj-$(CONFIG_SPI_MICROCHIP_CORE) += spi-microchip-core.o
obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI) += spi-microchip-core-qspi.o
obj-$(CONFIG_SPI_MICROCHIP_CORE_SPI) += spi-microchip-core-spi.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
@@ -97,6 +97,7 @@ obj-$(CONFIG_SPI_MTK_NOR) += spi-mtk-nor.o
obj-$(CONFIG_SPI_MTK_SNFI) += spi-mtk-snfi.o
obj-$(CONFIG_SPI_MXIC) += spi-mxic.o
obj-$(CONFIG_SPI_MXS) += spi-mxs.o
obj-$(CONFIG_SPI_POLARFIRE_SOC) += spi-mpfs.o
obj-$(CONFIG_SPI_WPCM_FIU) += spi-wpcm-fiu.o
obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.o
obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o

View File

@@ -147,6 +147,8 @@
#define SPI_NFI_CUS_SEC_SIZE_EN BIT(16)
#define REG_SPI_NFI_RD_CTL2 0x0510
#define SPI_NFI_DATA_READ_CMD GENMASK(7, 0)
#define REG_SPI_NFI_RD_CTL3 0x0514
#define REG_SPI_NFI_PG_CTL1 0x0524
@@ -179,7 +181,9 @@
#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03
#define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b
#define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b
#define SPI_NAND_OP_READ_FROM_CACHE_DUALIO 0xbb
#define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b
#define SPI_NAND_OP_READ_FROM_CACHE_QUADIO 0xeb
#define SPI_NAND_OP_WRITE_ENABLE 0x06
#define SPI_NAND_OP_WRITE_DISABLE 0x04
#define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02
@@ -219,13 +223,6 @@ struct airoha_snand_ctrl {
struct regmap *regmap_ctrl;
struct regmap *regmap_nfi;
struct clk *spi_clk;
struct {
size_t page_size;
size_t sec_size;
u8 sec_num;
u8 spare_size;
} nfi_cfg;
};
static int airoha_snand_set_fifo_op(struct airoha_snand_ctrl *as_ctrl,
@@ -486,92 +483,6 @@ static int airoha_snand_nfi_init(struct airoha_snand_ctrl *as_ctrl)
SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN);
}
static int airoha_snand_nfi_config(struct airoha_snand_ctrl *as_ctrl)
{
int err;
u32 val;
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
if (err)
return err;
/* auto FDM */
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_AUTO_FDM_EN);
if (err)
return err;
/* HW ECC */
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_HW_ECC_EN);
if (err)
return err;
/* DMA Burst */
err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_DMA_BURST_EN);
if (err)
return err;
/* page format */
switch (as_ctrl->nfi_cfg.spare_size) {
case 26:
val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1);
break;
case 27:
val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2);
break;
case 28:
val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3);
break;
default:
val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0);
break;
}
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT,
SPI_NFI_SPARE_SIZE, val);
if (err)
return err;
switch (as_ctrl->nfi_cfg.page_size) {
case 2048:
val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1);
break;
case 4096:
val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2);
break;
default:
val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0);
break;
}
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT,
SPI_NFI_PAGE_SIZE, val);
if (err)
return err;
/* sec num */
val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num);
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_SEC_NUM, val);
if (err)
return err;
/* enable cust sec size */
err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
SPI_NFI_CUS_SEC_SIZE_EN);
if (err)
return err;
/* set cust sec size */
val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, as_ctrl->nfi_cfg.sec_size);
return regmap_update_bits(as_ctrl->regmap_nfi,
REG_SPI_NFI_SECCUS_SIZE,
SPI_NFI_CUS_SEC_SIZE, val);
}
static bool airoha_snand_is_page_ops(const struct spi_mem_op *op)
{
if (op->addr.nbytes != 2)
@@ -604,33 +515,6 @@ static bool airoha_snand_is_page_ops(const struct spi_mem_op *op)
}
}
static int airoha_snand_adjust_op_size(struct spi_mem *mem,
struct spi_mem_op *op)
{
size_t max_len;
if (airoha_snand_is_page_ops(op)) {
struct airoha_snand_ctrl *as_ctrl;
as_ctrl = spi_controller_get_devdata(mem->spi->controller);
max_len = as_ctrl->nfi_cfg.sec_size;
max_len += as_ctrl->nfi_cfg.spare_size;
max_len *= as_ctrl->nfi_cfg.sec_num;
if (op->data.nbytes > max_len)
op->data.nbytes = max_len;
} else {
max_len = 1 + op->addr.nbytes + op->dummy.nbytes;
if (max_len >= 160)
return -EOPNOTSUPP;
if (op->data.nbytes > 160 - max_len)
op->data.nbytes = 160 - max_len;
}
return 0;
}
static bool airoha_snand_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
@@ -671,32 +555,89 @@ static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc)
static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct spi_mem_op *op = &desc->info.op_tmpl;
struct spi_device *spi = desc->mem->spi;
struct airoha_snand_ctrl *as_ctrl;
u8 *txrx_buf = spi_get_ctldata(spi);
dma_addr_t dma_addr;
u32 val, rd_mode;
u32 val, rd_mode, opcode;
size_t bytes;
int err;
switch (op->cmd.opcode) {
as_ctrl = spi_controller_get_devdata(spi->controller);
/* minimum oob size is 64 */
bytes = round_up(offs + len, 64);
/*
* DUALIO and QUADIO opcodes are not supported by the spi controller,
* replace them with supported opcodes.
*/
opcode = desc->info.op_tmpl.cmd.opcode;
switch (opcode) {
case SPI_NAND_OP_READ_FROM_CACHE_SINGLE:
case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST:
rd_mode = 0;
break;
case SPI_NAND_OP_READ_FROM_CACHE_DUAL:
case SPI_NAND_OP_READ_FROM_CACHE_DUALIO:
opcode = SPI_NAND_OP_READ_FROM_CACHE_DUAL;
rd_mode = 1;
break;
case SPI_NAND_OP_READ_FROM_CACHE_QUAD:
case SPI_NAND_OP_READ_FROM_CACHE_QUADIO:
opcode = SPI_NAND_OP_READ_FROM_CACHE_QUAD;
rd_mode = 2;
break;
default:
rd_mode = 0;
break;
/* unknown opcode */
return -EOPNOTSUPP;
}
as_ctrl = spi_controller_get_devdata(spi->controller);
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
if (err < 0)
return err;
err = airoha_snand_nfi_config(as_ctrl);
/* NFI reset */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
if (err)
goto error_dma_mode_off;
/* NFI configure:
* - No AutoFDM (custom sector size (SECCUS) register will be used)
* - No SoC's hardware ECC (flash internal ECC will be used)
* - Use burst mode (faster, but requires 16 byte alignment for addresses)
* - Setup for reading (SPI_NFI_READ_MODE)
* - Setup reading command: FIELD_PREP(SPI_NFI_OPMODE, 6)
* - Use DMA instead of PIO for data reading
*/
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_DMA_MODE |
SPI_NFI_READ_MODE |
SPI_NFI_DMA_BURST_EN |
SPI_NFI_HW_ECC_EN |
SPI_NFI_AUTO_FDM_EN |
SPI_NFI_OPMODE,
SPI_NFI_DMA_MODE |
SPI_NFI_READ_MODE |
SPI_NFI_DMA_BURST_EN |
FIELD_PREP(SPI_NFI_OPMODE, 6));
if (err)
goto error_dma_mode_off;
/* Set number of sector will be read */
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_SEC_NUM,
FIELD_PREP(SPI_NFI_SEC_NUM, 1));
if (err)
goto error_dma_mode_off;
/* Set custom sector size */
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
SPI_NFI_CUS_SEC_SIZE |
SPI_NFI_CUS_SEC_SIZE_EN,
FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) |
SPI_NFI_CUS_SEC_SIZE_EN);
if (err)
goto error_dma_mode_off;
@@ -712,18 +653,24 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
if (err)
goto error_dma_unmap;
/* set cust sec size */
val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num;
val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val);
/*
* Setup transfer length
* ---------------------
* The following rule MUST be met:
* transfer_length =
* = NFI_SNF_MISC_CTL2.read_data_byte_number =
* = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
*/
err = regmap_update_bits(as_ctrl->regmap_nfi,
REG_SPI_NFI_SNF_MISC_CTL2,
SPI_NFI_READ_DATA_BYTE_NUM, val);
SPI_NFI_READ_DATA_BYTE_NUM,
FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, bytes));
if (err)
goto error_dma_unmap;
/* set read command */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2,
op->cmd.opcode);
FIELD_PREP(SPI_NFI_DATA_READ_CMD, opcode));
if (err)
goto error_dma_unmap;
@@ -739,23 +686,11 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
if (err)
goto error_dma_unmap;
/* set nfi read */
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_OPMODE,
FIELD_PREP(SPI_NFI_OPMODE, 6));
if (err)
goto error_dma_unmap;
err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE);
if (err)
goto error_dma_unmap;
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0);
if (err)
goto error_dma_unmap;
/* trigger dma start read */
/* trigger dma reading */
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_RD_TRIG);
if (err)
@@ -813,59 +748,122 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc,
static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct spi_mem_op *op = &desc->info.op_tmpl;
struct spi_device *spi = desc->mem->spi;
u8 *txrx_buf = spi_get_ctldata(spi);
struct airoha_snand_ctrl *as_ctrl;
dma_addr_t dma_addr;
u32 wr_mode, val;
u32 wr_mode, val, opcode;
size_t bytes;
int err;
as_ctrl = spi_controller_get_devdata(spi->controller);
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
/* minimum oob size is 64 */
bytes = round_up(offs + len, 64);
opcode = desc->info.op_tmpl.cmd.opcode;
switch (opcode) {
case SPI_NAND_OP_PROGRAM_LOAD_SINGLE:
case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE:
wr_mode = 0;
break;
case SPI_NAND_OP_PROGRAM_LOAD_QUAD:
case SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD:
wr_mode = 2;
break;
default:
/* unknown opcode */
return -EOPNOTSUPP;
}
if (offs > 0)
memset(txrx_buf, 0xff, offs);
memcpy(txrx_buf + offs, buf, len);
if (bytes > offs + len)
memset(txrx_buf + offs + len, 0xff, bytes - offs - len);
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
if (err < 0)
return err;
memcpy(txrx_buf + offs, buf, len);
/* NFI reset */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_FIFO_FLUSH | SPI_NFI_RST);
if (err)
goto error_dma_mode_off;
/*
* NFI configure:
* - No AutoFDM (custom sector size (SECCUS) register will be used)
* - No SoC's hardware ECC (flash internal ECC will be used)
* - Use burst mode (faster, but requires 16 byte alignment for addresses)
* - Setup for writing (SPI_NFI_READ_MODE bit is cleared)
* - Setup writing command: FIELD_PREP(SPI_NFI_OPMODE, 3)
* - Use DMA instead of PIO for data writing
*/
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_DMA_MODE |
SPI_NFI_READ_MODE |
SPI_NFI_DMA_BURST_EN |
SPI_NFI_HW_ECC_EN |
SPI_NFI_AUTO_FDM_EN |
SPI_NFI_OPMODE,
SPI_NFI_DMA_MODE |
SPI_NFI_DMA_BURST_EN |
FIELD_PREP(SPI_NFI_OPMODE, 3));
if (err)
goto error_dma_mode_off;
/* Set number of sector will be written */
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_SEC_NUM,
FIELD_PREP(SPI_NFI_SEC_NUM, 1));
if (err)
goto error_dma_mode_off;
/* Set custom sector size */
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE,
SPI_NFI_CUS_SEC_SIZE |
SPI_NFI_CUS_SEC_SIZE_EN,
FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) |
SPI_NFI_CUS_SEC_SIZE_EN);
if (err)
goto error_dma_mode_off;
dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE,
DMA_TO_DEVICE);
err = dma_mapping_error(as_ctrl->dev, dma_addr);
if (err)
return err;
err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA);
if (err < 0)
goto error_dma_unmap;
err = airoha_snand_nfi_config(as_ctrl);
if (err)
goto error_dma_unmap;
if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD ||
op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD)
wr_mode = BIT(1);
else
wr_mode = 0;
goto error_dma_mode_off;
/* set dma addr */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR,
dma_addr);
if (err)
goto error_dma_unmap;
val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM,
as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num);
/*
* Setup transfer length
* ---------------------
* The following rule MUST be met:
* transfer_length =
* = NFI_SNF_MISC_CTL2.write_data_byte_number =
* = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size
*/
err = regmap_update_bits(as_ctrl->regmap_nfi,
REG_SPI_NFI_SNF_MISC_CTL2,
SPI_NFI_PROG_LOAD_BYTE_NUM, val);
SPI_NFI_PROG_LOAD_BYTE_NUM,
FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, bytes));
if (err)
goto error_dma_unmap;
/* set write command */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1,
FIELD_PREP(SPI_NFI_PG_LOAD_CMD,
op->cmd.opcode));
FIELD_PREP(SPI_NFI_PG_LOAD_CMD, opcode));
if (err)
goto error_dma_unmap;
/* set write mode */
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL,
FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode));
if (err)
@@ -877,26 +875,11 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
if (err)
goto error_dma_unmap;
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_READ_MODE);
if (err)
goto error_dma_unmap;
err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_OPMODE,
FIELD_PREP(SPI_NFI_OPMODE, 3));
if (err)
goto error_dma_unmap;
err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG,
SPI_NFI_DMA_MODE);
if (err)
goto error_dma_unmap;
err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80);
if (err)
goto error_dma_unmap;
/* trigger dma writing */
err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON,
SPI_NFI_WR_TRIG);
if (err)
@@ -941,6 +924,7 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc,
error_dma_unmap:
dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE,
DMA_TO_DEVICE);
error_dma_mode_off:
airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL);
return err;
}
@@ -1022,7 +1006,6 @@ static int airoha_snand_exec_op(struct spi_mem *mem,
}
static const struct spi_controller_mem_ops airoha_snand_mem_ops = {
.adjust_op_size = airoha_snand_adjust_op_size,
.supports_op = airoha_snand_supports_op,
.exec_op = airoha_snand_exec_op,
.dirmap_create = airoha_snand_dirmap_create,
@@ -1030,6 +1013,11 @@ static const struct spi_controller_mem_ops airoha_snand_mem_ops = {
.dirmap_write = airoha_snand_dirmap_write,
};
static const struct spi_controller_mem_ops airoha_snand_nodma_mem_ops = {
.supports_op = airoha_snand_supports_op,
.exec_op = airoha_snand_exec_op,
};
static int airoha_snand_setup(struct spi_device *spi)
{
struct airoha_snand_ctrl *as_ctrl;
@@ -1047,36 +1035,6 @@ static int airoha_snand_setup(struct spi_device *spi)
return 0;
}
static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl)
{
u32 val, sec_size, sec_num;
int err;
err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val);
if (err)
return err;
sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val);
err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, &val);
if (err)
return err;
sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val);
/* init default value */
as_ctrl->nfi_cfg.sec_size = sec_size;
as_ctrl->nfi_cfg.sec_num = sec_num;
as_ctrl->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024);
as_ctrl->nfi_cfg.spare_size = 16;
err = airoha_snand_nfi_init(as_ctrl);
if (err)
return err;
return airoha_snand_nfi_config(as_ctrl);
}
static const struct regmap_config spi_ctrl_regmap_config = {
.name = "ctrl",
.reg_bits = 32,
@@ -1104,7 +1062,9 @@ static int airoha_snand_probe(struct platform_device *pdev)
struct airoha_snand_ctrl *as_ctrl;
struct device *dev = &pdev->dev;
struct spi_controller *ctrl;
bool dma_enable = true;
void __iomem *base;
u32 sfc_strap;
int err;
ctrl = devm_spi_alloc_host(dev, sizeof(*as_ctrl));
@@ -1139,18 +1099,34 @@ static int airoha_snand_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(as_ctrl->spi_clk),
"unable to get spi clk\n");
if (device_is_compatible(dev, "airoha,en7523-snand")) {
err = regmap_read(as_ctrl->regmap_ctrl,
REG_SPI_CTRL_SFC_STRAP, &sfc_strap);
if (err)
return err;
if (!(sfc_strap & 0x04)) {
dma_enable = false;
dev_warn(dev, "Detected booting in RESERVED mode (UART_TXD was short to GND).\n");
dev_warn(dev, "This mode is known for incorrect DMA reading of some flashes.\n");
dev_warn(dev, "Much slower PIO mode will be used to prevent flash data damage.\n");
dev_warn(dev, "Unplug UART cable and power cycle board to get full performance.\n");
}
}
err = dma_set_mask(as_ctrl->dev, DMA_BIT_MASK(32));
if (err)
return err;
ctrl->num_chipselect = 2;
ctrl->mem_ops = &airoha_snand_mem_ops;
ctrl->mem_ops = dma_enable ? &airoha_snand_mem_ops
: &airoha_snand_nodma_mem_ops;
ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
ctrl->mode_bits = SPI_RX_DUAL;
ctrl->setup = airoha_snand_setup;
device_set_node(&ctrl->dev, dev_fwnode(dev));
err = airoha_snand_nfi_setup(as_ctrl);
err = airoha_snand_nfi_init(as_ctrl);
if (err)
return err;

File diff suppressed because it is too large Load Diff

View File

@@ -582,8 +582,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev)
host->auto_runtime_pm = true;
bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT];
bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH];
bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]);
bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]);
bs->tx_io = bs->regs + bs->reg_offsets[SPI_MSG_DATA];
bs->rx_io = bs->regs + bs->reg_offsets[SPI_RX_DATA];
/* Initialize hardware */
ret = clk_prepare_enable(bs->clk);

View File

@@ -109,6 +109,7 @@
* @rxbuf: Pointer to the RX buffer
* @tx_bytes: Number of bytes left to transfer
* @rx_bytes: Number of bytes requested
* @n_bytes: Number of bytes per word
* @dev_busy: Device busy flag
* @is_decoded_cs: Flag for decoder property set or not
* @tx_fifo_depth: Depth of the TX FIFO
@@ -120,16 +121,24 @@ struct cdns_spi {
struct clk *pclk;
unsigned int clk_rate;
u32 speed_hz;
const u8 *txbuf;
u8 *rxbuf;
const void *txbuf;
void *rxbuf;
int tx_bytes;
int rx_bytes;
u8 n_bytes;
u8 dev_busy;
u32 is_decoded_cs;
unsigned int tx_fifo_depth;
struct reset_control *rstc;
};
enum cdns_spi_frame_n_bytes {
CDNS_SPI_N_BYTES_NULL = 0,
CDNS_SPI_N_BYTES_U8 = 1,
CDNS_SPI_N_BYTES_U16 = 2,
CDNS_SPI_N_BYTES_U32 = 4
};
/* Macros for the SPI controller read/write */
static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset)
{
@@ -305,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi,
return 0;
}
static u8 cdns_spi_n_bytes(struct spi_transfer *transfer)
{
if (transfer->bits_per_word <= 8)
return CDNS_SPI_N_BYTES_U8;
else if (transfer->bits_per_word <= 16)
return CDNS_SPI_N_BYTES_U16;
else
return CDNS_SPI_N_BYTES_U32;
}
static inline void cdns_spi_reader(struct cdns_spi *xspi)
{
u32 rxw = 0;
if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) {
pr_err("%s: rxbuf address is not aligned for %d bytes\n",
__func__, xspi->n_bytes);
return;
}
rxw = cdns_spi_read(xspi, CDNS_SPI_RXD);
if (xspi->rxbuf) {
switch (xspi->n_bytes) {
case CDNS_SPI_N_BYTES_U8:
*(u8 *)xspi->rxbuf = rxw;
break;
case CDNS_SPI_N_BYTES_U16:
*(u16 *)xspi->rxbuf = rxw;
break;
case CDNS_SPI_N_BYTES_U32:
*(u32 *)xspi->rxbuf = rxw;
break;
default:
pr_err("%s invalid n_bytes %d\n", __func__,
xspi->n_bytes);
return;
}
xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes;
}
}
static inline void cdns_spi_writer(struct cdns_spi *xspi)
{
u32 txw = 0;
if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) {
pr_err("%s: txbuf address is not aligned for %d bytes\n",
__func__, xspi->n_bytes);
return;
}
if (xspi->txbuf) {
switch (xspi->n_bytes) {
case CDNS_SPI_N_BYTES_U8:
txw = *(u8 *)xspi->txbuf;
break;
case CDNS_SPI_N_BYTES_U16:
txw = *(u16 *)xspi->txbuf;
break;
case CDNS_SPI_N_BYTES_U32:
txw = *(u32 *)xspi->txbuf;
break;
default:
pr_err("%s invalid n_bytes %d\n", __func__,
xspi->n_bytes);
return;
}
cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
}
}
/**
* cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
* @xspi: Pointer to the cdns_spi structure
@@ -321,23 +402,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
while (ntx || nrx) {
if (nrx) {
u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD);
if (xspi->rxbuf)
*xspi->rxbuf++ = data;
cdns_spi_reader(xspi);
nrx--;
}
if (ntx) {
if (xspi->txbuf)
cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
else
cdns_spi_write(xspi, CDNS_SPI_TXD, 0);
cdns_spi_writer(xspi);
ntx--;
}
}
}
@@ -454,6 +526,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
udelay(10);
xspi->n_bytes = cdns_spi_n_bytes(transfer);
xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
@@ -654,6 +730,9 @@ static int cdns_spi_probe(struct platform_device *pdev)
ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6"))
ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
if (!spi_controller_is_target(ctlr)) {
ctlr->mode_bits |= SPI_CS_HIGH;
ctlr->set_cs = cdns_spi_chipselect;
@@ -797,6 +876,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = {
static const struct of_device_id cdns_spi_of_match[] = {
{ .compatible = "xlnx,zynq-spi-r1p6" },
{ .compatible = "cix,sky1-spi-r1p6" },
{ .compatible = "cdns,spi-r1p6" },
{ /* end of table */ }
};

View File

@@ -78,7 +78,7 @@ static int ch341_transfer_one(struct spi_controller *host,
ch341->tx_buf[0] = CH341A_CMD_SPI_STREAM;
memcpy(ch341->tx_buf + 1, trans->tx_buf, len);
memcpy(ch341->tx_buf + 1, trans->tx_buf, len - 1);
ret = usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, len,
NULL, CH341_DEFAULT_TIMEOUT);

View File

@@ -9,6 +9,7 @@
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/platform_data/edma.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/clk.h>
@@ -19,8 +20,6 @@
#include <linux/spi/spi_bitbang.h>
#include <linux/slab.h>
#include <linux/platform_data/spi-davinci.h>
#define CS_DEFAULT 0xFF
#define SPIFMT_PHASE_MASK BIT(16)
@@ -98,8 +97,69 @@
#define SPIDEF 0x4c
#define SPIFMT0 0x50
#define SPI_IO_TYPE_POLL 1
#define SPI_IO_TYPE_DMA 2
#define DMA_MIN_BYTES 16
enum {
SPI_VERSION_1, /* For DM355/DM365/DM6467 */
SPI_VERSION_2, /* For DA8xx */
};
/**
* struct davinci_spi_platform_data - Platform data for SPI master device on DaVinci
*
* @version: version of the SPI IP. Different DaVinci devices have slightly
* varying versions of the same IP.
* @num_chipselect: number of chipselects supported by this SPI master
* @intr_line: interrupt line used to connect the SPI IP to the ARM interrupt
* controller withn the SoC. Possible values are 0 and 1.
* @prescaler_limit: max clock prescaler value
* @cshold_bug: set this to true if the SPI controller on your chip requires
* a write to CSHOLD bit in between transfers (like in DM355).
* @dma_event_q: DMA event queue to use if SPI_IO_TYPE_DMA is used for any
* device on the bus.
*/
struct davinci_spi_platform_data {
u8 version;
u8 num_chipselect;
u8 intr_line;
u8 prescaler_limit;
bool cshold_bug;
enum dma_event_q dma_event_q;
};
/**
* struct davinci_spi_config - Per-chip-select configuration for SPI slave devices
*
* @wdelay: amount of delay between transmissions. Measured in number of
* SPI module clocks.
* @odd_parity: polarity of parity flag at the end of transmit data stream.
* 0 - odd parity, 1 - even parity.
* @parity_enable: enable transmission of parity at end of each transmit
* data stream.
* @io_type: type of IO transfer. Choose between polled, interrupt and DMA.
* @timer_disable: disable chip-select timers (setup and hold)
* @c2tdelay: chip-select setup time. Measured in number of SPI module clocks.
* @t2cdelay: chip-select hold time. Measured in number of SPI module clocks.
* @t2edelay: transmit data finished to SPI ENAn pin inactive time. Measured
* in number of SPI clocks.
* @c2edelay: chip-select active to SPI ENAn signal active time. Measured in
* number of SPI clocks.
*/
struct davinci_spi_config {
u8 wdelay;
u8 odd_parity;
u8 parity_enable;
u8 io_type;
u8 timer_disable;
u8 c2tdelay;
u8 t2cdelay;
u8 t2edelay;
u8 c2edelay;
};
/* SPI Controller driver's private data. */
struct davinci_spi {
struct spi_bitbang bitbang;

View File

@@ -288,7 +288,7 @@ static int dw_spi_bt1_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
ret = dw_spi_add_host(&pdev->dev, dws);
ret = dw_spi_add_controller(&pdev->dev, dws);
if (ret) {
pm_runtime_disable(&pdev->dev);
return ret;
@@ -303,7 +303,7 @@ static void dw_spi_bt1_remove(struct platform_device *pdev)
{
struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev);
dw_spi_remove_host(&dwsbt1->dws);
dw_spi_remove_controller(&dwsbt1->dws);
pm_runtime_disable(&pdev->dev);
}

View File

@@ -63,7 +63,7 @@ static void dw_spi_debugfs_init(struct dw_spi *dws)
{
char name[32];
snprintf(name, 32, "dw_spi%d", dws->host->bus_num);
snprintf(name, 32, "dw_spi%d", dws->ctlr->bus_num);
dws->debugfs = debugfs_create_dir(name, NULL);
dws->regset.regs = dw_spi_dbgfs_regs;
@@ -185,25 +185,25 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw)
irq_status = dw_readl(dws, DW_SPI_ISR);
if (irq_status & DW_SPI_INT_RXOI) {
dev_err(&dws->host->dev, "RX FIFO overflow detected\n");
dev_err(&dws->ctlr->dev, "RX FIFO overflow detected\n");
ret = -EIO;
}
if (irq_status & DW_SPI_INT_RXUI) {
dev_err(&dws->host->dev, "RX FIFO underflow detected\n");
dev_err(&dws->ctlr->dev, "RX FIFO underflow detected\n");
ret = -EIO;
}
if (irq_status & DW_SPI_INT_TXOI) {
dev_err(&dws->host->dev, "TX FIFO overflow detected\n");
dev_err(&dws->ctlr->dev, "TX FIFO overflow detected\n");
ret = -EIO;
}
/* Generically handle the erroneous situation */
if (ret) {
dw_spi_reset_chip(dws);
if (dws->host->cur_msg)
dws->host->cur_msg->status = ret;
if (dws->ctlr->cur_msg)
dws->ctlr->cur_msg->status = ret;
}
return ret;
@@ -215,7 +215,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
u16 irq_status = dw_readl(dws, DW_SPI_ISR);
if (dw_spi_check_status(dws, false)) {
spi_finalize_current_transfer(dws->host);
spi_finalize_current_transfer(dws->ctlr);
return IRQ_HANDLED;
}
@@ -229,7 +229,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
dw_reader(dws);
if (!dws->rx_len) {
dw_spi_mask_intr(dws, 0xff);
spi_finalize_current_transfer(dws->host);
spi_finalize_current_transfer(dws->ctlr);
} else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) {
dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1);
}
@@ -250,14 +250,14 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws)
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{
struct spi_controller *host = dev_id;
struct dw_spi *dws = spi_controller_get_devdata(host);
struct spi_controller *ctlr = dev_id;
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
u16 irq_status = dw_readl(dws, DW_SPI_ISR) & DW_SPI_INT_MASK;
if (!irq_status)
return IRQ_NONE;
if (!host->cur_msg) {
if (!ctlr->cur_msg) {
dw_spi_mask_intr(dws, 0xff);
return IRQ_HANDLED;
}
@@ -332,6 +332,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
dw_writel(dws, DW_SPI_CTRLR0, cr0);
if (spi_controller_is_target(dws->ctlr))
return;
if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD ||
cfg->tmode == DW_SPI_CTRLR0_TMOD_RO)
dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0);
@@ -410,11 +413,11 @@ static int dw_spi_poll_transfer(struct dw_spi *dws,
return 0;
}
static int dw_spi_transfer_one(struct spi_controller *host,
static int dw_spi_transfer_one(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *transfer)
{
struct dw_spi *dws = spi_controller_get_devdata(host);
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
struct dw_spi_cfg cfg = {
.tmode = DW_SPI_CTRLR0_TMOD_TR,
.dfs = transfer->bits_per_word,
@@ -439,7 +442,7 @@ static int dw_spi_transfer_one(struct spi_controller *host,
transfer->effective_speed_hz = dws->current_freq;
/* Check if current transfer is a DMA transaction */
dws->dma_mapped = spi_xfer_is_dma_mapped(host, spi, transfer);
dws->dma_mapped = spi_xfer_is_dma_mapped(ctlr, spi, transfer);
/* For poll mode just disable all interrupts */
dw_spi_mask_intr(dws, 0xff);
@@ -462,10 +465,9 @@ static int dw_spi_transfer_one(struct spi_controller *host,
return 1;
}
static void dw_spi_handle_err(struct spi_controller *host,
struct spi_message *msg)
static inline void dw_spi_abort(struct spi_controller *ctlr)
{
struct dw_spi *dws = spi_controller_get_devdata(host);
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
if (dws->dma_mapped)
dws->dma_ops->dma_stop(dws);
@@ -473,6 +475,19 @@ static void dw_spi_handle_err(struct spi_controller *host,
dw_spi_reset_chip(dws);
}
static void dw_spi_handle_err(struct spi_controller *ctlr,
struct spi_message *msg)
{
dw_spi_abort(ctlr);
}
static int dw_spi_target_abort(struct spi_controller *ctlr)
{
dw_spi_abort(ctlr);
return 0;
}
static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
if (op->data.dir == SPI_MEM_DATA_IN)
@@ -574,7 +589,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi)
while (len) {
entries = readl_relaxed(dws->regs + DW_SPI_TXFLR);
if (!entries) {
dev_err(&dws->host->dev, "CS de-assertion on Tx\n");
dev_err(&dws->ctlr->dev, "CS de-assertion on Tx\n");
return -EIO;
}
room = min(dws->fifo_len - entries, len);
@@ -594,7 +609,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi)
if (!entries) {
sts = readl_relaxed(dws->regs + DW_SPI_RISR);
if (sts & DW_SPI_INT_RXOI) {
dev_err(&dws->host->dev, "FIFO overflow on Rx\n");
dev_err(&dws->ctlr->dev, "FIFO overflow on Rx\n");
return -EIO;
}
continue;
@@ -635,7 +650,7 @@ static int dw_spi_wait_mem_op_done(struct dw_spi *dws)
spi_delay_exec(&delay, NULL);
if (retry < 0) {
dev_err(&dws->host->dev, "Mem op hanged up\n");
dev_err(&dws->ctlr->dev, "Mem op hanged up\n");
return -EIO;
}
@@ -834,18 +849,23 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
DW_SPI_GET_BYTE(dws->ver, 1));
}
/*
* Try to detect the number of native chip-selects if the platform
* driver didn't set it up. There can be up to 16 lines configured.
*/
if (!dws->num_cs) {
u32 ser;
if (spi_controller_is_target(dws->ctlr)) {
/* There is only one CS input signal in target mode */
dws->num_cs = 1;
} else {
/*
* Try to detect the number of native chip-selects if the platform
* driver didn't set it up. There can be up to 16 lines configured.
*/
if (!dws->num_cs) {
u32 ser;
dw_writel(dws, DW_SPI_SER, 0xffff);
ser = dw_readl(dws, DW_SPI_SER);
dw_writel(dws, DW_SPI_SER, 0);
dw_writel(dws, DW_SPI_SER, 0xffff);
ser = dw_readl(dws, DW_SPI_SER);
dw_writel(dws, DW_SPI_SER, 0);
dws->num_cs = hweight16(ser);
dws->num_cs = hweight16(ser);
}
}
/*
@@ -898,60 +918,72 @@ static const struct spi_controller_mem_caps dw_spi_mem_caps = {
.per_op_freq = true,
};
int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
int dw_spi_add_controller(struct device *dev, struct dw_spi *dws)
{
struct spi_controller *host;
struct spi_controller *ctlr;
bool target;
int ret;
if (!dws)
return -EINVAL;
host = spi_alloc_host(dev, 0);
if (!host)
target = device_property_read_bool(dev, "spi-slave");
if (target)
ctlr = spi_alloc_target(dev, 0);
else
ctlr = spi_alloc_host(dev, 0);
if (!ctlr)
return -ENOMEM;
device_set_node(&host->dev, dev_fwnode(dev));
device_set_node(&ctlr->dev, dev_fwnode(dev));
dws->host = host;
dws->ctlr = ctlr;
dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR);
spi_controller_set_devdata(host, dws);
spi_controller_set_devdata(ctlr, dws);
/* Basic HW init */
dw_spi_hw_init(dev, dws);
ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev),
host);
ctlr);
if (ret < 0 && ret != -ENOTCONN) {
dev_err(dev, "can not get IRQ\n");
goto err_free_host;
goto err_free_ctlr;
}
dw_spi_init_mem_ops(dws);
host->use_gpio_descriptors = true;
host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
if (dws->caps & DW_SPI_CAP_DFS32)
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
else
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
host->bus_num = dws->bus_num;
host->num_chipselect = dws->num_cs;
host->setup = dw_spi_setup;
host->cleanup = dw_spi_cleanup;
if (dws->set_cs)
host->set_cs = dws->set_cs;
else
host->set_cs = dw_spi_set_cs;
host->transfer_one = dw_spi_transfer_one;
host->handle_err = dw_spi_handle_err;
if (dws->mem_ops.exec_op) {
host->mem_ops = &dws->mem_ops;
host->mem_caps = &dw_spi_mem_caps;
ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16);
ctlr->bus_num = dws->bus_num;
ctlr->num_chipselect = dws->num_cs;
ctlr->setup = dw_spi_setup;
ctlr->cleanup = dw_spi_cleanup;
ctlr->transfer_one = dw_spi_transfer_one;
ctlr->handle_err = dw_spi_handle_err;
ctlr->auto_runtime_pm = true;
if (!target) {
ctlr->use_gpio_descriptors = true;
ctlr->mode_bits |= SPI_LOOP;
if (dws->set_cs)
ctlr->set_cs = dws->set_cs;
else
ctlr->set_cs = dw_spi_set_cs;
if (dws->mem_ops.exec_op) {
ctlr->mem_ops = &dws->mem_ops;
ctlr->mem_caps = &dw_spi_mem_caps;
}
ctlr->max_speed_hz = dws->max_freq;
ctlr->flags = SPI_CONTROLLER_GPIO_SS;
} else {
ctlr->target_abort = dw_spi_target_abort;
}
host->max_speed_hz = dws->max_freq;
host->flags = SPI_CONTROLLER_GPIO_SS;
host->auto_runtime_pm = true;
/* Get default rx sample delay */
device_property_read_u32(dev, "rx-sample-delay-ns",
@@ -964,14 +996,14 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
} else if (ret) {
dev_warn(dev, "DMA init failed\n");
} else {
host->can_dma = dws->dma_ops->can_dma;
host->flags |= SPI_CONTROLLER_MUST_TX;
ctlr->can_dma = dws->dma_ops->can_dma;
ctlr->flags |= SPI_CONTROLLER_MUST_TX;
}
}
ret = spi_register_controller(host);
ret = spi_register_controller(ctlr);
if (ret) {
dev_err_probe(dev, ret, "problem registering spi host\n");
dev_err_probe(dev, ret, "problem registering spi controller\n");
goto err_dma_exit;
}
@@ -983,47 +1015,47 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
dws->dma_ops->dma_exit(dws);
dw_spi_enable_chip(dws, 0);
err_free_irq:
free_irq(dws->irq, host);
err_free_host:
spi_controller_put(host);
free_irq(dws->irq, ctlr);
err_free_ctlr:
spi_controller_put(ctlr);
return ret;
}
EXPORT_SYMBOL_NS_GPL(dw_spi_add_host, "SPI_DW_CORE");
EXPORT_SYMBOL_NS_GPL(dw_spi_add_controller, "SPI_DW_CORE");
void dw_spi_remove_host(struct dw_spi *dws)
void dw_spi_remove_controller(struct dw_spi *dws)
{
dw_spi_debugfs_remove(dws);
spi_unregister_controller(dws->host);
spi_unregister_controller(dws->ctlr);
if (dws->dma_ops && dws->dma_ops->dma_exit)
dws->dma_ops->dma_exit(dws);
dw_spi_shutdown_chip(dws);
free_irq(dws->irq, dws->host);
free_irq(dws->irq, dws->ctlr);
}
EXPORT_SYMBOL_NS_GPL(dw_spi_remove_host, "SPI_DW_CORE");
EXPORT_SYMBOL_NS_GPL(dw_spi_remove_controller, "SPI_DW_CORE");
int dw_spi_suspend_host(struct dw_spi *dws)
int dw_spi_suspend_controller(struct dw_spi *dws)
{
int ret;
ret = spi_controller_suspend(dws->host);
ret = spi_controller_suspend(dws->ctlr);
if (ret)
return ret;
dw_spi_shutdown_chip(dws);
return 0;
}
EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_host, "SPI_DW_CORE");
EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_controller, "SPI_DW_CORE");
int dw_spi_resume_host(struct dw_spi *dws)
int dw_spi_resume_controller(struct dw_spi *dws)
{
dw_spi_hw_init(&dws->host->dev, dws);
return spi_controller_resume(dws->host);
dw_spi_hw_init(&dws->ctlr->dev, dws);
return spi_controller_resume(dws->ctlr);
}
EXPORT_SYMBOL_NS_GPL(dw_spi_resume_host, "SPI_DW_CORE");
EXPORT_SYMBOL_NS_GPL(dw_spi_resume_controller, "SPI_DW_CORE");
MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>");
MODULE_DESCRIPTION("Driver for DesignWare SPI controller core");

View File

@@ -139,8 +139,8 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws)
if (!dws->txchan)
goto free_rxchan;
dws->host->dma_rx = dws->rxchan;
dws->host->dma_tx = dws->txchan;
dws->ctlr->dma_rx = dws->rxchan;
dws->ctlr->dma_tx = dws->txchan;
init_completion(&dws->dma_completion);
@@ -183,8 +183,8 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws)
goto free_rxchan;
}
dws->host->dma_rx = dws->rxchan;
dws->host->dma_tx = dws->txchan;
dws->ctlr->dma_rx = dws->rxchan;
dws->ctlr->dma_tx = dws->txchan;
init_completion(&dws->dma_completion);
@@ -242,10 +242,10 @@ static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes)
}
}
static bool dw_spi_can_dma(struct spi_controller *host,
static bool dw_spi_can_dma(struct spi_controller *ctlr,
struct spi_device *spi, struct spi_transfer *xfer)
{
struct dw_spi *dws = spi_controller_get_devdata(host);
struct dw_spi *dws = spi_controller_get_devdata(ctlr);
enum dma_slave_buswidth dma_bus_width;
if (xfer->len <= dws->fifo_len)
@@ -271,7 +271,7 @@ static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed)
msecs_to_jiffies(ms));
if (ms == 0) {
dev_err(&dws->host->cur_msg->spi->dev,
dev_err(&dws->ctlr->cur_msg->spi->dev,
"DMA transaction timed out\n");
return -ETIMEDOUT;
}
@@ -299,7 +299,7 @@ static int dw_spi_dma_wait_tx_done(struct dw_spi *dws,
spi_delay_exec(&delay, xfer);
if (retry < 0) {
dev_err(&dws->host->dev, "Tx hanged up\n");
dev_err(&dws->ctlr->dev, "Tx hanged up\n");
return -EIO;
}
@@ -400,7 +400,7 @@ static int dw_spi_dma_wait_rx_done(struct dw_spi *dws)
spi_delay_exec(&delay, NULL);
if (retry < 0) {
dev_err(&dws->host->dev, "Rx hanged up\n");
dev_err(&dws->ctlr->dev, "Rx hanged up\n");
return -EIO;
}
@@ -656,13 +656,13 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer)
if (ret)
return ret;
if (dws->host->cur_msg->status == -EINPROGRESS) {
if (dws->ctlr->cur_msg->status == -EINPROGRESS) {
ret = dw_spi_dma_wait_tx_done(dws, xfer);
if (ret)
return ret;
}
if (xfer->rx_buf && dws->host->cur_msg->status == -EINPROGRESS)
if (xfer->rx_buf && dws->ctlr->cur_msg->status == -EINPROGRESS)
ret = dw_spi_dma_wait_rx_done(dws);
return ret;

View File

@@ -321,11 +321,6 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
struct dw_spi *dws;
int ret;
if (device_property_read_bool(&pdev->dev, "spi-slave")) {
dev_warn(&pdev->dev, "spi-slave is not yet supported\n");
return -ENODEV;
}
dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio),
GFP_KERNEL);
if (!dwsmmio)
@@ -382,7 +377,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
ret = dw_spi_add_host(&pdev->dev, dws);
ret = dw_spi_add_controller(&pdev->dev, dws);
if (ret)
goto out;
@@ -401,7 +396,7 @@ static void dw_spi_mmio_remove(struct platform_device *pdev)
{
struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev);
dw_spi_remove_host(&dwsmmio->dws);
dw_spi_remove_controller(&dwsmmio->dws);
pm_runtime_disable(&pdev->dev);
reset_control_assert(dwsmmio->rstc);
}

View File

@@ -127,7 +127,7 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en
goto err_free_irq_vectors;
}
ret = dw_spi_add_host(&pdev->dev, dws);
ret = dw_spi_add_controller(&pdev->dev, dws);
if (ret)
goto err_free_irq_vectors;
@@ -156,7 +156,7 @@ static void dw_spi_pci_remove(struct pci_dev *pdev)
pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
dw_spi_remove_host(dws);
dw_spi_remove_controller(dws);
pci_free_irq_vectors(pdev);
}
@@ -165,14 +165,14 @@ static int dw_spi_pci_suspend(struct device *dev)
{
struct dw_spi *dws = dev_get_drvdata(dev);
return dw_spi_suspend_host(dws);
return dw_spi_suspend_controller(dws);
}
static int dw_spi_pci_resume(struct device *dev)
{
struct dw_spi *dws = dev_get_drvdata(dev);
return dw_spi_resume_host(dws);
return dw_spi_resume_controller(dws);
}
#endif

View File

@@ -142,14 +142,14 @@ struct dw_spi_dma_ops {
int (*dma_init)(struct device *dev, struct dw_spi *dws);
void (*dma_exit)(struct dw_spi *dws);
int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer);
bool (*can_dma)(struct spi_controller *host, struct spi_device *spi,
bool (*can_dma)(struct spi_controller *ctlr, struct spi_device *spi,
struct spi_transfer *xfer);
int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer);
void (*dma_stop)(struct dw_spi *dws);
};
struct dw_spi {
struct spi_controller *host;
struct spi_controller *ctlr;
u32 ip; /* Synopsys DW SSI IP-core ID */
u32 ver; /* Synopsys component version */
@@ -288,10 +288,10 @@ extern void dw_spi_set_cs(struct spi_device *spi, bool enable);
extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi,
struct dw_spi_cfg *cfg);
extern int dw_spi_check_status(struct dw_spi *dws, bool raw);
extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws);
extern void dw_spi_remove_host(struct dw_spi *dws);
extern int dw_spi_suspend_host(struct dw_spi *dws);
extern int dw_spi_resume_host(struct dw_spi *dws);
extern int dw_spi_add_controller(struct device *dev, struct dw_spi *dws);
extern void dw_spi_remove_controller(struct dw_spi *dws);
extern int dw_spi_suspend_controller(struct dw_spi *dws);
extern int dw_spi_resume_controller(struct dw_spi *dws);
#ifdef CONFIG_SPI_DW_DMA

View File

@@ -36,6 +36,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/spi/spi.h>
@@ -196,11 +197,17 @@
*/
#define QUADSPI_QUIRK_USE_TDH_SETTING BIT(5)
/*
* Do not disable the "qspi" clock when changing its rate.
*/
#define QUADSPI_QUIRK_SKIP_CLK_DISABLE BIT(6)
struct fsl_qspi_devtype_data {
unsigned int rxfifo;
unsigned int txfifo;
int invalid_mstrid;
unsigned int ahb_buf_size;
unsigned int sfa_size;
unsigned int quirks;
bool little_endian;
};
@@ -261,12 +268,23 @@ static const struct fsl_qspi_devtype_data ls2080a_data = {
.little_endian = true,
};
static const struct fsl_qspi_devtype_data spacemit_k1_data = {
.rxfifo = SZ_128,
.txfifo = SZ_256,
.ahb_buf_size = SZ_512,
.sfa_size = SZ_1K,
.invalid_mstrid = QUADSPI_BUFXCR_INVALID_MSTRID,
.quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_SKIP_CLK_DISABLE,
.little_endian = true,
};
struct fsl_qspi {
void __iomem *iobase;
void __iomem *ahb_addr;
const struct fsl_qspi_devtype_data *devtype_data;
struct mutex lock;
struct completion c;
struct reset_control *resets;
struct clk *clk, *clk_en;
struct pm_qos_request pm_qos_req;
struct device *dev;
@@ -274,34 +292,39 @@ struct fsl_qspi {
u32 memmap_phy;
};
static inline int needs_swap_endian(struct fsl_qspi *q)
static bool needs_swap_endian(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN);
}
static inline int needs_4x_clock(struct fsl_qspi *q)
static bool needs_4x_clock(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK);
}
static inline int needs_fill_txfifo(struct fsl_qspi *q)
static bool needs_fill_txfifo(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890);
}
static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
static bool needs_wakeup_wait_mode(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_TKT245618;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_TKT245618);
}
static inline int needs_amba_base_offset(struct fsl_qspi *q)
static bool needs_amba_base_offset(struct fsl_qspi *q)
{
return !(q->devtype_data->quirks & QUADSPI_QUIRK_BASE_INTERNAL);
}
static inline int needs_tdh_setting(struct fsl_qspi *q)
static bool needs_tdh_setting(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING);
}
static bool needs_clk_disable(struct fsl_qspi *q)
{
return !(q->devtype_data->quirks & QUADSPI_QUIRK_SKIP_CLK_DISABLE);
}
/*
@@ -534,15 +557,18 @@ static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi,
if (needs_4x_clock(q))
rate *= 4;
fsl_qspi_clk_disable_unprep(q);
if (needs_clk_disable(q))
fsl_qspi_clk_disable_unprep(q);
ret = clk_set_rate(q->clk, rate);
if (ret)
return;
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return;
if (needs_clk_disable(q)) {
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return;
}
q->selected = spi_get_chipselect(spi, 0);
@@ -722,6 +748,7 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
u32 reg, addr_offset = 0;
u32 sfa_size;
int ret;
/* disable and unprepare clock to avoid glitch pass to controller */
@@ -780,17 +807,17 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q)
* In HW there can be a maximum of four chips on two buses with
* two chip selects on each bus. We use four chip selects in SW
* to differentiate between the four chips.
* We use ahb_buf_size for each chip and set SFA1AD, SFA2AD, SFB1AD,
* SFB2AD accordingly.
*
* By default we write the AHB buffer size to each chip, but
* a different size can be specified with devtype_data->sfa_size.
* The SFA1AD, SFA2AD, SFB1AD, and SFB2AD registers define the
* top (end) of these four regions.
*/
qspi_writel(q, q->devtype_data->ahb_buf_size + addr_offset,
base + QUADSPI_SFA1AD);
qspi_writel(q, q->devtype_data->ahb_buf_size * 2 + addr_offset,
base + QUADSPI_SFA2AD);
qspi_writel(q, q->devtype_data->ahb_buf_size * 3 + addr_offset,
base + QUADSPI_SFB1AD);
qspi_writel(q, q->devtype_data->ahb_buf_size * 4 + addr_offset,
base + QUADSPI_SFB2AD);
sfa_size = q->devtype_data->sfa_size ? : q->devtype_data->ahb_buf_size;
qspi_writel(q, addr_offset + 1 * sfa_size, base + QUADSPI_SFA1AD);
qspi_writel(q, addr_offset + 2 * sfa_size, base + QUADSPI_SFA2AD);
qspi_writel(q, addr_offset + 3 * sfa_size, base + QUADSPI_SFB1AD);
qspi_writel(q, addr_offset + 4 * sfa_size, base + QUADSPI_SFB2AD);
q->selected = -1;
@@ -857,6 +884,8 @@ static void fsl_qspi_cleanup(void *data)
{
struct fsl_qspi *q = data;
reset_control_assert(q->resets);
fsl_qspi_clk_disable_unprep(q);
mutex_destroy(&q->lock);
@@ -902,6 +931,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (!q->ahb_addr)
return -ENOMEM;
q->resets = devm_reset_control_array_get_optional_exclusive(dev);
if (IS_ERR(q->resets))
return PTR_ERR(q->resets);
/* find the clocks */
q->clk_en = devm_clk_get(dev, "qspi_en");
if (IS_ERR(q->clk_en))
@@ -923,6 +956,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = reset_control_deassert(q->resets);
if (ret)
return ret;
/* find the irq */
ret = platform_get_irq(pdev, 0);
if (ret < 0)
@@ -976,6 +1013,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, },
{ .compatible = "fsl,ls1021a-qspi", .data = &ls1021a_data, },
{ .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, },
{ .compatible = "spacemit,k1-qspi", .data = &spacemit_k1_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);

View File

@@ -42,6 +42,7 @@ MODULE_PARM_DESC(polling_limit_us,
"time in us to run a transfer in polling mode\n");
#define MXC_RPM_TIMEOUT 2000 /* 2000ms */
#define MXC_SPI_DEFAULT_SPEED 500000 /* 500KHz */
#define MXC_CSPIRXDATA 0x00
#define MXC_CSPITXDATA 0x04
@@ -424,8 +425,15 @@ static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx)
static void mx53_ecspi_rx_target(struct spi_imx_data *spi_imx)
{
u32 val = ioread32be(spi_imx->base + MXC_CSPIRXDATA);
u32 val = readl(spi_imx->base + MXC_CSPIRXDATA);
#ifdef __LITTLE_ENDIAN
unsigned int bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
if (bytes_per_word == 1)
swab32s(&val);
else if (bytes_per_word == 2)
swahw32s(&val);
#endif
if (spi_imx->rx_buf) {
int n_bytes = spi_imx->target_burst % sizeof(val);
@@ -446,6 +454,9 @@ static void mx53_ecspi_tx_target(struct spi_imx_data *spi_imx)
{
u32 val = 0;
int n_bytes = spi_imx->count % sizeof(val);
#ifdef __LITTLE_ENDIAN
unsigned int bytes_per_word;
#endif
if (!n_bytes)
n_bytes = sizeof(val);
@@ -458,7 +469,14 @@ static void mx53_ecspi_tx_target(struct spi_imx_data *spi_imx)
spi_imx->count -= n_bytes;
iowrite32be(val, spi_imx->base + MXC_CSPITXDATA);
#ifdef __LITTLE_ENDIAN
bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word);
if (bytes_per_word == 1)
swab32s(&val);
else if (bytes_per_word == 2)
swahw32s(&val);
#endif
writel(val, spi_imx->base + MXC_CSPITXDATA);
}
/* MX51 eCSPI */
@@ -591,7 +609,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
* is not functional for imx53 Soc, config SPI burst completed when
* BURST_LENGTH + 1 bits are received
*/
if (spi_imx->target_mode && is_imx53_ecspi(spi_imx))
if (spi_imx->target_mode)
cfg &= ~MX51_ECSPI_CONFIG_SBBCTRL(channel);
else
cfg |= MX51_ECSPI_CONFIG_SBBCTRL(channel);
@@ -679,7 +697,7 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
/* Clear BL field and set the right value */
ctrl &= ~MX51_ECSPI_CTRL_BL_MASK;
if (spi_imx->target_mode && is_imx53_ecspi(spi_imx))
if (spi_imx->target_mode)
ctrl |= (spi_imx->target_burst * 8 - 1)
<< MX51_ECSPI_CTRL_BL_OFFSET;
else {
@@ -690,8 +708,11 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
/* set clock speed */
ctrl &= ~(0xf << MX51_ECSPI_CTRL_POSTDIV_OFFSET |
0xf << MX51_ECSPI_CTRL_PREDIV_OFFSET);
ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk);
spi_imx->spi_bus_clk = clk;
if (!spi_imx->target_mode) {
ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk);
spi_imx->spi_bus_clk = clk;
}
mx51_configure_cpha(spi_imx, spi);
@@ -1313,15 +1334,18 @@ static int spi_imx_setupxfer(struct spi_device *spi,
if (!t)
return 0;
if (!t->speed_hz) {
if (!spi->max_speed_hz) {
dev_err(&spi->dev, "no speed_hz provided!\n");
return -EINVAL;
if (!spi_imx->target_mode) {
if (!t->speed_hz) {
if (!spi->max_speed_hz) {
dev_err(&spi->dev, "no speed_hz provided!\n");
return -EINVAL;
}
dev_dbg(&spi->dev, "using spi->max_speed_hz!\n");
spi_imx->spi_bus_clk = spi->max_speed_hz;
} else {
spi_imx->spi_bus_clk = t->speed_hz;
}
dev_dbg(&spi->dev, "using spi->max_speed_hz!\n");
spi_imx->spi_bus_clk = spi->max_speed_hz;
} else
spi_imx->spi_bus_clk = t->speed_hz;
}
spi_imx->bits_per_word = t->bits_per_word;
spi_imx->count = t->len;
@@ -1365,7 +1389,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
spi_imx->rx_only = ((t->tx_buf == NULL)
|| (t->tx_buf == spi->controller->dummy_tx));
if (is_imx53_ecspi(spi_imx) && spi_imx->target_mode) {
if (spi_imx->target_mode) {
spi_imx->rx = mx53_ecspi_rx_target;
spi_imx->tx = mx53_ecspi_tx_target;
spi_imx->target_burst = t->len;
@@ -1641,8 +1665,7 @@ static int spi_imx_pio_transfer_target(struct spi_device *spi,
struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller);
int ret = 0;
if (is_imx53_ecspi(spi_imx) &&
transfer->len > MX53_MAX_TRANSFER_BYTES) {
if (transfer->len > MX53_MAX_TRANSFER_BYTES) {
dev_err(&spi->dev, "Transaction too big, max size is %d bytes\n",
MX53_MAX_TRANSFER_BYTES);
return -EMSGSIZE;
@@ -1838,6 +1861,7 @@ static int spi_imx_probe(struct platform_device *pdev)
controller->prepare_message = spi_imx_prepare_message;
controller->unprepare_message = spi_imx_unprepare_message;
controller->target_abort = spi_imx_target_abort;
spi_imx->spi_bus_clk = MXC_SPI_DEFAULT_SPEED;
controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS |
SPI_MOSI_IDLE_LOW;

View File

@@ -12,6 +12,9 @@
#include <linux/spi/spi-mem.h>
#include <linux/sched/task_stack.h>
#define CREATE_TRACE_POINTS
#include <trace/events/spi-mem.h>
#include "internals.h"
#define SPI_MEM_MAX_BUSWIDTH 8
@@ -403,7 +406,9 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
if (ret)
return ret;
trace_spi_mem_start_op(mem, op);
ret = ctlr->mem_ops->exec_op(mem, op);
trace_spi_mem_stop_op(mem, op);
spi_mem_access_end(mem);

View File

@@ -0,0 +1,429 @@
// SPDX-License-Identifier: (GPL-2.0)
//
// Microchip CoreSPI controller driver
//
// Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries
//
// Author: Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#define MCHP_CORESPI_MAX_CS (8)
#define MCHP_CORESPI_DEFAULT_FIFO_DEPTH (4)
#define MCHP_CORESPI_DEFAULT_MOTOROLA_MODE (3)
#define MCHP_CORESPI_CONTROL_ENABLE BIT(0)
#define MCHP_CORESPI_CONTROL_MASTER BIT(1)
#define MCHP_CORESPI_CONTROL_TX_DATA_INT BIT(3)
#define MCHP_CORESPI_CONTROL_RX_OVER_INT BIT(4)
#define MCHP_CORESPI_CONTROL_TX_UNDER_INT BIT(5)
#define MCHP_CORESPI_CONTROL_FRAMEURUN BIT(6)
#define MCHP_CORESPI_CONTROL_OENOFF BIT(7)
#define MCHP_CORESPI_STATUS_ACTIVE BIT(7)
#define MCHP_CORESPI_STATUS_SSEL BIT(6)
#define MCHP_CORESPI_STATUS_TXFIFO_UNDERFLOW BIT(5)
#define MCHP_CORESPI_STATUS_RXFIFO_FULL BIT(4)
#define MCHP_CORESPI_STATUS_TXFIFO_FULL BIT(3)
#define MCHP_CORESPI_STATUS_RXFIFO_EMPTY BIT(2)
#define MCHP_CORESPI_STATUS_DONE BIT(1)
#define MCHP_CORESPI_STATUS_FIRSTFRAME BIT(0)
#define MCHP_CORESPI_INT_TXDONE BIT(0)
#define MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW BIT(2)
#define MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN BIT(3)
#define MCHP_CORESPI_INT_CMDINT BIT(4)
#define MCHP_CORESPI_INT_SSEND BIT(5)
#define MCHP_CORESPI_INT_DATA_RX BIT(6)
#define MCHP_CORESPI_INT_TXRFM BIT(7)
#define MCHP_CORESPI_CONTROL2_INTEN_TXRFMT BIT(7)
#define MCHP_CORESPI_CONTROL2_INTEN_DATA_RX BIT(6)
#define MCHP_CORESPI_CONTROL2_INTEN_SSEND BIT(5)
#define MCHP_CORESPI_CONTROL2_INTEN_CMD BIT(4)
#define INT_ENABLE_MASK (MCHP_CORESPI_CONTROL_TX_DATA_INT | MCHP_CORESPI_CONTROL_RX_OVER_INT | \
MCHP_CORESPI_CONTROL_TX_UNDER_INT)
#define MCHP_CORESPI_REG_CONTROL (0x00)
#define MCHP_CORESPI_REG_INTCLEAR (0x04)
#define MCHP_CORESPI_REG_RXDATA (0x08)
#define MCHP_CORESPI_REG_TXDATA (0x0c)
#define MCHP_CORESPI_REG_INTMASK (0X10)
#define MCHP_CORESPI_REG_INTRAW (0X14)
#define MCHP_CORESPI_REG_CONTROL2 (0x18)
#define MCHP_CORESPI_REG_COMMAND (0x1c)
#define MCHP_CORESPI_REG_STAT (0x20)
#define MCHP_CORESPI_REG_SSEL (0x24)
#define MCHP_CORESPI_REG_TXDATA_LAST (0X28)
#define MCHP_CORESPI_REG_CLK_DIV (0x2c)
struct mchp_corespi {
void __iomem *regs;
struct clk *clk;
const u8 *tx_buf;
u8 *rx_buf;
u32 clk_gen;
int irq;
unsigned int tx_len;
unsigned int rx_len;
u32 fifo_depth;
};
static inline void mchp_corespi_disable(struct mchp_corespi *spi)
{
u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
control &= ~MCHP_CORESPI_CONTROL_ENABLE;
writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
}
static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, u32 fifo_max)
{
for (int i = 0; i < fifo_max; i++) {
u32 data;
while (readb(spi->regs + MCHP_CORESPI_REG_STAT) &
MCHP_CORESPI_STATUS_RXFIFO_EMPTY)
;
/* On TX-only transfers always perform a dummy read */
data = readb(spi->regs + MCHP_CORESPI_REG_RXDATA);
if (spi->rx_buf)
*spi->rx_buf++ = data;
spi->rx_len--;
}
}
static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
{
u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
control |= INT_ENABLE_MASK;
writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
}
static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
{
u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
control &= ~INT_ENABLE_MASK;
writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
}
static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, u32 fifo_max)
{
for (int i = 0; i < fifo_max; i++) {
if (readb(spi->regs + MCHP_CORESPI_REG_STAT) &
MCHP_CORESPI_STATUS_TXFIFO_FULL)
break;
/* On RX-only transfers always perform a dummy write */
if (spi->tx_buf)
writeb(*spi->tx_buf++, spi->regs + MCHP_CORESPI_REG_TXDATA);
else
writeb(0xaa, spi->regs + MCHP_CORESPI_REG_TXDATA);
spi->tx_len--;
}
}
static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
{
struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
u32 reg;
reg = readb(corespi->regs + MCHP_CORESPI_REG_SSEL);
reg &= ~BIT(spi_get_chipselect(spi, 0));
reg |= !disable << spi_get_chipselect(spi, 0);
writeb(reg, corespi->regs + MCHP_CORESPI_REG_SSEL);
}
static int mchp_corespi_setup(struct spi_device *spi)
{
if (spi_get_csgpiod(spi, 0))
return 0;
if (spi->mode & (SPI_CS_HIGH)) {
dev_err(&spi->dev, "unable to support active-high CS in Motorola mode\n");
return -EOPNOTSUPP;
}
if (spi->mode & SPI_MODE_X_MASK & ~spi->controller->mode_bits) {
dev_err(&spi->dev, "incompatible CPOL/CPHA, must match controller's Motorola mode\n");
return -EINVAL;
}
return 0;
}
static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi)
{
u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
/* Master mode changes require core to be disabled.*/
control = (control & ~MCHP_CORESPI_CONTROL_ENABLE) | MCHP_CORESPI_CONTROL_MASTER;
writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
mchp_corespi_enable_ints(spi);
control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL);
control |= MCHP_CORESPI_CONTROL_ENABLE;
writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL);
}
static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
{
struct spi_controller *host = dev_id;
struct mchp_corespi *spi = spi_controller_get_devdata(host);
u8 intfield = readb(spi->regs + MCHP_CORESPI_REG_INTMASK) & 0xff;
bool finalise = false;
/* Interrupt line may be shared and not for us at all */
if (intfield == 0)
return IRQ_NONE;
if (intfield & MCHP_CORESPI_INT_TXDONE)
writeb(MCHP_CORESPI_INT_TXDONE, spi->regs + MCHP_CORESPI_REG_INTCLEAR);
if (intfield & MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW) {
writeb(MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW,
spi->regs + MCHP_CORESPI_REG_INTCLEAR);
finalise = true;
dev_err(&host->dev,
"RX OVERFLOW: rxlen: %u, txlen: %u\n",
spi->rx_len, spi->tx_len);
}
if (intfield & MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN) {
writeb(MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN,
spi->regs + MCHP_CORESPI_REG_INTCLEAR);
finalise = true;
dev_err(&host->dev,
"TX UNDERFLOW: rxlen: %u, txlen: %u\n",
spi->rx_len, spi->tx_len);
}
if (finalise)
spi_finalize_current_transfer(host);
return IRQ_HANDLED;
}
static int mchp_corespi_set_clk_div(struct mchp_corespi *spi,
unsigned long target_hz)
{
unsigned long pclk_hz, spi_hz;
u32 clk_div;
/* Get peripheral clock rate */
pclk_hz = clk_get_rate(spi->clk);
if (!pclk_hz)
return -EINVAL;
/*
* Calculate clock rate generated by SPI master
* Formula: SPICLK = PCLK / (2 * (CLK_DIV + 1))
*/
clk_div = DIV_ROUND_UP(pclk_hz, 2 * target_hz) - 1;
if (clk_div > 0xFF)
return -EINVAL;
spi_hz = pclk_hz / (2 * (clk_div + 1));
if (spi_hz > target_hz)
return -EINVAL;
writeb(clk_div, spi->regs + MCHP_CORESPI_REG_CLK_DIV);
return 0;
}
static int mchp_corespi_transfer_one(struct spi_controller *host,
struct spi_device *spi_dev,
struct spi_transfer *xfer)
{
struct mchp_corespi *spi = spi_controller_get_devdata(host);
int ret;
ret = mchp_corespi_set_clk_div(spi, (unsigned long)xfer->speed_hz);
if (ret) {
dev_err(&host->dev, "failed to set clock divider for target %u Hz\n",
xfer->speed_hz);
return ret;
}
spi->tx_buf = xfer->tx_buf;
spi->rx_buf = xfer->rx_buf;
spi->tx_len = xfer->len;
spi->rx_len = xfer->len;
while (spi->tx_len) {
unsigned int fifo_max = min(spi->tx_len, spi->fifo_depth);
mchp_corespi_write_fifo(spi, fifo_max);
mchp_corespi_read_fifo(spi, fifo_max);
}
spi_finalize_current_transfer(host);
return 1;
}
static int mchp_corespi_probe(struct platform_device *pdev)
{
const char *protocol = "motorola";
struct device *dev = &pdev->dev;
struct spi_controller *host;
struct mchp_corespi *spi;
struct resource *res;
u32 num_cs, mode, frame_size;
bool assert_ssel;
int ret = 0;
host = devm_spi_alloc_host(dev, sizeof(*spi));
if (!host)
return -ENOMEM;
platform_set_drvdata(pdev, host);
if (of_property_read_u32(dev->of_node, "num-cs", &num_cs))
num_cs = MCHP_CORESPI_MAX_CS;
/*
* Protocol: CFG_MODE
* CoreSPI can be configured for Motorola, TI or NSC.
* The current driver supports only Motorola mode.
*/
ret = of_property_read_string(dev->of_node, "microchip,protocol-configuration",
&protocol);
if (ret && ret != -EINVAL)
return dev_err_probe(dev, ret, "Error reading protocol-configuration\n");
if (strcmp(protocol, "motorola") != 0)
return dev_err_probe(dev, -EINVAL,
"CoreSPI: protocol '%s' not supported by this driver\n",
protocol);
/*
* Motorola mode (0-3): CFG_MOT_MODE
* Mode is fixed in the IP configurator.
*/
ret = of_property_read_u32(dev->of_node, "microchip,motorola-mode", &mode);
if (ret)
mode = MCHP_CORESPI_DEFAULT_MOTOROLA_MODE;
else if (mode > 3)
return dev_err_probe(dev, -EINVAL,
"invalid 'microchip,motorola-mode' value %u\n", mode);
/*
* Frame size: CFG_FRAME_SIZE
* The hardware allows frame sizes <= APB data width.
* However, this driver currently only supports 8-bit frames.
*/
ret = of_property_read_u32(dev->of_node, "microchip,frame-size", &frame_size);
if (!ret && frame_size != 8)
return dev_err_probe(dev, -EINVAL,
"CoreSPI: frame size %u not supported by this driver\n",
frame_size);
/*
* SSEL: CFG_MOT_SSEL
* CoreSPI deasserts SSEL when the TX FIFO empties.
* To prevent CS deassertion when TX FIFO drains, the ssel-active property
* keeps CS asserted for the full SPI transfer.
*/
assert_ssel = of_property_read_bool(dev->of_node, "microchip,ssel-active");
if (!assert_ssel)
return dev_err_probe(dev, -EINVAL,
"hardware must enable 'microchip,ssel-active' to keep CS asserted for the SPI transfer\n");
spi = spi_controller_get_devdata(host);
host->num_chipselect = num_cs;
host->mode_bits = mode;
host->setup = mchp_corespi_setup;
host->use_gpio_descriptors = true;
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
host->transfer_one = mchp_corespi_transfer_one;
host->set_cs = mchp_corespi_set_cs;
host->dev.of_node = dev->of_node;
ret = of_property_read_u32(dev->of_node, "fifo-depth", &spi->fifo_depth);
if (ret)
spi->fifo_depth = MCHP_CORESPI_DEFAULT_FIFO_DEPTH;
spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(spi->regs))
return PTR_ERR(spi->regs);
spi->irq = platform_get_irq(pdev, 0);
if (spi->irq < 0)
return spi->irq;
ret = devm_request_irq(dev, spi->irq, mchp_corespi_interrupt, IRQF_SHARED,
dev_name(dev), host);
if (ret)
return dev_err_probe(dev, ret, "could not request irq\n");
spi->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(spi->clk))
return dev_err_probe(dev, PTR_ERR(spi->clk), "could not get clk\n");
mchp_corespi_init(host, spi);
ret = devm_spi_register_controller(dev, host);
if (ret) {
mchp_corespi_disable(spi);
return dev_err_probe(dev, ret, "unable to register host for CoreSPI controller\n");
}
return 0;
}
static void mchp_corespi_remove(struct platform_device *pdev)
{
struct spi_controller *host = platform_get_drvdata(pdev);
struct mchp_corespi *spi = spi_controller_get_devdata(host);
mchp_corespi_disable_ints(spi);
mchp_corespi_disable(spi);
}
/*
* Platform driver data structure
*/
#if defined(CONFIG_OF)
static const struct of_device_id mchp_corespi_dt_ids[] = {
{ .compatible = "microchip,corespi-rtl-v5" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
#endif
static struct platform_driver mchp_corespi_driver = {
.probe = mchp_corespi_probe,
.driver = {
.name = "microchip-corespi",
.of_match_table = of_match_ptr(mchp_corespi_dt_ids),
},
.remove = mchp_corespi_remove,
};
module_platform_driver(mchp_corespi_driver);
MODULE_DESCRIPTION("Microchip CoreSPI controller driver");
MODULE_AUTHOR("Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>");
MODULE_LICENSE("GPL");

View File

@@ -99,7 +99,7 @@
#define REG_CTRL2 (0x48)
#define REG_FRAMESUP (0x50)
struct mchp_corespi {
struct mpfs_spi {
void __iomem *regs;
struct clk *clk;
const u8 *tx_buf;
@@ -113,34 +113,34 @@ struct mchp_corespi {
int n_bytes;
};
static inline u32 mchp_corespi_read(struct mchp_corespi *spi, unsigned int reg)
static inline u32 mpfs_spi_read(struct mpfs_spi *spi, unsigned int reg)
{
return readl(spi->regs + reg);
}
static inline void mchp_corespi_write(struct mchp_corespi *spi, unsigned int reg, u32 val)
static inline void mpfs_spi_write(struct mpfs_spi *spi, unsigned int reg, u32 val)
{
writel(val, spi->regs + reg);
}
static inline void mchp_corespi_disable(struct mchp_corespi *spi)
static inline void mpfs_spi_disable(struct mpfs_spi *spi)
{
u32 control = mchp_corespi_read(spi, REG_CONTROL);
u32 control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
}
static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, int fifo_max)
static inline void mpfs_spi_read_fifo(struct mpfs_spi *spi, int fifo_max)
{
for (int i = 0; i < fifo_max; i++) {
u32 data;
while (mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)
while (mpfs_spi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)
;
data = mchp_corespi_read(spi, REG_RX_DATA);
data = mpfs_spi_read(spi, REG_RX_DATA);
spi->rx_len -= spi->n_bytes;
@@ -158,34 +158,34 @@ static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, int fifo_max
}
}
static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
static void mpfs_spi_enable_ints(struct mpfs_spi *spi)
{
u32 control = mchp_corespi_read(spi, REG_CONTROL);
u32 control = mpfs_spi_read(spi, REG_CONTROL);
control |= INT_ENABLE_MASK;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
}
static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
static void mpfs_spi_disable_ints(struct mpfs_spi *spi)
{
u32 control = mchp_corespi_read(spi, REG_CONTROL);
u32 control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~INT_ENABLE_MASK;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
}
static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len)
static inline void mpfs_spi_set_xfer_size(struct mpfs_spi *spi, int len)
{
u32 control;
u32 lenpart;
u32 frames = mchp_corespi_read(spi, REG_FRAMESUP);
u32 frames = mpfs_spi_read(spi, REG_FRAMESUP);
/*
* Writing to FRAMECNT in REG_CONTROL will reset the frame count, taking
* a shortcut requires an explicit clear.
*/
if (frames == len) {
mchp_corespi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT);
mpfs_spi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT);
return;
}
@@ -208,20 +208,20 @@ static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len)
* that matches the documentation.
*/
lenpart = len & 0xffff;
control = mchp_corespi_read(spi, REG_CONTROL);
control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_FRAMECNT_MASK;
control |= lenpart << CONTROL_FRAMECNT_SHIFT;
mchp_corespi_write(spi, REG_CONTROL, control);
mchp_corespi_write(spi, REG_FRAMESUP, len);
mpfs_spi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_FRAMESUP, len);
}
static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_max)
static inline void mpfs_spi_write_fifo(struct mpfs_spi *spi, int fifo_max)
{
int i = 0;
mchp_corespi_set_xfer_size(spi, fifo_max);
mpfs_spi_set_xfer_size(spi, fifo_max);
while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
while ((i < fifo_max) && !(mpfs_spi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
u32 word;
if (spi->n_bytes == 4)
@@ -231,7 +231,7 @@ static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_ma
else
word = spi->tx_buf ? *spi->tx_buf : 0xaa;
mchp_corespi_write(spi, REG_TX_DATA, word);
mpfs_spi_write(spi, REG_TX_DATA, word);
if (spi->tx_buf)
spi->tx_buf += spi->n_bytes;
i++;
@@ -240,9 +240,9 @@ static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_ma
spi->tx_len -= i * spi->n_bytes;
}
static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt)
static inline void mpfs_spi_set_framesize(struct mpfs_spi *spi, int bt)
{
u32 frame_size = mchp_corespi_read(spi, REG_FRAME_SIZE);
u32 frame_size = mpfs_spi_read(spi, REG_FRAME_SIZE);
u32 control;
if ((frame_size & FRAME_SIZE_MASK) == bt)
@@ -252,25 +252,25 @@ static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt)
* Disable the SPI controller. Writes to the frame size have
* no effect when the controller is enabled.
*/
control = mchp_corespi_read(spi, REG_CONTROL);
control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
mchp_corespi_write(spi, REG_FRAME_SIZE, bt);
mpfs_spi_write(spi, REG_FRAME_SIZE, bt);
control |= CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
}
static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
static void mpfs_spi_set_cs(struct spi_device *spi, bool disable)
{
u32 reg;
struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
struct mpfs_spi *mspi = spi_controller_get_devdata(spi->controller);
reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
reg = mpfs_spi_read(mspi, REG_SLAVE_SELECT);
reg &= ~BIT(spi_get_chipselect(spi, 0));
reg |= !disable << spi_get_chipselect(spi, 0);
corespi->pending_slave_select = reg;
mspi->pending_slave_select = reg;
/*
* Only deassert chip select immediately. Writing to some registers
@@ -281,12 +281,12 @@ static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
* doesn't see any spurious clock transitions whilst CS is enabled.
*/
if (((spi->mode & SPI_CS_HIGH) == 0) == disable)
mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
mpfs_spi_write(mspi, REG_SLAVE_SELECT, reg);
}
static int mchp_corespi_setup(struct spi_device *spi)
static int mpfs_spi_setup(struct spi_device *spi)
{
struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
struct mpfs_spi *mspi = spi_controller_get_devdata(spi->controller);
u32 reg;
if (spi_is_csgpiod(spi))
@@ -298,21 +298,21 @@ static int mchp_corespi_setup(struct spi_device *spi)
* driving their select line low.
*/
if (spi->mode & SPI_CS_HIGH) {
reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
reg = mpfs_spi_read(mspi, REG_SLAVE_SELECT);
reg |= BIT(spi_get_chipselect(spi, 0));
corespi->pending_slave_select = reg;
mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
mspi->pending_slave_select = reg;
mpfs_spi_write(mspi, REG_SLAVE_SELECT, reg);
}
return 0;
}
static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi)
static void mpfs_spi_init(struct spi_controller *host, struct mpfs_spi *spi)
{
unsigned long clk_hz;
u32 control = mchp_corespi_read(spi, REG_CONTROL);
u32 control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
control |= CONTROL_MASTER;
control &= ~CONTROL_MODE_MASK;
@@ -328,15 +328,15 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *
*/
control |= CONTROL_SPS | CONTROL_BIGFIFO;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE);
mpfs_spi_set_framesize(spi, DEFAULT_FRAMESIZE);
/* max. possible spi clock rate is the apb clock rate */
clk_hz = clk_get_rate(spi->clk);
host->max_speed_hz = clk_hz;
mchp_corespi_enable_ints(spi);
mpfs_spi_enable_ints(spi);
/*
* It is required to enable direct mode, otherwise control over the chip
@@ -344,34 +344,34 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *
* can deal with active high targets.
*/
spi->pending_slave_select = SSELOUT | SSEL_DIRECT;
mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
mpfs_spi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
control = mchp_corespi_read(spi, REG_CONTROL);
control = mpfs_spi_read(spi, REG_CONTROL);
control &= ~CONTROL_RESET;
control |= CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
}
static inline void mchp_corespi_set_clk_gen(struct mchp_corespi *spi)
static inline void mpfs_spi_set_clk_gen(struct mpfs_spi *spi)
{
u32 control;
control = mchp_corespi_read(spi, REG_CONTROL);
control = mpfs_spi_read(spi, REG_CONTROL);
if (spi->clk_mode)
control |= CONTROL_CLKMODE;
else
control &= ~CONTROL_CLKMODE;
mchp_corespi_write(spi, REG_CLK_GEN, spi->clk_gen);
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CLK_GEN, spi->clk_gen);
mpfs_spi_write(spi, REG_CONTROL, control);
}
static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int mode)
static inline void mpfs_spi_set_mode(struct mpfs_spi *spi, unsigned int mode)
{
u32 mode_val;
u32 control = mchp_corespi_read(spi, REG_CONTROL);
u32 control = mpfs_spi_read(spi, REG_CONTROL);
switch (mode & SPI_MODE_X_MASK) {
case SPI_MODE_0:
@@ -394,22 +394,22 @@ static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int
*/
control &= ~CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
control &= ~(SPI_MODE_X_MASK << MODE_X_MASK_SHIFT);
control |= mode_val;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
control |= CONTROL_ENABLE;
mchp_corespi_write(spi, REG_CONTROL, control);
mpfs_spi_write(spi, REG_CONTROL, control);
}
static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
static irqreturn_t mpfs_spi_interrupt(int irq, void *dev_id)
{
struct spi_controller *host = dev_id;
struct mchp_corespi *spi = spi_controller_get_devdata(host);
u32 intfield = mchp_corespi_read(spi, REG_MIS) & 0xf;
struct mpfs_spi *spi = spi_controller_get_devdata(host);
u32 intfield = mpfs_spi_read(spi, REG_MIS) & 0xf;
bool finalise = false;
/* Interrupt line may be shared and not for us at all */
@@ -417,7 +417,7 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
return IRQ_NONE;
if (intfield & INT_RX_CHANNEL_OVERFLOW) {
mchp_corespi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
mpfs_spi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
finalise = true;
dev_err(&host->dev,
"%s: RX OVERFLOW: rxlen: %d, txlen: %d\n", __func__,
@@ -425,7 +425,7 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
}
if (intfield & INT_TX_CHANNEL_UNDERRUN) {
mchp_corespi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN);
mpfs_spi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN);
finalise = true;
dev_err(&host->dev,
"%s: TX UNDERFLOW: rxlen: %d, txlen: %d\n", __func__,
@@ -438,8 +438,8 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int mchp_corespi_calculate_clkgen(struct mchp_corespi *spi,
unsigned long target_hz)
static int mpfs_spi_calculate_clkgen(struct mpfs_spi *spi,
unsigned long target_hz)
{
unsigned long clk_hz, spi_hz, clk_gen;
@@ -475,20 +475,20 @@ static int mchp_corespi_calculate_clkgen(struct mchp_corespi *spi,
return 0;
}
static int mchp_corespi_transfer_one(struct spi_controller *host,
struct spi_device *spi_dev,
struct spi_transfer *xfer)
static int mpfs_spi_transfer_one(struct spi_controller *host,
struct spi_device *spi_dev,
struct spi_transfer *xfer)
{
struct mchp_corespi *spi = spi_controller_get_devdata(host);
struct mpfs_spi *spi = spi_controller_get_devdata(host);
int ret;
ret = mchp_corespi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz);
ret = mpfs_spi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz);
if (ret) {
dev_err(&host->dev, "failed to set clk_gen for target %u Hz\n", xfer->speed_hz);
return ret;
}
mchp_corespi_set_clk_gen(spi);
mpfs_spi_set_clk_gen(spi);
spi->tx_buf = xfer->tx_buf;
spi->rx_buf = xfer->rx_buf;
@@ -496,45 +496,46 @@ static int mchp_corespi_transfer_one(struct spi_controller *host,
spi->rx_len = xfer->len;
spi->n_bytes = roundup_pow_of_two(DIV_ROUND_UP(xfer->bits_per_word, BITS_PER_BYTE));
mchp_corespi_set_framesize(spi, xfer->bits_per_word);
mpfs_spi_set_framesize(spi, xfer->bits_per_word);
mchp_corespi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST);
mpfs_spi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST);
mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
mpfs_spi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
while (spi->tx_len) {
int fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes);
mchp_corespi_write_fifo(spi, fifo_max);
mchp_corespi_read_fifo(spi, fifo_max);
mpfs_spi_write_fifo(spi, fifo_max);
mpfs_spi_read_fifo(spi, fifo_max);
}
spi_finalize_current_transfer(host);
return 1;
}
static int mchp_corespi_prepare_message(struct spi_controller *host,
struct spi_message *msg)
static int mpfs_spi_prepare_message(struct spi_controller *host,
struct spi_message *msg)
{
struct spi_device *spi_dev = msg->spi;
struct mchp_corespi *spi = spi_controller_get_devdata(host);
struct mpfs_spi *spi = spi_controller_get_devdata(host);
mchp_corespi_set_mode(spi, spi_dev->mode);
mpfs_spi_set_mode(spi, spi_dev->mode);
return 0;
}
static int mchp_corespi_probe(struct platform_device *pdev)
static int mpfs_spi_probe(struct platform_device *pdev)
{
struct spi_controller *host;
struct mchp_corespi *spi;
struct mpfs_spi *spi;
struct resource *res;
u32 num_cs;
int ret = 0;
host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi));
if (!host)
return -ENOMEM;
return dev_err_probe(&pdev->dev, -ENOMEM,
"unable to allocate host for SPI controller\n");
platform_set_drvdata(pdev, host);
@@ -544,11 +545,11 @@ static int mchp_corespi_probe(struct platform_device *pdev)
host->num_chipselect = num_cs;
host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
host->use_gpio_descriptors = true;
host->setup = mchp_corespi_setup;
host->setup = mpfs_spi_setup;
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
host->transfer_one = mchp_corespi_transfer_one;
host->prepare_message = mchp_corespi_prepare_message;
host->set_cs = mchp_corespi_set_cs;
host->transfer_one = mpfs_spi_transfer_one;
host->prepare_message = mpfs_spi_prepare_message;
host->set_cs = mpfs_spi_set_cs;
host->dev.of_node = pdev->dev.of_node;
spi = spi_controller_get_devdata(host);
@@ -561,7 +562,7 @@ static int mchp_corespi_probe(struct platform_device *pdev)
if (spi->irq < 0)
return spi->irq;
ret = devm_request_irq(&pdev->dev, spi->irq, mchp_corespi_interrupt,
ret = devm_request_irq(&pdev->dev, spi->irq, mpfs_spi_interrupt,
IRQF_SHARED, dev_name(&pdev->dev), host);
if (ret)
return dev_err_probe(&pdev->dev, ret,
@@ -572,11 +573,11 @@ static int mchp_corespi_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk),
"could not get clk\n");
mchp_corespi_init(host, spi);
mpfs_spi_init(host, spi);
ret = devm_spi_register_controller(&pdev->dev, host);
if (ret) {
mchp_corespi_disable(spi);
mpfs_spi_disable(spi);
return dev_err_probe(&pdev->dev, ret,
"unable to register host for SPI controller\n");
}
@@ -586,13 +587,13 @@ static int mchp_corespi_probe(struct platform_device *pdev)
return 0;
}
static void mchp_corespi_remove(struct platform_device *pdev)
static void mpfs_spi_remove(struct platform_device *pdev)
{
struct spi_controller *host = platform_get_drvdata(pdev);
struct mchp_corespi *spi = spi_controller_get_devdata(host);
struct mpfs_spi *spi = spi_controller_get_devdata(host);
mchp_corespi_disable_ints(spi);
mchp_corespi_disable(spi);
mpfs_spi_disable_ints(spi);
mpfs_spi_disable(spi);
}
#define MICROCHIP_SPI_PM_OPS (NULL)
@@ -602,23 +603,23 @@ static void mchp_corespi_remove(struct platform_device *pdev)
*/
#if defined(CONFIG_OF)
static const struct of_device_id mchp_corespi_dt_ids[] = {
static const struct of_device_id mpfs_spi_dt_ids[] = {
{ .compatible = "microchip,mpfs-spi" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
MODULE_DEVICE_TABLE(of, mpfs_spi_dt_ids);
#endif
static struct platform_driver mchp_corespi_driver = {
.probe = mchp_corespi_probe,
static struct platform_driver mpfs_spi_driver = {
.probe = mpfs_spi_probe,
.driver = {
.name = "microchip-corespi",
.name = "microchip-spi",
.pm = MICROCHIP_SPI_PM_OPS,
.of_match_table = of_match_ptr(mchp_corespi_dt_ids),
.of_match_table = of_match_ptr(mpfs_spi_dt_ids),
},
.remove = mchp_corespi_remove,
.remove = mpfs_spi_remove,
};
module_platform_driver(mchp_corespi_driver);
module_platform_driver(mpfs_spi_driver);
MODULE_DESCRIPTION("Microchip coreSPI SPI controller driver");
MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");

View File

@@ -51,12 +51,14 @@ static int spi_offload_trigger_pwm_validate(struct spi_offload_trigger *trigger,
wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, periodic->frequency_hz);
/* REVISIT: 50% duty-cycle for now - may add config parameter later */
wf.duty_length_ns = wf.period_length_ns / 2;
wf.duty_offset_ns = periodic->offset_ns;
ret = pwm_round_waveform_might_sleep(st->pwm, &wf);
if (ret < 0)
return ret;
periodic->frequency_hz = DIV_ROUND_UP_ULL(NSEC_PER_SEC, wf.period_length_ns);
periodic->offset_ns = wf.duty_offset_ns;
return 0;
}
@@ -77,6 +79,7 @@ static int spi_offload_trigger_pwm_enable(struct spi_offload_trigger *trigger,
wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, periodic->frequency_hz);
/* REVISIT: 50% duty-cycle for now - may add config parameter later */
wf.duty_length_ns = wf.period_length_ns / 2;
wf.duty_offset_ns = periodic->offset_ns;
return pwm_set_waveform_might_sleep(st->pwm, &wf, false);
}

View File

@@ -448,7 +448,7 @@ static int qcom_spi_ecc_finish_io_req_pipelined(struct nand_device *nand,
return snandc->qspi->ecc_stats.bitflips;
}
static struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = {
static const struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = {
.init_ctx = qcom_spi_ecc_init_ctx_pipelined,
.cleanup_ctx = qcom_spi_ecc_cleanup_ctx_pipelined,
.prepare_io_req = qcom_spi_ecc_prepare_io_req_pipelined,

View File

@@ -24,6 +24,7 @@
/* Registers */
#define RSPI_SPDR 0x00
#define RSPI_SPCR 0x08
#define RSPI_SPPCR 0x0e
#define RSPI_SSLP 0x10
#define RSPI_SPBR 0x11
#define RSPI_SPSCR 0x13
@@ -34,13 +35,18 @@
#define RSPI_SPFCR 0x6c
/* Register SPCR */
#define RSPI_SPCR_BPEN BIT(31)
#define RSPI_SPCR_MSTR BIT(30)
#define RSPI_SPCR_SPRIE BIT(17)
#define RSPI_SPCR_SCKASE BIT(12)
#define RSPI_SPCR_SPE BIT(0)
/* Register SPPCR */
#define RSPI_SPPCR_SPLP2 BIT(1)
/* Register SPBR */
#define RSPI_SPBR_SPR_MIN 0
#define RSPI_SPBR_SPR_PCLK_MIN 1
#define RSPI_SPBR_SPR_MAX 255
/* Register SPCMD */
@@ -58,7 +64,6 @@
/* Register SPDCR2 */
#define RSPI_SPDCR2_TTRG GENMASK(11, 8)
#define RSPI_SPDCR2_RTRG GENMASK(3, 0)
#define RSPI_FIFO_SIZE 16
/* Register SPSR */
#define RSPI_SPSR_SPRF BIT(15)
@@ -67,17 +72,41 @@
#define RSPI_SPSRC_CLEAR 0xfd80
#define RSPI_RESET_NUM 2
#define RSPI_CLK_NUM 3
struct rzv2h_rspi_best_clock {
struct clk *clk;
unsigned long clk_rate;
unsigned long error;
u32 actual_hz;
u8 brdv;
u8 spr;
};
struct rzv2h_rspi_info {
void (*find_tclk_rate)(struct clk *clk, u32 hz, u8 spr_min, u8 spr_max,
struct rzv2h_rspi_best_clock *best_clk);
void (*find_pclk_rate)(struct clk *clk, u32 hz, u8 spr_low, u8 spr_high,
struct rzv2h_rspi_best_clock *best_clk);
const char *tclk_name;
unsigned int fifo_size;
unsigned int num_clks;
};
struct rzv2h_rspi_priv {
struct reset_control_bulk_data resets[RSPI_RESET_NUM];
struct spi_controller *controller;
const struct rzv2h_rspi_info *info;
void __iomem *base;
struct clk *tclk;
struct clk *pclk;
wait_queue_head_t wait;
unsigned int bytes_per_word;
u32 last_speed_hz;
u32 freq;
u16 status;
u8 spr;
u8 brdv;
bool use_pclk;
};
#define RZV2H_RSPI_TX(func, type) \
@@ -232,9 +261,112 @@ static inline u32 rzv2h_rspi_calc_bitrate(unsigned long tclk_rate, u8 spr,
return DIV_ROUND_UP(tclk_rate, (2 * (spr + 1) * (1 << brdv)));
}
static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz)
static void rzv2h_rspi_find_rate_variable(struct clk *clk, u32 hz,
u8 spr_min, u8 spr_max,
struct rzv2h_rspi_best_clock *best)
{
unsigned long tclk_rate;
long clk_rate, clk_min_rate, clk_max_rate;
int min_rate_spr, max_rate_spr;
unsigned long error;
u32 actual_hz;
u8 brdv;
int spr;
/*
* On T2H / N2H, the source for the SPI clock is PCLKSPIn, which is a
* 1/32, 1/30, 1/25 or 1/24 divider of PLL4, which is 2400MHz,
* resulting in either 75MHz, 80MHz, 96MHz or 100MHz.
*/
clk_min_rate = clk_round_rate(clk, 0);
if (clk_min_rate < 0)
return;
clk_max_rate = clk_round_rate(clk, ULONG_MAX);
if (clk_max_rate < 0)
return;
/*
* From the manual:
* Bit rate = f(PCLKSPIn) / (2 * (n + 1) * 2^N)
*
* If we adapt it to the current context, we get the following:
* hz = rate / ((spr + 1) * (1 << (brdv + 1)))
*
* This can be written in multiple forms depending on what we want to
* determine.
*
* To find the rate, having hz, spr and brdv:
* rate = hz * (spr + 1) * (1 << (brdv + 1)
*
* To find the spr, having rate, hz, and spr:
* spr = rate / (hz * (1 << (brdv + 1)) - 1
*/
for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) {
/* Calculate the divisor needed to find the SPR from a rate. */
u32 rate_div = hz * (1 << (brdv + 1));
/*
* If the SPR for the minimum rate is greater than the maximum
* allowed value skip this BRDV. The divisor increases with each
* BRDV iteration, so the following BRDV might result in a
* minimum SPR that is in the valid range.
*/
min_rate_spr = DIV_ROUND_CLOSEST(clk_min_rate, rate_div) - 1;
if (min_rate_spr > spr_max)
continue;
/*
* If the SPR for the maximum rate is less than the minimum
* allowed value, exit. The divisor only increases with each
* BRDV iteration, so the following BRDV cannot result in a
* maximum SPR that is in the valid range.
*/
max_rate_spr = DIV_ROUND_CLOSEST(clk_max_rate, rate_div) - 1;
if (max_rate_spr < spr_min)
break;
if (min_rate_spr < spr_min)
min_rate_spr = spr_min;
if (max_rate_spr > spr_max)
max_rate_spr = spr_max;
for (spr = min_rate_spr; spr <= max_rate_spr; spr++) {
clk_rate = (spr + 1) * rate_div;
clk_rate = clk_round_rate(clk, clk_rate);
if (clk_rate <= 0)
continue;
actual_hz = rzv2h_rspi_calc_bitrate(clk_rate, spr, brdv);
error = abs((long)hz - (long)actual_hz);
if (error >= best->error)
continue;
*best = (struct rzv2h_rspi_best_clock) {
.clk = clk,
.clk_rate = clk_rate,
.error = error,
.actual_hz = actual_hz,
.brdv = brdv,
.spr = spr,
};
if (!error)
return;
}
}
}
static void rzv2h_rspi_find_rate_fixed(struct clk *clk, u32 hz,
u8 spr_min, u8 spr_max,
struct rzv2h_rspi_best_clock *best)
{
unsigned long clk_rate;
unsigned long error;
u32 actual_hz;
int spr;
u8 brdv;
@@ -247,21 +379,63 @@ static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz)
* * n = SPR - is RSPI_SPBR.SPR (from 0 to 255)
* * N = BRDV - is RSPI_SPCMD.BRDV (from 0 to 3)
*/
tclk_rate = clk_get_rate(rspi->tclk);
clk_rate = clk_get_rate(clk);
for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) {
spr = DIV_ROUND_UP(tclk_rate, hz * (1 << (brdv + 1)));
spr = DIV_ROUND_UP(clk_rate, hz * (1 << (brdv + 1)));
spr--;
if (spr >= RSPI_SPBR_SPR_MIN && spr <= RSPI_SPBR_SPR_MAX)
if (spr >= spr_min && spr <= spr_max)
goto clock_found;
}
return 0;
return;
clock_found:
rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_BRDV, brdv);
writeb(spr, rspi->base + RSPI_SPBR);
actual_hz = rzv2h_rspi_calc_bitrate(clk_rate, spr, brdv);
error = abs((long)hz - (long)actual_hz);
return rzv2h_rspi_calc_bitrate(tclk_rate, spr, brdv);
if (error >= best->error)
return;
*best = (struct rzv2h_rspi_best_clock) {
.clk = clk,
.clk_rate = clk_rate,
.error = error,
.actual_hz = actual_hz,
.brdv = brdv,
.spr = spr,
};
}
static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz)
{
struct rzv2h_rspi_best_clock best_clock = {
.error = ULONG_MAX,
};
int ret;
rspi->info->find_tclk_rate(rspi->tclk, hz, RSPI_SPBR_SPR_MIN,
RSPI_SPBR_SPR_MAX, &best_clock);
/*
* T2H and N2H can also use PCLK as a source, which is 125MHz, but not
* when both SPR and BRDV are 0.
*/
if (best_clock.error && rspi->info->find_pclk_rate)
rspi->info->find_pclk_rate(rspi->pclk, hz, RSPI_SPBR_SPR_PCLK_MIN,
RSPI_SPBR_SPR_MAX, &best_clock);
if (!best_clock.clk_rate)
return -EINVAL;
ret = clk_set_rate(best_clock.clk, best_clock.clk_rate);
if (ret)
return 0;
rspi->use_pclk = best_clock.clk == rspi->pclk;
rspi->spr = best_clock.spr;
rspi->brdv = best_clock.brdv;
return best_clock.actual_hz;
}
static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
@@ -274,43 +448,11 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
u8 bits_per_word;
u32 conf32;
u16 conf16;
u8 conf8;
/* Make sure SPCR.SPE is 0 before amending the configuration */
rzv2h_rspi_spe_disable(rspi);
/* Configure the device to work in "host" mode */
conf32 = RSPI_SPCR_MSTR;
/* Auto-stop function */
conf32 |= RSPI_SPCR_SCKASE;
/* SPI receive buffer full interrupt enable */
conf32 |= RSPI_SPCR_SPRIE;
writel(conf32, rspi->base + RSPI_SPCR);
/* Use SPCMD0 only */
writeb(0x0, rspi->base + RSPI_SPSCR);
/* Setup mode */
conf32 = FIELD_PREP(RSPI_SPCMD_CPOL, !!(spi->mode & SPI_CPOL));
conf32 |= FIELD_PREP(RSPI_SPCMD_CPHA, !!(spi->mode & SPI_CPHA));
conf32 |= FIELD_PREP(RSPI_SPCMD_LSBF, !!(spi->mode & SPI_LSB_FIRST));
conf32 |= FIELD_PREP(RSPI_SPCMD_SSLKP, 1);
conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0));
writel(conf32, rspi->base + RSPI_SPCMD);
if (spi->mode & SPI_CS_HIGH)
writeb(BIT(spi_get_chipselect(spi, 0)), rspi->base + RSPI_SSLP);
else
writeb(0, rspi->base + RSPI_SSLP);
/* Setup FIFO thresholds */
conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, RSPI_FIFO_SIZE - 1);
conf16 |= FIELD_PREP(RSPI_SPDCR2_RTRG, 0);
writew(conf16, rspi->base + RSPI_SPDCR2);
rzv2h_rspi_clear_fifos(rspi);
list_for_each_entry(xfer, &message->transfers, transfer_list) {
if (!xfer->speed_hz)
continue;
@@ -323,11 +465,58 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr,
return -EINVAL;
rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word));
rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_SPB, bits_per_word - 1);
rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz);
if (!rspi->freq)
return -EINVAL;
if (speed_hz != rspi->last_speed_hz) {
rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz);
if (!rspi->freq)
return -EINVAL;
rspi->last_speed_hz = speed_hz;
}
writeb(rspi->spr, rspi->base + RSPI_SPBR);
/* Configure the device to work in "host" mode */
conf32 = RSPI_SPCR_MSTR;
/* Auto-stop function */
conf32 |= RSPI_SPCR_SCKASE;
/* SPI receive buffer full interrupt enable */
conf32 |= RSPI_SPCR_SPRIE;
/* Bypass synchronization circuit */
conf32 |= FIELD_PREP(RSPI_SPCR_BPEN, rspi->use_pclk);
writel(conf32, rspi->base + RSPI_SPCR);
/* Use SPCMD0 only */
writeb(0x0, rspi->base + RSPI_SPSCR);
/* Setup loopback */
conf8 = FIELD_PREP(RSPI_SPPCR_SPLP2, !!(spi->mode & SPI_LOOP));
writeb(conf8, rspi->base + RSPI_SPPCR);
/* Setup mode */
conf32 = FIELD_PREP(RSPI_SPCMD_CPOL, !!(spi->mode & SPI_CPOL));
conf32 |= FIELD_PREP(RSPI_SPCMD_CPHA, !!(spi->mode & SPI_CPHA));
conf32 |= FIELD_PREP(RSPI_SPCMD_LSBF, !!(spi->mode & SPI_LSB_FIRST));
conf32 |= FIELD_PREP(RSPI_SPCMD_SPB, bits_per_word - 1);
conf32 |= FIELD_PREP(RSPI_SPCMD_BRDV, rspi->brdv);
conf32 |= FIELD_PREP(RSPI_SPCMD_SSLKP, 1);
conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0));
writel(conf32, rspi->base + RSPI_SPCMD);
if (spi->mode & SPI_CS_HIGH)
writeb(BIT(spi_get_chipselect(spi, 0)), rspi->base + RSPI_SSLP);
else
writeb(0, rspi->base + RSPI_SSLP);
/* Setup FIFO thresholds */
conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, rspi->info->fifo_size - 1);
conf16 |= FIELD_PREP(RSPI_SPDCR2_RTRG, 0);
writew(conf16, rspi->base + RSPI_SPDCR2);
rzv2h_rspi_clear_fifos(rspi);
rzv2h_rspi_spe_enable(rspi);
@@ -350,8 +539,8 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct rzv2h_rspi_priv *rspi;
struct clk_bulk_data *clks;
unsigned long tclk_rate;
int irq_rx, ret, i;
long tclk_rate;
controller = devm_spi_alloc_host(dev, sizeof(*rspi));
if (!controller)
@@ -362,30 +551,32 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
rspi->controller = controller;
rspi->info = device_get_match_data(dev);
rspi->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rspi->base))
return PTR_ERR(rspi->base);
ret = devm_clk_bulk_get_all_enabled(dev, &clks);
if (ret != RSPI_CLK_NUM)
if (ret != rspi->info->num_clks)
return dev_err_probe(dev, ret >= 0 ? -EINVAL : ret,
"cannot get clocks\n");
for (i = 0; i < RSPI_CLK_NUM; i++) {
if (!strcmp(clks[i].id, "tclk")) {
for (i = 0; i < rspi->info->num_clks; i++) {
if (!strcmp(clks[i].id, rspi->info->tclk_name)) {
rspi->tclk = clks[i].clk;
break;
} else if (rspi->info->find_pclk_rate &&
!strcmp(clks[i].id, "pclk")) {
rspi->pclk = clks[i].clk;
}
}
if (!rspi->tclk)
return dev_err_probe(dev, -EINVAL, "Failed to get tclk\n");
tclk_rate = clk_get_rate(rspi->tclk);
rspi->resets[0].id = "presetn";
rspi->resets[1].id = "tresetn";
ret = devm_reset_control_bulk_get_exclusive(dev, RSPI_RESET_NUM,
rspi->resets);
ret = devm_reset_control_bulk_get_optional_exclusive(dev, RSPI_RESET_NUM,
rspi->resets);
if (ret)
return dev_err_probe(dev, ret, "cannot get resets\n");
@@ -407,15 +598,29 @@ static int rzv2h_rspi_probe(struct platform_device *pdev)
}
controller->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
SPI_LSB_FIRST;
SPI_LSB_FIRST | SPI_LOOP;
controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
controller->prepare_message = rzv2h_rspi_prepare_message;
controller->unprepare_message = rzv2h_rspi_unprepare_message;
controller->num_chipselect = 4;
controller->transfer_one = rzv2h_rspi_transfer_one;
tclk_rate = clk_round_rate(rspi->tclk, 0);
if (tclk_rate < 0) {
ret = tclk_rate;
goto quit_resets;
}
controller->min_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate,
RSPI_SPBR_SPR_MAX,
RSPI_SPCMD_BRDV_MAX);
tclk_rate = clk_round_rate(rspi->tclk, ULONG_MAX);
if (tclk_rate < 0) {
ret = tclk_rate;
goto quit_resets;
}
controller->max_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate,
RSPI_SPBR_SPR_MIN,
RSPI_SPCMD_BRDV_MIN);
@@ -445,8 +650,24 @@ static void rzv2h_rspi_remove(struct platform_device *pdev)
reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets);
}
static const struct rzv2h_rspi_info rzv2h_info = {
.find_tclk_rate = rzv2h_rspi_find_rate_fixed,
.tclk_name = "tclk",
.fifo_size = 16,
.num_clks = 3,
};
static const struct rzv2h_rspi_info rzt2h_info = {
.find_tclk_rate = rzv2h_rspi_find_rate_variable,
.find_pclk_rate = rzv2h_rspi_find_rate_fixed,
.tclk_name = "pclkspi",
.fifo_size = 4,
.num_clks = 2,
};
static const struct of_device_id rzv2h_rspi_match[] = {
{ .compatible = "renesas,r9a09g057-rspi" },
{ .compatible = "renesas,r9a09g057-rspi", &rzv2h_info },
{ .compatible = "renesas,r9a09g077-rspi", &rzt2h_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzv2h_rspi_match);

View File

@@ -42,6 +42,7 @@
#define SPIFMC_TRAN_CSR_TRAN_MODE_RX BIT(0)
#define SPIFMC_TRAN_CSR_TRAN_MODE_TX BIT(1)
#define SPIFMC_TRAN_CSR_FAST_MODE BIT(3)
#define SPIFMC_TRAN_CSR_BUS_WIDTH_MASK GENMASK(5, 4)
#define SPIFMC_TRAN_CSR_BUS_WIDTH_1_BIT (0x00 << 4)
#define SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT (0x01 << 4)
#define SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT (0x02 << 4)
@@ -122,8 +123,7 @@ static u32 sg2044_spifmc_init_reg(struct sg2044_spifmc *spifmc)
reg = readl(spifmc->io_base + SPIFMC_TRAN_CSR);
reg &= ~(SPIFMC_TRAN_CSR_TRAN_MODE_MASK |
SPIFMC_TRAN_CSR_FAST_MODE |
SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT |
SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT |
SPIFMC_TRAN_CSR_BUS_WIDTH_MASK |
SPIFMC_TRAN_CSR_DMA_EN |
SPIFMC_TRAN_CSR_ADDR_BYTES_MASK |
SPIFMC_TRAN_CSR_WITH_CMD |

View File

@@ -1019,13 +1019,20 @@ static void tegra_qspi_dump_regs(struct tegra_qspi *tqspi)
tegra_qspi_readl(tqspi, QSPI_FIFO_STATUS));
}
static void tegra_qspi_reset(struct tegra_qspi *tqspi)
{
if (device_reset(tqspi->dev) < 0) {
dev_warn_once(tqspi->dev, "device reset failed\n");
tegra_qspi_mask_clear_irq(tqspi);
}
}
static void tegra_qspi_handle_error(struct tegra_qspi *tqspi)
{
dev_err(tqspi->dev, "error in transfer, fifo status 0x%08x\n", tqspi->status_reg);
tegra_qspi_dump_regs(tqspi);
tegra_qspi_flush_fifos(tqspi, true);
if (device_reset(tqspi->dev) < 0)
dev_warn_once(tqspi->dev, "device reset failed\n");
tegra_qspi_reset(tqspi);
}
static void tegra_qspi_transfer_end(struct spi_device *spi)
@@ -1041,6 +1048,49 @@ static void tegra_qspi_transfer_end(struct spi_device *spi)
tegra_qspi_writel(tqspi, tqspi->def_command1_reg, QSPI_COMMAND1);
}
static irqreturn_t handle_cpu_based_xfer(struct tegra_qspi *tqspi);
static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi);
/**
* tegra_qspi_handle_timeout - Handle transfer timeout with hardware check
* @tqspi: QSPI controller instance
*
* When a timeout occurs but hardware has completed the transfer (interrupt
* was lost or delayed), manually trigger transfer completion processing.
* This avoids failing transfers that actually succeeded.
*
* Returns: 0 if transfer was completed, -ETIMEDOUT if real timeout
*/
static int tegra_qspi_handle_timeout(struct tegra_qspi *tqspi)
{
irqreturn_t ret;
u32 status;
/* Check if hardware actually completed the transfer */
status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
if (!(status & QSPI_RDY))
return -ETIMEDOUT;
/*
* Hardware completed but interrupt was lost/delayed. Manually
* process the completion by calling the appropriate handler.
*/
dev_warn_ratelimited(tqspi->dev,
"QSPI interrupt timeout, but transfer complete\n");
/* Clear the transfer status */
status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS);
tegra_qspi_writel(tqspi, status, QSPI_TRANS_STATUS);
/* Manually trigger completion handler */
if (!tqspi->is_curr_dma_xfer)
ret = handle_cpu_based_xfer(tqspi);
else
ret = handle_dma_based_xfer(tqspi);
return (ret == IRQ_HANDLED) ? 0 : -EIO;
}
static u32 tegra_qspi_cmd_config(bool is_ddr, u8 bus_width, u8 len)
{
u32 cmd_config = 0;
@@ -1072,6 +1122,30 @@ static u32 tegra_qspi_addr_config(bool is_ddr, u8 bus_width, u8 len)
return addr_config;
}
static void tegra_qspi_dma_stop(struct tegra_qspi *tqspi)
{
u32 value;
if ((tqspi->cur_direction & DATA_DIR_TX) && tqspi->tx_dma_chan)
dmaengine_terminate_all(tqspi->tx_dma_chan);
if ((tqspi->cur_direction & DATA_DIR_RX) && tqspi->rx_dma_chan)
dmaengine_terminate_all(tqspi->rx_dma_chan);
value = tegra_qspi_readl(tqspi, QSPI_DMA_CTL);
value &= ~QSPI_DMA_EN;
tegra_qspi_writel(tqspi, value, QSPI_DMA_CTL);
}
static void tegra_qspi_pio_stop(struct tegra_qspi *tqspi)
{
u32 value;
value = tegra_qspi_readl(tqspi, QSPI_COMMAND1);
value &= ~QSPI_PIO;
tegra_qspi_writel(tqspi, value, QSPI_COMMAND1);
}
static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
struct spi_message *msg)
{
@@ -1079,7 +1153,7 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
struct spi_transfer *xfer;
struct spi_device *spi = msg->spi;
u8 transfer_phase = 0;
u32 cmd1 = 0, dma_ctl = 0;
u32 cmd1 = 0;
int ret = 0;
u32 address_value = 0;
u32 cmd_config = 0, addr_config = 0;
@@ -1146,41 +1220,28 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
QSPI_DMA_TIMEOUT);
if (WARN_ON_ONCE(ret == 0)) {
dev_err_ratelimited(tqspi->dev,
"QSPI Transfer failed with timeout\n");
if (tqspi->is_curr_dma_xfer) {
if ((tqspi->cur_direction & DATA_DIR_TX) &&
tqspi->tx_dma_chan)
dmaengine_terminate_all(tqspi->tx_dma_chan);
if ((tqspi->cur_direction & DATA_DIR_RX) &&
tqspi->rx_dma_chan)
dmaengine_terminate_all(tqspi->rx_dma_chan);
}
/*
* Check if hardware completed the transfer
* even though interrupt was lost or delayed.
* If so, process the completion and continue.
*/
ret = tegra_qspi_handle_timeout(tqspi);
if (ret < 0) {
/* Real timeout - clean up and fail */
dev_err(tqspi->dev, "transfer timeout\n");
/* Abort transfer by resetting pio/dma bit */
if (!tqspi->is_curr_dma_xfer) {
cmd1 = tegra_qspi_readl
(tqspi,
QSPI_COMMAND1);
cmd1 &= ~QSPI_PIO;
tegra_qspi_writel
(tqspi, cmd1,
QSPI_COMMAND1);
} else {
dma_ctl = tegra_qspi_readl
(tqspi,
QSPI_DMA_CTL);
dma_ctl &= ~QSPI_DMA_EN;
tegra_qspi_writel(tqspi, dma_ctl,
QSPI_DMA_CTL);
}
/* Abort transfer by resetting pio/dma bit */
if (tqspi->is_curr_dma_xfer)
tegra_qspi_dma_stop(tqspi);
else
tegra_qspi_pio_stop(tqspi);
/* Reset controller if timeout happens */
if (device_reset(tqspi->dev) < 0)
dev_warn_once(tqspi->dev,
"device reset failed\n");
ret = -EIO;
goto exit;
/* Reset controller if timeout happens */
tegra_qspi_reset(tqspi);
ret = -EIO;
goto exit;
}
}
if (tqspi->tx_status || tqspi->rx_status) {
@@ -1200,11 +1261,13 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi,
tegra_qspi_transfer_end(spi);
spi_transfer_delay_exec(xfer);
}
tqspi->curr_xfer = NULL;
transfer_phase++;
}
ret = 0;
exit:
tqspi->curr_xfer = NULL;
msg->status = ret;
return ret;
@@ -1269,16 +1332,23 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi,
ret = wait_for_completion_timeout(&tqspi->xfer_completion,
QSPI_DMA_TIMEOUT);
if (WARN_ON(ret == 0)) {
dev_err(tqspi->dev, "transfer timeout\n");
if (tqspi->is_curr_dma_xfer) {
if ((tqspi->cur_direction & DATA_DIR_TX) && tqspi->tx_dma_chan)
dmaengine_terminate_all(tqspi->tx_dma_chan);
if ((tqspi->cur_direction & DATA_DIR_RX) && tqspi->rx_dma_chan)
dmaengine_terminate_all(tqspi->rx_dma_chan);
/*
* Check if hardware completed the transfer even though
* interrupt was lost or delayed. If so, process the
* completion and continue.
*/
ret = tegra_qspi_handle_timeout(tqspi);
if (ret < 0) {
/* Real timeout - clean up and fail */
dev_err(tqspi->dev, "transfer timeout\n");
if (tqspi->is_curr_dma_xfer)
tegra_qspi_dma_stop(tqspi);
tegra_qspi_handle_error(tqspi);
ret = -EIO;
goto complete_xfer;
}
tegra_qspi_handle_error(tqspi);
ret = -EIO;
goto complete_xfer;
}
if (tqspi->tx_status || tqspi->rx_status) {
@@ -1290,6 +1360,8 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi,
msg->actual_length += xfer->len + dummy_bytes;
complete_xfer:
tqspi->curr_xfer = NULL;
if (ret < 0) {
tegra_qspi_transfer_end(spi);
spi_transfer_delay_exec(xfer);
@@ -1395,6 +1467,7 @@ static irqreturn_t handle_cpu_based_xfer(struct tegra_qspi *tqspi)
tegra_qspi_calculate_curr_xfer_param(tqspi, t);
tegra_qspi_start_cpu_based_transfer(tqspi, t);
exit:
tqspi->curr_xfer = NULL;
spin_unlock_irqrestore(&tqspi->lock, flags);
return IRQ_HANDLED;
}
@@ -1480,6 +1553,15 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data)
{
struct tegra_qspi *tqspi = context_data;
/*
* Occasionally the IRQ thread takes a long time to wake up (usually
* when the CPU that it's running on is excessively busy) and we have
* already reached the timeout before and cleaned up the timed out
* transfer. Avoid any processing in that case and bail out early.
*/
if (!tqspi->curr_xfer)
return IRQ_NONE;
tqspi->status_reg = tegra_qspi_readl(tqspi, QSPI_FIFO_STATUS);
if (tqspi->cur_direction & DATA_DIR_TX)

View File

@@ -141,7 +141,7 @@ static ssize_t tle62x0_gpio_show(struct device *dev,
value = (st->gpio_state >> gpio_num) & 1;
mutex_unlock(&st->lock);
return sysfs_emit(buf, "%d", value);
return sysfs_emit(buf, "%d\n", value);
}
static ssize_t tle62x0_gpio_store(struct device *dev,

View File

@@ -704,6 +704,7 @@ static const struct class spidev_class = {
*/
static const struct spi_device_id spidev_spi_ids[] = {
{ .name = /* abb */ "spi-sensor" },
{ .name = /* arduino */ "unoq-mcu" },
{ .name = /* cisco */ "spi-petra" },
{ .name = /* dh */ "dhcom-board" },
{ .name = /* elgin */ "jg10309-01" },
@@ -737,6 +738,7 @@ static int spidev_of_check(struct device *dev)
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "abb,spi-sensor", .data = &spidev_of_check },
{ .compatible = "arduino,unoq-mcu", .data = &spidev_of_check },
{ .compatible = "cisco,spi-petra", .data = &spidev_of_check },
{ .compatible = "dh,dhcom-board", .data = &spidev_of_check },
{ .compatible = "elgin,jg10309-01", .data = &spidev_of_check },

View File

@@ -1,73 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2009 Texas Instruments.
*/
#ifndef __ARCH_ARM_DAVINCI_SPI_H
#define __ARCH_ARM_DAVINCI_SPI_H
#include <linux/platform_data/edma.h>
#define SPI_INTERN_CS 0xFF
enum {
SPI_VERSION_1, /* For DM355/DM365/DM6467 */
SPI_VERSION_2, /* For DA8xx */
};
/**
* davinci_spi_platform_data - Platform data for SPI master device on DaVinci
*
* @version: version of the SPI IP. Different DaVinci devices have slightly
* varying versions of the same IP.
* @num_chipselect: number of chipselects supported by this SPI master
* @intr_line: interrupt line used to connect the SPI IP to the ARM interrupt
* controller withn the SoC. Possible values are 0 and 1.
* @cshold_bug: set this to true if the SPI controller on your chip requires
* a write to CSHOLD bit in between transfers (like in DM355).
* @dma_event_q: DMA event queue to use if SPI_IO_TYPE_DMA is used for any
* device on the bus.
*/
struct davinci_spi_platform_data {
u8 version;
u8 num_chipselect;
u8 intr_line;
u8 prescaler_limit;
bool cshold_bug;
enum dma_event_q dma_event_q;
};
/**
* davinci_spi_config - Per-chip-select configuration for SPI slave devices
*
* @wdelay: amount of delay between transmissions. Measured in number of
* SPI module clocks.
* @odd_parity: polarity of parity flag at the end of transmit data stream.
* 0 - odd parity, 1 - even parity.
* @parity_enable: enable transmission of parity at end of each transmit
* data stream.
* @io_type: type of IO transfer. Choose between polled, interrupt and DMA.
* @timer_disable: disable chip-select timers (setup and hold)
* @c2tdelay: chip-select setup time. Measured in number of SPI module clocks.
* @t2cdelay: chip-select hold time. Measured in number of SPI module clocks.
* @t2edelay: transmit data finished to SPI ENAn pin inactive time. Measured
* in number of SPI clocks.
* @c2edelay: chip-select active to SPI ENAn signal active time. Measured in
* number of SPI clocks.
*/
struct davinci_spi_config {
u8 wdelay;
u8 odd_parity;
u8 parity_enable;
#define SPI_IO_TYPE_INTR 0
#define SPI_IO_TYPE_POLL 1
#define SPI_IO_TYPE_DMA 2
u8 io_type;
u8 timer_disable;
u8 c2tdelay;
u8 t2cdelay;
u8 t2edelay;
u8 c2edelay;
};
#endif /* __ARCH_ARM_DAVINCI_SPI_H */

View File

@@ -57,8 +57,17 @@ enum spi_offload_trigger_type {
SPI_OFFLOAD_TRIGGER_PERIODIC,
};
/**
* spi_offload_trigger_periodic - configuration parameters for periodic triggers
* @frequency_hz: The rate that the trigger should fire in Hz.
* @offset_ns: A delay in nanoseconds between when this trigger fires
* compared to another trigger. This requires specialized hardware
* that supports such synchronization with a delay between two or
* more triggers. Set to 0 when not needed.
*/
struct spi_offload_trigger_periodic {
u64 frequency_hz;
u64 offset_ns;
};
struct spi_offload_trigger_config {

View File

@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM spi-mem
#undef TRACE_SYSTEM_VAR
#define TRACE_SYSTEM_VAR spi_mem
#if !defined(_TRACE_SPI_MEM_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SPI_MEM_H
#include <linux/tracepoint.h>
#include <linux/spi/spi-mem.h>
#define decode_dtr(dtr) \
__print_symbolic(dtr, \
{ 0, "S" }, \
{ 1, "D" })
TRACE_EVENT(spi_mem_start_op,
TP_PROTO(struct spi_mem *mem, const struct spi_mem_op *op),
TP_ARGS(mem, op),
TP_STRUCT__entry(
__string(name, mem->name)
__dynamic_array(u8, op, 1 + op->addr.nbytes + op->dummy.nbytes)
__dynamic_array(u8, data, op->data.dir == SPI_MEM_DATA_OUT ?
min(op->data.nbytes, 64) : 0)
__field(u32, data_len)
__field(u32, max_freq)
__field(u8, cmd_buswidth)
__field(bool, cmd_dtr)
__field(u8, addr_buswidth)
__field(bool, addr_dtr)
__field(u8, dummy_nbytes)
__field(u8, data_buswidth)
__field(bool, data_dtr)
),
TP_fast_assign(
int i;
__assign_str(name);
__entry->max_freq = op->max_freq ?: mem->spi->max_speed_hz;
__entry->cmd_buswidth = op->cmd.buswidth;
__entry->cmd_dtr = op->cmd.dtr;
*((u8 *)__get_dynamic_array(op)) = op->cmd.opcode;
__entry->addr_buswidth = op->addr.buswidth;
__entry->addr_dtr = op->addr.dtr;
for (i = 0; i < op->addr.nbytes; i++)
((u8 *)__get_dynamic_array(op))[i + 1] =
op->addr.val >> (8 * (op->addr.nbytes - i - 1));
memset(((u8 *)__get_dynamic_array(op)) + op->addr.nbytes + 1,
0xff, op->dummy.nbytes);
__entry->data_len = op->data.nbytes;
__entry->data_buswidth = op->data.buswidth;
__entry->data_dtr = op->data.dtr;
if (op->data.dir == SPI_MEM_DATA_OUT)
memcpy(__get_dynamic_array(data), op->data.buf.out,
__get_dynamic_array_len(data));
),
TP_printk("%s %u%s-%u%s-%u%s @%u Hz op=[%*phD] len=%u tx=[%*phD]",
__get_str(name),
__entry->cmd_buswidth, decode_dtr(__entry->cmd_dtr),
__entry->addr_buswidth, decode_dtr(__entry->addr_dtr),
__entry->data_buswidth, decode_dtr(__entry->data_dtr),
__entry->max_freq,
__get_dynamic_array_len(op), __get_dynamic_array(op),
__entry->data_len,
__get_dynamic_array_len(data), __get_dynamic_array(data))
);
TRACE_EVENT(spi_mem_stop_op,
TP_PROTO(struct spi_mem *mem, const struct spi_mem_op *op),
TP_ARGS(mem, op),
TP_STRUCT__entry(
__string(name, mem->name)
__dynamic_array(u8, data, op->data.dir == SPI_MEM_DATA_IN ?
min(op->data.nbytes, 64) : 0)
__field(u32, data_len)
),
TP_fast_assign(
__assign_str(name);
__entry->data_len = op->data.nbytes;
if (op->data.dir == SPI_MEM_DATA_IN)
memcpy(__get_dynamic_array(data), op->data.buf.in,
__get_dynamic_array_len(data));
),
TP_printk("%s len=%u rx=[%*phD]",
__get_str(name),
__entry->data_len,
__get_dynamic_array_len(data), __get_dynamic_array(data))
);
#endif /* _TRACE_SPI_MEM_H */
/* This part must be outside protection */
#include <trace/define_trace.h>