Commit 84fccbba authored by Linus Torvalds's avatar Linus Torvalds

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

Pull spi updates from Mark Brown:
 "One small core feature this time around but mostly driver improvements
  and additions for SPI:

   - Add support for controlling the idle state of MOSI, some systems
     can support this and depending on the system integration may need
     it to avoid glitching in some situations

   - Support for polling mode in the S3C64xx driver and DMA on the
     Qualcomm QSPI driver

   - Support for several Allwinner SoCs, AMD Pensando Elba, Intel Mount
     Evans, Renesas RZ/V2M, and ST STM32H7"

* tag 'spi-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (66 commits)
  spi: dt-bindings: atmel,at91rm9200-spi: fix broken sam9x7 compatible
  spi: dt-bindings: atmel,at91rm9200-spi: add sam9x7 compatible
  spi: Add support for Renesas CSI
  spi: dt-bindings: Add bindings for RZ/V2M CSI
  spi: sun6i: Use the new helper to derive the xfer timeout value
  spi: atmel: Prevent false timeouts on long transfers
  spi: dt-bindings: stm32: do not disable spi-slave property for stm32f4-f7
  spi: Create a helper to derive adaptive timeouts
  spi: spi-geni-qcom: correctly handle -EPROBE_DEFER from dma_request_chan()
  spi: stm32: disable spi-slave property for stm32f4-f7
  spi: stm32: introduction of stm32h7 SPI device mode support
  spi: stm32: use dmaengine_terminate_{a}sync instead of _all
  spi: stm32: renaming of spi_master into spi_controller
  spi: dw: Remove misleading comment for Mount Evans SoC
  spi: dt-bindings: snps,dw-apb-ssi: Add compatible for Intel Mount Evans SoC
  spi: dw: Add compatible for Intel Mount Evans SoC
  spi: s3c64xx: Use dev_err_probe()
  spi: s3c64xx: Use the managed spi master allocation function
  spi: spl022: Probe defer is no error
  spi: spi-imx: fix mixing of native and gpio chipselects for imx51/imx53/imx6 variants
  ...
