mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 02:05:33 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
This commit is contained in:
commit
29d2c4d13d
54
Documentation/devicetree/bindings/misc/lwn,bk4-spi.yaml
Normal file
54
Documentation/devicetree/bindings/misc/lwn,bk4-spi.yaml
Normal file
@ -0,0 +1,54 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/misc/lwn,bk4-spi.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Liebherr's BK4 external SPI controller
|
||||
|
||||
maintainers:
|
||||
- Lukasz Majewski <lukma@denx.de>
|
||||
|
||||
description: |
|
||||
Liebherr's BK4 external SPI controller is a device which handles data
|
||||
acquisition from compatible industrial peripherals.
|
||||
The SPI is used for data and management purposes in both master and
|
||||
slave modes.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: lwn,bk4-spi
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 30000000
|
||||
|
||||
fsl,spi-cs-sck-delay: true
|
||||
|
||||
fsl,spi-sck-cs-delay: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- spi-max-frequency
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
spidev@0 {
|
||||
compatible = "lwn,bk4-spi";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <30000000>;
|
||||
fsl,spi-cs-sck-delay = <200>;
|
||||
fsl,spi-sck-cs-delay = <400>;
|
||||
};
|
||||
};
|
@ -1,26 +0,0 @@
|
||||
* Liebherr's BK4 controller external SPI
|
||||
|
||||
A device which handles data acquisition from compatible industrial
|
||||
peripherals.
|
||||
The SPI is used for data and management purposes in both master and
|
||||
slave modes.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Should be "lwn,bk4"
|
||||
|
||||
Required SPI properties:
|
||||
|
||||
- reg : Should be address of the device chip select within
|
||||
the controller.
|
||||
|
||||
- spi-max-frequency : Maximum SPI clocking speed of device in Hz, should be
|
||||
30MHz at most for the Liebherr's BK4 external bus.
|
||||
|
||||
Example:
|
||||
|
||||
spidev0: spi@0 {
|
||||
compatible = "lwn,bk4";
|
||||
spi-max-frequency = <30000000>;
|
||||
reg = <0>;
|
||||
};
|
@ -68,6 +68,7 @@ properties:
|
||||
- items:
|
||||
- enum:
|
||||
- amd,pensando-elba-qspi
|
||||
- amd,versal2-ospi
|
||||
- intel,lgm-qspi
|
||||
- intel,socfpga-qspi
|
||||
- mobileye,eyeq5-ospi
|
||||
|
@ -69,6 +69,11 @@ properties:
|
||||
Should be generally avoided and be replaced by
|
||||
spi-cs-high + ACTIVE_HIGH.
|
||||
|
||||
The simplest way to obtain an active-high CS signal is to configure the
|
||||
controller's cs-gpio property with the ACTIVE_HIGH flag and set the
|
||||
peripheral's spi-cs-high property. See example below for a better
|
||||
understanding.
|
||||
|
||||
fifo-depth:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
@ -189,3 +194,23 @@ examples:
|
||||
stacked-memories = /bits/ 64 <0x10000000 0x10000000>;
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
spi@20204000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,bcm2835-spi";
|
||||
reg = <0x7e204000 0x1000>;
|
||||
interrupts = <2 22>;
|
||||
clocks = <&clk_spi>;
|
||||
cs-gpios = <&gpio 8 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
display@0 {
|
||||
compatible = "lg,lg4573";
|
||||
spi-max-frequency = <1000000>;
|
||||
reg = <0>;
|
||||
spi-cs-high;
|
||||
};
|
||||
};
|
||||
|
@ -1229,6 +1229,8 @@ spinand_select_op_variant(struct spinand_device *spinand,
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
spi_mem_adjust_op_freq(spinand->spimem, &op);
|
||||
|
||||
if (!spi_mem_supports_op(spinand->spimem, &op))
|
||||
break;
|
||||
|
||||
|
@ -542,6 +542,18 @@ config SPI_JCORE
|
||||
This enables support for the SPI master controller in the J-Core
|
||||
synthesizable, open source SoC.
|
||||
|
||||
config SPI_KSPI2
|
||||
tristate "Support for KEBA SPI master type 2 hardware"
|
||||
depends on HAS_IOMEM
|
||||
depends on KEBA_CP500 || COMPILE_TEST
|
||||
select AUXILIARY_BUS
|
||||
help
|
||||
This driver supports KEBA SPI master type 2 FPGA implementation,
|
||||
as found on CP500 devices for example.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called spi-kspi2.
|
||||
|
||||
config SPI_LM70_LLP
|
||||
tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
|
||||
depends on PARPORT
|
||||
|
@ -74,6 +74,7 @@ obj-$(CONFIG_SPI_INTEL_PCI) += spi-intel-pci.o
|
||||
obj-$(CONFIG_SPI_INTEL_PLATFORM) += spi-intel-platform.o
|
||||
obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o
|
||||
obj-$(CONFIG_SPI_JCORE) += spi-jcore.o
|
||||
obj-$(CONFIG_SPI_KSPI2) += spi-kspi2.o
|
||||
obj-$(CONFIG_SPI_LJCA) += spi-ljca.o
|
||||
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
|
||||
obj-$(CONFIG_SPI_LOONGSON_CORE) += spi-loongson-core.o
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -298,19 +298,16 @@ static const struct amd_spi_freq amd_spi_freq[] = {
|
||||
{ AMD_SPI_MIN_HZ, F_800KHz, 0},
|
||||
};
|
||||
|
||||
static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
|
||||
static void amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
|
||||
{
|
||||
unsigned int i, spd7_val, alt_spd;
|
||||
|
||||
if (speed_hz < AMD_SPI_MIN_HZ)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amd_spi_freq); i++)
|
||||
if (speed_hz >= amd_spi_freq[i].speed_hz)
|
||||
break;
|
||||
|
||||
if (amd_spi->speed_hz == amd_spi_freq[i].speed_hz)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
amd_spi->speed_hz = amd_spi_freq[i].speed_hz;
|
||||
|
||||
@ -329,8 +326,6 @@ static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
|
||||
amd_spi_setclear_reg32(amd_spi, AMD_SPI_SPEED_REG, spd7_val,
|
||||
AMD_SPI_SPD7_MASK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
|
||||
@ -479,6 +474,9 @@ static bool amd_spi_supports_op(struct spi_mem *mem,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->max_freq < mem->spi->controller->min_speed_hz)
|
||||
return false;
|
||||
|
||||
return spi_mem_default_supports_op(mem, op);
|
||||
}
|
||||
|
||||
@ -676,9 +674,7 @@ static int amd_spi_exec_mem_op(struct spi_mem *mem,
|
||||
|
||||
amd_spi = spi_controller_get_devdata(mem->spi->controller);
|
||||
|
||||
ret = amd_set_spi_freq(amd_spi, mem->spi->max_speed_hz);
|
||||
if (ret)
|
||||
return ret;
|
||||
amd_set_spi_freq(amd_spi, op->max_freq);
|
||||
|
||||
if (amd_spi->version == AMD_SPI_V2)
|
||||
amd_set_spi_addr_mode(amd_spi, op);
|
||||
@ -705,6 +701,10 @@ static const struct spi_controller_mem_ops amd_spi_mem_ops = {
|
||||
.supports_op = amd_spi_supports_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps amd_spi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int amd_spi_host_transfer(struct spi_controller *host,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
@ -782,6 +782,7 @@ static int amd_spi_probe(struct platform_device *pdev)
|
||||
host->setup = amd_spi_host_setup;
|
||||
host->transfer_one_message = amd_spi_host_transfer;
|
||||
host->mem_ops = &amd_spi_mem_ops;
|
||||
host->mem_caps = &amd_spi_mem_caps;
|
||||
host->max_transfer_size = amd_spi_max_transfer_size;
|
||||
host->max_message_size = amd_spi_max_transfer_size;
|
||||
|
||||
|
@ -259,7 +259,7 @@ static int amlogic_spifc_a1_exec_op(struct spi_mem *mem,
|
||||
size_t data_size = op->data.nbytes;
|
||||
int ret;
|
||||
|
||||
ret = amlogic_spifc_a1_set_freq(spifc, mem->spi->max_speed_hz);
|
||||
ret = amlogic_spifc_a1_set_freq(spifc, op->max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -320,6 +320,10 @@ static const struct spi_controller_mem_ops amlogic_spifc_a1_mem_ops = {
|
||||
.adjust_op_size = amlogic_spifc_a1_adjust_op_size,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps amlogic_spifc_a1_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int amlogic_spifc_a1_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctrl;
|
||||
@ -356,6 +360,7 @@ static int amlogic_spifc_a1_probe(struct platform_device *pdev)
|
||||
ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctrl->auto_runtime_pm = true;
|
||||
ctrl->mem_ops = &amlogic_spifc_a1_mem_ops;
|
||||
ctrl->mem_caps = &amlogic_spifc_a1_mem_caps;
|
||||
ctrl->min_speed_hz = SPIFC_A1_MIN_HZ;
|
||||
ctrl->max_speed_hz = SPIFC_A1_MAX_HZ;
|
||||
ctrl->mode_bits = (SPI_RX_DUAL | SPI_TX_DUAL |
|
||||
|
@ -43,10 +43,13 @@ static_assert(CQSPI_MAX_CHIPSELECT <= SPI_CS_CNT_MAX);
|
||||
#define CQSPI_SLOW_SRAM BIT(4)
|
||||
#define CQSPI_NEEDS_APB_AHB_HAZARD_WAR BIT(5)
|
||||
#define CQSPI_RD_NO_IRQ BIT(6)
|
||||
#define CQSPI_DISABLE_STIG_MODE BIT(7)
|
||||
#define CQSPI_DMA_SET_MASK BIT(7)
|
||||
#define CQSPI_SUPPORT_DEVICE_RESET BIT(8)
|
||||
#define CQSPI_DISABLE_STIG_MODE BIT(9)
|
||||
|
||||
/* Capabilities */
|
||||
#define CQSPI_SUPPORTS_OCTAL BIT(0)
|
||||
#define CQSPI_SUPPORTS_QUAD BIT(1)
|
||||
|
||||
#define CQSPI_OP_WIDTH(part) ((part).nbytes ? ilog2((part).buswidth) : 0)
|
||||
|
||||
@ -111,7 +114,7 @@ struct cqspi_st {
|
||||
|
||||
struct cqspi_driver_platdata {
|
||||
u32 hwcaps_mask;
|
||||
u8 quirks;
|
||||
u16 quirks;
|
||||
int (*indirect_read_dma)(struct cqspi_flash_pdata *f_pdata,
|
||||
u_char *rxbuf, loff_t from_addr, size_t n_rx);
|
||||
u32 (*get_dma_status)(struct cqspi_st *cqspi);
|
||||
@ -146,6 +149,8 @@ struct cqspi_driver_platdata {
|
||||
#define CQSPI_REG_CONFIG_IDLE_LSB 31
|
||||
#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF
|
||||
#define CQSPI_REG_CONFIG_BAUD_MASK 0xF
|
||||
#define CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK BIT(5)
|
||||
#define CQSPI_REG_CONFIG_RESET_CFG_FLD_MASK BIT(6)
|
||||
|
||||
#define CQSPI_REG_RD_INSTR 0x04
|
||||
#define CQSPI_REG_RD_INSTR_OPCODE_LSB 0
|
||||
@ -832,6 +837,25 @@ failrd:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cqspi_device_reset(struct cqspi_st *cqspi)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(cqspi->iobase + CQSPI_REG_CONFIG);
|
||||
reg |= CQSPI_REG_CONFIG_RESET_CFG_FLD_MASK;
|
||||
writel(reg, cqspi->iobase + CQSPI_REG_CONFIG);
|
||||
/*
|
||||
* NOTE: Delay timing implementation is derived from
|
||||
* spi_nor_hw_reset()
|
||||
*/
|
||||
writel(reg & ~CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, cqspi->iobase + CQSPI_REG_CONFIG);
|
||||
usleep_range(1, 5);
|
||||
writel(reg | CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, cqspi->iobase + CQSPI_REG_CONFIG);
|
||||
usleep_range(100, 150);
|
||||
writel(reg & ~CQSPI_REG_CONFIG_RESET_PIN_FLD_MASK, cqspi->iobase + CQSPI_REG_CONFIG);
|
||||
usleep_range(1000, 1200);
|
||||
}
|
||||
|
||||
static void cqspi_controller_enable(struct cqspi_st *cqspi, bool enable)
|
||||
{
|
||||
void __iomem *reg_base = cqspi->iobase;
|
||||
@ -1409,7 +1433,7 @@ static int cqspi_mem_process(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
struct cqspi_flash_pdata *f_pdata;
|
||||
|
||||
f_pdata = &cqspi->f_pdata[spi_get_chipselect(mem->spi, 0)];
|
||||
cqspi_configure(f_pdata, mem->spi->max_speed_hz);
|
||||
cqspi_configure(f_pdata, op->max_freq);
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) {
|
||||
/*
|
||||
@ -1658,6 +1682,7 @@ static const struct spi_controller_mem_ops cqspi_mem_ops = {
|
||||
|
||||
static const struct spi_controller_mem_caps cqspi_mem_caps = {
|
||||
.dtr = true,
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int cqspi_setup_flash(struct cqspi_st *cqspi)
|
||||
@ -1865,6 +1890,8 @@ static int cqspi_probe(struct platform_device *pdev)
|
||||
cqspi->master_ref_clk_hz);
|
||||
if (ddata->hwcaps_mask & CQSPI_SUPPORTS_OCTAL)
|
||||
host->mode_bits |= SPI_RX_OCTAL | SPI_TX_OCTAL;
|
||||
if (ddata->hwcaps_mask & CQSPI_SUPPORTS_QUAD)
|
||||
host->mode_bits |= SPI_TX_QUAD;
|
||||
if (!(ddata->quirks & CQSPI_DISABLE_DAC_MODE)) {
|
||||
cqspi->use_direct_mode = true;
|
||||
cqspi->use_direct_mode_wr = true;
|
||||
@ -1886,8 +1913,7 @@ static int cqspi_probe(struct platform_device *pdev)
|
||||
if (ddata->quirks & CQSPI_DISABLE_STIG_MODE)
|
||||
cqspi->disable_stig_mode = true;
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node,
|
||||
"xlnx,versal-ospi-1.0")) {
|
||||
if (ddata->quirks & CQSPI_DMA_SET_MASK) {
|
||||
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
|
||||
if (ret)
|
||||
goto probe_reset_failed;
|
||||
@ -1917,6 +1943,9 @@ static int cqspi_probe(struct platform_device *pdev)
|
||||
|
||||
host->num_chipselect = cqspi->num_chipselect;
|
||||
|
||||
if (ddata->quirks & CQSPI_SUPPORT_DEVICE_RESET)
|
||||
cqspi_device_reset(cqspi);
|
||||
|
||||
if (cqspi->use_direct_mode) {
|
||||
ret = cqspi_request_mmap_dma(cqspi);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
@ -2037,7 +2066,7 @@ static const struct cqspi_driver_platdata k2g_qspi = {
|
||||
};
|
||||
|
||||
static const struct cqspi_driver_platdata am654_ospi = {
|
||||
.hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
|
||||
.hwcaps_mask = CQSPI_SUPPORTS_OCTAL | CQSPI_SUPPORTS_QUAD,
|
||||
.quirks = CQSPI_NEEDS_WR_DELAY,
|
||||
};
|
||||
|
||||
@ -2054,7 +2083,17 @@ static const struct cqspi_driver_platdata socfpga_qspi = {
|
||||
|
||||
static const struct cqspi_driver_platdata versal_ospi = {
|
||||
.hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
|
||||
.quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA,
|
||||
.quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA
|
||||
| CQSPI_DMA_SET_MASK,
|
||||
.indirect_read_dma = cqspi_versal_indirect_read_dma,
|
||||
.get_dma_status = cqspi_get_versal_dma_status,
|
||||
};
|
||||
|
||||
static const struct cqspi_driver_platdata versal2_ospi = {
|
||||
.hwcaps_mask = CQSPI_SUPPORTS_OCTAL,
|
||||
.quirks = CQSPI_DISABLE_DAC_MODE | CQSPI_SUPPORT_EXTERNAL_DMA
|
||||
| CQSPI_DMA_SET_MASK
|
||||
| CQSPI_SUPPORT_DEVICE_RESET,
|
||||
.indirect_read_dma = cqspi_versal_indirect_read_dma,
|
||||
.get_dma_status = cqspi_get_versal_dma_status,
|
||||
};
|
||||
@ -2111,6 +2150,10 @@ static const struct of_device_id cqspi_dt_ids[] = {
|
||||
.compatible = "mobileye,eyeq5-ospi",
|
||||
.data = &mobileye_eyeq5_ospi,
|
||||
},
|
||||
{
|
||||
.compatible = "amd,versal2-ospi",
|
||||
.data = &versal2_ospi,
|
||||
},
|
||||
{ /* end of table */ }
|
||||
};
|
||||
|
||||
|
@ -677,7 +677,7 @@ static int dw_spi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
* operation. Transmit-only mode is suitable for the rest of them.
|
||||
*/
|
||||
cfg.dfs = 8;
|
||||
cfg.freq = clamp(mem->spi->max_speed_hz, 0U, dws->max_mem_freq);
|
||||
cfg.freq = clamp(op->max_freq, 0U, dws->max_mem_freq);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
cfg.tmode = DW_SPI_CTRLR0_TMOD_EPROMREAD;
|
||||
cfg.ndf = op->data.nbytes;
|
||||
@ -894,6 +894,10 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws)
|
||||
dw_writel(dws, DW_SPI_CS_OVERRIDE, 0xF);
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_caps dw_spi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||
{
|
||||
struct spi_controller *host;
|
||||
@ -941,8 +945,10 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws)
|
||||
host->set_cs = dw_spi_set_cs;
|
||||
host->transfer_one = dw_spi_transfer_one;
|
||||
host->handle_err = dw_spi_handle_err;
|
||||
if (dws->mem_ops.exec_op)
|
||||
if (dws->mem_ops.exec_op) {
|
||||
host->mem_ops = &dws->mem_ops;
|
||||
host->mem_caps = &dw_spi_mem_caps;
|
||||
}
|
||||
host->max_speed_hz = dws->max_freq;
|
||||
host->flags = SPI_CONTROLLER_GPIO_SS;
|
||||
host->auto_runtime_pm = true;
|
||||
|
@ -522,9 +522,10 @@ static void fsl_qspi_invalidate(struct fsl_qspi *q)
|
||||
qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
|
||||
}
|
||||
|
||||
static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi)
|
||||
static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned long rate = spi->max_speed_hz;
|
||||
unsigned long rate = op->max_freq;
|
||||
int ret;
|
||||
|
||||
if (q->selected == spi_get_chipselect(spi, 0))
|
||||
@ -652,7 +653,7 @@ static int fsl_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
fsl_qspi_readl_poll_tout(q, base + QUADSPI_SR, (QUADSPI_SR_IP_ACC_MASK |
|
||||
QUADSPI_SR_AHB_ACC_MASK), 10, 1000);
|
||||
|
||||
fsl_qspi_select_mem(q, mem->spi);
|
||||
fsl_qspi_select_mem(q, mem->spi, op);
|
||||
|
||||
if (needs_amba_base_offset(q))
|
||||
addr_offset = q->memmap_phy;
|
||||
@ -839,6 +840,10 @@ static const struct spi_controller_mem_ops fsl_qspi_mem_ops = {
|
||||
.get_name = fsl_qspi_get_name,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps fsl_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int fsl_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
@ -923,6 +928,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
||||
ctlr->bus_num = -1;
|
||||
ctlr->num_chipselect = 4;
|
||||
ctlr->mem_ops = &fsl_qspi_mem_ops;
|
||||
ctlr->mem_caps = &fsl_qspi_mem_caps;
|
||||
|
||||
fsl_qspi_default_setup(q);
|
||||
|
||||
|
@ -618,7 +618,7 @@ static struct spi_controller *fsl_spi_probe(struct device *dev,
|
||||
if (ret < 0)
|
||||
goto err_probe;
|
||||
|
||||
dev_info(dev, "at 0x%p (irq = %d), %s mode\n", reg_base,
|
||||
dev_info(dev, "at MMIO %pa (irq = %d), %s mode\n", &mem->start,
|
||||
mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
|
||||
|
||||
return host;
|
||||
|
431
drivers/spi/spi-kspi2.c
Normal file
431
drivers/spi/spi-kspi2.c
Normal file
@ -0,0 +1,431 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) KEBA Industrial Automation Gmbh 2024
|
||||
*
|
||||
* Driver for KEBA SPI host controller type 2 FPGA IP core
|
||||
*/
|
||||
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/misc/keba.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define KSPI2 "kspi2"
|
||||
|
||||
#define KSPI2_CLK_FREQ_REG 0x03
|
||||
#define KSPI2_CLK_FREQ_MASK 0x0f
|
||||
#define KSPI2_CLK_FREQ_62_5M 0x0
|
||||
#define KSPI2_CLK_FREQ_33_3M 0x1
|
||||
#define KSPI2_CLK_FREQ_125M 0x2
|
||||
#define KSPI2_CLK_FREQ_50M 0x3
|
||||
#define KSPI2_CLK_FREQ_100M 0x4
|
||||
|
||||
#define KSPI2_CONTROL_REG 0x04
|
||||
#define KSPI2_CONTROL_CLK_DIV_MAX 0x0f
|
||||
#define KSPI2_CONTROL_CLK_DIV_MASK 0x0f
|
||||
#define KSPI2_CONTROL_CPHA 0x10
|
||||
#define KSPI2_CONTROL_CPOL 0x20
|
||||
#define KSPI2_CONTROL_CLK_MODE_MASK 0x30
|
||||
#define KSPI2_CONTROL_INIT KSPI2_CONTROL_CLK_DIV_MAX
|
||||
|
||||
#define KSPI2_STATUS_REG 0x08
|
||||
#define KSPI2_STATUS_IN_USE 0x01
|
||||
#define KSPI2_STATUS_BUSY 0x02
|
||||
|
||||
#define KSPI2_DATA_REG 0x0c
|
||||
|
||||
#define KSPI2_CS_NR_REG 0x10
|
||||
#define KSPI2_CS_NR_NONE 0xff
|
||||
|
||||
#define KSPI2_MODE_BITS (SPI_CPHA | SPI_CPOL)
|
||||
#define KSPI2_NUM_CS 255
|
||||
|
||||
#define KSPI2_SPEED_HZ_MIN(kspi) (kspi->base_speed_hz / 65536)
|
||||
#define KSPI2_SPEED_HZ_MAX(kspi) (kspi->base_speed_hz / 2)
|
||||
|
||||
/* timeout is 10 times the time to transfer one byte at slowest clock */
|
||||
#define KSPI2_XFER_TIMEOUT_US(kspi) (USEC_PER_SEC / \
|
||||
KSPI2_SPEED_HZ_MIN(kspi) * 8 * 10)
|
||||
|
||||
#define KSPI2_INUSE_SLEEP_US (2 * USEC_PER_MSEC)
|
||||
#define KSPI2_INUSE_TIMEOUT_US (10 * USEC_PER_SEC)
|
||||
|
||||
struct kspi2 {
|
||||
struct keba_spi_auxdev *auxdev;
|
||||
void __iomem *base;
|
||||
struct spi_controller *host;
|
||||
|
||||
u32 base_speed_hz; /* SPI base clock frequency in HZ */
|
||||
u8 control_shadow;
|
||||
|
||||
struct spi_device **device;
|
||||
int device_size;
|
||||
};
|
||||
|
||||
static int kspi2_inuse_lock(struct kspi2 *kspi)
|
||||
{
|
||||
u8 sts;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The SPI controller has an IN_USE bit for locking access to the
|
||||
* controller. This enables the use of the SPI controller by other none
|
||||
* Linux processors.
|
||||
*
|
||||
* If the SPI controller is free, then the first read returns
|
||||
* IN_USE == 0. After that the SPI controller is locked and further
|
||||
* reads of IN_USE return 1.
|
||||
*
|
||||
* The SPI controller is unlocked by writing 1 into IN_USE.
|
||||
*
|
||||
* The IN_USE bit acts as a hardware semaphore for the SPI controller.
|
||||
* Poll for semaphore, but sleep while polling to free the CPU.
|
||||
*/
|
||||
ret = readb_poll_timeout(kspi->base + KSPI2_STATUS_REG,
|
||||
sts, (sts & KSPI2_STATUS_IN_USE) == 0,
|
||||
KSPI2_INUSE_SLEEP_US, KSPI2_INUSE_TIMEOUT_US);
|
||||
if (ret != 0)
|
||||
dev_warn(&kspi->auxdev->auxdev.dev, "%s err!\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kspi2_inuse_unlock(struct kspi2 *kspi)
|
||||
{
|
||||
/* unlock the controller by writing 1 into IN_USE */
|
||||
iowrite8(KSPI2_STATUS_IN_USE, kspi->base + KSPI2_STATUS_REG);
|
||||
}
|
||||
|
||||
static int kspi2_prepare_hardware(struct spi_controller *host)
|
||||
{
|
||||
struct kspi2 *kspi = spi_controller_get_devdata(host);
|
||||
|
||||
/* lock hardware semaphore before actual use of controller */
|
||||
return kspi2_inuse_lock(kspi);
|
||||
}
|
||||
|
||||
static int kspi2_unprepare_hardware(struct spi_controller *host)
|
||||
{
|
||||
struct kspi2 *kspi = spi_controller_get_devdata(host);
|
||||
|
||||
/* unlock hardware semaphore after actual use of controller */
|
||||
kspi2_inuse_unlock(kspi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 kspi2_calc_minimal_divider(struct kspi2 *kspi, u32 max_speed_hz)
|
||||
{
|
||||
u8 div;
|
||||
|
||||
/*
|
||||
* Divider values 2, 4, 8, 16, ..., 65536 are possible. They are coded
|
||||
* as 0, 1, 2, 3, ..., 15 in the CONTROL_CLK_DIV bit.
|
||||
*/
|
||||
for (div = 0; div < KSPI2_CONTROL_CLK_DIV_MAX; div++) {
|
||||
if ((kspi->base_speed_hz >> (div + 1)) <= max_speed_hz)
|
||||
return div;
|
||||
}
|
||||
|
||||
/* return divider for slowest clock if loop fails to find one */
|
||||
return KSPI2_CONTROL_CLK_DIV_MAX;
|
||||
}
|
||||
|
||||
static void kspi2_write_control_reg(struct kspi2 *kspi, u8 val, u8 mask)
|
||||
{
|
||||
/* write control register only when necessary to improve performance */
|
||||
if (val != (kspi->control_shadow & mask)) {
|
||||
kspi->control_shadow = (kspi->control_shadow & ~mask) | val;
|
||||
iowrite8(kspi->control_shadow, kspi->base + KSPI2_CONTROL_REG);
|
||||
}
|
||||
}
|
||||
|
||||
static int kspi2_txrx_byte(struct kspi2 *kspi, u8 tx, u8 *rx)
|
||||
{
|
||||
u8 sts;
|
||||
int ret;
|
||||
|
||||
/* start transfer by writing TX byte */
|
||||
iowrite8(tx, kspi->base + KSPI2_DATA_REG);
|
||||
|
||||
/* wait till finished (BUSY == 0) */
|
||||
ret = readb_poll_timeout(kspi->base + KSPI2_STATUS_REG,
|
||||
sts, (sts & KSPI2_STATUS_BUSY) == 0,
|
||||
0, KSPI2_XFER_TIMEOUT_US(kspi));
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
/* read RX byte */
|
||||
if (rx)
|
||||
*rx = ioread8(kspi->base + KSPI2_DATA_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kspi2_process_transfer(struct kspi2 *kspi, struct spi_transfer *t)
|
||||
{
|
||||
u8 tx = 0;
|
||||
u8 rx;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < t->len; i++) {
|
||||
if (t->tx_buf)
|
||||
tx = ((const u8 *)t->tx_buf)[i];
|
||||
|
||||
ret = kspi2_txrx_byte(kspi, tx, &rx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (t->rx_buf)
|
||||
((u8 *)t->rx_buf)[i] = rx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kspi2_setup_transfer(struct kspi2 *kspi,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
u32 max_speed_hz = spi->max_speed_hz;
|
||||
u8 clk_div;
|
||||
|
||||
/*
|
||||
* spi_device (spi) has default parameters. Some of these can be
|
||||
* overwritten by parameters in spi_transfer (t).
|
||||
*/
|
||||
if (t->bits_per_word && ((t->bits_per_word % 8) != 0)) {
|
||||
dev_err(&spi->dev, "Word width %d not supported!\n",
|
||||
t->bits_per_word);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (t->speed_hz && (t->speed_hz < max_speed_hz))
|
||||
max_speed_hz = t->speed_hz;
|
||||
|
||||
clk_div = kspi2_calc_minimal_divider(kspi, max_speed_hz);
|
||||
kspi2_write_control_reg(kspi, clk_div, KSPI2_CONTROL_CLK_DIV_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kspi2_transfer_one(struct spi_controller *host,
|
||||
struct spi_device *spi,
|
||||
struct spi_transfer *t)
|
||||
{
|
||||
struct kspi2 *kspi = spi_controller_get_devdata(host);
|
||||
int ret;
|
||||
|
||||
ret = kspi2_setup_transfer(kspi, spi, t);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
if (t->len) {
|
||||
ret = kspi2_process_transfer(kspi, t);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kspi2_set_cs(struct spi_device *spi, bool enable)
|
||||
{
|
||||
struct spi_controller *host = spi->controller;
|
||||
struct kspi2 *kspi = spi_controller_get_devdata(host);
|
||||
|
||||
/* controller is using active low chip select signals by design */
|
||||
if (!enable)
|
||||
iowrite8(spi_get_chipselect(spi, 0), kspi->base + KSPI2_CS_NR_REG);
|
||||
else
|
||||
iowrite8(KSPI2_CS_NR_NONE, kspi->base + KSPI2_CS_NR_REG);
|
||||
}
|
||||
|
||||
static int kspi2_prepare_message(struct spi_controller *host,
|
||||
struct spi_message *msg)
|
||||
{
|
||||
struct kspi2 *kspi = spi_controller_get_devdata(host);
|
||||
struct spi_device *spi = msg->spi;
|
||||
u8 mode = 0;
|
||||
|
||||
/* setup SPI clock phase and polarity */
|
||||
if (spi->mode & SPI_CPHA)
|
||||
mode |= KSPI2_CONTROL_CPHA;
|
||||
if (spi->mode & SPI_CPOL)
|
||||
mode |= KSPI2_CONTROL_CPOL;
|
||||
kspi2_write_control_reg(kspi, mode, KSPI2_CONTROL_CLK_MODE_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kspi2_setup(struct spi_device *spi)
|
||||
{
|
||||
struct kspi2 *kspi = spi_controller_get_devdata(spi->controller);
|
||||
|
||||
/*
|
||||
* Check only parameters. Actual setup is done in kspi2_prepare_message
|
||||
* and directly before the SPI transfer starts.
|
||||
*/
|
||||
|
||||
if (spi->mode & ~KSPI2_MODE_BITS) {
|
||||
dev_err(&spi->dev, "Mode %d not supported!\n", spi->mode);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((spi->bits_per_word % 8) != 0) {
|
||||
dev_err(&spi->dev, "Word width %d not supported!\n",
|
||||
spi->bits_per_word);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((spi->max_speed_hz == 0) ||
|
||||
(spi->max_speed_hz > KSPI2_SPEED_HZ_MAX(kspi)))
|
||||
spi->max_speed_hz = KSPI2_SPEED_HZ_MAX(kspi);
|
||||
|
||||
if (spi->max_speed_hz < KSPI2_SPEED_HZ_MIN(kspi)) {
|
||||
dev_err(&spi->dev, "Requested speed of %d Hz is too low!\n",
|
||||
spi->max_speed_hz);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kspi2_unregister_devices(struct kspi2 *kspi)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < kspi->device_size; i++) {
|
||||
struct spi_device *device = kspi->device[i];
|
||||
|
||||
if (device)
|
||||
spi_unregister_device(device);
|
||||
}
|
||||
}
|
||||
|
||||
static int kspi2_register_devices(struct kspi2 *kspi)
|
||||
{
|
||||
struct spi_board_info *info = kspi->auxdev->info;
|
||||
int i;
|
||||
|
||||
/* register all known SPI devices */
|
||||
for (i = 0; i < kspi->auxdev->info_size; i++) {
|
||||
struct spi_device *device = spi_new_device(kspi->host, &info[i]);
|
||||
|
||||
if (!device) {
|
||||
kspi2_unregister_devices(kspi);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
kspi->device[i] = device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kspi2_init(struct kspi2 *kspi)
|
||||
{
|
||||
iowrite8(KSPI2_CONTROL_INIT, kspi->base + KSPI2_CONTROL_REG);
|
||||
kspi->control_shadow = KSPI2_CONTROL_INIT;
|
||||
|
||||
iowrite8(KSPI2_CS_NR_NONE, kspi->base + KSPI2_CS_NR_REG);
|
||||
}
|
||||
|
||||
static int kspi2_probe(struct auxiliary_device *auxdev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct device *dev = &auxdev->dev;
|
||||
struct spi_controller *host;
|
||||
struct kspi2 *kspi;
|
||||
u8 clk_reg;
|
||||
int ret;
|
||||
|
||||
host = devm_spi_alloc_host(dev, sizeof(struct kspi2));
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
kspi = spi_controller_get_devdata(host);
|
||||
kspi->auxdev = container_of(auxdev, struct keba_spi_auxdev, auxdev);
|
||||
kspi->host = host;
|
||||
kspi->device = devm_kcalloc(dev, kspi->auxdev->info_size,
|
||||
sizeof(*kspi->device), GFP_KERNEL);
|
||||
if (!kspi->device)
|
||||
return -ENOMEM;
|
||||
kspi->device_size = kspi->auxdev->info_size;
|
||||
auxiliary_set_drvdata(auxdev, kspi);
|
||||
|
||||
kspi->base = devm_ioremap_resource(dev, &kspi->auxdev->io);
|
||||
if (IS_ERR(kspi->base))
|
||||
return PTR_ERR(kspi->base);
|
||||
|
||||
/* read the SPI base clock frequency */
|
||||
clk_reg = ioread8(kspi->base + KSPI2_CLK_FREQ_REG);
|
||||
switch (clk_reg & KSPI2_CLK_FREQ_MASK) {
|
||||
case KSPI2_CLK_FREQ_62_5M:
|
||||
kspi->base_speed_hz = 62500000; break;
|
||||
case KSPI2_CLK_FREQ_33_3M:
|
||||
kspi->base_speed_hz = 33333333; break;
|
||||
case KSPI2_CLK_FREQ_125M:
|
||||
kspi->base_speed_hz = 125000000; break;
|
||||
case KSPI2_CLK_FREQ_50M:
|
||||
kspi->base_speed_hz = 50000000; break;
|
||||
case KSPI2_CLK_FREQ_100M:
|
||||
kspi->base_speed_hz = 100000000; break;
|
||||
default:
|
||||
dev_err(dev, "Undefined SPI base clock frequency!\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
kspi2_init(kspi);
|
||||
|
||||
host->bus_num = -1;
|
||||
host->num_chipselect = KSPI2_NUM_CS;
|
||||
host->mode_bits = KSPI2_MODE_BITS;
|
||||
host->setup = kspi2_setup;
|
||||
host->prepare_transfer_hardware = kspi2_prepare_hardware;
|
||||
host->unprepare_transfer_hardware = kspi2_unprepare_hardware;
|
||||
host->prepare_message = kspi2_prepare_message;
|
||||
host->set_cs = kspi2_set_cs;
|
||||
host->transfer_one = kspi2_transfer_one;
|
||||
ret = devm_spi_register_controller(dev, host);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register host (%d)!\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = kspi2_register_devices(kspi);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register devices (%d)!\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kspi2_remove(struct auxiliary_device *auxdev)
|
||||
{
|
||||
struct kspi2 *kspi = auxiliary_get_drvdata(auxdev);
|
||||
|
||||
kspi2_unregister_devices(kspi);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id kspi2_devtype_aux[] = {
|
||||
{ .name = "keba.spi" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, kspi2_devtype_aux);
|
||||
|
||||
static struct auxiliary_driver kspi2_driver_aux = {
|
||||
.name = KSPI2,
|
||||
.id_table = kspi2_devtype_aux,
|
||||
.probe = kspi2_probe,
|
||||
.remove = kspi2_remove,
|
||||
};
|
||||
module_auxiliary_driver(kspi2_driver_aux);
|
||||
|
||||
MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
|
||||
MODULE_DESCRIPTION("KEBA SPI host controller driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -187,6 +187,16 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op->max_freq && mem->spi->controller->min_speed_hz &&
|
||||
op->max_freq < mem->spi->controller->min_speed_hz)
|
||||
return false;
|
||||
|
||||
if (op->max_freq &&
|
||||
op->max_freq < mem->spi->max_speed_hz) {
|
||||
if (!spi_mem_controller_is_capable(ctlr, per_op_freq))
|
||||
return false;
|
||||
}
|
||||
|
||||
return spi_mem_check_buswidth(mem, op);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
|
||||
@ -364,6 +374,9 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
u8 *tmpbuf;
|
||||
int ret;
|
||||
|
||||
/* Make sure the operation frequency is correct before going futher */
|
||||
spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op);
|
||||
|
||||
ret = spi_mem_check_op(op);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -410,6 +423,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
xfers[xferpos].tx_buf = tmpbuf;
|
||||
xfers[xferpos].len = op->cmd.nbytes;
|
||||
xfers[xferpos].tx_nbits = op->cmd.buswidth;
|
||||
xfers[xferpos].speed_hz = op->max_freq;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
totalxferlen++;
|
||||
@ -424,6 +438,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
xfers[xferpos].tx_buf = tmpbuf + 1;
|
||||
xfers[xferpos].len = op->addr.nbytes;
|
||||
xfers[xferpos].tx_nbits = op->addr.buswidth;
|
||||
xfers[xferpos].speed_hz = op->max_freq;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
totalxferlen += op->addr.nbytes;
|
||||
@ -435,6 +450,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
xfers[xferpos].len = op->dummy.nbytes;
|
||||
xfers[xferpos].tx_nbits = op->dummy.buswidth;
|
||||
xfers[xferpos].dummy_data = 1;
|
||||
xfers[xferpos].speed_hz = op->max_freq;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
totalxferlen += op->dummy.nbytes;
|
||||
@ -450,6 +466,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
}
|
||||
|
||||
xfers[xferpos].len = op->data.nbytes;
|
||||
xfers[xferpos].speed_hz = op->max_freq;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
totalxferlen += op->data.nbytes;
|
||||
@ -528,6 +545,53 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
|
||||
|
||||
/**
|
||||
* spi_mem_adjust_op_freq() - Adjust the frequency of a SPI mem operation to
|
||||
* match controller, PCB and chip limitations
|
||||
* @mem: the SPI memory
|
||||
* @op: the operation to adjust
|
||||
*
|
||||
* Some chips have per-op frequency limitations and must adapt the maximum
|
||||
* speed. This function allows SPI mem drivers to set @op->max_freq to the
|
||||
* maximum supported value.
|
||||
*/
|
||||
void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
{
|
||||
if (!op->max_freq || op->max_freq > mem->spi->max_speed_hz)
|
||||
op->max_freq = mem->spi->max_speed_hz;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_freq);
|
||||
|
||||
/**
|
||||
* spi_mem_calc_op_duration() - Derives the theoretical length (in ns) of an
|
||||
* operation. This helps finding the best variant
|
||||
* among a list of possible choices.
|
||||
* @op: the operation to benchmark
|
||||
*
|
||||
* Some chips have per-op frequency limitations, PCBs usually have their own
|
||||
* limitations as well, and controllers can support dual, quad or even octal
|
||||
* modes, sometimes in DTR. All these combinations make it impossible to
|
||||
* statically list the best combination for all situations. If we want something
|
||||
* accurate, all these combinations should be rated (eg. with a time estimate)
|
||||
* and the best pick should be taken based on these calculations.
|
||||
*
|
||||
* Returns a ns estimate for the time this op would take.
|
||||
*/
|
||||
u64 spi_mem_calc_op_duration(struct spi_mem_op *op)
|
||||
{
|
||||
u64 ncycles = 0;
|
||||
u32 ns_per_cycles;
|
||||
|
||||
ns_per_cycles = 1000000000 / op->max_freq;
|
||||
ncycles += ((op->cmd.nbytes * 8) / op->cmd.buswidth) / (op->cmd.dtr ? 2 : 1);
|
||||
ncycles += ((op->addr.nbytes * 8) / op->addr.buswidth) / (op->addr.dtr ? 2 : 1);
|
||||
ncycles += ((op->dummy.nbytes * 8) / op->dummy.buswidth) / (op->dummy.dtr ? 2 : 1);
|
||||
ncycles += ((op->data.nbytes * 8) / op->data.buswidth) / (op->data.dtr ? 2 : 1);
|
||||
|
||||
return ncycles * ns_per_cycles;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_calc_op_duration);
|
||||
|
||||
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
|
||||
u64 offs, size_t len, void *buf)
|
||||
{
|
||||
|
@ -265,7 +265,8 @@ static irqreturn_t mchp_coreqspi_isr(int irq, void *dev_id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi)
|
||||
static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned long clk_hz;
|
||||
u32 control, baud_rate_val = 0;
|
||||
@ -274,11 +275,11 @@ static int mchp_coreqspi_setup_clock(struct mchp_coreqspi *qspi, struct spi_devi
|
||||
if (!clk_hz)
|
||||
return -EINVAL;
|
||||
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * spi->max_speed_hz);
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq);
|
||||
if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER) {
|
||||
dev_err(&spi->dev,
|
||||
"could not configure the clock for spi clock %d Hz & system clock %ld Hz\n",
|
||||
spi->max_speed_hz, clk_hz);
|
||||
op->max_freq, clk_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -399,7 +400,7 @@ static int mchp_coreqspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *o
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
err = mchp_coreqspi_setup_clock(qspi, mem->spi);
|
||||
err = mchp_coreqspi_setup_clock(qspi, mem->spi, op);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
@ -457,6 +458,10 @@ error:
|
||||
|
||||
static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct mchp_coreqspi *qspi = spi_controller_get_devdata(mem->spi->controller);
|
||||
unsigned long clk_hz;
|
||||
u32 baud_rate_val;
|
||||
|
||||
if (!spi_mem_default_supports_op(mem, op))
|
||||
return false;
|
||||
|
||||
@ -479,6 +484,14 @@ static bool mchp_coreqspi_supports_op(struct spi_mem *mem, const struct spi_mem_
|
||||
return false;
|
||||
}
|
||||
|
||||
clk_hz = clk_get_rate(qspi->clk);
|
||||
if (!clk_hz)
|
||||
return false;
|
||||
|
||||
baud_rate_val = DIV_ROUND_UP(clk_hz, 2 * op->max_freq);
|
||||
if (baud_rate_val > MAX_DIVIDER || baud_rate_val < MIN_DIVIDER)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -498,6 +511,10 @@ static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = {
|
||||
.exec_op = mchp_coreqspi_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps mchp_coreqspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int mchp_coreqspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
@ -540,6 +557,7 @@ static int mchp_coreqspi_probe(struct platform_device *pdev)
|
||||
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->mem_ops = &mchp_coreqspi_mem_ops;
|
||||
ctlr->mem_caps = &mchp_coreqspi_mem_caps;
|
||||
ctlr->setup = mchp_coreqspi_setup_op;
|
||||
ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
|
||||
SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
|
@ -961,7 +961,7 @@ static int mtk_spi_mem_exec_op(struct spi_mem *mem,
|
||||
|
||||
mtk_spi_reset(mdata);
|
||||
mtk_spi_hw_init(mem->spi->controller, mem->spi);
|
||||
mtk_spi_prepare_transfer(mem->spi->controller, mem->spi->max_speed_hz);
|
||||
mtk_spi_prepare_transfer(mem->spi->controller, op->max_freq);
|
||||
|
||||
reg_val = readl(mdata->base + SPI_CFG3_IPM_REG);
|
||||
/* opcode byte len */
|
||||
@ -1122,6 +1122,10 @@ static const struct spi_controller_mem_ops mtk_spi_mem_ops = {
|
||||
.exec_op = mtk_spi_mem_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps mtk_spi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int mtk_spi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -1160,6 +1164,7 @@ static int mtk_spi_probe(struct platform_device *pdev)
|
||||
if (mdata->dev_comp->ipm_design) {
|
||||
mdata->dev = dev;
|
||||
host->mem_ops = &mtk_spi_mem_ops;
|
||||
host->mem_caps = &mtk_spi_mem_caps;
|
||||
init_completion(&mdata->spimem_done);
|
||||
}
|
||||
|
||||
|
@ -522,7 +522,7 @@ static int mxic_spi_mem_exec_op(struct spi_mem *mem,
|
||||
int i, ret;
|
||||
u8 addr[8], cmd[2];
|
||||
|
||||
ret = mxic_spi_set_freq(mxic, mem->spi->max_speed_hz);
|
||||
ret = mxic_spi_set_freq(mxic, op->max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -582,6 +582,7 @@ static const struct spi_controller_mem_caps mxic_spi_mem_caps = {
|
||||
.dtr = true,
|
||||
.ecc = true,
|
||||
.swap16 = true,
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static void mxic_spi_set_cs(struct spi_device *spi, bool lvl)
|
||||
|
@ -381,6 +381,8 @@ static int mxs_spi_transfer_one(struct spi_controller *host,
|
||||
if (status)
|
||||
break;
|
||||
|
||||
t->effective_speed_hz = ssp->clk_rate;
|
||||
|
||||
/* De-assert on last transfer, inverted by cs_change flag */
|
||||
flag = (&t->transfer_list == m->transfers.prev) ^ t->cs_change ?
|
||||
TXRX_DEASSERT_CS : 0;
|
||||
|
@ -705,9 +705,10 @@ static void nxp_fspi_dll_calibration(struct nxp_fspi *f)
|
||||
* Value for rest of the CS FLSHxxCR0 register would be zero.
|
||||
*
|
||||
*/
|
||||
static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi)
|
||||
static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned long rate = spi->max_speed_hz;
|
||||
unsigned long rate = op->max_freq;
|
||||
int ret;
|
||||
uint64_t size_kb;
|
||||
|
||||
@ -931,7 +932,7 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true);
|
||||
WARN_ON(err);
|
||||
|
||||
nxp_fspi_select_mem(f, mem->spi);
|
||||
nxp_fspi_select_mem(f, mem->spi, op);
|
||||
|
||||
nxp_fspi_prepare_lut(f, op);
|
||||
/*
|
||||
@ -1149,6 +1150,10 @@ static const struct spi_controller_mem_ops nxp_fspi_mem_ops = {
|
||||
.get_name = nxp_fspi_get_name,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps nxp_fspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int nxp_fspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctlr;
|
||||
@ -1246,6 +1251,7 @@ static int nxp_fspi_probe(struct platform_device *pdev)
|
||||
ctlr->bus_num = -1;
|
||||
ctlr->num_chipselect = NXP_FSPI_MAX_CHIPSELECT;
|
||||
ctlr->mem_ops = &nxp_fspi_mem_ops;
|
||||
ctlr->mem_caps = &nxp_fspi_mem_caps;
|
||||
|
||||
nxp_fspi_default_setup(f);
|
||||
|
||||
|
@ -13,12 +13,14 @@
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
|
||||
/* System control */
|
||||
@ -110,6 +112,7 @@
|
||||
#define SFC_VER_3 0x3
|
||||
#define SFC_VER_4 0x4
|
||||
#define SFC_VER_5 0x5
|
||||
#define SFC_VER_8 0x8
|
||||
|
||||
/* Delay line controller register */
|
||||
#define SFC_DLL_CTRL0 0x3C
|
||||
@ -150,16 +153,13 @@
|
||||
/* Data */
|
||||
#define SFC_DATA 0x108
|
||||
|
||||
/* The controller and documentation reports that it supports up to 4 CS
|
||||
* devices (0-3), however I have only been able to test a single CS (CS 0)
|
||||
* due to the configuration of my device.
|
||||
*/
|
||||
#define SFC_MAX_CHIPSELECT_NUM 4
|
||||
#define SFC_CS1_REG_OFFSET 0x200
|
||||
|
||||
#define SFC_MAX_CHIPSELECT_NUM 2
|
||||
|
||||
/* The SFC can transfer max 16KB - 1 at one time
|
||||
* we set it to 15.5KB here for alignment.
|
||||
*/
|
||||
#define SFC_MAX_IOSIZE_VER3 (512 * 31)
|
||||
/* Although up to 4GB, 64KB is enough with less mem reserved */
|
||||
#define SFC_MAX_IOSIZE_VER4 (0x10000U)
|
||||
|
||||
/* DMA is only enabled for large data transmission */
|
||||
#define SFC_DMA_TRANS_THRETHOLD (0x40)
|
||||
@ -169,12 +169,14 @@
|
||||
*/
|
||||
#define SFC_MAX_SPEED (150 * 1000 * 1000)
|
||||
|
||||
#define ROCKCHIP_AUTOSUSPEND_DELAY 2000
|
||||
|
||||
struct rockchip_sfc {
|
||||
struct device *dev;
|
||||
void __iomem *regbase;
|
||||
struct clk *hclk;
|
||||
struct clk *clk;
|
||||
u32 frequency;
|
||||
u32 speed[SFC_MAX_CHIPSELECT_NUM];
|
||||
/* virtual mapped addr for dma_buffer */
|
||||
void *buffer;
|
||||
dma_addr_t dma_buffer;
|
||||
@ -216,6 +218,22 @@ static u32 rockchip_sfc_get_max_iosize(struct rockchip_sfc *sfc)
|
||||
return SFC_MAX_IOSIZE_VER3;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_clk_set_rate(struct rockchip_sfc *sfc, unsigned long speed)
|
||||
{
|
||||
if (sfc->version >= SFC_VER_8)
|
||||
return clk_set_rate(sfc->clk, speed * 2);
|
||||
else
|
||||
return clk_set_rate(sfc->clk, speed);
|
||||
}
|
||||
|
||||
static unsigned long rockchip_sfc_clk_get_rate(struct rockchip_sfc *sfc)
|
||||
{
|
||||
if (sfc->version >= SFC_VER_8)
|
||||
return clk_get_rate(sfc->clk) / 2;
|
||||
else
|
||||
return clk_get_rate(sfc->clk);
|
||||
}
|
||||
|
||||
static void rockchip_sfc_irq_unmask(struct rockchip_sfc *sfc, u32 mask)
|
||||
{
|
||||
u32 reg;
|
||||
@ -302,6 +320,7 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
|
||||
u32 len)
|
||||
{
|
||||
u32 ctrl = 0, cmd = 0;
|
||||
u8 cs = spi_get_chipselect(mem->spi, 0);
|
||||
|
||||
/* set CMD */
|
||||
cmd = op->cmd.opcode;
|
||||
@ -315,7 +334,8 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
|
||||
cmd |= SFC_CMD_ADDR_24BITS << SFC_CMD_ADDR_SHIFT;
|
||||
} else {
|
||||
cmd |= SFC_CMD_ADDR_XBITS << SFC_CMD_ADDR_SHIFT;
|
||||
writel(op->addr.nbytes * 8 - 1, sfc->regbase + SFC_ABIT);
|
||||
writel(op->addr.nbytes * 8 - 1,
|
||||
sfc->regbase + cs * SFC_CS1_REG_OFFSET + SFC_ABIT);
|
||||
}
|
||||
|
||||
ctrl |= ((op->addr.buswidth >> 1) << SFC_CTRL_ADDR_BITS_SHIFT);
|
||||
@ -347,7 +367,7 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
|
||||
|
||||
/* set the Controller */
|
||||
ctrl |= SFC_CTRL_PHASE_SEL_NEGETIVE;
|
||||
cmd |= spi_get_chipselect(mem->spi, 0) << SFC_CMD_CS_SHIFT;
|
||||
cmd |= cs << SFC_CMD_CS_SHIFT;
|
||||
|
||||
dev_dbg(sfc->dev, "sfc addr.nbytes=%x(x%d) dummy.nbytes=%x(x%d)\n",
|
||||
op->addr.nbytes, op->addr.buswidth,
|
||||
@ -355,7 +375,7 @@ static int rockchip_sfc_xfer_setup(struct rockchip_sfc *sfc,
|
||||
dev_dbg(sfc->dev, "sfc ctrl=%x cmd=%x addr=%llx len=%x\n",
|
||||
ctrl, cmd, op->addr.val, len);
|
||||
|
||||
writel(ctrl, sfc->regbase + SFC_CTRL);
|
||||
writel(ctrl, sfc->regbase + cs * SFC_CS1_REG_OFFSET + SFC_CTRL);
|
||||
writel(cmd, sfc->regbase + SFC_CMD);
|
||||
if (op->addr.nbytes)
|
||||
writel(op->addr.val, sfc->regbase + SFC_ADDR);
|
||||
@ -453,8 +473,10 @@ static int rockchip_sfc_xfer_data_dma(struct rockchip_sfc *sfc,
|
||||
|
||||
dev_dbg(sfc->dev, "sfc xfer_dma len=%x\n", len);
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
if (op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
memcpy(sfc->buffer, op->data.buf.out, len);
|
||||
dma_sync_single_for_device(sfc->dev, sfc->dma_buffer, len, DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
ret = rockchip_sfc_fifo_transfer_dma(sfc, sfc->dma_buffer, len);
|
||||
if (!wait_for_completion_timeout(&sfc->cp, msecs_to_jiffies(2000))) {
|
||||
@ -462,8 +484,11 @@ static int rockchip_sfc_xfer_data_dma(struct rockchip_sfc *sfc,
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
rockchip_sfc_irq_mask(sfc, SFC_IMR_DMA);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
dma_sync_single_for_cpu(sfc->dev, sfc->dma_buffer, len, DMA_FROM_DEVICE);
|
||||
memcpy(op->data.buf.in, sfc->buffer, len);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -473,6 +498,16 @@ static int rockchip_sfc_xfer_done(struct rockchip_sfc *sfc, u32 timeout_us)
|
||||
int ret = 0;
|
||||
u32 status;
|
||||
|
||||
/*
|
||||
* There is very little data left in fifo, and the controller will
|
||||
* complete the transmission in a short period of time.
|
||||
*/
|
||||
ret = readl_poll_timeout(sfc->regbase + SFC_SR, status,
|
||||
!(status & SFC_SR_IS_BUSY),
|
||||
0, 10);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
ret = readl_poll_timeout(sfc->regbase + SFC_SR, status,
|
||||
!(status & SFC_SR_IS_BUSY),
|
||||
20, timeout_us);
|
||||
@ -491,14 +526,22 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op
|
||||
struct rockchip_sfc *sfc = spi_controller_get_devdata(mem->spi->controller);
|
||||
u32 len = op->data.nbytes;
|
||||
int ret;
|
||||
u8 cs = spi_get_chipselect(mem->spi, 0);
|
||||
|
||||
if (unlikely(mem->spi->max_speed_hz != sfc->frequency)) {
|
||||
ret = clk_set_rate(sfc->clk, mem->spi->max_speed_hz);
|
||||
ret = pm_runtime_get_sync(sfc->dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(sfc->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (unlikely(op->max_freq != sfc->speed[cs]) &&
|
||||
!has_acpi_companion(sfc->dev)) {
|
||||
ret = rockchip_sfc_clk_set_rate(sfc, op->max_freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
sfc->frequency = mem->spi->max_speed_hz;
|
||||
goto out;
|
||||
sfc->speed[cs] = op->max_freq;
|
||||
dev_dbg(sfc->dev, "set_freq=%dHz real_freq=%ldHz\n",
|
||||
sfc->frequency, clk_get_rate(sfc->clk));
|
||||
sfc->speed[cs], rockchip_sfc_clk_get_rate(sfc));
|
||||
}
|
||||
|
||||
rockchip_sfc_adjust_op_work((struct spi_mem_op *)op);
|
||||
@ -515,11 +558,17 @@ static int rockchip_sfc_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op
|
||||
if (ret != len) {
|
||||
dev_err(sfc->dev, "xfer data failed ret %d dir %d\n", ret, op->data.dir);
|
||||
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return rockchip_sfc_xfer_done(sfc, 100000);
|
||||
ret = rockchip_sfc_xfer_done(sfc, 100000);
|
||||
out:
|
||||
pm_runtime_mark_last_busy(sfc->dev);
|
||||
pm_runtime_put_autosuspend(sfc->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
|
||||
@ -536,6 +585,10 @@ static const struct spi_controller_mem_ops rockchip_sfc_mem_ops = {
|
||||
.adjust_op_size = rockchip_sfc_adjust_op_size,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps rockchip_sfc_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static irqreturn_t rockchip_sfc_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct rockchip_sfc *sfc = dev_id;
|
||||
@ -561,6 +614,7 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
struct spi_controller *host;
|
||||
struct rockchip_sfc *sfc;
|
||||
int ret;
|
||||
u32 i, val;
|
||||
|
||||
host = devm_spi_alloc_host(&pdev->dev, sizeof(*sfc));
|
||||
if (!host)
|
||||
@ -568,6 +622,7 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
|
||||
host->flags = SPI_CONTROLLER_HALF_DUPLEX;
|
||||
host->mem_ops = &rockchip_sfc_mem_ops;
|
||||
host->mem_caps = &rockchip_sfc_mem_caps;
|
||||
host->dev.of_node = pdev->dev.of_node;
|
||||
host->mode_bits = SPI_TX_QUAD | SPI_TX_DUAL | SPI_RX_QUAD | SPI_RX_DUAL;
|
||||
host->max_speed_hz = SFC_MAX_SPEED;
|
||||
@ -581,31 +636,29 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(sfc->regbase))
|
||||
return PTR_ERR(sfc->regbase);
|
||||
|
||||
sfc->clk = devm_clk_get(&pdev->dev, "clk_sfc");
|
||||
if (!has_acpi_companion(&pdev->dev))
|
||||
sfc->clk = devm_clk_get(&pdev->dev, "clk_sfc");
|
||||
if (IS_ERR(sfc->clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(sfc->clk),
|
||||
"Failed to get sfc interface clk\n");
|
||||
|
||||
sfc->hclk = devm_clk_get(&pdev->dev, "hclk_sfc");
|
||||
if (!has_acpi_companion(&pdev->dev))
|
||||
sfc->hclk = devm_clk_get(&pdev->dev, "hclk_sfc");
|
||||
if (IS_ERR(sfc->hclk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(sfc->hclk),
|
||||
"Failed to get sfc ahb clk\n");
|
||||
|
||||
sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, "rockchip,sfc-no-dma");
|
||||
|
||||
if (sfc->use_dma) {
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
dev_warn(dev, "Unable to set dma mask\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sfc->buffer = dmam_alloc_coherent(dev, SFC_MAX_IOSIZE_VER3,
|
||||
&sfc->dma_buffer, GFP_KERNEL);
|
||||
if (!sfc->buffer)
|
||||
return -ENOMEM;
|
||||
if (has_acpi_companion(&pdev->dev)) {
|
||||
ret = device_property_read_u32(&pdev->dev, "clock-frequency", &val);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to find clock-frequency in ACPI\n");
|
||||
for (i = 0; i < SFC_MAX_CHIPSELECT_NUM; i++)
|
||||
sfc->speed[i] = val;
|
||||
}
|
||||
|
||||
sfc->use_dma = !of_property_read_bool(sfc->dev->of_node, "rockchip,sfc-no-dma");
|
||||
|
||||
ret = clk_prepare_enable(sfc->hclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to enable ahb clk\n");
|
||||
@ -630,19 +683,47 @@ static int rockchip_sfc_probe(struct platform_device *pdev)
|
||||
goto err_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, sfc);
|
||||
|
||||
ret = rockchip_sfc_init(sfc);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc);
|
||||
sfc->version = rockchip_sfc_get_version(sfc);
|
||||
sfc->max_iosize = rockchip_sfc_get_max_iosize(sfc);
|
||||
|
||||
ret = spi_register_controller(host);
|
||||
pm_runtime_set_autosuspend_delay(dev, ROCKCHIP_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_noresume(dev);
|
||||
|
||||
if (sfc->use_dma) {
|
||||
sfc->buffer = (u8 *)__get_free_pages(GFP_KERNEL | GFP_DMA32,
|
||||
get_order(sfc->max_iosize));
|
||||
if (!sfc->buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto err_dma;
|
||||
}
|
||||
sfc->dma_buffer = virt_to_phys(sfc->buffer);
|
||||
}
|
||||
|
||||
ret = devm_spi_register_controller(dev, host);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
goto err_register;
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_register:
|
||||
free_pages((unsigned long)sfc->buffer, get_order(sfc->max_iosize));
|
||||
err_dma:
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_dont_use_autosuspend(dev);
|
||||
err_irq:
|
||||
clk_disable_unprepare(sfc->clk);
|
||||
err_clk:
|
||||
@ -657,11 +738,80 @@ static void rockchip_sfc_remove(struct platform_device *pdev)
|
||||
struct spi_controller *host = sfc->host;
|
||||
|
||||
spi_unregister_controller(host);
|
||||
free_pages((unsigned long)sfc->buffer, get_order(sfc->max_iosize));
|
||||
|
||||
clk_disable_unprepare(sfc->clk);
|
||||
clk_disable_unprepare(sfc->hclk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rockchip_sfc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct rockchip_sfc *sfc = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(sfc->clk);
|
||||
clk_disable_unprepare(sfc->hclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_sfc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct rockchip_sfc *sfc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(sfc->hclk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(sfc->clk);
|
||||
if (ret < 0)
|
||||
clk_disable_unprepare(sfc->hclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rockchip_sfc_suspend(struct device *dev)
|
||||
{
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int rockchip_sfc_resume(struct device *dev)
|
||||
{
|
||||
struct rockchip_sfc *sfc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
pm_runtime_put_noidle(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rockchip_sfc_init(sfc);
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct dev_pm_ops rockchip_sfc_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(rockchip_sfc_runtime_suspend,
|
||||
rockchip_sfc_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(rockchip_sfc_suspend, rockchip_sfc_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_sfc_dt_ids[] = {
|
||||
{ .compatible = "rockchip,sfc"},
|
||||
{ /* sentinel */ }
|
||||
@ -672,6 +822,7 @@ static struct platform_driver rockchip_sfc_driver = {
|
||||
.driver = {
|
||||
.name = "rockchip-sfc",
|
||||
.of_match_table = rockchip_sfc_dt_ids,
|
||||
.pm = &rockchip_sfc_pm_ops,
|
||||
},
|
||||
.probe = rockchip_sfc_probe,
|
||||
.remove = rockchip_sfc_remove,
|
||||
|
@ -7,13 +7,15 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_data/sc18is602.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
enum chips { sc18is602, sc18is602b, sc18is603 };
|
||||
@ -236,9 +238,7 @@ static int sc18is602_setup(struct spi_device *spi)
|
||||
|
||||
static int sc18is602_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_client_get_device_id(client);
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct sc18is602_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct sc18is602 *hw;
|
||||
struct spi_controller *host;
|
||||
@ -251,8 +251,9 @@ static int sc18is602_probe(struct i2c_client *client)
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
device_set_node(&host->dev, dev_fwnode(dev));
|
||||
|
||||
hw = spi_controller_get_devdata(host);
|
||||
i2c_set_clientdata(client, hw);
|
||||
|
||||
/* assert reset and then release */
|
||||
hw->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
@ -265,11 +266,7 @@ static int sc18is602_probe(struct i2c_client *client)
|
||||
hw->dev = dev;
|
||||
hw->ctrl = 0xff;
|
||||
|
||||
if (client->dev.of_node)
|
||||
hw->id = (uintptr_t)of_device_get_match_data(&client->dev);
|
||||
else
|
||||
hw->id = id->driver_data;
|
||||
|
||||
hw->id = (uintptr_t)i2c_get_match_data(client);
|
||||
switch (hw->id) {
|
||||
case sc18is602:
|
||||
case sc18is602b:
|
||||
@ -278,28 +275,21 @@ static int sc18is602_probe(struct i2c_client *client)
|
||||
break;
|
||||
case sc18is603:
|
||||
host->num_chipselect = 2;
|
||||
if (pdata) {
|
||||
if (pdata)
|
||||
hw->freq = pdata->clock_frequency;
|
||||
} else {
|
||||
const __be32 *val;
|
||||
int len;
|
||||
|
||||
val = of_get_property(np, "clock-frequency", &len);
|
||||
if (val && len >= sizeof(__be32))
|
||||
hw->freq = be32_to_cpup(val);
|
||||
}
|
||||
else
|
||||
device_property_read_u32(dev, "clock-frequency", &hw->freq);
|
||||
if (!hw->freq)
|
||||
hw->freq = SC18IS602_CLOCK;
|
||||
break;
|
||||
}
|
||||
host->bus_num = np ? -1 : client->adapter->nr;
|
||||
host->bus_num = dev_fwnode(dev) ? -1 : client->adapter->nr;
|
||||
host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST;
|
||||
host->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
host->setup = sc18is602_setup;
|
||||
host->transfer_one_message = sc18is602_transfer_one;
|
||||
host->max_transfer_size = sc18is602_max_transfer_size;
|
||||
host->max_message_size = sc18is602_max_transfer_size;
|
||||
host->dev.of_node = np;
|
||||
host->min_speed_hz = hw->freq / 128;
|
||||
host->max_speed_hz = hw->freq / 4;
|
||||
|
||||
@ -314,7 +304,7 @@ static const struct i2c_device_id sc18is602_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sc18is602_id);
|
||||
|
||||
static const struct of_device_id sc18is602_of_match[] __maybe_unused = {
|
||||
static const struct of_device_id sc18is602_of_match[] = {
|
||||
{
|
||||
.compatible = "nxp,sc18is602",
|
||||
.data = (void *)sc18is602
|
||||
@ -334,7 +324,7 @@ MODULE_DEVICE_TABLE(of, sc18is602_of_match);
|
||||
static struct i2c_driver sc18is602_driver = {
|
||||
.driver = {
|
||||
.name = "sc18is602",
|
||||
.of_match_table = of_match_ptr(sc18is602_of_match),
|
||||
.of_match_table = sc18is602_of_match,
|
||||
},
|
||||
.probe = sc18is602_probe,
|
||||
.id_table = sc18is602_id,
|
||||
|
@ -335,7 +335,6 @@ static void f_ospi_config_indir_protocol(struct f_ospi *ospi,
|
||||
static int f_ospi_indir_prepare_op(struct f_ospi *ospi, struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct spi_device *spi = mem->spi;
|
||||
u32 irq_stat_en;
|
||||
int ret;
|
||||
|
||||
@ -343,7 +342,7 @@ static int f_ospi_indir_prepare_op(struct f_ospi *ospi, struct spi_mem *mem,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
f_ospi_config_clk(ospi, spi->max_speed_hz);
|
||||
f_ospi_config_clk(ospi, op->max_freq);
|
||||
|
||||
f_ospi_config_indir_protocol(ospi, mem, op);
|
||||
|
||||
@ -577,6 +576,10 @@ static const struct spi_controller_mem_ops f_ospi_mem_ops = {
|
||||
.exec_op = f_ospi_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps f_ospi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int f_ospi_init(struct f_ospi *ospi)
|
||||
{
|
||||
int ret;
|
||||
@ -614,6 +617,7 @@ static int f_ospi_probe(struct platform_device *pdev)
|
||||
| SPI_RX_DUAL | SPI_RX_QUAD | SPI_RX_OCTAL
|
||||
| SPI_MODE_0 | SPI_MODE_1 | SPI_LSB_FIRST;
|
||||
ctlr->mem_ops = &f_ospi_mem_ops;
|
||||
ctlr->mem_caps = &f_ospi_mem_caps;
|
||||
ctlr->bus_num = -1;
|
||||
of_property_read_u32(dev->of_node, "num-cs", &num_cs);
|
||||
if (num_cs > OSPI_NUM_CS) {
|
||||
|
@ -623,7 +623,7 @@ static int ti_qspi_exec_mem_op(struct spi_mem *mem,
|
||||
mutex_lock(&qspi->list_lock);
|
||||
|
||||
if (!qspi->mmap_enabled || qspi->current_cs != spi_get_chipselect(mem->spi, 0)) {
|
||||
ti_qspi_setup_clk(qspi, mem->spi->max_speed_hz);
|
||||
ti_qspi_setup_clk(qspi, op->max_freq);
|
||||
ti_qspi_enable_memory_map(mem->spi);
|
||||
}
|
||||
ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
|
||||
@ -658,6 +658,10 @@ static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
|
||||
.adjust_op_size = ti_qspi_adjust_op_size,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps ti_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
static int ti_qspi_start_transfer_one(struct spi_controller *host,
|
||||
struct spi_message *m)
|
||||
{
|
||||
@ -777,6 +781,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
|
||||
host->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
|
||||
SPI_BPW_MASK(8);
|
||||
host->mem_ops = &ti_qspi_mem_ops;
|
||||
host->mem_caps = &ti_qspi_mem_caps;
|
||||
|
||||
if (!of_property_read_u32(np, "num-cs", &num_cs))
|
||||
host->num_chipselect = num_cs;
|
||||
|
@ -318,6 +318,7 @@ static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
|
||||
* zynq_qspi_config_op - Configure QSPI controller for specified transfer
|
||||
* @xqspi: Pointer to the zynq_qspi structure
|
||||
* @spi: Pointer to the spi_device structure
|
||||
* @op: The memory operation to execute
|
||||
*
|
||||
* Sets the operational mode of QSPI controller for the next QSPI transfer and
|
||||
* sets the requested clock frequency.
|
||||
@ -331,7 +332,8 @@ static void zynq_qspi_chipselect(struct spi_device *spi, bool assert)
|
||||
* controller the driver will set the highest or lowest frequency supported by
|
||||
* controller.
|
||||
*/
|
||||
static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi)
|
||||
static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
u32 config_reg, baud_rate_val = 0;
|
||||
|
||||
@ -346,7 +348,7 @@ static int zynq_qspi_config_op(struct zynq_qspi *xqspi, struct spi_device *spi)
|
||||
*/
|
||||
while ((baud_rate_val < ZYNQ_QSPI_CONFIG_BAUD_DIV_MAX) &&
|
||||
(clk_get_rate(xqspi->refclk) / (2 << baud_rate_val)) >
|
||||
spi->max_speed_hz)
|
||||
op->max_freq)
|
||||
baud_rate_val++;
|
||||
|
||||
config_reg = zynq_qspi_read(xqspi, ZYNQ_QSPI_CONFIG_OFFSET);
|
||||
@ -379,12 +381,21 @@ static int zynq_qspi_setup_op(struct spi_device *spi)
|
||||
{
|
||||
struct spi_controller *ctlr = spi->controller;
|
||||
struct zynq_qspi *qspi = spi_controller_get_devdata(ctlr);
|
||||
int ret;
|
||||
|
||||
if (ctlr->busy)
|
||||
return -EBUSY;
|
||||
|
||||
clk_enable(qspi->refclk);
|
||||
clk_enable(qspi->pclk);
|
||||
ret = clk_enable(qspi->refclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(qspi->pclk);
|
||||
if (ret) {
|
||||
clk_disable(qspi->refclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
zynq_qspi_write(qspi, ZYNQ_QSPI_ENABLE_OFFSET,
|
||||
ZYNQ_QSPI_ENABLE_ENABLE_MASK);
|
||||
|
||||
@ -534,7 +545,7 @@ static int zynq_qspi_exec_mem_op(struct spi_mem *mem,
|
||||
op->dummy.buswidth, op->data.buswidth);
|
||||
|
||||
zynq_qspi_chipselect(mem->spi, true);
|
||||
zynq_qspi_config_op(xqspi, mem->spi);
|
||||
zynq_qspi_config_op(xqspi, mem->spi, op);
|
||||
|
||||
if (op->cmd.opcode) {
|
||||
reinit_completion(&xqspi->data_completion);
|
||||
@ -620,6 +631,10 @@ static const struct spi_controller_mem_ops zynq_qspi_mem_ops = {
|
||||
.exec_op = zynq_qspi_exec_mem_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps zynq_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* zynq_qspi_probe - Probe method for the QSPI driver
|
||||
* @pdev: Pointer to the platform_device structure
|
||||
@ -706,6 +721,7 @@ static int zynq_qspi_probe(struct platform_device *pdev)
|
||||
ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
|
||||
SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
ctlr->mem_ops = &zynq_qspi_mem_ops;
|
||||
ctlr->mem_caps = &zynq_qspi_mem_caps;
|
||||
ctlr->setup = zynq_qspi_setup_op;
|
||||
ctlr->max_speed_hz = clk_get_rate(xqspi->refclk) / 2;
|
||||
ctlr->dev.of_node = np;
|
||||
|
@ -535,7 +535,7 @@ static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi,
|
||||
* zynqmp_qspi_config_op - Configure QSPI controller for specified
|
||||
* transfer
|
||||
* @xqspi: Pointer to the zynqmp_qspi structure
|
||||
* @qspi: Pointer to the spi_device structure
|
||||
* @op: The memory operation to execute
|
||||
*
|
||||
* Sets the operational mode of QSPI controller for the next QSPI transfer and
|
||||
* sets the requested clock frequency.
|
||||
@ -553,12 +553,12 @@ static inline u32 zynqmp_qspi_selectspimode(struct zynqmp_qspi *xqspi,
|
||||
* frequency supported by controller.
|
||||
*/
|
||||
static int zynqmp_qspi_config_op(struct zynqmp_qspi *xqspi,
|
||||
struct spi_device *qspi)
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
ulong clk_rate;
|
||||
u32 config_reg, req_speed_hz, baud_rate_val = 0;
|
||||
|
||||
req_speed_hz = qspi->max_speed_hz;
|
||||
req_speed_hz = op->max_freq;
|
||||
|
||||
if (xqspi->speed_hz != req_speed_hz) {
|
||||
xqspi->speed_hz = req_speed_hz;
|
||||
@ -1072,7 +1072,7 @@ static int zynqmp_qspi_exec_op(struct spi_mem *mem,
|
||||
op->dummy.buswidth, op->data.buswidth);
|
||||
|
||||
mutex_lock(&xqspi->op_lock);
|
||||
zynqmp_qspi_config_op(xqspi, mem->spi);
|
||||
zynqmp_qspi_config_op(xqspi, op);
|
||||
zynqmp_qspi_chipselect(mem->spi, false);
|
||||
genfifoentry |= xqspi->genfifocs;
|
||||
genfifoentry |= xqspi->genfifobus;
|
||||
@ -1224,6 +1224,10 @@ static const struct spi_controller_mem_ops zynqmp_qspi_mem_ops = {
|
||||
.exec_op = zynqmp_qspi_exec_op,
|
||||
};
|
||||
|
||||
static const struct spi_controller_mem_caps zynqmp_qspi_mem_caps = {
|
||||
.per_op_freq = true,
|
||||
};
|
||||
|
||||
/**
|
||||
* zynqmp_qspi_probe - Probe method for the QSPI driver
|
||||
* @pdev: Pointer to the platform_device structure
|
||||
@ -1333,6 +1337,7 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->mem_ops = &zynqmp_qspi_mem_ops;
|
||||
ctlr->mem_caps = &zynqmp_qspi_mem_caps;
|
||||
ctlr->setup = zynqmp_qspi_setup_op;
|
||||
ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
ctlr->dev.of_node = np;
|
||||
|
@ -410,29 +410,21 @@ static int spi_probe(struct device *dev)
|
||||
{
|
||||
const struct spi_driver *sdrv = to_spi_driver(dev->driver);
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
int ret;
|
||||
|
||||
ret = of_clk_set_defaults(dev->of_node, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (dev->of_node) {
|
||||
if (is_of_node(fwnode))
|
||||
spi->irq = of_irq_get(dev->of_node, 0);
|
||||
if (spi->irq == -EPROBE_DEFER)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get irq\n");
|
||||
if (spi->irq < 0)
|
||||
spi->irq = 0;
|
||||
}
|
||||
|
||||
if (has_acpi_companion(dev) && spi->irq < 0) {
|
||||
struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
|
||||
|
||||
spi->irq = acpi_dev_gpio_irq_get(adev, 0);
|
||||
if (spi->irq == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (spi->irq < 0)
|
||||
spi->irq = 0;
|
||||
}
|
||||
else if (is_acpi_device_node(fwnode) && spi->irq < 0)
|
||||
spi->irq = acpi_dev_gpio_irq_get(to_acpi_device_node(fwnode), 0);
|
||||
if (spi->irq == -EPROBE_DEFER)
|
||||
return dev_err_probe(dev, spi->irq, "Failed to get irq\n");
|
||||
if (spi->irq < 0)
|
||||
spi->irq = 0;
|
||||
|
||||
ret = dev_pm_domain_attach(dev, true);
|
||||
if (ret)
|
||||
@ -874,15 +866,18 @@ EXPORT_SYMBOL_GPL(spi_new_device);
|
||||
*/
|
||||
void spi_unregister_device(struct spi_device *spi)
|
||||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
|
||||
if (!spi)
|
||||
return;
|
||||
|
||||
if (spi->dev.of_node) {
|
||||
of_node_clear_flag(spi->dev.of_node, OF_POPULATED);
|
||||
of_node_put(spi->dev.of_node);
|
||||
fwnode = dev_fwnode(&spi->dev);
|
||||
if (is_of_node(fwnode)) {
|
||||
of_node_clear_flag(to_of_node(fwnode), OF_POPULATED);
|
||||
of_node_put(to_of_node(fwnode));
|
||||
} else if (is_acpi_device_node(fwnode)) {
|
||||
acpi_device_clear_enumerated(to_acpi_device_node(fwnode));
|
||||
}
|
||||
if (ACPI_COMPANION(&spi->dev))
|
||||
acpi_device_clear_enumerated(ACPI_COMPANION(&spi->dev));
|
||||
device_remove_software_node(&spi->dev);
|
||||
device_del(&spi->dev);
|
||||
spi_cleanup(spi);
|
||||
@ -1059,7 +1054,7 @@ static void spi_toggle_csgpiod(struct spi_device *spi, u8 idx, bool enable, bool
|
||||
* ambiguity. That's why we use enable, that takes SPI_CS_HIGH
|
||||
* into account.
|
||||
*/
|
||||
if (has_acpi_companion(&spi->dev))
|
||||
if (is_acpi_device_node(dev_fwnode(&spi->dev)))
|
||||
gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx), !enable);
|
||||
else
|
||||
/* Polarity handled by GPIO library */
|
||||
@ -4841,7 +4836,7 @@ extern struct notifier_block spi_of_notifier;
|
||||
#if IS_ENABLED(CONFIG_ACPI)
|
||||
static int spi_acpi_controller_match(struct device *dev, const void *data)
|
||||
{
|
||||
return ACPI_COMPANION(dev->parent) == data;
|
||||
return device_match_acpi_dev(dev->parent, data);
|
||||
}
|
||||
|
||||
struct spi_controller *acpi_spi_find_controller_by_adev(struct acpi_device *adev)
|
||||
|
@ -698,19 +698,24 @@ static const struct class spidev_class = {
|
||||
.name = "spidev",
|
||||
};
|
||||
|
||||
/*
|
||||
* The spi device ids are expected to match the device names of the
|
||||
* spidev_dt_ids array below. Both arrays are kept in the same ordering.
|
||||
*/
|
||||
static const struct spi_device_id spidev_spi_ids[] = {
|
||||
{ .name = "bh2228fv" },
|
||||
{ .name = "dh2228fv" },
|
||||
{ .name = "jg10309-01" },
|
||||
{ .name = "ltc2488" },
|
||||
{ .name = "sx1301" },
|
||||
{ .name = "bk4" },
|
||||
{ .name = "dhcom-board" },
|
||||
{ .name = "m53cpld" },
|
||||
{ .name = "spi-petra" },
|
||||
{ .name = "spi-authenta" },
|
||||
{ .name = "em3581" },
|
||||
{ .name = "si3210" },
|
||||
{ .name = /* cisco */ "spi-petra" },
|
||||
{ .name = /* dh */ "dhcom-board" },
|
||||
{ .name = /* elgin */ "jg10309-01" },
|
||||
{ .name = /* lineartechnology */ "ltc2488" },
|
||||
{ .name = /* lwn */ "bk4" },
|
||||
{ .name = /* lwn */ "bk4-spi" },
|
||||
{ .name = /* menlo */ "m53cpld" },
|
||||
{ .name = /* micron */ "spi-authenta" },
|
||||
{ .name = /* rohm */ "bh2228fv" },
|
||||
{ .name = /* rohm */ "dh2228fv" },
|
||||
{ .name = /* semtech */ "sx1301" },
|
||||
{ .name = /* silabs */ "em3581" },
|
||||
{ .name = /* silabs */ "si3210" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, spidev_spi_ids);
|
||||
@ -734,6 +739,7 @@ static const struct of_device_id spidev_dt_ids[] = {
|
||||
{ .compatible = "elgin,jg10309-01", .data = &spidev_of_check },
|
||||
{ .compatible = "lineartechnology,ltc2488", .data = &spidev_of_check },
|
||||
{ .compatible = "lwn,bk4", .data = &spidev_of_check },
|
||||
{ .compatible = "lwn,bk4-spi", .data = &spidev_of_check },
|
||||
{ .compatible = "menlo,m53cpld", .data = &spidev_of_check },
|
||||
{ .compatible = "micron,spi-authenta", .data = &spidev_of_check },
|
||||
{ .compatible = "rohm,bh2228fv", .data = &spidev_of_check },
|
||||
|
@ -15,16 +15,32 @@
|
||||
|
||||
#define SPI_MEM_OP_CMD(__opcode, __buswidth) \
|
||||
{ \
|
||||
.nbytes = 1, \
|
||||
.buswidth = __buswidth, \
|
||||
.opcode = __opcode, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_CMD(__opcode, __buswidth) \
|
||||
{ \
|
||||
.nbytes = 1, \
|
||||
.opcode = __opcode, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
|
||||
{ \
|
||||
.nbytes = __nbytes, \
|
||||
.buswidth = __buswidth, \
|
||||
.val = __val, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_ADDR(__nbytes, __val, __buswidth) \
|
||||
{ \
|
||||
.nbytes = __nbytes, \
|
||||
.val = __val, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_NO_ADDR { }
|
||||
@ -35,22 +51,47 @@
|
||||
.buswidth = __buswidth, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_DUMMY(__nbytes, __buswidth) \
|
||||
{ \
|
||||
.nbytes = __nbytes, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_NO_DUMMY { }
|
||||
|
||||
#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth) \
|
||||
{ \
|
||||
.buswidth = __buswidth, \
|
||||
.dir = SPI_MEM_DATA_IN, \
|
||||
.nbytes = __nbytes, \
|
||||
.buf.in = __buf, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_DATA_IN(__nbytes, __buf, __buswidth) \
|
||||
{ \
|
||||
.dir = SPI_MEM_DATA_IN, \
|
||||
.nbytes = __nbytes, \
|
||||
.buf.in = __buf, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth) \
|
||||
{ \
|
||||
.buswidth = __buswidth, \
|
||||
.dir = SPI_MEM_DATA_OUT, \
|
||||
.nbytes = __nbytes, \
|
||||
.buf.out = __buf, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_DTR_OP_DATA_OUT(__nbytes, __buf, __buswidth) \
|
||||
{ \
|
||||
.dir = SPI_MEM_DATA_OUT, \
|
||||
.nbytes = __nbytes, \
|
||||
.buf.out = __buf, \
|
||||
.buswidth = __buswidth, \
|
||||
.dtr = true, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_NO_DATA { }
|
||||
@ -68,6 +109,9 @@ enum spi_mem_data_dir {
|
||||
SPI_MEM_DATA_OUT,
|
||||
};
|
||||
|
||||
#define SPI_MEM_OP_MAX_FREQ(__freq) \
|
||||
.max_freq = __freq
|
||||
|
||||
/**
|
||||
* struct spi_mem_op - describes a SPI memory operation
|
||||
* @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is
|
||||
@ -97,6 +141,9 @@ enum spi_mem_data_dir {
|
||||
* operation does not involve transferring data
|
||||
* @data.buf.in: input buffer (must be DMA-able)
|
||||
* @data.buf.out: output buffer (must be DMA-able)
|
||||
* @max_freq: frequency limitation wrt this operation. 0 means there is no
|
||||
* specific constraint and the highest achievable frequency can be
|
||||
* attempted.
|
||||
*/
|
||||
struct spi_mem_op {
|
||||
struct {
|
||||
@ -135,14 +182,17 @@ struct spi_mem_op {
|
||||
const void *out;
|
||||
} buf;
|
||||
} data;
|
||||
|
||||
unsigned int max_freq;
|
||||
};
|
||||
|
||||
#define SPI_MEM_OP(__cmd, __addr, __dummy, __data) \
|
||||
#define SPI_MEM_OP(__cmd, __addr, __dummy, __data, ...) \
|
||||
{ \
|
||||
.cmd = __cmd, \
|
||||
.addr = __addr, \
|
||||
.dummy = __dummy, \
|
||||
.data = __data, \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,11 +352,13 @@ struct spi_controller_mem_ops {
|
||||
* @ecc: Supports operations with error correction
|
||||
* @swap16: Supports swapping bytes on a 16 bit boundary when configured in
|
||||
* Octal DTR
|
||||
* @per_op_freq: Supports per operation frequency switching
|
||||
*/
|
||||
struct spi_controller_mem_caps {
|
||||
bool dtr;
|
||||
bool ecc;
|
||||
bool swap16;
|
||||
bool per_op_freq;
|
||||
};
|
||||
|
||||
#define spi_mem_controller_is_capable(ctlr, cap) \
|
||||
@ -371,6 +423,8 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
|
||||
#endif /* CONFIG_SPI_MEM */
|
||||
|
||||
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
|
||||
void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op);
|
||||
u64 spi_mem_calc_op_duration(struct spi_mem_op *op);
|
||||
|
||||
bool spi_mem_supports_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op);
|
||||
|
Loading…
x
Reference in New Issue
Block a user