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:
- Maxime Ripard <mripard@kernel.org>
properties:
"#address-cells": true
"#size-cells": true
compatible:
const: allwinner,sun4i-a10-spi
......@@ -46,12 +43,9 @@ properties:
- const: rx
- const: tx
num-cs: true
patternProperties:
"^.*@[0-9a-f]+":
type: object
additionalProperties: true
properties:
reg:
items:
......@@ -71,7 +65,7 @@ required:
- clocks
- clock-names
additionalProperties: false
unevaluatedProperties: false
examples:
- |
......
......@@ -14,11 +14,9 @@ maintainers:
- Maxime Ripard <mripard@kernel.org>
properties:
"#address-cells": true
"#size-cells": true
compatible:
oneOf:
- const: allwinner,sun50i-r329-spi
- const: allwinner,sun6i-a31-spi
- const: allwinner,sun8i-h3-spi
- items:
......@@ -28,6 +26,15 @@ properties:
- allwinner,sun50i-h616-spi
- allwinner,suniv-f1c100s-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:
maxItems: 1
......@@ -58,12 +65,9 @@ properties:
- const: rx
- const: tx
num-cs: true
patternProperties:
"^.*@[0-9a-f]+":
type: object
additionalProperties: true
properties:
reg:
items:
......@@ -83,7 +87,7 @@ required:
- clocks
- clock-names
additionalProperties: false
unevaluatedProperties: false
examples:
- |
......
......@@ -20,6 +20,10 @@ properties:
- items:
- const: microchip,sam9x60-spi
- const: atmel,at91rm9200-spi
- items:
- const: microchip,sam9x7-spi
- const: microchip,sam9x60-spi
- const: atmel,at91rm9200-spi
reg:
maxItems: 1
......
......@@ -46,12 +46,28 @@ allOf:
maxItems: 2
items:
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:
compatible:
oneOf:
- items:
- enum:
- amd,pensando-elba-qspi
- ti,k2g-qspi
- ti,am654-ospi
- intel,lgm-qspi
......@@ -76,8 +92,6 @@ properties:
description:
Size of the data FIFO in words.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [ 128, 256 ]
default: 128
cdns,fifo-width:
$ref: /schemas/types.yaml#/definitions/uint32
......
......@@ -29,6 +29,9 @@ properties:
reg:
maxItems: 1
iommus:
maxItems: 1
interrupts:
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:
minItems: 2
maxItems: 3
cs-gpios: true
dmas:
minItems: 2
maxItems: 2
......
......@@ -74,6 +74,8 @@ properties:
const: intel,keembay-ssi
- description: Intel Thunder Bay SPI Controller
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
const: amd,pensando-elba-spi
- description: Baikal-T1 SPI Controller
......
......@@ -17,9 +17,6 @@ allOf:
- $ref: spi-controller.yaml#
properties:
"#address-cells": true
"#size-cells": true
compatible:
const: socionext,uniphier-scssi
......
......@@ -17,7 +17,7 @@ description: |
properties:
$nodename:
pattern: "^spi(@.*|-[0-9a-f])*$"
pattern: "^spi(@.*|-([0-9]|[1-9][0-9]+))?$"
"#address-cells":
enum: [0, 1]
......
......@@ -32,6 +32,12 @@ properties:
clocks:
maxItems: 2
iommus:
maxItems: 1
power-domains:
maxItems: 1
required:
- compatible
- reg
......
......@@ -825,6 +825,12 @@ config SPI_RSPI
help
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
tristate "QTI QSPI controller"
depends on ARCH_QCOM || COMPILE_TEST
......@@ -936,6 +942,7 @@ config SPI_SPRD_ADI
config SPI_STM32
tristate "STMicroelectronics STM32 SPI controller"
depends on ARCH_STM32 || COMPILE_TEST
select SPI_SLAVE
help
SPI driver for STMicroelectronics STM32 SoCs.
......
......@@ -113,6 +113,7 @@ obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.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_SC18IS602) += spi-sc18is602.o
obj-$(CONFIG_SPI_SH) += spi-sh.o
......
......@@ -233,7 +233,8 @@
*/
#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
......@@ -1279,7 +1280,8 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
struct atmel_spi_device *asd;
int timeout;
int ret;
unsigned long dma_timeout;
unsigned int dma_timeout;
long ret_timeout;
as = spi_controller_get_devdata(host);
......@@ -1333,11 +1335,13 @@ static int atmel_spi_one_transfer(struct spi_controller *host,
atmel_spi_unlock(as);
}
dma_timeout = wait_for_completion_timeout(&as->xfer_completion,
SPI_DMA_TIMEOUT);
if (WARN_ON(dma_timeout == 0)) {
dev_err(&spi->dev, "spi transfer timeout\n");
as->done_status = -EIO;
dma_timeout = msecs_to_jiffies(spi_controller_xfer_timeout(host, xfer));
ret_timeout = wait_for_completion_interruptible_timeout(&as->xfer_completion,
dma_timeout);
if (ret_timeout <= 0) {
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)
......
......@@ -40,6 +40,7 @@
#define CQSPI_SUPPORT_EXTERNAL_DMA BIT(2)
#define CQSPI_NO_SUPPORT_WR_COMPLETION BIT(3)
#define CQSPI_SLOW_SRAM BIT(4)
#define CQSPI_NEEDS_APB_AHB_HAZARD_WAR BIT(5)
/* Capabilities */
#define CQSPI_SUPPORTS_OCTAL BIT(0)
......@@ -90,6 +91,7 @@ struct cqspi_st {
u32 pd_dev_id;
bool wr_completion;
bool slow_sram;
bool apb_ahb_hazard;
};
struct cqspi_driver_platdata {
......@@ -1027,6 +1029,13 @@ static int cqspi_indirect_write_execute(struct cqspi_flash_pdata *f_pdata,
if (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) {
size_t write_words, mod_bytes;
......@@ -1754,6 +1763,8 @@ static int cqspi_probe(struct platform_device *pdev)
cqspi->wr_completion = false;
if (ddata->quirks & CQSPI_SLOW_SRAM)
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,
"xlnx,versal-ospi-1.0")) {
......@@ -1888,6 +1899,10 @@ static const struct cqspi_driver_platdata jh7110_qspi = {
.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[] = {
{
.compatible = "cdns,qspi-nor",
......@@ -1917,6 +1932,10 @@ static const struct of_device_id cqspi_dt_ids[] = {
.compatible = "starfive,jh7110-qspi",
.data = &jh7110_qspi,
},
{
.compatible = "amd,pensando-elba-qspi",
.data = &pensando_cdns_qspi,
},
{ /* end of table */ }
};
......
......@@ -102,6 +102,7 @@
* @regs: Virtual address of the SPI controller registers
* @ref_clk: Pointer to the peripheral 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
* @txbuf: Pointer to the TX buffer
* @rxbuf: Pointer to the RX buffer
......
......@@ -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),
};
static int dw_spi_debugfs_init(struct dw_spi *dws)
static void dw_spi_debugfs_init(struct dw_spi *dws)
{
char name[32];
snprintf(name, 32, "dw_spi%d", dws->master->bus_num);
dws->debugfs = debugfs_create_dir(name, NULL);
if (!dws->debugfs)
return -ENOMEM;
dws->regset.regs = dw_spi_dbgfs_regs;
dws->regset.nregs = ARRAY_SIZE(dw_spi_dbgfs_regs);
dws->regset.base = dws->regs;
debugfs_create_regset32("registers", 0400, dws->debugfs, &dws->regset);
return 0;
}
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
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)
......@@ -426,7 +421,10 @@ static int dw_spi_transfer_one(struct spi_controller *master,
int ret;
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_len = transfer->len / dws->n_bytes;
dws->rx = transfer->rx_buf;
......
......@@ -72,12 +72,22 @@ static void dw_spi_dma_maxburst_init(struct dw_spi *dws)
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;
dma_get_slave_caps(dws->txchan, &tx);
dma_get_slave_caps(dws->rxchan, &rx);
ret = dma_get_slave_caps(dws->rxchan, &rx);
if (ret)
return ret;
if (!(tx.directions & BIT(DMA_MEM_TO_DEV) &&
rx.directions & BIT(DMA_DEV_TO_MEM)))
return -ENXIO;
if (tx.max_sg_burst > 0 && rx.max_sg_burst > 0)
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)
dws->dma_sg_burst = rx.max_sg_burst;
else
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)
......@@ -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 pci_dev *dma_dev;
dma_cap_mask_t mask;
int ret = -EBUSY;
/*
* 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)
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);
return 0;
free_txchan:
dma_release_channel(dws->txchan);
dws->txchan = NULL;
free_rxchan:
dma_release_channel(dws->rxchan);
dws->rxchan = NULL;
err_exit:
pci_dev_put(dma_dev);
return -EBUSY;
return ret;
}
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);
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;
free_txchan:
dma_release_channel(dws->txchan);
dws->txchan = NULL;
free_rxchan:
dma_release_channel(dws->rxchan);
dws->rxchan = NULL;
......@@ -198,22 +228,32 @@ static irqreturn_t dw_spi_dma_transfer_handler(struct dw_spi *dws)
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,
struct spi_device *spi, struct spi_transfer *xfer)
{
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)
{
if (n_bytes == 1)
return DMA_SLAVE_BUSWIDTH_1_BYTE;
else if (n_bytes == 2)
return DMA_SLAVE_BUSWIDTH_2_BYTES;
dma_bus_width = dw_spi_dma_convert_width(dws->n_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)
......
......@@ -236,6 +236,24 @@ static int dw_spi_intel_init(struct platform_device *pdev,
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,
struct dw_spi_mmio *dwsmmio)
{
......@@ -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 = "intel,keembay-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 = "canaan,k210-spi", dw_spi_canaan_k210_init},
{ .compatible = "amd,pensando-elba-spi", .data = dw_spi_elba_init},
......
......@@ -190,6 +190,7 @@ struct dw_spi {
struct dma_chan *rxchan;
u32 rxburst;
u32 dma_sg_burst;
u32 dma_addr_widths;
unsigned long dma_chan_busy;
dma_addr_t dma_addr; /* phy address of the Data register */
const struct dw_spi_dma_ops *dma_ops;
......
......@@ -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);
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) {
dev_err(fsl_lpspi->dev,
"per-clk should be at least two times of transfer speed");
......@@ -911,7 +917,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
if (ret == -EPROBE_DEFER)
goto out_pm_get;
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
/*
* disable LPSPI module IRQ when enable DMA mode successfully,
......
......@@ -35,7 +35,7 @@
#define CS_DEMUX_OUTPUT_SEL GENMASK(3, 0)
#define SE_SPI_TRANS_CFG 0x25c
#define CS_TOGGLE BIT(0)
#define CS_TOGGLE BIT(1)
#define SE_SPI_WORD_LEN 0x268
#define WORD_LEN_MSK GENMASK(9, 0)
......
......@@ -169,7 +169,7 @@ static int hisi_spi_debugfs_init(struct hisi_spi *hs)
master = container_of(hs->dev, struct spi_controller, dev);
snprintf(name, 32, "hisi_spi%d", master->bus_num);
hs->debugfs = debugfs_create_dir(name, NULL);
if (!hs->debugfs)
if (IS_ERR(hs->debugfs))
return -ENOMEM;
hs->regset.regs = hisi_spi_regs;
......
......@@ -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_SBBCTRL(cs) (1 << ((cs & 3) + 8))
#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_INT 0x10
......@@ -516,6 +517,13 @@ static void mx51_ecspi_disable(struct spi_imx_data *spi_imx)
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,
struct spi_message *msg)
{
......@@ -526,6 +534,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
u32 testreg, delay;
u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG);
u32 current_cfg = cfg;
int channel = mx51_ecspi_channel(spi);
/* set Master or Slave mode */
if (spi_imx->slave_mode)
......@@ -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);
/* 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
......@@ -561,22 +570,27 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx,
* BURST_LENGTH + 1 bits are received
*/
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
cfg |= MX51_ECSPI_CONFIG_SBBCTRL(spi_get_chipselect(spi, 0));
cfg |= MX51_ECSPI_CONFIG_SBBCTRL(channel);
if (spi->mode & SPI_CPOL) {
cfg |= MX51_ECSPI_CONFIG_SCLKPOL(spi_get_chipselect(spi, 0));
cfg |= MX51_ECSPI_CONFIG_SCLKCTL(spi_get_chipselect(spi, 0));
cfg |= MX51_ECSPI_CONFIG_SCLKPOL(channel);
cfg |= MX51_ECSPI_CONFIG_SCLKCTL(channel);
} else {
cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(spi_get_chipselect(spi, 0));
cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(spi_get_chipselect(spi, 0));
cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(channel);
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)
cfg |= MX51_ECSPI_CONFIG_SSBPOL(spi_get_chipselect(spi, 0));
cfg |= MX51_ECSPI_CONFIG_SSBPOL(channel);
else
cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(spi_get_chipselect(spi, 0));
cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(channel);
if (cfg == current_cfg)
return 0;
......@@ -621,14 +635,15 @@ static void mx51_configure_cpha(struct spi_imx_data *spi_imx,
bool cpha = (spi->mode & SPI_CPHA);
bool flip_cpha = (spi->mode & SPI_RX_CPHA_FLIP) && spi_imx->rx_only;
u32 cfg = readl(spi_imx->base + MX51_ECSPI_CONFIG);
int channel = mx51_ecspi_channel(spi);
/* Flip cpha logical value iff flip_cpha */
cpha ^= flip_cpha;
if (cpha)
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(spi_get_chipselect(spi, 0));
cfg |= MX51_ECSPI_CONFIG_SCLKPHA(channel);
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);
}
......@@ -1737,20 +1752,21 @@ static int spi_imx_probe(struct platform_device *pdev)
else
controller->num_chipselect = 3;
spi_imx->controller->transfer_one = spi_imx_transfer_one;
spi_imx->controller->setup = spi_imx_setup;
spi_imx->controller->cleanup = spi_imx_cleanup;
spi_imx->controller->prepare_message = spi_imx_prepare_message;
spi_imx->controller->unprepare_message = spi_imx_unprepare_message;
spi_imx->controller->slave_abort = spi_imx_slave_abort;
spi_imx->controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS;
controller->transfer_one = spi_imx_transfer_one;
controller->setup = spi_imx_setup;
controller->cleanup = spi_imx_cleanup;
controller->prepare_message = spi_imx_prepare_message;
controller->unprepare_message = spi_imx_unprepare_message;
controller->slave_abort = spi_imx_slave_abort;
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) ||
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))
spi_imx->controller->mode_bits |= SPI_RX_CPHA_FLIP;
controller->mode_bits |= SPI_RX_CPHA_FLIP;
if (is_imx51_ecspi(spi_imx) &&
device_property_read_u32(&pdev->dev, "cs-gpios", NULL))
......@@ -1759,7 +1775,12 @@ static int spi_imx_probe(struct platform_device *pdev)
* setting the burst length to the word size. This is
* 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;
......
......@@ -1144,7 +1144,8 @@ static int mtk_spi_probe(struct platform_device *pdev)
if (mdata->dev_comp->must_tx)
master->flags = SPI_MASTER_MUST_TX;
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) {
mdata->dev = dev;
......@@ -1269,7 +1270,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
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 mtk_spi *mdata = spi_master_get_devdata(master);
......@@ -1278,21 +1279,25 @@ static int mtk_spi_remove(struct platform_device *pdev)
if (mdata->use_spimem && !completion_done(&mdata->spimem_done))
complete(&mdata->spimem_done);
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
return ret;
mtk_spi_reset(mdata);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
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);
if (mdata->dev_comp->no_need_unprepare) {
clk_unprepare(mdata->spi_clk);
clk_unprepare(mdata->spi_hclk);
if (mdata->dev_comp->no_need_unprepare) {
clk_unprepare(mdata->spi_clk);
clk_unprepare(mdata->spi_hclk);
}
}
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
......@@ -1311,7 +1316,7 @@ static int mtk_spi_suspend(struct device *dev)
clk_disable_unprepare(mdata->spi_hclk);
}
return ret;
return 0;
}
static int mtk_spi_resume(struct device *dev)
......@@ -1412,7 +1417,7 @@ static struct platform_driver mtk_spi_driver = {
.of_match_table = mtk_spi_of_match,
},
.probe = mtk_spi_probe,
.remove = mtk_spi_remove,
.remove_new = mtk_spi_remove,
};
module_platform_driver(mtk_spi_driver);
......
......@@ -2217,8 +2217,8 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
amba_set_drvdata(adev, pl022);
status = devm_spi_register_master(&adev->dev, master);
if (status != 0) {
dev_err(&adev->dev,
"probe - problem registering spi master\n");
dev_err_probe(&adev->dev, status,
"problem registering spi master\n");
goto err_spi_register;
}
dev_dbg(dev, "probe succeeded\n");
......
This diff is collapsed.
This diff is collapsed.
......@@ -19,7 +19,6 @@
#include <linux/platform_data/spi-s3c64xx.h>
#define MAX_SPI_PORTS 12
#define S3C64XX_SPI_QUIRK_POLL (1 << 0)
#define S3C64XX_SPI_QUIRK_CS_AUTO (1 << 1)
#define AUTOSUSPEND_TIMEOUT 2000
......@@ -59,6 +58,8 @@
#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD (1<<17)
#define S3C64XX_SPI_MODE_BUS_TSZ_WORD (2<<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_RXDMA_ON (1<<2)
#define S3C64XX_SPI_MODE_TXDMA_ON (1<<1)
......@@ -115,8 +116,10 @@
#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 is_polling(x) (x->port_conf->quirks & S3C64XX_SPI_QUIRK_POLL)
#define is_polling(x) (x->cntrlr_info->polling)
#define RXBUSY (1<<2)
#define TXBUSY (1<<3)
......@@ -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,
struct spi_transfer *xfer)
struct spi_transfer *xfer, bool use_irq)
{
void __iomem *regs = sdd->regs;
unsigned long val;
......@@ -562,11 +565,24 @@ static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
u32 cpy_len;
u8 *buf;
int ms;
unsigned long time_us;
/* millisecs to xfer 'len' bytes @ 'cur_speed' */
ms = xfer->len * 8 * 1000 / sdd->cur_speed;
/* microsecs to xfer 'len' bytes @ 'cur_speed' */
time_us = (xfer->len * 8 * 1000 * 1000) / sdd->cur_speed;
ms = (time_us / 1000);
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);
do {
status = readl(regs + S3C64XX_SPI_STATUS);
......@@ -729,10 +745,13 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
void *rx_buf = NULL;
int target_len = 0, origin_len = 0;
int use_dma = 0;
bool use_irq = false;
int status;
u32 speed;
u8 bpw;
unsigned long flags;
u32 rdy_lv;
u32 val;
reinit_completion(&sdd->xfer_completion);
......@@ -753,17 +772,46 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
sdd->rx_dma.ch && sdd->tx_dma.ch) {
use_dma = 1;
} else if (xfer->len > fifo_len) {
} else if (xfer->len >= fifo_len) {
tx_buf = xfer->tx_buf;
rx_buf = xfer->rx_buf;
origin_len = xfer->len;
target_len = xfer->len;
if (xfer->len > fifo_len)
xfer->len = fifo_len;
xfer->len = fifo_len - 1;
}
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);
/* Pending only which is to be done */
......@@ -785,7 +833,7 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
if (use_dma)
status = s3c64xx_wait_for_dma(sdd, xfer);
else
status = s3c64xx_wait_for_pio(sdd, xfer);
status = s3c64xx_wait_for_pio(sdd, xfer, use_irq);
if (status) {
dev_err(&spi->dev,
......@@ -824,8 +872,8 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
if (xfer->rx_buf)
xfer->rx_buf += xfer->len;
if (target_len > fifo_len)
xfer->len = fifo_len;
if (target_len >= fifo_len)
xfer->len = fifo_len - 1;
else
xfer->len = target_len;
}
......@@ -995,6 +1043,14 @@ static irqreturn_t s3c64xx_spi_irq(int irq, void *data)
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 */
writel(clr, 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)
}
sci->no_cs = of_property_read_bool(dev->of_node, "no-cs-readback");
sci->polling = !of_property_present(dev->of_node, "dmas");
return sci;
}
......@@ -1103,29 +1160,23 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
return PTR_ERR(sci);
}
if (!sci) {
dev_err(&pdev->dev, "platform_data missing!\n");
return -ENODEV;
}
if (!sci)
return dev_err_probe(&pdev->dev, -ENODEV,
"Platform_data missing!\n");
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
return -ENXIO;
}
if (!mem_res)
return dev_err_probe(&pdev->dev, -ENXIO,
"Unable to get SPI MEM resource\n");
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq);
return irq;
}
if (irq < 0)
return dev_err_probe(&pdev->dev, irq, "Failed to get IRQ\n");
master = spi_alloc_master(&pdev->dev,
sizeof(struct s3c64xx_spi_driver_data));
if (master == NULL) {
dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
return -ENOMEM;
}
master = devm_spi_alloc_master(&pdev->dev, sizeof(*sdd));
if (!master)
return dev_err_probe(&pdev->dev, -ENOMEM,
"Unable to allocate SPI Master\n");
platform_set_drvdata(pdev, master);
......@@ -1137,11 +1188,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
sdd->sfr_start = mem_res->start;
if (pdev->dev.of_node) {
ret = of_alias_get_id(pdev->dev.of_node, "spi");
if (ret < 0) {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
ret);
goto err_deref_master;
}
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"Failed to get alias id\n");
sdd->port_id = ret;
} else {
sdd->port_id = pdev->id;
......@@ -1175,59 +1224,31 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
master->can_dma = s3c64xx_spi_can_dma;
sdd->regs = devm_ioremap_resource(&pdev->dev, mem_res);
if (IS_ERR(sdd->regs)) {
ret = PTR_ERR(sdd->regs);
goto err_deref_master;
}
if (IS_ERR(sdd->regs))
return PTR_ERR(sdd->regs);
if (sci->cfg_gpio && sci->cfg_gpio()) {
dev_err(&pdev->dev, "Unable to config gpio\n");
ret = -EBUSY;
goto err_deref_master;
}
if (sci->cfg_gpio && sci->cfg_gpio())
return dev_err_probe(&pdev->dev, -EBUSY,
"Unable to config gpio\n");
/* Setup clocks */
sdd->clk = devm_clk_get(&pdev->dev, "spi");
if (IS_ERR(sdd->clk)) {
dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
ret = PTR_ERR(sdd->clk);
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;
}
sdd->clk = devm_clk_get_enabled(&pdev->dev, "spi");
if (IS_ERR(sdd->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(sdd->clk),
"Unable to acquire clock 'spi'\n");
sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
sdd->src_clk = devm_clk_get(&pdev->dev, clk_name);
if (IS_ERR(sdd->src_clk)) {
dev_err(&pdev->dev,
"Unable to acquire clock '%s'\n", clk_name);
ret = PTR_ERR(sdd->src_clk);
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;
}
sdd->src_clk = devm_clk_get_enabled(&pdev->dev, clk_name);
if (IS_ERR(sdd->src_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(sdd->src_clk),
"Unable to acquire clock '%s'\n",
clk_name);
if (sdd->port_conf->clk_ioclk) {
sdd->ioclk = devm_clk_get(&pdev->dev, "spi_ioclk");
if (IS_ERR(sdd->ioclk)) {
dev_err(&pdev->dev, "Unable to acquire 'ioclk'\n");
ret = PTR_ERR(sdd->ioclk);
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;
}
sdd->ioclk = devm_clk_get_enabled(&pdev->dev, "spi_ioclk");
if (IS_ERR(sdd->ioclk))
return dev_err_probe(&pdev->dev, PTR_ERR(sdd->ioclk),
"Unable to acquire 'ioclk'\n");
}
pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT);
......@@ -1275,14 +1296,6 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
pm_runtime_disable(&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;
}
......@@ -1300,12 +1313,6 @@ static void s3c64xx_spi_remove(struct platform_device *pdev)
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_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
......
......@@ -337,7 +337,7 @@ static struct i2c_driver sc18is602_driver = {
.name = "sc18is602",
.of_match_table = of_match_ptr(sc18is602_of_match),
},
.probe_new = sc18is602_probe,
.probe = sc18is602_probe,
.id_table = sc18is602_id,
};
......
......@@ -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,
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,
op->dummy.buswidth, op->data.buswidth };
bool is_match_found;
......@@ -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)
{
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;
}
......@@ -634,18 +634,12 @@ static int f_ospi_probe(struct platform_device *pdev)
goto err_put_ctlr;
}
ospi->clk = devm_clk_get(dev, NULL);
ospi->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(ospi->clk)) {
ret = PTR_ERR(ospi->clk);
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);
ret = f_ospi_init(ospi);
......@@ -661,9 +655,6 @@ static int f_ospi_probe(struct platform_device *pdev)
err_destroy_mutex:
mutex_destroy(&ospi->mlock);
err_disable_clk:
clk_disable_unprepare(ospi->clk);
err_put_ctlr:
spi_controller_put(ctlr);
......@@ -674,8 +665,6 @@ static void f_ospi_remove(struct platform_device *pdev)
{
struct f_ospi *ospi = platform_get_drvdata(pdev);
clk_disable_unprepare(ospi->clk);
mutex_destroy(&ospi->mlock);
}
......
This diff is collapsed.
......@@ -42,7 +42,9 @@
#define SUN6I_TFR_CTL_CS_MANUAL BIT(6)
#define SUN6I_TFR_CTL_CS_LEVEL BIT(7)
#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_SDM BIT(13)
#define SUN6I_TFR_CTL_XCH BIT(31)
#define SUN6I_INT_CTL_REG 0x10
......@@ -85,6 +87,11 @@
#define SUN6I_TXDATA_REG 0x200
#define SUN6I_RXDATA_REG 0x300
struct sun6i_spi_cfg {
unsigned long fifo_depth;
bool has_clk_ctl;
};
struct sun6i_spi {
struct spi_master *master;
void __iomem *base_addr;
......@@ -99,7 +106,7 @@ struct sun6i_spi {
const u8 *tx_buf;
u8 *rx_buf;
int len;
unsigned long fifo_depth;
const struct sun6i_spi_cfg *cfg;
};
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)
u8 byte;
/* 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);
......@@ -256,7 +263,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
struct spi_transfer *tfr)
{
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 trig_level;
unsigned int tx_len = 0, rx_len = 0;
......@@ -289,14 +296,14 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
* the hardcoded value used in old generation of Allwinner
* SPI controller. (See spi-sun4i.c)
*/
trig_level = sspi->fifo_depth / 4 * 3;
trig_level = sspi->cfg->fifo_depth / 4 * 3;
} else {
/*
* Setup FIFO DMA request trigger level
* We choose 1/2 of the full fifo depth, that value will
* be used as DMA burst length.
*/
trig_level = sspi->fifo_depth / 2;
trig_level = sspi->cfg->fifo_depth / 2;
if (tfr->tx_buf)
reg |= SUN6I_FIFO_CTL_TF_DRQ_EN;
......@@ -346,39 +353,65 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
sun6i_spi_write(sspi, SUN6I_TFR_CTL_REG, reg);
/* Ensure that we have a parent clock fast enough */
mclk_rate = clk_get_rate(sspi->mclk);
if (mclk_rate < (2 * tfr->speed_hz)) {
clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
if (sspi->cfg->has_clk_ctl) {
unsigned int mclk_rate = clk_get_rate(sspi->mclk);
/*
* Setup clock divider.
*
* We have two choices there. Either we can use the clock
* divide rate 1, which is calculated thanks to this formula:
* SPI_CLK = MOD_CLK / (2 ^ cdr)
* Or we can use CDR2, which is calculated with the formula:
* SPI_CLK = MOD_CLK / (2 * (cdr + 1))
* Wether we use the former or the latter is set through the
* DRS bit.
*
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
/* Ensure that we have a parent clock fast enough */
if (mclk_rate < (2 * tfr->speed_hz)) {
clk_set_rate(sspi->mclk, 2 * tfr->speed_hz);
mclk_rate = clk_get_rate(sspi->mclk);
}
/*
* Setup clock divider.
*
* We have two choices there. Either we can use the clock
* divide rate 1, which is calculated thanks to this formula:
* SPI_CLK = MOD_CLK / (2 ^ cdr)
* Or we can use CDR2, which is calculated with the formula:
* SPI_CLK = MOD_CLK / (2 * (cdr + 1))
* Wether we use the former or the latter is set through the
* DRS bit.
*
* First try CDR2, and if we can't reach the expected
* frequency, fall back to CDR1.
*/
div_cdr1 = DIV_ROUND_UP(mclk_rate, tfr->speed_hz);
div_cdr2 = DIV_ROUND_UP(div_cdr1, 2);
if (div_cdr2 <= (SUN6I_CLK_CTL_CDR2_MASK + 1)) {
reg = SUN6I_CLK_CTL_CDR2(div_cdr2 - 1) | SUN6I_CLK_CTL_DRS;
tfr->effective_speed_hz = mclk_rate / (2 * div_cdr2);
} else {
div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
reg = SUN6I_CLK_CTL_CDR1(div);
tfr->effective_speed_hz = mclk_rate / (1 << div);
}
sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
} else {
div = min(SUN6I_CLK_CTL_CDR1_MASK, order_base_2(div_cdr1));
reg = SUN6I_CLK_CTL_CDR1(div);
tfr->effective_speed_hz = mclk_rate / (1 << div);
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);
}
sun6i_spi_write(sspi, SUN6I_CLK_CTL_REG, reg);
/* Finally enable the bus - doing so before might raise SCK to HIGH */
reg = sun6i_spi_read(sspi, SUN6I_GBL_CTL_REG);
reg |= SUN6I_GBL_CTL_BUS_ENABLE;
......@@ -410,9 +443,9 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
reg = SUN6I_INT_CTL_TC;
if (!use_dma) {
if (rx_len > sspi->fifo_depth)
if (rx_len > sspi->cfg->fifo_depth)
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;
}
......@@ -422,7 +455,7 @@ static int sun6i_spi_transfer_one(struct spi_master *master,
reg = sun6i_spi_read(sspi, SUN6I_TFR_CTL_REG);
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;
timeout = wait_for_completion_timeout(&sspi->done,
msecs_to_jiffies(tx_time));
......@@ -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
* 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)
......@@ -582,7 +615,7 @@ static int sun6i_spi_probe(struct platform_device *pdev)
}
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->min_speed_hz = 3 * 1000;
......@@ -695,9 +728,27 @@ static void sun6i_spi_remove(struct platform_device *pdev)
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[] = {
{ .compatible = "allwinner,sun6i-a31-spi", .data = (void *)SUN6I_FIFO_DEPTH },
{ .compatible = "allwinner,sun8i-h3-spi", .data = (void *)SUN8I_FIFO_DEPTH },
{ .compatible = "allwinner,sun6i-a31-spi", .data = &sun6i_a31_spi_cfg },
{ .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);
......
......@@ -241,7 +241,7 @@ static struct i2c_driver spi_xcomm_driver = {
.name = "spi-xcomm",
},
.id_table = spi_xcomm_ids,
.probe_new = spi_xcomm_probe,
.probe = spi_xcomm_probe,
};
module_i2c_driver(spi_xcomm_driver);
......
......@@ -64,7 +64,8 @@ static_assert(N_SPI_MINORS > 0 && N_SPI_MINORS <= 256);
| SPI_NO_CS | SPI_READY | SPI_TX_DUAL \
| SPI_TX_QUAD | SPI_TX_OCTAL | SPI_RX_DUAL \
| SPI_RX_QUAD | SPI_RX_OCTAL \
| SPI_RX_CPHA_FLIP)
| SPI_RX_CPHA_FLIP | SPI_3WIRE_HIZ \
| SPI_MOSI_IDLE_LOW)
struct spidev_data {
dev_t devt;
......
......@@ -36,6 +36,7 @@ struct s3c64xx_spi_info {
int src_clk_nr;
int num_cs;
bool no_cs;
bool polling;
int (*cfg_gpio)(void);
};
......
......@@ -1261,6 +1261,23 @@ static inline bool spi_is_bpw_supported(struct spi_device *spi, u32 bpw)
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 */
......
......@@ -28,6 +28,7 @@
#define SPI_RX_OCTAL _BITUL(14) /* receive with 8 wires */
#define SPI_3WIRE_HIZ _BITUL(15) /* high impedance turnaround */
#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.
......@@ -37,6 +38,6 @@
* 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.
*/
#define SPI_MODE_USER_MASK (_BITUL(17) - 1)
#define SPI_MODE_USER_MASK (_BITUL(18) - 1)
#endif /* _UAPI_SPI_H */
......@@ -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)
{
printf("Usage: %s [-DsbdlHOLC3vpNR24SI]\n", prog);
puts(" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\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"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -L --lsb least significant bit first\n"
" -C --cs-high chip select active high\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"
" -4 --quad quad transfer\n"
" -8 --octal octal transfer\n"
" -S --size transfer size\n"
" -I --iter iterations\n");
printf("Usage: %s [-2348CDFHILMNORSZbdilopsv]\n", prog);
puts("general device settings:\n"
" -D --device device to use (default /dev/spidev1.1)\n"
" -s --speed max speed (Hz)\n"
" -d --delay delay (usec)\n"
" -l --loop loopback\n"
"spi mode:\n"
" -H --cpha clock phase\n"
" -O --cpol clock polarity\n"
" -F --rx-cpha-flip flip CPHA on Rx only xfer\n"
"number of wires for transmission:\n"
" -2 --dual dual transfer\n"
" -4 --quad quad 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"
" -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);
}
......@@ -201,31 +210,34 @@ static void parse_opts(int argc, char *argv[])
{
while (1) {
static const struct option lopts[] = {
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "bpw", 1, 0, 'b' },
{ "input", 1, 0, 'i' },
{ "output", 1, 0, 'o' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "lsb", 0, 0, 'L' },
{ "cs-high", 0, 0, 'C' },
{ "3wire", 0, 0, '3' },
{ "no-cs", 0, 0, 'N' },
{ "ready", 0, 0, 'R' },
{ "dual", 0, 0, '2' },
{ "verbose", 0, 0, 'v' },
{ "quad", 0, 0, '4' },
{ "octal", 0, 0, '8' },
{ "size", 1, 0, 'S' },
{ "iter", 1, 0, 'I' },
{ "device", 1, 0, 'D' },
{ "speed", 1, 0, 's' },
{ "delay", 1, 0, 'd' },
{ "loop", 0, 0, 'l' },
{ "cpha", 0, 0, 'H' },
{ "cpol", 0, 0, 'O' },
{ "rx-cpha-flip", 0, 0, 'F' },
{ "dual", 0, 0, '2' },
{ "quad", 0, 0, '4' },
{ "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' },
{ "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 },
};
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);
if (c == -1)
......@@ -268,6 +280,15 @@ static void parse_opts(int argc, char *argv[])
case '3':
mode |= SPI_3WIRE;
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':
mode |= SPI_NO_CS;
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