parents 362067b6 e884a133
...@@ -14,9 +14,6 @@ maintainers: ...@@ -14,9 +14,6 @@ maintainers:
- Maxime Ripard <mripard@kernel.org> - Maxime Ripard <mripard@kernel.org>
properties: properties:
"#address-cells": true
"#size-cells": true
compatible: compatible:
const: allwinner,sun4i-a10-spi const: allwinner,sun4i-a10-spi
...@@ -46,12 +43,9 @@ properties: ...@@ -46,12 +43,9 @@ properties:
- const: rx - const: rx
- const: tx - const: tx
num-cs: true
patternProperties: patternProperties:
"^.*@[0-9a-f]+": "^.*@[0-9a-f]+":
type: object type: object
additionalProperties: true
properties: properties:
reg: reg:
items: items:
...@@ -71,7 +65,7 @@ required: ...@@ -71,7 +65,7 @@ required:
- clocks - clocks
- clock-names - clock-names
additionalProperties: false unevaluatedProperties: false
examples: examples:
- | - |
......
...@@ -14,11 +14,9 @@ maintainers: ...@@ -14,11 +14,9 @@ maintainers:
- Maxime Ripard <mripard@kernel.org> - Maxime Ripard <mripard@kernel.org>
properties: properties:
"#address-cells": true
"#size-cells": true
compatible: compatible:
oneOf: oneOf:
- const: allwinner,sun50i-r329-spi
- const: allwinner,sun6i-a31-spi - const: allwinner,sun6i-a31-spi
- const: allwinner,sun8i-h3-spi - const: allwinner,sun8i-h3-spi
- items: - items:
...@@ -28,6 +26,15 @@ properties: ...@@ -28,6 +26,15 @@ properties:
- allwinner,sun50i-h616-spi - allwinner,sun50i-h616-spi
- allwinner,suniv-f1c100s-spi - allwinner,suniv-f1c100s-spi
- const: allwinner,sun8i-h3-spi - const: allwinner,sun8i-h3-spi
- items:
- enum:
- allwinner,sun20i-d1-spi
- allwinner,sun50i-r329-spi-dbi
- const: allwinner,sun50i-r329-spi
- items:
- const: allwinner,sun20i-d1-spi-dbi
- const: allwinner,sun50i-r329-spi-dbi
- const: allwinner,sun50i-r329-spi
reg: reg:
maxItems: 1 maxItems: 1
...@@ -58,12 +65,9 @@ properties: ...@@ -58,12 +65,9 @@ properties:
- const: rx - const: rx
- const: tx - const: tx
num-cs: true
patternProperties: patternProperties:
"^.*@[0-9a-f]+": "^.*@[0-9a-f]+":
type: object type: object
additionalProperties: true
properties: properties:
reg: reg:
items: items:
...@@ -83,7 +87,7 @@ required: ...@@ -83,7 +87,7 @@ required:
- clocks - clocks
- clock-names - clock-names
additionalProperties: false unevaluatedProperties: false
examples: examples:
- | - |
......
...@@ -20,6 +20,10 @@ properties: ...@@ -20,6 +20,10 @@ properties:
- items: - items:
- const: microchip,sam9x60-spi - const: microchip,sam9x60-spi
- const: atmel,at91rm9200-spi - const: atmel,at91rm9200-spi
- items:
- const: microchip,sam9x7-spi
- const: microchip,sam9x60-spi
- const: atmel,at91rm9200-spi
reg: reg:
maxItems: 1 maxItems: 1
......
...@@ -46,12 +46,28 @@ allOf: ...@@ -46,12 +46,28 @@ allOf:
maxItems: 2 maxItems: 2
items: items:
enum: [ qspi, qspi-ocp ] enum: [ qspi, qspi-ocp ]
- if:
properties:
compatible:
contains:
const: amd,pensando-elba-qspi
then:
properties:
cdns,fifo-depth:
enum: [ 128, 256, 1024 ]
default: 1024
else:
properties:
cdns,fifo-depth:
enum: [ 128, 256 ]
default: 128
properties: properties:
compatible: compatible:
oneOf: oneOf:
- items: - items:
- enum: - enum:
- amd,pensando-elba-qspi
- ti,k2g-qspi - ti,k2g-qspi
- ti,am654-ospi - ti,am654-ospi
- intel,lgm-qspi - intel,lgm-qspi
...@@ -76,8 +92,6 @@ properties: ...@@ -76,8 +92,6 @@ properties:
description: description:
Size of the data FIFO in words. Size of the data FIFO in words.
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32
enum: [ 128, 256 ]
default: 128
cdns,fifo-width: cdns,fifo-width:
$ref: /schemas/types.yaml#/definitions/uint32 $ref: /schemas/types.yaml#/definitions/uint32
......
...@@ -29,6 +29,9 @@ properties: ...@@ -29,6 +29,9 @@ properties:
reg: reg:
maxItems: 1 maxItems: 1
iommus:
maxItems: 1
interrupts: interrupts:
maxItems: 1 maxItems: 1
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/spi/renesas,rzv2m-csi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/V2M Clocked Serial Interface (CSI)
maintainers:
- Fabrizio Castro <fabrizio.castro.jz@renesas.com>
- Geert Uytterhoeven <geert+renesas@glider.be>
allOf:
- $ref: spi-controller.yaml#
properties:
compatible:
const: renesas,rzv2m-csi
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: The clock used to generate the output clock (CSICLK)
- description: Internal clock to access the registers (PCLK)
clock-names:
items:
- const: csiclk
- const: pclk
resets:
maxItems: 1
power-domains:
maxItems: 1
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
- power-domains
- '#address-cells'
- '#size-cells'
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/r9a09g011-cpg.h>
csi4: spi@a4020200 {
compatible = "renesas,rzv2m-csi";
reg = <0xa4020200 0x80>;
interrupts = <GIC_SPI 230 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD R9A09G011_CSI4_CLK>,
<&cpg CPG_MOD R9A09G011_CPERI_GRPH_PCLK>;
clock-names = "csiclk", "pclk";
resets = <&cpg R9A09G011_CSI_GPH_PRESETN>;
power-domains = <&cpg>;
#address-cells = <1>;
#size-cells = <0>;
};
...@@ -35,8 +35,6 @@ properties: ...@@ -35,8 +35,6 @@ properties:
minItems: 2 minItems: 2
maxItems: 3 maxItems: 3
cs-gpios: true
dmas: dmas:
minItems: 2 minItems: 2
maxItems: 2 maxItems: 2
......
...@@ -74,6 +74,8 @@ properties: ...@@ -74,6 +74,8 @@ properties:
const: intel,keembay-ssi const: intel,keembay-ssi
- description: Intel Thunder Bay SPI Controller - description: Intel Thunder Bay SPI Controller
const: intel,thunderbay-ssi const: intel,thunderbay-ssi
- description: Intel Mount Evans Integrated Management Complex SPI Controller
const: intel,mountevans-imc-ssi
- description: AMD Pensando Elba SoC SPI Controller - description: AMD Pensando Elba SoC SPI Controller
const: amd,pensando-elba-spi const: amd,pensando-elba-spi
- description: Baikal-T1 SPI Controller - description: Baikal-T1 SPI Controller
......
...@@ -17,9 +17,6 @@ allOf: ...@@ -17,9 +17,6 @@ allOf:
- $ref: spi-controller.yaml# - $ref: spi-controller.yaml#
properties: properties:
"#address-cells": true
"#size-cells": true
compatible: compatible:
const: socionext,uniphier-scssi const: socionext,uniphier-scssi
......
...@@ -17,7 +17,7 @@ description: | ...@@ -17,7 +17,7 @@ description: |
properties: properties:
$nodename: $nodename:
pattern: "^spi(@.*|-[0-9a-f])*$" pattern: "^spi(@.*|-([0-9]|[1-9][0-9]+))?$"
"#address-cells": "#address-cells":
enum: [0, 1] enum: [0, 1]
......
...@@ -32,6 +32,12 @@ properties: ...@@ -32,6 +32,12 @@ properties:
clocks: clocks:
maxItems: 2 maxItems: 2
iommus:
maxItems: 1
power-domains:
maxItems: 1
required: required:
- compatible - compatible
- reg - reg
......
...@@ -825,6 +825,12 @@ config SPI_RSPI ...@@ -825,6 +825,12 @@ config SPI_RSPI
help help
SPI driver for Renesas RSPI and QSPI blocks. SPI driver for Renesas RSPI and QSPI blocks.
config SPI_RZV2M_CSI
tristate "Renesas RZV2M CSI controller"
depends on ARCH_RENESAS || COMPILE_TEST
help
SPI driver for Renesas RZ/V2M Clocked Serial Interface (CSI)
config SPI_QCOM_QSPI config SPI_QCOM_QSPI
tristate "QTI QSPI controller" tristate "QTI QSPI controller"
depends on ARCH_QCOM || COMPILE_TEST depends on ARCH_QCOM || COMPILE_TEST
...@@ -936,6 +942,7 @@ config SPI_SPRD_ADI ...@@ -936,6 +942,7 @@ config SPI_SPRD_ADI
config SPI_STM32 config SPI_STM32
tristate "STMicroelectronics STM32 SPI controller" tristate "STMicroelectronics STM32 SPI controller"
depends on ARCH_STM32 || COMPILE_TEST depends on ARCH_STM32 || COMPILE_TEST
select SPI_SLAVE
help help
SPI driver for STMicroelectronics STM32 SoCs. SPI driver for STMicroelectronics STM32 SoCs.
......
...@@ -113,6 +113,7 @@ obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o ...@@ -113,6 +113,7 @@ obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
obj-$(CONFIG_SPI_RSPI) += spi-rspi.o obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o obj-$(CONFIG_SPI_SC18IS602) += spi-sc18is602.o
obj-$(CONFIG_SPI_SH) += spi-sh.o obj-$(CONFIG_SPI_SH) += spi-sh.o
......
...@@ -233,7 +233,8 @@ ...@@ -233,7 +233,8 @@
*/ */
#define DMA_MIN_BYTES 16 #define DMA_MIN_BYTES 16
#define SPI_DMA_TIMEOUT (msecs_to_jiffies(1000)) #define SPI_DMA_MIN_TIMEOUT (msecs_to_jiffies(1000))
#define SPI_DMA_TIMEOUT_PER_10K (msecs_to_jiffies(4))
#define AUTOSUSPEND_TIMEOUT 2000 #define AUTOSUSPEND_TIMEOUT 2000
...@@ -1279,7 +1280,8 @@ static int atmel_spi_one_transfer(struct spi_controller *host, ...@@ -1279,7 +1280,8 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
struct atmel_spi_device *asd; struct atmel_spi_device *asd;
int timeout; int timeout;
int ret; int ret;
unsigned long dma_timeout; unsigned int dma_timeout;
long ret_timeout;
as = spi_controller_get_devdata(host); as = spi_controller_get_devdata(host);
...@@ -1333,11 +1335,13 @@ static int atmel_spi_one_transfer(struct spi_controller *host, ...@@ -1333,11 +1335,13 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
atmel_spi_unlock(as); atmel_spi_unlock(as);
} }
dma_timeout = wait_for_completion_timeout(&as->xfer_completion, dma_timeout = msecs_to_jiffies(spi_controller_xfer_timeout(host, xfer));
SPI_DMA_TIMEOUT); ret_timeout = wait_for_completion_interruptible_timeout(&as->xfer_completion,
if (WARN_ON(dma_timeout == 0)) { dma_timeout);
dev_err(&spi->dev, "spi transfer timeout\n"); if (ret_timeout <= 0) {
as->done_status = -EIO; dev_err(&spi->dev, "spi transfer %s\n",
!ret_timeout ? "timeout" : "canceled");
as->done_status = ret_timeout < 0 ? ret_timeout : -EIO;
} }
if (as->done_status) if (as->done_status)
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#define CQSPI_SUPPORT_EXTERNAL_DMA BIT(2) #define CQSPI_SUPPORT_EXTERNAL_DMA BIT(2)
#define CQSPI_NO_SUPPORT_WR_COMPLETION BIT(3) #define CQSPI_NO_SUPPORT_WR_COMPLETION BIT(3)
#define CQSPI_SLOW_SRAM BIT(4) #define CQSPI_SLOW_SRAM BIT(4)
#define CQSPI_NEEDS_APB_AHB_HAZARD_WAR BIT(5)
/* Capabilities */ /* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0) #define CQSPI_SUPPORTS_OCTAL BIT(0)
...@@ -90,6 +91,7 @@ struct cqspi_st { ...@@ -90,6 +91,7 @@ struct cqspi_st {
u32 pd_dev_id; u32 pd_dev_id;
bool wr_completion; bool wr_completion;
bool slow_sram; bool slow_sram;
bool apb_ahb_hazard;
}; };
struct cqspi_driver_platdata { struct cqspi_driver_platdata {
...@@ -1027,6 +1029,13 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata, ...@@ -1027,6 +1029,13 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata,
if (cqspi->wr_delay) if (cqspi->wr_delay)
ndelay(cqspi->wr_delay); ndelay(cqspi->wr_delay);
/*
* If a hazard exists between the APB and AHB interfaces, perform a
* dummy readback from the controller to ensure synchronization.
*/
if (cqspi->apb_ahb_hazard)
readl(reg_base + CQSPI_REG_INDIRECTWR);
while (remaining > 0) { while (remaining > 0) {
size_t write_words, mod_bytes; size_t write_words, mod_bytes;
...@@ -1754,6 +1763,8 @@ static int cqspi_probe(struct platform_device *pdev) ...@@ -1754,6 +1763,8 @@ static int cqspi_probe(struct platform_device *pdev)
cqspi->wr_completion = false; cqspi->wr_completion = false;
if (ddata->quirks & CQSPI_SLOW_SRAM) if (ddata->quirks & CQSPI_SLOW_SRAM)
cqspi->slow_sram = true; cqspi->slow_sram = true;
if (ddata->quirks & CQSPI_NEEDS_APB_AHB_HAZARD_WAR)
cqspi->apb_ahb_hazard = true;
if (of_device_is_compatible(pdev->dev.of_node, if (of_device_is_compatible(pdev->dev.of_node,
"xlnx,versal-ospi-1.0")) { "xlnx,versal-ospi-1.0")) {
...@@ -1888,6 +1899,10 @@ static const struct cqspi_driver_platdata jh7110_qspi = { ...@@ -1888,6 +1899,10 @@ static const struct cqspi_driver_platdata jh7110_qspi = {
.quirks = CQSPI_DISABLE_DAC_MODE, .quirks = CQSPI_DISABLE_DAC_MODE,
}; };
static const struct cqspi_driver_platdata pensando_cdns_qspi = {
.quirks = CQSPI_NEEDS_APB_AHB_HAZARD_WAR | CQSPI_DISABLE_DAC_MODE,
};
static const struct of_device_id cqspi_dt_ids[] = { static const struct of_device_id cqspi_dt_ids[] = {
{ {
.compatible = "cdns,qspi-nor", .compatible = "cdns,qspi-nor",
...@@ -1917,6 +1932,10 @@ static const struct of_device_id cqspi_dt_ids[] = { ...@@ -1917,6 +1932,10 @@ static const struct of_device_id cqspi_dt_ids[] = {
.compatible = "starfive,jh7110-qspi", .compatible = "starfive,jh7110-qspi",
.data = &jh7110_qspi, .data = &jh7110_qspi,
}, },
{
.compatible = "amd,pensando-elba-qspi",
.data = &pensando_cdns_qspi,
},
{ /* end of table */ } { /* end of table */ }
}; };
......
...@@ -102,6 +102,7 @@ ...@@ -102,6 +102,7 @@
* @regs: Virtual address of the SPI controller registers * @regs: Virtual address of the SPI controller registers
* @ref_clk: Pointer to the peripheral clock * @ref_clk: Pointer to the peripheral clock
* @pclk: Pointer to the APB clock * @pclk: Pointer to the APB clock
* @clk_rate: Reference clock frequency, taken from @ref_clk
* @speed_hz: Current SPI bus clock speed in Hz * @speed_hz: Current SPI bus clock speed in Hz
* @txbuf: Pointer to the TX buffer * @txbuf: Pointer to the TX buffer
* @rxbuf: Pointer to the RX buffer * @rxbuf: Pointer to the RX buffer
......
...@@ -57,21 +57,17 @@ static const struct debugfs_reg32 dw_spi_dbgfs_regs[] = { ...@@ -57,21 +57,17 @@ static const struct debugfs_reg32 dw_spi_dbgfs_regs[] = {
DW_SPI_DBGFS_REG("RX_SAMPLE_DLY", DW_SPI_RX_SAMPLE_DLY), DW_SPI_DBGFS_REG("RX_SAMPLE_DLY", DW_SPI_RX_SAMPLE_DLY),
}; };
static int dw_spi_debugfs_init(struct dw_spi *dws) static void dw_spi_debugfs_init(struct dw_spi *dws)
{ {
char name[32]; char name[32];
snprintf(name, 32, "dw_spi%d", dws->master->bus_num); snprintf(name, 32, "dw_spi%d", dws->master->bus_num);
dws->debugfs = debugfs_create_dir(name, NULL); dws->debugfs = debugfs_create_dir(name, NULL);
if (!dws->debugfs)
return -ENOMEM;
dws->regset.regs = dw_spi_dbgfs_regs; dws->regset.regs = dw_spi_dbgfs_regs;
dws->regset.nregs = ARRAY_SIZE(dw_spi_dbgfs_regs); dws->regset.nregs = ARRAY_SIZE(dw_spi_dbgfs_regs);
dws->regset.base = dws->regs; dws->regset.base = dws->regs;
debugfs_create_regset32("registers", 0400, dws->debugfs, &dws->regset); debugfs_create_regset32("registers", 0400, dws->debugfs, &dws->regset);
return 0;
} }
static void dw_spi_debugfs_remove(struct dw_spi *dws) static void dw_spi_debugfs_remove(struct dw_spi *dws)
...@@ -80,9 +76,8 @@ static void dw_spi_debugfs_remove(struct dw_spi *dws) ...@@ -80,9 +76,8 @@ static void dw_spi_debugfs_remove(struct dw_spi *dws)
} }
#else #else
static inline int dw_spi_debugfs_init(struct dw_spi *dws) static inline void dw_spi_debugfs_init(struct dw_spi *dws)
{ {
return 0;
} }
static inline void dw_spi_debugfs_remove(struct dw_spi *dws) static inline void dw_spi_debugfs_remove(struct dw_spi *dws)
...@@ -426,7 +421,10 @@ static int dw_spi_transfer_one(struct spi_controller *master, ...@@ -426,7 +421,10 @@ static int dw_spi_transfer_one(struct spi_controller *master,
int ret; int ret;
dws->dma_mapped = 0; dws->dma_mapped = 0;
dws->n_bytes = DIV_ROUND_UP(transfer->bits_per_word, BITS_PER_BYTE); dws->n_bytes =
roundup_pow_of_two(DIV_ROUND_UP(transfer->bits_per_word,
BITS_PER_BYTE));
dws->tx = (void *)transfer->tx_buf; dws->tx = (void *)transfer->tx_buf;
dws->tx_len = transfer->len / dws->n_bytes; dws->tx_len = transfer->len / dws->n_bytes;
dws->rx = transfer->rx_buf; dws->rx = transfer->rx_buf;
......
...@@ -72,12 +72,22 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws) ...@@ -72,12 +72,22 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws)
dw_writel(dws, DW_SPI_DMATDLR, dws->txburst); dw_writel(dws, DW_SPI_DMATDLR, dws->txburst);
} }
static void dw_spi_dma_sg_burst_init(struct dw_spi *dws) static int dw_spi_dma_caps_init(struct dw_spi *dws)
{ {
struct dma_slave_caps tx = {0}, rx = {0}; struct dma_slave_caps tx, rx;
int ret;
ret = dma_get_slave_caps(dws->txchan, &tx);
if (ret)
return ret;
ret = dma_get_slave_caps(dws->rxchan, &rx);
if (ret)
return ret;
dma_get_slave_caps(dws->txchan, &tx); if (!(tx.directions & BIT(DMA_MEM_TO_DEV) &&
dma_get_slave_caps(dws->rxchan, &rx); rx.directions & BIT(DMA_DEV_TO_MEM)))
return -ENXIO;
if (tx.max_sg_burst > 0 && rx.max_sg_burst > 0) if (tx.max_sg_burst > 0 && rx.max_sg_burst > 0)
dws->dma_sg_burst = min(tx.max_sg_burst, rx.max_sg_burst); dws->dma_sg_burst = min(tx.max_sg_burst, rx.max_sg_burst);
...@@ -87,6 +97,15 @@ static void dw_spi_dma_sg_burst_init(struct dw_spi *dws) ...@@ -87,6 +97,15 @@ static void dw_spi_dma_sg_burst_init(struct dw_spi *dws)
dws->dma_sg_burst = rx.max_sg_burst; dws->dma_sg_burst = rx.max_sg_burst;
else else
dws->dma_sg_burst = 0; dws->dma_sg_burst = 0;
/*
* Assuming both channels belong to the same DMA controller hence the
* peripheral side address width capabilities most likely would be
* the same.
*/
dws->dma_addr_widths = tx.dst_addr_widths & rx.src_addr_widths;
return 0;
} }
static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws)
...@@ -95,6 +114,7 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) ...@@ -95,6 +114,7 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws)
struct dw_dma_slave dma_rx = { .src_id = 0 }, *rx = &dma_rx; struct dw_dma_slave dma_rx = { .src_id = 0 }, *rx = &dma_rx;
struct pci_dev *dma_dev; struct pci_dev *dma_dev;
dma_cap_mask_t mask; dma_cap_mask_t mask;
int ret = -EBUSY;
/* /*
* Get pci device for DMA controller, currently it could only * Get pci device for DMA controller, currently it could only
...@@ -124,20 +144,25 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) ...@@ -124,20 +144,25 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws)
init_completion(&dws->dma_completion); init_completion(&dws->dma_completion);
dw_spi_dma_maxburst_init(dws); ret = dw_spi_dma_caps_init(dws);
if (ret)
goto free_txchan;
dw_spi_dma_sg_burst_init(dws); dw_spi_dma_maxburst_init(dws);
pci_dev_put(dma_dev); pci_dev_put(dma_dev);
return 0; return 0;
free_txchan:
dma_release_channel(dws->txchan);
dws->txchan = NULL;
free_rxchan: free_rxchan:
dma_release_channel(dws->rxchan); dma_release_channel(dws->rxchan);
dws->rxchan = NULL; dws->rxchan = NULL;
err_exit: err_exit:
pci_dev_put(dma_dev); pci_dev_put(dma_dev);
return -EBUSY; return ret;
} }
static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws)
...@@ -163,12 +188,17 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) ...@@ -163,12 +188,17 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws)
init_completion(&dws->dma_completion); init_completion(&dws->dma_completion);
dw_spi_dma_maxburst_init(dws); ret = dw_spi_dma_caps_init(dws);
if (ret)
goto free_txchan;
dw_spi_dma_sg_burst_init(dws); dw_spi_dma_maxburst_init(dws);
return 0; return 0;
free_txchan:
dma_release_channel(dws->txchan);
dws->txchan = NULL;
free_rxchan: free_rxchan:
dma_release_channel(dws->rxchan); dma_release_channel(dws->rxchan);
dws->rxchan = NULL; dws->rxchan = NULL;
...@@ -198,22 +228,32 @@ static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws) ...@@ -198,22 +228,32 @@ static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes)
{
switch (n_bytes) {
case 1:
return DMA_SLAVE_BUSWIDTH_1_BYTE;
case 2:
return DMA_SLAVE_BUSWIDTH_2_BYTES;
case 4:
return DMA_SLAVE_BUSWIDTH_4_BYTES;
default:
return DMA_SLAVE_BUSWIDTH_UNDEFINED;
}
}
static bool dw_spi_can_dma(struct spi_controller *master, static bool dw_spi_can_dma(struct spi_controller *master,
struct spi_device *spi, struct spi_transfer *xfer) struct spi_device *spi, struct spi_transfer *xfer)
{ {
struct dw_spi *dws = spi_controller_get_devdata(master); struct dw_spi *dws = spi_controller_get_devdata(master);
enum dma_slave_buswidth dma_bus_width;
return xfer->len > dws->fifo_len; if (xfer->len <= dws->fifo_len)
} return false;
static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes) dma_bus_width = dw_spi_dma_convert_width(dws->n_bytes);
{
if (n_bytes == 1)
return DMA_SLAVE_BUSWIDTH_1_BYTE;
else if (n_bytes == 2)
return DMA_SLAVE_BUSWIDTH_2_BYTES;
return DMA_SLAVE_BUSWIDTH_UNDEFINED; return dws->dma_addr_widths & BIT(dma_bus_width);
} }
static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed) static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed)
......
...@@ -236,6 +236,24 @@ static int dw_spi_intel_init(struct platform_device *pdev, ...@@ -236,6 +236,24 @@ static int dw_spi_intel_init(struct platform_device *pdev,
return 0; return 0;
} }
/*
* DMA-based mem ops are not configured for this device and are not tested.
*/
static int dw_spi_mountevans_imc_init(struct platform_device *pdev,
struct dw_spi_mmio *dwsmmio)
{
/*
* The Intel Mount Evans SoC's Integrated Management Complex DW
* apb_ssi_v4.02a controller has an errata where a full TX FIFO can
* result in data corruption. The suggested workaround is to never
* completely fill the FIFO. The TX FIFO has a size of 32 so the
* fifo_len is set to 31.
*/
dwsmmio->dws.fifo_len = 31;
return 0;
}
static int dw_spi_canaan_k210_init(struct platform_device *pdev, static int dw_spi_canaan_k210_init(struct platform_device *pdev,
struct dw_spi_mmio *dwsmmio) struct dw_spi_mmio *dwsmmio)
{ {
...@@ -405,6 +423,10 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { ...@@ -405,6 +423,10 @@ static const struct of_device_id dw_spi_mmio_of_match[] = {
{ .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init}, { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init},
{ .compatible = "intel,keembay-ssi", .data = dw_spi_intel_init}, { .compatible = "intel,keembay-ssi", .data = dw_spi_intel_init},
{ .compatible = "intel,thunderbay-ssi", .data = dw_spi_intel_init}, { .compatible = "intel,thunderbay-ssi", .data = dw_spi_intel_init},
{
.compatible = "intel,mountevans-imc-ssi",
.data = dw_spi_mountevans_imc_init,
},
{ .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init}, { .compatible = "microchip,sparx5-spi", dw_spi_mscc_sparx5_init},
{ .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init}, { .compatible = "canaan,k210-spi", dw_spi_canaan_k210_init},
{ .compatible = "amd,pensando-elba-spi", .data = dw_spi_elba_init}, { .compatible = "amd,pensando-elba-spi", .data = dw_spi_elba_init},
......
...@@ -190,6 +190,7 @@ struct dw_spi { ...@@ -190,6 +190,7 @@ struct dw_spi {
struct dma_chan *rxchan; struct dma_chan *rxchan;
u32 rxburst; u32 rxburst;
u32 dma_sg_burst; u32 dma_sg_burst;
u32 dma_addr_widths;
unsigned long dma_chan_busy; unsigned long dma_chan_busy;
dma_addr_t dma_addr; /* phy address of the Data register */ dma_addr_t dma_addr; /* phy address of the Data register */
const struct dw_spi_dma_ops *dma_ops; const struct dw_spi_dma_ops *dma_ops;
......
...@@ -303,6 +303,12 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi) ...@@ -303,6 +303,12 @@ static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
perclk_rate = clk_get_rate(fsl_lpspi->clk_per); perclk_rate = clk_get_rate(fsl_lpspi->clk_per);
if (!config.speed_hz) {
dev_err(fsl_lpspi->dev,
"error: the transmission speed provided is 0!\n");
return -EINVAL;
}
if (config.speed_hz > perclk_rate / 2) { if (config.speed_hz > perclk_rate / 2) {
dev_err(fsl_lpspi->dev, dev_err(fsl_lpspi->dev,
"per-clk should be at least two times of transfer speed"); "per-clk should be at least two times of transfer speed");
...@@ -911,7 +917,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev) ...@@ -911,7 +917,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
if (ret == -EPROBE_DEFER) if (ret == -EPROBE_DEFER)
goto out_pm_get; goto out_pm_get;
if (ret < 0) if (ret < 0)
dev_err(&pdev->dev, "dma setup error %d, use pio\n", ret); dev_warn(&pdev->dev, "dma setup error %d, use pio\n", ret);
else else
/* /*
* disable LPSPI module IRQ when enable DMA mode successfully, * disable LPSPI module IRQ when enable DMA mode successfully,
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#define CS_DEMUX_OUTPUT_SEL GENMASK(3, 0) #define CS_DEMUX_OUTPUT_SEL GENMASK(3, 0)
#define SE_SPI_TRANS_CFG 0x25c #define SE_SPI_TRANS_CFG 0x25c
#define CS_TOGGLE BIT(0) #define CS_TOGGLE BIT(1)
#define SE_SPI_WORD_LEN 0x268 #define SE_SPI_WORD_LEN 0x268
#define WORD_LEN_MSK GENMASK(9, 0) #define WORD_LEN_MSK GENMASK(9, 0)
......
...@@ -169,7 +169,7 @@ static int hisi_spi_debugfs_init(struct hisi_spi *hs) ...@@ -169,7 +169,7 @@ static int hisi_spi_debugfs_init(struct hisi_spi *hs)
master = container_of(hs->dev, struct spi_controller, dev); master = container_of(hs->dev, struct spi_controller, dev);
snprintf(name, 32, "hisi_spi%d", master->bus_num); snprintf(name, 32, "hisi_spi%d", master->bus_num);
hs->debugfs = debugfs_create_dir(name, NULL); hs->debugfs = debugfs_create_dir(name, NULL);
if (!hs->debugfs) if (IS_ERR(hs->debugfs))
return -ENOMEM; return -ENOMEM;
hs->regset.regs = hisi_spi_regs; hs->regset.regs = hisi_spi_regs;
......
...@@ -281,6 +281,7 @@ static bool spi_imx_can_dma(struct spi_controller *controller, struct spi_device ...@@ -281,6 +281,7 @@ static bool spi_imx_can_dma(struct spi_controller *controller, struct spi_device
#define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs & 3) + 4)) #define MX51_ECSPI_CONFIG_SCLKPOL(cs) (1 << ((cs & 3) + 4))
#define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs & 3) + 8)) #define MX51_ECSPI_CONFIG_SBBCTRL(cs) (1 << ((cs & 3) + 8))
#define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs & 3) + 12)) #define MX51_ECSPI_CONFIG_SSBPOL(cs) (1 << ((cs & 3) + 12))
#define MX51_ECSPI_CONFIG_DATACTL(cs) (1 << ((cs & 3) + 16))
#define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs & 3) + 20)) #define MX51_ECSPI_CONFIG_SCLKCTL(cs) (1 << ((cs & 3) + 20))
#define MX51_ECSPI_INT 0x10 #define MX51_ECSPI_INT 0x10
...@@ -516,6 +517,13 @@ static void mx51_ecspi_disable(struct spi_imx_data *spi_imx) ...@@ -516,6 +517,13 @@ static void mx51_ecspi_disable(struct spi_imx_data *spi_imx)
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
} }
static int mx51_ecspi_channel(const struct spi_device *spi)
{
if (!spi_get_csgpiod(spi, 0))
return spi_get_chipselect(spi, 0);
return spi->controller->unused_native_cs;
}
static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx, static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
struct spi_message *msg) struct spi_message *msg)
{ {
...@@ -526,6 +534,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx, ...@@ -526,6 +534,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
u32 testreg, delay; u32 testreg, delay;
u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG); u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG);
u32 current_cfg = cfg; u32 current_cfg = cfg;
int channel = mx51_ecspi_channel(spi);
/* set Master or Slave mode */ /* set Master or Slave mode */
if (spi_imx->slave_mode) if (spi_imx->slave_mode)
...@@ -540,7 +549,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx, ...@@ -540,7 +549,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
ctrl |= MX51_ECSPI_CTRL_DRCTL(spi_imx->spi_drctl); ctrl |= MX51_ECSPI_CTRL_DRCTL(spi_imx->spi_drctl);
/* set chip select to use */ /* set chip select to use */
ctrl |= MX51_ECSPI_CTRL_CS(spi_get_chipselect(spi, 0)); ctrl |= MX51_ECSPI_CTRL_CS(channel);
/* /*
* The ctrl register must be written first, with the EN bit set other * The ctrl register must be written first, with the EN bit set other
...@@ -561,22 +570,27 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx, ...@@ -561,22 +570,27 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
* BURST_LENGTH + 1 bits are received * BURST_LENGTH + 1 bits are received
*/ */
if (spi_imx->slave_mode && is_imx53_ecspi(spi_imx)) if (spi_imx->slave_mode && is_imx53_ecspi(spi_imx))
cfg &= ~MX51_ECSPI_CONFIG_SBBCTRL(spi_get_chipselect(spi, 0)); cfg &= ~MX51_ECSPI_CONFIG_SBBCTRL(channel);
else else
cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi_get_chipselect(spi, 0)); cfg |= MX51_ECSPI_CONFIG_SBBCTRL(channel);
if (spi->mode & SPI_CPOL) { if (spi->mode & SPI_CPOL) {
cfg |= MX51_ECSPI_CONFIG_SCLKPOL(spi_get_chipselect(spi, 0)); cfg |= MX51_ECSPI_CONFIG_SCLKPOL(channel);
cfg |= MX51_ECSPI_CONFIG_SCLKCTL(spi_get_chipselect(spi, 0)); cfg |= MX51_ECSPI_CONFIG_SCLKCTL(channel);
} else { } else {
cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(spi_get_chipselect(spi, 0)); cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(channel);
cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(spi_get_chipselect(spi, 0)); cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(channel);
} }
if (spi->mode & SPI_MOSI_IDLE_LOW)
cfg |= MX51_ECSPI_CONFIG_DATACTL(channel);
else
cfg &= ~MX51_ECSPI_CONFIG_DATACTL(channel);
if (spi->mode & SPI_CS_HIGH) if (spi->mode & SPI_CS_HIGH)
cfg |= MX51_ECSPI_CONFIG_SSBPOL(spi_get_chipselect(spi, 0)); cfg |= MX51_ECSPI_CONFIG_SSBPOL(channel);
else else
cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(spi_get_chipselect(spi, 0)); cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(channel);
if (cfg == current_cfg) if (cfg == current_cfg)
return 0; return 0;
...@@ -621,14 +635,15 @@ static void mx51_configure_cpha(struct spi_imx_data *spi_imx, ...@@ -621,14 +635,15 @@ static void mx51_configure_cpha(struct spi_imx_data *spi_imx,
bool cpha = (spi->mode & SPI_CPHA); bool cpha = (spi->mode & SPI_CPHA);
bool flip_cpha = (spi->mode & SPI_RX_CPHA_FLIP) && spi_imx->rx_only; bool flip_cpha = (spi->mode & SPI_RX_CPHA_FLIP) && spi_imx->rx_only;
u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG); u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG);
int channel = mx51_ecspi_channel(spi);
/* Flip cpha logical value iff flip_cpha */ /* Flip cpha logical value iff flip_cpha */
cpha ^= flip_cpha; cpha ^= flip_cpha;
if (cpha) if (cpha)
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(spi_get_chipselect(spi, 0)); cfg |= MX51_ECSPI_CONFIG_SCLKPHA(channel);
else else
cfg &= ~MX51_ECSPI_CONFIG_SCLKPHA(spi_get_chipselect(spi, 0)); cfg &= ~MX51_ECSPI_CONFIG_SCLKPHA(channel);
writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG); writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG);
} }
...@@ -1737,20 +1752,21 @@ static int spi_imx_probe(struct platform_device *pdev) ...@@ -1737,20 +1752,21 @@ static int spi_imx_probe(struct platform_device *pdev)
else else
controller->num_chipselect = 3; controller->num_chipselect = 3;
spi_imx->controller->transfer_one = spi_imx_transfer_one; controller->transfer_one = spi_imx_transfer_one;
spi_imx->controller->setup = spi_imx_setup; controller->setup = spi_imx_setup;
spi_imx->controller->cleanup = spi_imx_cleanup; controller->cleanup = spi_imx_cleanup;
spi_imx->controller->prepare_message = spi_imx_prepare_message; controller->prepare_message = spi_imx_prepare_message;
spi_imx->controller->unprepare_message = spi_imx_unprepare_message; controller->unprepare_message = spi_imx_unprepare_message;
spi_imx->controller->slave_abort = spi_imx_slave_abort; controller->slave_abort = spi_imx_slave_abort;
spi_imx->controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS; controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS |
SPI_MOSI_IDLE_LOW;
if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx) || if (is_imx35_cspi(spi_imx) || is_imx51_ecspi(spi_imx) ||
is_imx53_ecspi(spi_imx)) is_imx53_ecspi(spi_imx))
spi_imx->controller->mode_bits |= SPI_LOOP | SPI_READY; controller->mode_bits |= SPI_LOOP | SPI_READY;
if (is_imx51_ecspi(spi_imx) || is_imx53_ecspi(spi_imx)) if (is_imx51_ecspi(spi_imx) || is_imx53_ecspi(spi_imx))
spi_imx->controller->mode_bits |= SPI_RX_CPHA_FLIP; controller->mode_bits |= SPI_RX_CPHA_FLIP;
if (is_imx51_ecspi(spi_imx) && if (is_imx51_ecspi(spi_imx) &&
device_property_read_u32(&pdev->dev, "cs-gpios", NULL)) device_property_read_u32(&pdev->dev, "cs-gpios", NULL))
...@@ -1759,7 +1775,12 @@ static int spi_imx_probe(struct platform_device *pdev) ...@@ -1759,7 +1775,12 @@ static int spi_imx_probe(struct platform_device *pdev)
* setting the burst length to the word size. This is * setting the burst length to the word size. This is
* considerably faster than manually controlling the CS. * considerably faster than manually controlling the CS.
*/ */
spi_imx->controller->mode_bits |= SPI_CS_WORD; controller->mode_bits |= SPI_CS_WORD;
if (is_imx51_ecspi(spi_imx) || is_imx53_ecspi(spi_imx)) {
controller->max_native_cs = 4;
controller->flags |= SPI_MASTER_GPIO_SS;
}
spi_imx->spi_drctl = spi_drctl; spi_imx->spi_drctl = spi_drctl;
......
...@@ -1144,7 +1144,8 @@ static int mtk_spi_probe(struct platform_device *pdev) ...@@ -1144,7 +1144,8 @@ static int mtk_spi_probe(struct platform_device *pdev)
if (mdata->dev_comp->must_tx) if (mdata->dev_comp->must_tx)
master->flags = SPI_MASTER_MUST_TX; master->flags = SPI_MASTER_MUST_TX;
if (mdata->dev_comp->ipm_design) if (mdata->dev_comp->ipm_design)
master->mode_bits |= SPI_LOOP; master->mode_bits |= SPI_LOOP | SPI_RX_DUAL | SPI_TX_DUAL |
SPI_RX_QUAD | SPI_TX_QUAD;
if (mdata->dev_comp->ipm_design) { if (mdata->dev_comp->ipm_design) {
mdata->dev = dev; mdata->dev = dev;
...@@ -1269,7 +1270,7 @@ static int mtk_spi_probe(struct platform_device *pdev) ...@@ -1269,7 +1270,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int mtk_spi_remove(struct platform_device *pdev) static void mtk_spi_remove(struct platform_device *pdev)
{ {
struct spi_master *master = platform_get_drvdata(pdev); struct spi_master *master = platform_get_drvdata(pdev);
struct mtk_spi *mdata = spi_master_get_devdata(master); struct mtk_spi *mdata = spi_master_get_devdata(master);
...@@ -1278,21 +1279,25 @@ static int mtk_spi_remove(struct platform_device *pdev) ...@@ -1278,21 +1279,25 @@ static int mtk_spi_remove(struct platform_device *pdev)
if (mdata->use_spimem && !completion_done(&mdata->spimem_done)) if (mdata->use_spimem && !completion_done(&mdata->spimem_done))
complete(&mdata->spimem_done); complete(&mdata->spimem_done);
ret = pm_runtime_resume_and_get(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) if (ret < 0) {
return ret; dev_warn(&pdev->dev, "Failed to resume hardware (%pe)\n", ERR_PTR(ret));
} else {
/*
* If pm runtime resume failed, clks are disabled and
* unprepared. So don't access the hardware and skip clk
* unpreparing.
*/
mtk_spi_reset(mdata); mtk_spi_reset(mdata);
if (mdata->dev_comp->no_need_unprepare) { if (mdata->dev_comp->no_need_unprepare) {
clk_unprepare(mdata->spi_clk); clk_unprepare(mdata->spi_clk);
clk_unprepare(mdata->spi_hclk); clk_unprepare(mdata->spi_hclk);
} }
}
pm_runtime_put_noidle(&pdev->dev); pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
return 0;
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
...@@ -1311,7 +1316,7 @@ static int mtk_spi_suspend(struct device *dev) ...@@ -1311,7 +1316,7 @@ static int mtk_spi_suspend(struct device *dev)
clk_disable_unprepare(mdata->spi_hclk); clk_disable_unprepare(mdata->spi_hclk);
} }
return ret; return 0;
} }
static int mtk_spi_resume(struct device *dev) static int mtk_spi_resume(struct device *dev)
...@@ -1412,7 +1417,7 @@ static struct platform_driver mtk_spi_driver = { ...@@ -1412,7 +1417,7 @@ static struct platform_driver mtk_spi_driver = {
.of_match_table = mtk_spi_of_match, .of_match_table = mtk_spi_of_match,
}, },
.probe = mtk_spi_probe, .probe = mtk_spi_probe,
.remove = mtk_spi_remove, .remove_new = mtk_spi_remove,
}; };
module_platform_driver(mtk_spi_driver); module_platform_driver(mtk_spi_driver);
......
...@@ -2217,8 +2217,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -2217,8 +2217,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
amba_set_drvdata(adev, pl022); amba_set_drvdata(adev, pl022);
status = devm_spi_register_master(&adev->dev, master); status = devm_spi_register_master(&adev->dev, master);
if (status != 0) { if (status != 0) {
dev_err(&adev->dev, dev_err_probe(&adev->dev, status,
"probe - problem registering spi master\n"); "problem registering spi master\n");
goto err_spi_register; goto err_spi_register;
} }
dev_dbg(dev, "probe succeeded\n"); dev_dbg(dev, "probe succeeded\n");
......
This diff is collapsed.
This diff is collapsed.
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/platform_data/spi-s3c64xx.h> #include <linux/platform_data/spi-s3c64xx.h>
#define MAX_SPI_PORTS 12 #define MAX_SPI_PORTS 12
#define S3C64XX_SPI_QUIRK_POLL (1 << 0)
#define S3C64XX_SPI_QUIRK_CS_AUTO (1 << 1) #define S3C64XX_SPI_QUIRK_CS_AUTO (1 << 1)
#define AUTOSUSPEND_TIMEOUT 2000 #define AUTOSUSPEND_TIMEOUT 2000
...@@ -59,6 +58,8 @@ ...@@ -59,6 +58,8 @@
#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD (1<<17) #define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD (1<<17)
#define S3C64XX_SPI_MODE_BUS_TSZ_WORD (2<<17) #define S3C64XX_SPI_MODE_BUS_TSZ_WORD (2<<17)
#define S3C64XX_SPI_MODE_BUS_TSZ_MASK (3<<17) #define S3C64XX_SPI_MODE_BUS_TSZ_MASK (3<<17)
#define S3C64XX_SPI_MODE_RX_RDY_LVL GENMASK(16, 11)
#define S3C64XX_SPI_MODE_RX_RDY_LVL_SHIFT 11
#define S3C64XX_SPI_MODE_SELF_LOOPBACK (1<<3) #define S3C64XX_SPI_MODE_SELF_LOOPBACK (1<<3)
#define S3C64XX_SPI_MODE_RXDMA_ON (1<<2) #define S3C64XX_SPI_MODE_RXDMA_ON (1<<2)
#define S3C64XX_SPI_MODE_TXDMA_ON (1<<1) #define S3C64XX_SPI_MODE_TXDMA_ON (1<<1)
...@@ -115,8 +116,10 @@ ...@@ -115,8 +116,10 @@
#define S3C64XX_SPI_TRAILCNT S3C64XX_SPI_MAX_TRAILCNT #define S3C64XX_SPI_TRAILCNT S3C64XX_SPI_MAX_TRAILCNT
#define S3C64XX_SPI_POLLING_SIZE 32
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
#define is_polling(x) (x->port_conf->quirks & S3C64XX_SPI_QUIRK_POLL) #define is_polling(x) (x->cntrlr_info->polling)
#define RXBUSY (1<<2) #define RXBUSY (1<<2)
#define TXBUSY (1<<3) #define TXBUSY (1<<3)
...@@ -553,7 +556,7 @@ static int s3c64xx_wait_for_dma(struct s3c64xx_spi_driver_data *sdd, ...@@ -553,7 +556,7 @@ static int s3c64xx_wait_for_dma(struct s3c64xx_spi_driver_data *sdd,
} }
static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd, static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer) struct spi_transfer *xfer, bool use_irq)
{ {
void __iomem *regs = sdd->regs; void __iomem *regs = sdd->regs;
unsigned long val; unsigned long val;
...@@ -562,11 +565,24 @@ static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd, ...@@ -562,11 +565,24 @@ static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
u32 cpy_len; u32 cpy_len;
u8 *buf; u8 *buf;
int ms; int ms;
unsigned long time_us;
/* millisecs to xfer 'len' bytes @ 'cur_speed' */ /* microsecs to xfer 'len' bytes @ 'cur_speed' */
ms = xfer->len * 8 * 1000 / sdd->cur_speed; time_us = (xfer->len * 8 * 1000 * 1000) / sdd->cur_speed;
ms = (time_us / 1000);
ms += 10; /* some tolerance */ ms += 10; /* some tolerance */
/* sleep during signal transfer time */
status = readl(regs + S3C64XX_SPI_STATUS);
if (RX_FIFO_LVL(status, sdd) < xfer->len)
usleep_range(time_us / 2, time_us);
if (use_irq) {
val = msecs_to_jiffies(ms);
if (!wait_for_completion_timeout(&sdd->xfer_completion, val))
return -EIO;
}
val = msecs_to_loops(ms); val = msecs_to_loops(ms);
do { do {
status = readl(regs + S3C64XX_SPI_STATUS); status = readl(regs + S3C64XX_SPI_STATUS);
...@@ -729,10 +745,13 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, ...@@ -729,10 +745,13 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
void *rx_buf = NULL; void *rx_buf = NULL;
int target_len = 0, origin_len = 0; int target_len = 0, origin_len = 0;
int use_dma = 0; int use_dma = 0;
bool use_irq = false;
int status; int status;
u32 speed; u32 speed;
u8 bpw; u8 bpw;
unsigned long flags; unsigned long flags;
u32 rdy_lv;
u32 val;
reinit_completion(&sdd->xfer_completion); reinit_completion(&sdd->xfer_completion);
...@@ -753,17 +772,46 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, ...@@ -753,17 +772,46 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
sdd->rx_dma.ch && sdd->tx_dma.ch) { sdd->rx_dma.ch && sdd->tx_dma.ch) {
use_dma = 1; use_dma = 1;
} else if (xfer->len > fifo_len) { } else if (xfer->len >= fifo_len) {
tx_buf = xfer->tx_buf; tx_buf = xfer->tx_buf;
rx_buf = xfer->rx_buf; rx_buf = xfer->rx_buf;
origin_len = xfer->len; origin_len = xfer->len;
target_len = xfer->len; target_len = xfer->len;
if (xfer->len > fifo_len) xfer->len = fifo_len - 1;
xfer->len = fifo_len;
} }
do { do {
/* transfer size is greater than 32, change to IRQ mode */
if (!use_dma && xfer->len > S3C64XX_SPI_POLLING_SIZE)
use_irq = true;
if (use_irq) {
reinit_completion(&sdd->xfer_completion);
rdy_lv = xfer->len;
/* Setup RDY_FIFO trigger Level
* RDY_LVL =
* fifo_lvl up to 64 byte -> N bytes
* 128 byte -> RDY_LVL * 2 bytes
* 256 byte -> RDY_LVL * 4 bytes
*/
if (fifo_len == 128)
rdy_lv /= 2;
else if (fifo_len == 256)
rdy_lv /= 4;
val = readl(sdd->regs + S3C64XX_SPI_MODE_CFG);
val &= ~S3C64XX_SPI_MODE_RX_RDY_LVL;
val |= (rdy_lv << S3C64XX_SPI_MODE_RX_RDY_LVL_SHIFT);
writel(val, sdd->regs + S3C64XX_SPI_MODE_CFG);
/* Enable FIFO_RDY_EN IRQ */
val = readl(sdd->regs + S3C64XX_SPI_INT_EN);
writel((val | S3C64XX_SPI_INT_RX_FIFORDY_EN),
sdd->regs + S3C64XX_SPI_INT_EN);
}
spin_lock_irqsave(&sdd->lock, flags); spin_lock_irqsave(&sdd->lock, flags);
/* Pending only which is to be done */ /* Pending only which is to be done */
...@@ -785,7 +833,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, ...@@ -785,7 +833,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
if (use_dma) if (use_dma)
status = s3c64xx_wait_for_dma(sdd, xfer); status = s3c64xx_wait_for_dma(sdd, xfer);
else else
status = s3c64xx_wait_for_pio(sdd, xfer); status = s3c64xx_wait_for_pio(sdd, xfer, use_irq);
if (status) { if (status) {
dev_err(&spi->dev, dev_err(&spi->dev,
...@@ -824,8 +872,8 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master, ...@@ -824,8 +872,8 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
if (xfer->rx_buf) if (xfer->rx_buf)
xfer->rx_buf += xfer->len; xfer->rx_buf += xfer->len;
if (target_len > fifo_len) if (target_len >= fifo_len)
xfer->len = fifo_len; xfer->len = fifo_len - 1;
else else
xfer->len = target_len; xfer->len = target_len;
} }
...@@ -995,6 +1043,14 @@ static irqreturn_t s3c64xx_spi_irq(int irq, void *data) ...@@ -995,6 +1043,14 @@ static irqreturn_t s3c64xx_spi_irq(int irq, void *data)
dev_err(&spi->dev, "TX underrun\n"); dev_err(&spi->dev, "TX underrun\n");
} }
if (val & S3C64XX_SPI_ST_RX_FIFORDY) {
complete(&sdd->xfer_completion);
/* No pending clear irq, turn-off INT_EN_RX_FIFO_RDY */
val = readl(sdd->regs + S3C64XX_SPI_INT_EN);
writel((val & ~S3C64XX_SPI_INT_RX_FIFORDY_EN),
sdd->regs + S3C64XX_SPI_INT_EN);
}
/* Clear the pending irq by setting and then clearing it */ /* Clear the pending irq by setting and then clearing it */
writel(clr, sdd->regs + S3C64XX_SPI_PENDING_CLR); writel(clr, sdd->regs + S3C64XX_SPI_PENDING_CLR);
writel(0, sdd->regs + S3C64XX_SPI_PENDING_CLR); writel(0, sdd->regs + S3C64XX_SPI_PENDING_CLR);
...@@ -1068,6 +1124,7 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) ...@@ -1068,6 +1124,7 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
} }
sci->no_cs = of_property_read_bool(dev->of_node, "no-cs-readback"); sci->no_cs = of_property_read_bool(dev->of_node, "no-cs-readback");
sci->polling = !of_property_present(dev->of_node, "dmas");
return sci; return sci;
} }
...@@ -1103,29 +1160,23 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1103,29 +1160,23 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
return PTR_ERR(sci); return PTR_ERR(sci);
} }
if (!sci) { if (!sci)
dev_err(&pdev->dev, "platform_data missing!\n"); return dev_err_probe(&pdev->dev, -ENODEV,
return -ENODEV; "Platform_data missing!\n");
}
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res == NULL) { if (!mem_res)
dev_err(&pdev->dev, "Unable to get SPI MEM resource\n"); return dev_err_probe(&pdev->dev, -ENXIO,
return -ENXIO; "Unable to get SPI MEM resource\n");
}
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0)
dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq); return dev_err_probe(&pdev->dev, irq, "Failed to get IRQ\n");
return irq;
}
master = spi_alloc_master(&pdev->dev, master = devm_spi_alloc_master(&pdev->dev, sizeof(*sdd));
sizeof(struct s3c64xx_spi_driver_data)); if (!master)
if (master == NULL) { return dev_err_probe(&pdev->dev, -ENOMEM,
dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); "Unable to allocate SPI Master\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, master); platform_set_drvdata(pdev, master);
...@@ -1137,11 +1188,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1137,11 +1188,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
sdd->sfr_start = mem_res->start; sdd->sfr_start = mem_res->start;
if (pdev->dev.of_node) { if (pdev->dev.of_node) {
ret = of_alias_get_id(pdev->dev.of_node, "spi"); ret = of_alias_get_id(pdev->dev.of_node, "spi");
if (ret < 0) { if (ret < 0)
dev_err(&pdev->dev, "failed to get alias id, errno %d\n", return dev_err_probe(&pdev->dev, ret,
ret); "Failed to get alias id\n");
goto err_deref_master;
}
sdd->port_id = ret; sdd->port_id = ret;
} else { } else {
sdd->port_id = pdev->id; sdd->port_id = pdev->id;
...@@ -1175,59 +1224,31 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1175,59 +1224,31 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
master->can_dma = s3c64xx_spi_can_dma; master->can_dma = s3c64xx_spi_can_dma;
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res); sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(sdd->regs)) { if (IS_ERR(sdd->regs))
ret = PTR_ERR(sdd->regs); return PTR_ERR(sdd->regs);
goto err_deref_master;
}
if (sci->cfg_gpio && sci->cfg_gpio()) { if (sci->cfg_gpio && sci->cfg_gpio())
dev_err(&pdev->dev, "Unable to config gpio\n"); return dev_err_probe(&pdev->dev, -EBUSY,
ret = -EBUSY; "Unable to config gpio\n");
goto err_deref_master;
}
/* Setup clocks */ /* Setup clocks */
sdd->clk = devm_clk_get(&pdev->dev, "spi"); sdd->clk = devm_clk_get_enabled(&pdev->dev, "spi");
if (IS_ERR(sdd->clk)) { if (IS_ERR(sdd->clk))
dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n"); return dev_err_probe(&pdev->dev, PTR_ERR(sdd->clk),
ret = PTR_ERR(sdd->clk); "Unable to acquire clock 'spi'\n");
goto err_deref_master;
}
ret = clk_prepare_enable(sdd->clk);
if (ret) {
dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
goto err_deref_master;
}
sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr); sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
sdd->src_clk = devm_clk_get(&pdev->dev, clk_name); sdd->src_clk = devm_clk_get_enabled(&pdev->dev, clk_name);
if (IS_ERR(sdd->src_clk)) { if (IS_ERR(sdd->src_clk))
dev_err(&pdev->dev, return dev_err_probe(&pdev->dev, PTR_ERR(sdd->src_clk),
"Unable to acquire clock '%s'\n", clk_name); "Unable to acquire clock '%s'\n",
ret = PTR_ERR(sdd->src_clk); clk_name);
goto err_disable_clk;
}
ret = clk_prepare_enable(sdd->src_clk);
if (ret) {
dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name);
goto err_disable_clk;
}
if (sdd->port_conf->clk_ioclk) { if (sdd->port_conf->clk_ioclk) {
sdd->ioclk = devm_clk_get(&pdev->dev, "spi_ioclk"); sdd->ioclk = devm_clk_get_enabled(&pdev->dev, "spi_ioclk");
if (IS_ERR(sdd->ioclk)) { if (IS_ERR(sdd->ioclk))
dev_err(&pdev->dev, "Unable to acquire 'ioclk'\n"); return dev_err_probe(&pdev->dev, PTR_ERR(sdd->ioclk),
ret = PTR_ERR(sdd->ioclk); "Unable to acquire 'ioclk'\n");
goto err_disable_src_clk;
}
ret = clk_prepare_enable(sdd->ioclk);
if (ret) {
dev_err(&pdev->dev, "Couldn't enable clock 'ioclk'\n");
goto err_disable_src_clk;
}
} }
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT); pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT);
...@@ -1275,14 +1296,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1275,14 +1296,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev); pm_runtime_set_suspended(&pdev->dev);
clk_disable_unprepare(sdd->ioclk);
err_disable_src_clk:
clk_disable_unprepare(sdd->src_clk);
err_disable_clk:
clk_disable_unprepare(sdd->clk);
err_deref_master:
spi_master_put(master);
return ret; return ret;
} }
...@@ -1300,12 +1313,6 @@ static void s3c64xx_spi_remove(struct platform_device *pdev) ...@@ -1300,12 +1313,6 @@ static void s3c64xx_spi_remove(struct platform_device *pdev)
dma_release_channel(sdd->tx_dma.ch); dma_release_channel(sdd->tx_dma.ch);
} }
clk_disable_unprepare(sdd->ioclk);
clk_disable_unprepare(sdd->src_clk);
clk_disable_unprepare(sdd->clk);
pm_runtime_put_noidle(&pdev->dev); pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev); pm_runtime_set_suspended(&pdev->dev);
......
...@@ -337,7 +337,7 @@ static struct i2c_driver sc18is602_driver = { ...@@ -337,7 +337,7 @@ static struct i2c_driver sc18is602_driver = {
.name = "sc18is602", .name = "sc18is602",
.of_match_table = of_match_ptr(sc18is602_of_match), .of_match_table = of_match_ptr(sc18is602_of_match),
}, },
.probe_new = sc18is602_probe, .probe = sc18is602_probe,
.id_table = sc18is602_id, .id_table = sc18is602_id,
}; };
......
...@@ -526,7 +526,7 @@ static int f_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) ...@@ -526,7 +526,7 @@ static int f_ospi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
static bool f_ospi_supports_op_width(struct spi_mem *mem, static bool f_ospi_supports_op_width(struct spi_mem *mem,
const struct spi_mem_op *op) const struct spi_mem_op *op)
{ {
u8 width_available[] = { 0, 1, 2, 4, 8 }; static const u8 width_available[] = { 0, 1, 2, 4, 8 };
u8 width_op[] = { op->cmd.buswidth, op->addr.buswidth, u8 width_op[] = { op->cmd.buswidth, op->addr.buswidth,
op->dummy.buswidth, op->data.buswidth }; op->dummy.buswidth, op->data.buswidth };
bool is_match_found; bool is_match_found;
...@@ -566,7 +566,7 @@ static bool f_ospi_supports_op(struct spi_mem *mem, ...@@ -566,7 +566,7 @@ static bool f_ospi_supports_op(struct spi_mem *mem,
static int f_ospi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op) static int f_ospi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{ {
op->data.nbytes = min((int)op->data.nbytes, (int)(OSPI_DAT_SIZE_MAX)); op->data.nbytes = min_t(int, op->data.nbytes, OSPI_DAT_SIZE_MAX);
return 0; return 0;
} }
...@@ -634,18 +634,12 @@ static int f_ospi_probe(struct platform_device *pdev) ...@@ -634,18 +634,12 @@ static int f_ospi_probe(struct platform_device *pdev)
goto err_put_ctlr; goto err_put_ctlr;
} }
ospi->clk = devm_clk_get(dev, NULL); ospi->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(ospi->clk)) { if (IS_ERR(ospi->clk)) {
ret = PTR_ERR(ospi->clk); ret = PTR_ERR(ospi->clk);
goto err_put_ctlr; goto err_put_ctlr;
} }
ret = clk_prepare_enable(ospi->clk);
if (ret) {
dev_err(dev, "Failed to enable the clock\n");
goto err_disable_clk;
}
mutex_init(&ospi->mlock); mutex_init(&ospi->mlock);
ret = f_ospi_init(ospi); ret = f_ospi_init(ospi);
...@@ -661,9 +655,6 @@ static int f_ospi_probe(struct platform_device *pdev) ...@@ -661,9 +655,6 @@ static int f_ospi_probe(struct platform_device *pdev)
err_destroy_mutex: err_destroy_mutex:
mutex_destroy(&ospi->mlock); mutex_destroy(&ospi->mlock);
err_disable_clk:
clk_disable_unprepare(ospi->clk);
err_put_ctlr: err_put_ctlr:
spi_controller_put(ctlr); spi_controller_put(ctlr);
...@@ -674,8 +665,6 @@ static void f_ospi_remove(struct platform_device *pdev) ...@@ -674,8 +665,6 @@ static void f_ospi_remove(struct platform_device *pdev)
{ {
struct f_ospi *ospi = platform_get_drvdata(pdev); struct f_ospi *ospi = platform_get_drvdata(pdev);
clk_disable_unprepare(ospi->clk);
mutex_destroy(&ospi->mlock); mutex_destroy(&ospi->mlock);
} }
......
This diff is collapsed.
...@@ -42,7 +42,9 @@ ...@@ -42,7 +42,9 @@
#define SUN6I_TFR_CTL_CS_MANUAL BIT(6) #define SUN6I_TFR_CTL_CS_MANUAL BIT(6)
#define SUN6I_TFR_CTL_CS_LEVEL BIT(7) #define SUN6I_TFR_CTL_CS_LEVEL BIT(7)
#define SUN6I_TFR_CTL_DHB BIT(8) #define SUN6I_TFR_CTL_DHB BIT(8)
#define SUN6I_TFR_CTL_SDC BIT(11)
#define SUN6I_TFR_CTL_FBS BIT(12) #define SUN6I_TFR_CTL_FBS BIT(12)
#define SUN6I_TFR_CTL_SDM BIT(13)
#define SUN6I_TFR_CTL_XCH BIT(31) #define SUN6I_TFR_CTL_XCH BIT(31)
#define SUN6I_INT_CTL_REG 0x10 #define SUN6I_INT_CTL_REG 0x10
...@@ -85,6 +87,11 @@ ...@@ -85,6 +87,11 @@
#define SUN6I_TXDATA_REG 0x200 #define SUN6I_TXDATA_REG 0x200
#define SUN6I_RXDATA_REG 0x300 #define SUN6I_RXDATA_REG 0x300
struct sun6i_spi_cfg {
unsigned long fifo_depth;
bool has_clk_ctl;
};
struct sun6i_spi { struct sun6i_spi {
struct spi_master *master; struct spi_master *master;
void __iomem *base_addr; void __iomem *base_addr;
...@@ -99,7 +106,7 @@ struct sun6i_spi { ...@@ -99,7 +106,7 @@ struct sun6i_spi {
const u8 *tx_buf; const u8 *tx_buf;
u8 *rx_buf; u8 *rx_buf;
int len; int len;
unsigned long fifo_depth; const struct sun6i_spi_cfg *cfg;
}; };
static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg) static inline u32 sun6i_spi_read(struct sun6i_spi *sspi, u32 reg)
...@@ -156,7 +163,7 @@ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi) ...@@ -156,7 +163,7 @@ static inline void sun6i_spi_fill_fifo(struct sun6i_spi *sspi)
u8 byte; u8 byte;
/* See how much data we can fit */ /* See how much data we can fit */
cnt = sspi->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi); cnt = sspi->cfg->fifo_depth - sun6i_spi_get_tx_fifo_count(sspi);
len = min((int)cnt, sspi->len); len = min((int)cnt, sspi->len);
...@@ -256,7 +263,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -256,7 +263,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
struct spi_transfer *tfr) struct spi_transfer *tfr)
{ {
struct sun6i_spi *sspi = spi_master_get_devdata(master); struct sun6i_spi *sspi = spi_master_get_devdata(master);
unsigned int mclk_rate, div, div_cdr1, div_cdr2, timeout; unsigned int div, div_cdr1, div_cdr2, timeout;
unsigned int start, end, tx_time; unsigned int start, end, tx_time;
unsigned int trig_level; unsigned int trig_level;
unsigned int tx_len = 0, rx_len = 0; unsigned int tx_len = 0, rx_len = 0;
...@@ -289,14 +296,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -289,14 +296,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
* the hardcoded value used in old generation of Allwinner * the hardcoded value used in old generation of Allwinner
* SPI controller. (See spi-sun4i.c) * SPI controller. (See spi-sun4i.c)
*/ */
trig_level = sspi->fifo_depth / 4 * 3; trig_level = sspi->cfg->fifo_depth / 4 * 3;
} else { } else {
/* /*
* Setup FIFO DMA request trigger level * Setup FIFO DMA request trigger level
* We choose 1/2 of the full fifo depth, that value will * We choose 1/2 of the full fifo depth, that value will
* be used as DMA burst length. * be used as DMA burst length.
*/ */
trig_level = sspi->fifo_depth / 2; trig_level = sspi->cfg->fifo_depth / 2;
if (tfr->tx_buf) if (tfr->tx_buf)
reg |= SUN6I_FIFO_CTL_TF_DRQ_EN; reg |= SUN6I_FIFO_CTL_TF_DRQ_EN;
...@@ -346,8 +353,10 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -346,8 +353,10 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg); sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
if (sspi->cfg->has_clk_ctl) {
unsigned int mclk_rate = clk_get_rate(sspi->mclk);
/* Ensure that we have a parent clock fast enough */ /* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
if (mclk_rate < (2 * tfr->speed_hz)) { if (mclk_rate < (2 * tfr->speed_hz)) {
clk_set_rate(sspi->mclk, 2 * tfr->speed_hz); clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk); mclk_rate = clk_get_rate(sspi->mclk);
...@@ -379,6 +388,30 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -379,6 +388,30 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
} }
sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg); sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
} else {
clk_set_rate(sspi->mclk, tfr->speed_hz);
tfr->effective_speed_hz = clk_get_rate(sspi->mclk);
/*
* Configure work mode.
*
* There are three work modes depending on the controller clock
* frequency:
* - normal sample mode : CLK <= 24MHz SDM=1 SDC=0
* - delay half-cycle sample mode : CLK <= 40MHz SDM=0 SDC=0
* - delay one-cycle sample mode : CLK >= 80MHz SDM=0 SDC=1
*/
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
reg &= ~(SUN6I_TFR_CTL_SDM | SUN6I_TFR_CTL_SDC);
if (tfr->effective_speed_hz <= 24000000)
reg |= SUN6I_TFR_CTL_SDM;
else if (tfr->effective_speed_hz >= 80000000)
reg |= SUN6I_TFR_CTL_SDC;
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
}
/* Finally enable the bus - doing so before might raise SCK to HIGH */ /* Finally enable the bus - doing so before might raise SCK to HIGH */
reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG); reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG);
reg |= SUN6I_GBL_CTL_BUS_ENABLE; reg |= SUN6I_GBL_CTL_BUS_ENABLE;
...@@ -410,9 +443,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -410,9 +443,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
reg = SUN6I_INT_CTL_TC; reg = SUN6I_INT_CTL_TC;
if (!use_dma) { if (!use_dma) {
if (rx_len > sspi->fifo_depth) if (rx_len > sspi->cfg->fifo_depth)
reg |= SUN6I_INT_CTL_RF_RDY; reg |= SUN6I_INT_CTL_RF_RDY;
if (tx_len > sspi->fifo_depth) if (tx_len > sspi->cfg->fifo_depth)
reg |= SUN6I_INT_CTL_TF_ERQ; reg |= SUN6I_INT_CTL_TF_ERQ;
} }
...@@ -422,7 +455,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master, ...@@ -422,7 +455,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG); reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH); sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg | SUN6I_TFR_CTL_XCH);
tx_time = max(tfr->len * 8 * 2 / (tfr->speed_hz / 1000), 100U); tx_time = spi_controller_xfer_timeout(master, tfr);
start = jiffies; start = jiffies;
timeout = wait_for_completion_timeout(&sspi->done, timeout = wait_for_completion_timeout(&sspi->done,
msecs_to_jiffies(tx_time)); msecs_to_jiffies(tx_time));
...@@ -543,7 +576,7 @@ static bool sun6i_spi_can_dma(struct spi_master *master, ...@@ -543,7 +576,7 @@ static bool sun6i_spi_can_dma(struct spi_master *master,
* the fifo length we can just fill the fifo and wait for a single * the fifo length we can just fill the fifo and wait for a single
* irq, so don't bother setting up dma * irq, so don't bother setting up dma
*/ */
return xfer->len > sspi->fifo_depth; return xfer->len > sspi->cfg->fifo_depth;
} }
static int sun6i_spi_probe(struct platform_device *pdev) static int sun6i_spi_probe(struct platform_device *pdev)
...@@ -582,7 +615,7 @@ static int sun6i_spi_probe(struct platform_device *pdev) ...@@ -582,7 +615,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
} }
sspi->master = master; sspi->master = master;
sspi->fifo_depth = (unsigned long)of_device_get_match_data(&pdev->dev); sspi->cfg = of_device_get_match_data(&pdev->dev);
master->max_speed_hz = 100 * 1000 * 1000; master->max_speed_hz = 100 * 1000 * 1000;
master->min_speed_hz = 3 * 1000; master->min_speed_hz = 3 * 1000;
...@@ -695,9 +728,27 @@ static void sun6i_spi_remove(struct platform_device *pdev) ...@@ -695,9 +728,27 @@ static void sun6i_spi_remove(struct platform_device *pdev)
dma_release_channel(master->dma_rx); dma_release_channel(master->dma_rx);
} }
static const struct sun6i_spi_cfg sun6i_a31_spi_cfg = {
.fifo_depth = SUN6I_FIFO_DEPTH,
.has_clk_ctl = true,
};
static const struct sun6i_spi_cfg sun8i_h3_spi_cfg = {
.fifo_depth = SUN8I_FIFO_DEPTH,
.has_clk_ctl = true,
};
static const struct sun6i_spi_cfg sun50i_r329_spi_cfg = {
.fifo_depth = SUN8I_FIFO_DEPTH,
};
static const struct of_device_id sun6i_spi_match[] = { static const struct of_device_id sun6i_spi_match[] = {
{ .compatible = "allwinner,sun6i-a31-spi", .data = (void *)SUN6I_FIFO_DEPTH }, { .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_cfg },
{ .compatible = "allwinner,sun8i-h3-spi", .data = (void *)SUN8I_FIFO_DEPTH }, { .compatible = "allwinner,sun8i-h3-spi", .data = &sun8i_h3_spi_cfg },
{
.compatible = "allwinner,sun50i-r329-spi",
.data = &sun50i_r329_spi_cfg
},
{} {}
}; };
MODULE_DEVICE_TABLE(of, sun6i_spi_match); MODULE_DEVICE_TABLE(of, sun6i_spi_match);
......
...@@ -241,7 +241,7 @@ static struct i2c_driver spi_xcomm_driver = { ...@@ -241,7 +241,7 @@ static struct i2c_driver spi_xcomm_driver = {
.name = "spi-xcomm", .name = "spi-xcomm",
}, },
.id_table = spi_xcomm_ids, .id_table = spi_xcomm_ids,
.probe_new = spi_xcomm_probe, .probe = spi_xcomm_probe,
}; };
module_i2c_driver(spi_xcomm_driver); module_i2c_driver(spi_xcomm_driver);
......
...@@ -64,7 +64,8 @@ static_assert(N_SPI_MINORS > 0 && N_SPI_MINORS <= 256); ...@@ -64,7 +64,8 @@ static_assert(N_SPI_MINORS > 0 && N_SPI_MINORS <= 256);
| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \ | SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
| SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \ | SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \
| SPI_RX_QUAD | SPI_RX_OCTAL \ | SPI_RX_QUAD | SPI_RX_OCTAL \
| SPI_RX_CPHA_FLIP) | SPI_RX_CPHA_FLIP | SPI_3WIRE_HIZ \
| SPI_MOSI_IDLE_LOW)
struct spidev_data { struct spidev_data {
dev_t devt; dev_t devt;
......
...@@ -36,6 +36,7 @@ struct s3c64xx_spi_info { ...@@ -36,6 +36,7 @@ struct s3c64xx_spi_info {
int src_clk_nr; int src_clk_nr;
int num_cs; int num_cs;
bool no_cs; bool no_cs;
bool polling;
int (*cfg_gpio)(void); int (*cfg_gpio)(void);
}; };
......
...@@ -1261,6 +1261,23 @@ static inline bool spi_is_bpw_supported(struct spi_device *spi, u32 bpw) ...@@ -1261,6 +1261,23 @@ static inline bool spi_is_bpw_supported(struct spi_device *spi, u32 bpw)
return false; return false;
} }
/**
* spi_controller_xfer_timeout - Compute a suitable timeout value
* @ctlr: SPI device
* @xfer: Transfer descriptor
*
* Compute a relevant timeout value for the given transfer. We derive the time
* that it would take on a single data line and take twice this amount of time
* with a minimum of 500ms to avoid false positives on loaded systems.
*
* Returns: Transfer timeout value in milliseconds.
*/
static inline unsigned int spi_controller_xfer_timeout(struct spi_controller *ctlr,
struct spi_transfer *xfer)
{
return max(xfer->len * 8 * 2 / (xfer->speed_hz / 1000), 500U);
}
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* SPI transfer replacement methods which make use of spi_res */ /* SPI transfer replacement methods which make use of spi_res */
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#define SPI_RX_OCTAL _BITUL(14) /* receive with 8 wires */ #define SPI_RX_OCTAL _BITUL(14) /* receive with 8 wires */
#define SPI_3WIRE_HIZ _BITUL(15) /* high impedance turnaround */ #define SPI_3WIRE_HIZ _BITUL(15) /* high impedance turnaround */
#define SPI_RX_CPHA_FLIP _BITUL(16) /* flip CPHA on Rx only xfer */ #define SPI_RX_CPHA_FLIP _BITUL(16) /* flip CPHA on Rx only xfer */
#define SPI_MOSI_IDLE_LOW _BITUL(17) /* leave mosi line low when idle */
/* /*
* All the bits defined above should be covered by SPI_MODE_USER_MASK. * All the bits defined above should be covered by SPI_MODE_USER_MASK.
...@@ -37,6 +38,6 @@ ...@@ -37,6 +38,6 @@
* These bits must not overlap. A static assert check should make sure of that. * These bits must not overlap. A static assert check should make sure of that.
* If adding extra bits, make sure to increase the bit index below as well. * If adding extra bits, make sure to increase the bit index below as well.
*/ */
#define SPI_MODE_USER_MASK (_BITUL(17) - 1) #define SPI_MODE_USER_MASK (_BITUL(18) - 1)
#endif /* _UAPI_SPI_H */ #endif /* _UAPI_SPI_H */
...@@ -172,28 +172,37 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len) ...@@ -172,28 +172,37 @@ static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
static void print_usage(const char *prog) static void print_usage(const char *prog)
{ {
printf("Usage: %s [-DsbdlHOLC3vpNR24SI]\n", prog); printf("Usage: %s [-2348CDFHILMNORSZbdilopsv]\n", prog);
puts(" -D --device device to use (default /dev/spidev1.1)\n" puts("general device settings:\n"
" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n" " -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n" " -d --delay delay (usec)\n"
" -b --bpw bits per word\n"
" -i --input input data from a file (e.g. \"test.bin\")\n"
" -o --output output data to a file (e.g. \"results.bin\")\n"
" -l --loop loopback\n" " -l --loop loopback\n"
"spi mode:\n"
" -H --cpha clock phase\n" " -H --cpha clock phase\n"
" -O --cpol clock polarity\n" " -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n" " -F --rx-cpha-flip flip CPHA on Rx only xfer\n"
" -C --cs-high chip select active high\n" "number of wires for transmission:\n"
" -3 --3wire SI/SO signals shared\n"
" -v --verbose Verbose (show tx buffer)\n"
" -p Send data (e.g. \"1234\\xde\\xad\")\n"
" -N --no-cs no chip select\n"
" -R --ready slave pulls low to pause\n"
" -2 --dual dual transfer\n" " -2 --dual dual transfer\n"
" -4 --quad quad transfer\n" " -4 --quad quad transfer\n"
" -8 --octal octal transfer\n" " -8 --octal octal transfer\n"
" -3 --3wire SI/SO signals shared\n"
" -Z --3wire-hiz high impedance turnaround\n"
"data:\n"
" -i --input input data from a file (e.g. \"test.bin\")\n"
" -o --output output data to a file (e.g. \"results.bin\")\n"
" -p Send data (e.g. \"1234\\xde\\xad\")\n"
" -S --size transfer size\n" " -S --size transfer size\n"
" -I --iter iterations\n"); " -I --iter iterations\n"
"additional parameters:\n"
" -b --bpw bits per word\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\n"
" -N --no-cs no chip select\n"
" -R --ready slave pulls low to pause\n"
" -M --mosi-idle-low leave mosi line low when idle\n"
"misc:\n"
" -v --verbose Verbose (show tx buffer)\n");
exit(1); exit(1);
} }
...@@ -204,28 +213,31 @@ static void parse_opts(int argc, char *argv[]) ...@@ -204,28 +213,31 @@ static void parse_opts(int argc, char *argv[])
{ "device", 1, 0, 'D' }, { "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' }, { "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' }, { "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "loop", 0, 0, 'l' }, { "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' }, { "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' }, { "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' }, { "rx-cpha-flip", 0, 0, 'F' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ "dual", 0, 0, '2' }, { "dual", 0, 0, '2' },
{ "verbose", 0, 0, 'v' },
{ "quad", 0, 0, '4' }, { "quad", 0, 0, '4' },
{ "octal", 0, 0, '8' }, { "octal", 0, 0, '8' },
{ "3wire", 0, 0, '3' },
{ "3wire-hiz", 0, 0, 'Z' },
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "size", 1, 0, 'S' }, { "size", 1, 0, 'S' },
{ "iter", 1, 0, 'I' }, { "iter", 1, 0, 'I' },
{ "bpw", 1, 0, 'b' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ "mosi-idle-low", 0, 0, 'M' },
{ "verbose", 0, 0, 'v' },
{ NULL, 0, 0, 0 }, { NULL, 0, 0, 0 },
}; };
int c; int c;
c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR248p:vS:I:", c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3ZFMNR248p:vS:I:",
lopts, NULL); lopts, NULL);
if (c == -1) if (c == -1)
...@@ -268,6 +280,15 @@ static void parse_opts(int argc, char *argv[]) ...@@ -268,6 +280,15 @@ static void parse_opts(int argc, char *argv[])
case '3': case '3':
mode |= SPI_3WIRE; mode |= SPI_3WIRE;
break; break;
case 'Z':
mode |= SPI_3WIRE_HIZ;
break;
case 'F':
mode |= SPI_RX_CPHA_FLIP;
break;
case 'M':
mode |= SPI_MOSI_IDLE_LOW;
break;
case 'N': case 'N':
mode |= SPI_NO_CS; mode |= SPI_NO_CS;
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment