mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'spi-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"This is a fairly quiet release for the most part, though we do have
one really nice improvement in the spi-mem framework which will
improve performance for flash devices especially when built on by
changes in the MTD subsystem which are also due to be sent this merge
window.
There's also been some substantial work on some of the drivers,
highlights include:
- Support for per-operation bus frequency in the spi-mem framework,
meaning speeds are no longer limited by the slowest operation
- ACPI support and improved power management for Rockchip SFC
controllers
- Support for Atmel SAM7G5 QuadSPI and KEBA SPI controllers"
* tag 'spi-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (49 commits)
spi: pxa2xx: Introduce __lpss_ssp_update_priv() helper
spi: ti-qspi: Use syscon_regmap_lookup_by_phandle_args
spi: amd: Fix -Wuninitialized in amd_spi_exec_mem_op()
spi: spi-mem: Estimate the time taken by operations
spi: spi-mem: Create macros for DTR operation
spi: spi-mem: Reorder spi-mem macro assignments
spi: zynqmp-gqspi: Support per spi-mem operation frequency switches
spi: zynq-qspi: Support per spi-mem operation frequency switches
spi: spi-ti-qspi: Support per spi-mem operation frequency switches
spi: spi-sn-f-ospi: Support per spi-mem operation frequency switches
spi: rockchip-sfc: Support per spi-mem operation frequency switches
spi: nxp-fspi: Support per spi-mem operation frequency switches
spi: mxic: Support per spi-mem operation frequency switches
spi: mt65xx: Support per spi-mem operation frequency switches
spi: microchip-core-qspi: Support per spi-mem operation frequency switches
spi: fsl-qspi: Support per spi-mem operation frequency switches
spi: dw: Support per spi-mem operation frequency switches
spi: cadence-qspi: Support per spi-mem operation frequency switches
spi: amlogic-spifc-a1: Support per spi-mem operation frequency switches
spi: amd: Drop redundant check
...
This commit is contained in:
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1214,6 +1214,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
|
||||
|
||||
+904
-89
File diff suppressed because it is too large
Load Diff
+13
-13
@@ -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);
|
||||
}
|
||||
|
||||
@@ -672,13 +670,10 @@ static int amd_spi_exec_mem_op(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct amd_spi *amd_spi;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
@@ -693,10 +688,10 @@ static int amd_spi_exec_mem_op(struct spi_mem *mem,
|
||||
amd_spi_mem_data_out(amd_spi, op);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops amd_spi_mem_ops = {
|
||||
@@ -705,6 +700,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 +781,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user