diff options
Diffstat (limited to 'drivers/spi')
27 files changed, 2139 insertions, 788 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 55675750182e..5520403896fc 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -435,7 +435,8 @@ config SPI_FSL_LPSPI config SPI_FSL_QUADSPI tristate "Freescale QSPI controller" - depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST + depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || \ + ARCH_SPACEMIT || COMPILE_TEST depends on HAS_IOMEM help This enables support for the Quad SPI controller in master mode. @@ -706,15 +707,6 @@ config SPI_MESON_SPIFC This enables master mode support for the SPIFC (SPI flash controller) available in Amlogic Meson SoCs. -config SPI_MICROCHIP_CORE - tristate "Microchip FPGA SPI controllers" - depends on SPI_MASTER - help - This enables the SPI driver for Microchip FPGA SPI controllers. - Say Y or M here if you want to use the "hard" controllers on - PolarFire SoC. - If built as a module, it will be called spi-microchip-core. - config SPI_MICROCHIP_CORE_QSPI tristate "Microchip FPGA QSPI controllers" depends on SPI_MASTER @@ -724,6 +716,15 @@ config SPI_MICROCHIP_CORE_QSPI PolarFire SoC. If built as a module, it will be called spi-microchip-core-qspi. +config SPI_MICROCHIP_CORE_SPI + tristate "Microchip FPGA CoreSPI controller" + depends on SPI_MASTER + help + This enables the SPI driver for Microchip FPGA CoreSPI controller. + Say Y or M here if you want to use the "soft" controllers on + PolarFire SoC. + If built as a module, it will be called spi-microchip-core-spi. + config SPI_MT65XX tristate "MediaTek SPI controller" depends on ARCH_MEDIATEK || COMPILE_TEST @@ -871,6 +872,16 @@ config SPI_PL022 controller. If you have an embedded system with an AMBA(R) bus and a PL022 controller, say Y or M here. +config SPI_POLARFIRE_SOC + tristate "Microchip FPGA SPI controllers" + depends on SPI_MASTER + depends on ARCH_MICROCHIP || COMPILE_TEST + help + This enables the SPI driver for Microchip FPGA SPI controllers. + Say Y or M here if you want to use the "hard" controllers on + PolarFire SoC. + If built as a module, it will be called spi-mpfs. + config SPI_PPC4xx tristate "PPC4xx SPI Controller" depends on PPC32 && 4xx diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8ff74a13faaa..863b628ff1ec 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -86,8 +86,8 @@ obj-$(CONFIG_SPI_LOONGSON_PLATFORM) += spi-loongson-plat.o obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o -obj-$(CONFIG_SPI_MICROCHIP_CORE) += spi-microchip-core.o obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI) += spi-microchip-core-qspi.o +obj-$(CONFIG_SPI_MICROCHIP_CORE_SPI) += spi-microchip-core-spi.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o @@ -97,6 +97,7 @@ obj-$(CONFIG_SPI_MTK_NOR) += spi-mtk-nor.o obj-$(CONFIG_SPI_MTK_SNFI) += spi-mtk-snfi.o obj-$(CONFIG_SPI_MXIC) += spi-mxic.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o +obj-$(CONFIG_SPI_POLARFIRE_SOC) += spi-mpfs.o obj-$(CONFIG_SPI_WPCM_FIU) += spi-wpcm-fiu.o obj-$(CONFIG_SPI_NPCM_FIU) += spi-npcm-fiu.o obj-$(CONFIG_SPI_NPCM_PSPI) += spi-npcm-pspi.o diff --git a/drivers/spi/spi-airoha-snfi.c b/drivers/spi/spi-airoha-snfi.c index b78163eaed61..70327aebc26b 100644 --- a/drivers/spi/spi-airoha-snfi.c +++ b/drivers/spi/spi-airoha-snfi.c @@ -147,6 +147,8 @@ #define SPI_NFI_CUS_SEC_SIZE_EN BIT(16) #define REG_SPI_NFI_RD_CTL2 0x0510 +#define SPI_NFI_DATA_READ_CMD GENMASK(7, 0) + #define REG_SPI_NFI_RD_CTL3 0x0514 #define REG_SPI_NFI_PG_CTL1 0x0524 @@ -179,7 +181,9 @@ #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03 #define SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST 0x0b #define SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3b +#define SPI_NAND_OP_READ_FROM_CACHE_DUALIO 0xbb #define SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6b +#define SPI_NAND_OP_READ_FROM_CACHE_QUADIO 0xeb #define SPI_NAND_OP_WRITE_ENABLE 0x06 #define SPI_NAND_OP_WRITE_DISABLE 0x04 #define SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02 @@ -219,13 +223,6 @@ struct airoha_snand_ctrl { struct regmap *regmap_ctrl; struct regmap *regmap_nfi; struct clk *spi_clk; - - struct { - size_t page_size; - size_t sec_size; - u8 sec_num; - u8 spare_size; - } nfi_cfg; }; static int airoha_snand_set_fifo_op(struct airoha_snand_ctrl *as_ctrl, @@ -486,92 +483,6 @@ static int airoha_snand_nfi_init(struct airoha_snand_ctrl *as_ctrl) SPI_NFI_ALL_IRQ_EN, SPI_NFI_AHB_DONE_EN); } -static int airoha_snand_nfi_config(struct airoha_snand_ctrl *as_ctrl) -{ - int err; - u32 val; - - err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, - SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); - if (err) - return err; - - /* auto FDM */ - err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, - SPI_NFI_AUTO_FDM_EN); - if (err) - return err; - - /* HW ECC */ - err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, - SPI_NFI_HW_ECC_EN); - if (err) - return err; - - /* DMA Burst */ - err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, - SPI_NFI_DMA_BURST_EN); - if (err) - return err; - - /* page format */ - switch (as_ctrl->nfi_cfg.spare_size) { - case 26: - val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x1); - break; - case 27: - val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x2); - break; - case 28: - val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x3); - break; - default: - val = FIELD_PREP(SPI_NFI_SPARE_SIZE, 0x0); - break; - } - - err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT, - SPI_NFI_SPARE_SIZE, val); - if (err) - return err; - - switch (as_ctrl->nfi_cfg.page_size) { - case 2048: - val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x1); - break; - case 4096: - val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x2); - break; - default: - val = FIELD_PREP(SPI_NFI_PAGE_SIZE, 0x0); - break; - } - - err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_PAGEFMT, - SPI_NFI_PAGE_SIZE, val); - if (err) - return err; - - /* sec num */ - val = FIELD_PREP(SPI_NFI_SEC_NUM, as_ctrl->nfi_cfg.sec_num); - err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, - SPI_NFI_SEC_NUM, val); - if (err) - return err; - - /* enable cust sec size */ - err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, - SPI_NFI_CUS_SEC_SIZE_EN); - if (err) - return err; - - /* set cust sec size */ - val = FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, as_ctrl->nfi_cfg.sec_size); - return regmap_update_bits(as_ctrl->regmap_nfi, - REG_SPI_NFI_SECCUS_SIZE, - SPI_NFI_CUS_SEC_SIZE, val); -} - static bool airoha_snand_is_page_ops(const struct spi_mem_op *op) { if (op->addr.nbytes != 2) @@ -604,33 +515,6 @@ static bool airoha_snand_is_page_ops(const struct spi_mem_op *op) } } -static int airoha_snand_adjust_op_size(struct spi_mem *mem, - struct spi_mem_op *op) -{ - size_t max_len; - - if (airoha_snand_is_page_ops(op)) { - struct airoha_snand_ctrl *as_ctrl; - - as_ctrl = spi_controller_get_devdata(mem->spi->controller); - max_len = as_ctrl->nfi_cfg.sec_size; - max_len += as_ctrl->nfi_cfg.spare_size; - max_len *= as_ctrl->nfi_cfg.sec_num; - - if (op->data.nbytes > max_len) - op->data.nbytes = max_len; - } else { - max_len = 1 + op->addr.nbytes + op->dummy.nbytes; - if (max_len >= 160) - return -EOPNOTSUPP; - - if (op->data.nbytes > 160 - max_len) - op->data.nbytes = 160 - max_len; - } - - return 0; -} - static bool airoha_snand_supports_op(struct spi_mem *mem, const struct spi_mem_op *op) { @@ -671,32 +555,89 @@ static int airoha_snand_dirmap_create(struct spi_mem_dirmap_desc *desc) static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, void *buf) { - struct spi_mem_op *op = &desc->info.op_tmpl; struct spi_device *spi = desc->mem->spi; struct airoha_snand_ctrl *as_ctrl; u8 *txrx_buf = spi_get_ctldata(spi); dma_addr_t dma_addr; - u32 val, rd_mode; + u32 val, rd_mode, opcode; + size_t bytes; int err; - switch (op->cmd.opcode) { + as_ctrl = spi_controller_get_devdata(spi->controller); + + /* minimum oob size is 64 */ + bytes = round_up(offs + len, 64); + + /* + * DUALIO and QUADIO opcodes are not supported by the spi controller, + * replace them with supported opcodes. + */ + opcode = desc->info.op_tmpl.cmd.opcode; + switch (opcode) { + case SPI_NAND_OP_READ_FROM_CACHE_SINGLE: + case SPI_NAND_OP_READ_FROM_CACHE_SINGLE_FAST: + rd_mode = 0; + break; case SPI_NAND_OP_READ_FROM_CACHE_DUAL: + case SPI_NAND_OP_READ_FROM_CACHE_DUALIO: + opcode = SPI_NAND_OP_READ_FROM_CACHE_DUAL; rd_mode = 1; break; case SPI_NAND_OP_READ_FROM_CACHE_QUAD: + case SPI_NAND_OP_READ_FROM_CACHE_QUADIO: + opcode = SPI_NAND_OP_READ_FROM_CACHE_QUAD; rd_mode = 2; break; default: - rd_mode = 0; - break; + /* unknown opcode */ + return -EOPNOTSUPP; } - as_ctrl = spi_controller_get_devdata(spi->controller); err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); if (err < 0) return err; - err = airoha_snand_nfi_config(as_ctrl); + /* NFI reset */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); + if (err) + goto error_dma_mode_off; + + /* NFI configure: + * - No AutoFDM (custom sector size (SECCUS) register will be used) + * - No SoC's hardware ECC (flash internal ECC will be used) + * - Use burst mode (faster, but requires 16 byte alignment for addresses) + * - Setup for reading (SPI_NFI_READ_MODE) + * - Setup reading command: FIELD_PREP(SPI_NFI_OPMODE, 6) + * - Use DMA instead of PIO for data reading + */ + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_DMA_MODE | + SPI_NFI_READ_MODE | + SPI_NFI_DMA_BURST_EN | + SPI_NFI_HW_ECC_EN | + SPI_NFI_AUTO_FDM_EN | + SPI_NFI_OPMODE, + SPI_NFI_DMA_MODE | + SPI_NFI_READ_MODE | + SPI_NFI_DMA_BURST_EN | + FIELD_PREP(SPI_NFI_OPMODE, 6)); + if (err) + goto error_dma_mode_off; + + /* Set number of sector will be read */ + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_SEC_NUM, + FIELD_PREP(SPI_NFI_SEC_NUM, 1)); + if (err) + goto error_dma_mode_off; + + /* Set custom sector size */ + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE | + SPI_NFI_CUS_SEC_SIZE_EN, + FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) | + SPI_NFI_CUS_SEC_SIZE_EN); if (err) goto error_dma_mode_off; @@ -712,18 +653,24 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, if (err) goto error_dma_unmap; - /* set cust sec size */ - val = as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num; - val = FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, val); + /* + * Setup transfer length + * --------------------- + * The following rule MUST be met: + * transfer_length = + * = NFI_SNF_MISC_CTL2.read_data_byte_number = + * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size + */ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL2, - SPI_NFI_READ_DATA_BYTE_NUM, val); + SPI_NFI_READ_DATA_BYTE_NUM, + FIELD_PREP(SPI_NFI_READ_DATA_BYTE_NUM, bytes)); if (err) goto error_dma_unmap; /* set read command */ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_RD_CTL2, - op->cmd.opcode); + FIELD_PREP(SPI_NFI_DATA_READ_CMD, opcode)); if (err) goto error_dma_unmap; @@ -739,23 +686,11 @@ static ssize_t airoha_snand_dirmap_read(struct spi_mem_dirmap_desc *desc, if (err) goto error_dma_unmap; - /* set nfi read */ - err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, - SPI_NFI_OPMODE, - FIELD_PREP(SPI_NFI_OPMODE, 6)); - if (err) - goto error_dma_unmap; - - err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, - SPI_NFI_READ_MODE | SPI_NFI_DMA_MODE); - if (err) - goto error_dma_unmap; - err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x0); if (err) goto error_dma_unmap; - /* trigger dma start read */ + /* trigger dma reading */ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, SPI_NFI_RD_TRIG); if (err) @@ -813,59 +748,122 @@ error_dma_mode_off: static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, u64 offs, size_t len, const void *buf) { - struct spi_mem_op *op = &desc->info.op_tmpl; struct spi_device *spi = desc->mem->spi; u8 *txrx_buf = spi_get_ctldata(spi); struct airoha_snand_ctrl *as_ctrl; dma_addr_t dma_addr; - u32 wr_mode, val; + u32 wr_mode, val, opcode; + size_t bytes; int err; as_ctrl = spi_controller_get_devdata(spi->controller); - err = airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); - if (err < 0) - return err; + /* minimum oob size is 64 */ + bytes = round_up(offs + len, 64); + + opcode = desc->info.op_tmpl.cmd.opcode; + switch (opcode) { + case SPI_NAND_OP_PROGRAM_LOAD_SINGLE: + case SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE: + wr_mode = 0; + break; + case SPI_NAND_OP_PROGRAM_LOAD_QUAD: + case SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD: + wr_mode = 2; + break; + default: + /* unknown opcode */ + return -EOPNOTSUPP; + } + + if (offs > 0) + memset(txrx_buf, 0xff, offs); memcpy(txrx_buf + offs, buf, len); - dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE, - DMA_TO_DEVICE); - err = dma_mapping_error(as_ctrl->dev, dma_addr); - if (err) - return err; + if (bytes > offs + len) + memset(txrx_buf + offs + len, 0xff, bytes - offs - len); err = airoha_snand_set_mode(as_ctrl, SPI_MODE_DMA); if (err < 0) - goto error_dma_unmap; + return err; + + /* NFI reset */ + err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_FIFO_FLUSH | SPI_NFI_RST); + if (err) + goto error_dma_mode_off; - err = airoha_snand_nfi_config(as_ctrl); + /* + * NFI configure: + * - No AutoFDM (custom sector size (SECCUS) register will be used) + * - No SoC's hardware ECC (flash internal ECC will be used) + * - Use burst mode (faster, but requires 16 byte alignment for addresses) + * - Setup for writing (SPI_NFI_READ_MODE bit is cleared) + * - Setup writing command: FIELD_PREP(SPI_NFI_OPMODE, 3) + * - Use DMA instead of PIO for data writing + */ + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, + SPI_NFI_DMA_MODE | + SPI_NFI_READ_MODE | + SPI_NFI_DMA_BURST_EN | + SPI_NFI_HW_ECC_EN | + SPI_NFI_AUTO_FDM_EN | + SPI_NFI_OPMODE, + SPI_NFI_DMA_MODE | + SPI_NFI_DMA_BURST_EN | + FIELD_PREP(SPI_NFI_OPMODE, 3)); if (err) - goto error_dma_unmap; + goto error_dma_mode_off; - if (op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_QUAD || - op->cmd.opcode == SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD) - wr_mode = BIT(1); - else - wr_mode = 0; + /* Set number of sector will be written */ + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, + SPI_NFI_SEC_NUM, + FIELD_PREP(SPI_NFI_SEC_NUM, 1)); + if (err) + goto error_dma_mode_off; + + /* Set custom sector size */ + err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, + SPI_NFI_CUS_SEC_SIZE | + SPI_NFI_CUS_SEC_SIZE_EN, + FIELD_PREP(SPI_NFI_CUS_SEC_SIZE, bytes) | + SPI_NFI_CUS_SEC_SIZE_EN); + if (err) + goto error_dma_mode_off; + + dma_addr = dma_map_single(as_ctrl->dev, txrx_buf, SPI_NAND_CACHE_SIZE, + DMA_TO_DEVICE); + err = dma_mapping_error(as_ctrl->dev, dma_addr); + if (err) + goto error_dma_mode_off; + /* set dma addr */ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_STRADDR, dma_addr); if (err) goto error_dma_unmap; - val = FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, - as_ctrl->nfi_cfg.sec_size * as_ctrl->nfi_cfg.sec_num); + /* + * Setup transfer length + * --------------------- + * The following rule MUST be met: + * transfer_length = + * = NFI_SNF_MISC_CTL2.write_data_byte_number = + * = NFI_CON.sector_number * NFI_SECCUS.custom_sector_size + */ err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL2, - SPI_NFI_PROG_LOAD_BYTE_NUM, val); + SPI_NFI_PROG_LOAD_BYTE_NUM, + FIELD_PREP(SPI_NFI_PROG_LOAD_BYTE_NUM, bytes)); if (err) goto error_dma_unmap; + /* set write command */ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_PG_CTL1, - FIELD_PREP(SPI_NFI_PG_LOAD_CMD, - op->cmd.opcode)); + FIELD_PREP(SPI_NFI_PG_LOAD_CMD, opcode)); if (err) goto error_dma_unmap; + /* set write mode */ err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_SNF_MISC_CTL, FIELD_PREP(SPI_NFI_DATA_READ_WR_MODE, wr_mode)); if (err) @@ -877,26 +875,11 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, if (err) goto error_dma_unmap; - err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, - SPI_NFI_READ_MODE); - if (err) - goto error_dma_unmap; - - err = regmap_update_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, - SPI_NFI_OPMODE, - FIELD_PREP(SPI_NFI_OPMODE, 3)); - if (err) - goto error_dma_unmap; - - err = regmap_set_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CNFG, - SPI_NFI_DMA_MODE); - if (err) - goto error_dma_unmap; - err = regmap_write(as_ctrl->regmap_nfi, REG_SPI_NFI_CMD, 0x80); if (err) goto error_dma_unmap; + /* trigger dma writing */ err = regmap_clear_bits(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, SPI_NFI_WR_TRIG); if (err) @@ -941,6 +924,7 @@ static ssize_t airoha_snand_dirmap_write(struct spi_mem_dirmap_desc *desc, error_dma_unmap: dma_unmap_single(as_ctrl->dev, dma_addr, SPI_NAND_CACHE_SIZE, DMA_TO_DEVICE); +error_dma_mode_off: airoha_snand_set_mode(as_ctrl, SPI_MODE_MANUAL); return err; } @@ -1022,7 +1006,6 @@ static int airoha_snand_exec_op(struct spi_mem *mem, } static const struct spi_controller_mem_ops airoha_snand_mem_ops = { - .adjust_op_size = airoha_snand_adjust_op_size, .supports_op = airoha_snand_supports_op, .exec_op = airoha_snand_exec_op, .dirmap_create = airoha_snand_dirmap_create, @@ -1030,6 +1013,11 @@ static const struct spi_controller_mem_ops airoha_snand_mem_ops = { .dirmap_write = airoha_snand_dirmap_write, }; +static const struct spi_controller_mem_ops airoha_snand_nodma_mem_ops = { + .supports_op = airoha_snand_supports_op, + .exec_op = airoha_snand_exec_op, +}; + static int airoha_snand_setup(struct spi_device *spi) { struct airoha_snand_ctrl *as_ctrl; @@ -1047,36 +1035,6 @@ static int airoha_snand_setup(struct spi_device *spi) return 0; } -static int airoha_snand_nfi_setup(struct airoha_snand_ctrl *as_ctrl) -{ - u32 val, sec_size, sec_num; - int err; - - err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_CON, &val); - if (err) - return err; - - sec_num = FIELD_GET(SPI_NFI_SEC_NUM, val); - - err = regmap_read(as_ctrl->regmap_nfi, REG_SPI_NFI_SECCUS_SIZE, &val); - if (err) - return err; - - sec_size = FIELD_GET(SPI_NFI_CUS_SEC_SIZE, val); - - /* init default value */ - as_ctrl->nfi_cfg.sec_size = sec_size; - as_ctrl->nfi_cfg.sec_num = sec_num; - as_ctrl->nfi_cfg.page_size = round_down(sec_size * sec_num, 1024); - as_ctrl->nfi_cfg.spare_size = 16; - - err = airoha_snand_nfi_init(as_ctrl); - if (err) - return err; - - return airoha_snand_nfi_config(as_ctrl); -} - static const struct regmap_config spi_ctrl_regmap_config = { .name = "ctrl", .reg_bits = 32, @@ -1104,7 +1062,9 @@ static int airoha_snand_probe(struct platform_device *pdev) struct airoha_snand_ctrl *as_ctrl; struct device *dev = &pdev->dev; struct spi_controller *ctrl; + bool dma_enable = true; void __iomem *base; + u32 sfc_strap; int err; ctrl = devm_spi_alloc_host(dev, sizeof(*as_ctrl)); @@ -1139,18 +1099,34 @@ static int airoha_snand_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(as_ctrl->spi_clk), "unable to get spi clk\n"); + if (device_is_compatible(dev, "airoha,en7523-snand")) { + err = regmap_read(as_ctrl->regmap_ctrl, + REG_SPI_CTRL_SFC_STRAP, &sfc_strap); + if (err) + return err; + + if (!(sfc_strap & 0x04)) { + dma_enable = false; + dev_warn(dev, "Detected booting in RESERVED mode (UART_TXD was short to GND).\n"); + dev_warn(dev, "This mode is known for incorrect DMA reading of some flashes.\n"); + dev_warn(dev, "Much slower PIO mode will be used to prevent flash data damage.\n"); + dev_warn(dev, "Unplug UART cable and power cycle board to get full performance.\n"); + } + } + err = dma_set_mask(as_ctrl->dev, DMA_BIT_MASK(32)); if (err) return err; ctrl->num_chipselect = 2; - ctrl->mem_ops = &airoha_snand_mem_ops; + ctrl->mem_ops = dma_enable ? &airoha_snand_mem_ops + : &airoha_snand_nodma_mem_ops; ctrl->bits_per_word_mask = SPI_BPW_MASK(8); ctrl->mode_bits = SPI_RX_DUAL; ctrl->setup = airoha_snand_setup; device_set_node(&ctrl->dev, dev_fwnode(dev)); - err = airoha_snand_nfi_setup(as_ctrl); + err = airoha_snand_nfi_init(as_ctrl); if (err) return err; diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 62a11142bd63..db3e096f2eb0 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -7,6 +7,7 @@ */ #include <linux/clk.h> +#include <linux/io.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> @@ -67,6 +68,7 @@ struct aspeed_spi_chip { u32 ahb_window_size; u32 ctl_val[ASPEED_SPI_MAX]; u32 clk_freq; + bool force_user_mode; }; struct aspeed_spi_data { @@ -78,10 +80,14 @@ struct aspeed_spi_data { u32 timing; u32 hclk_mask; u32 hdiv_max; - - u32 (*segment_start)(struct aspeed_spi *aspi, u32 reg); - u32 (*segment_end)(struct aspeed_spi *aspi, u32 reg); - u32 (*segment_reg)(struct aspeed_spi *aspi, u32 start, u32 end); + u32 min_window_size; + + phys_addr_t (*segment_start)(struct aspeed_spi *aspi, u32 reg); + phys_addr_t (*segment_end)(struct aspeed_spi *aspi, u32 reg); + u32 (*segment_reg)(struct aspeed_spi *aspi, phys_addr_t start, + phys_addr_t end); + int (*adjust_window)(struct aspeed_spi *aspi); + u32 (*get_clk_div)(struct aspeed_spi_chip *chip, u32 hz); int (*calibrate)(struct aspeed_spi_chip *chip, u32 hdiv, const u8 *golden_buf, u8 *test_buf); }; @@ -92,9 +98,9 @@ struct aspeed_spi { const struct aspeed_spi_data *data; void __iomem *regs; - void __iomem *ahb_base; - u32 ahb_base_phy; + phys_addr_t ahb_base_phy; u32 ahb_window_size; + u32 num_cs; struct device *dev; struct clk *clk; @@ -258,11 +264,15 @@ static ssize_t aspeed_spi_write_user(struct aspeed_spi_chip *chip, const struct spi_mem_op *op) { int ret; + int io_mode = aspeed_spi_get_io_mode(op); aspeed_spi_start_user(chip); ret = aspeed_spi_send_cmd_addr(chip, op->addr.nbytes, op->addr.val, op->cmd.opcode); if (ret < 0) goto stop_user; + + aspeed_spi_set_io_mode(chip, io_mode); + aspeed_spi_write_to_ahb(chip->ahb_base, op->data.buf.out, op->data.nbytes); stop_user: aspeed_spi_stop_user(chip); @@ -376,89 +386,270 @@ static const char *aspeed_spi_get_name(struct spi_mem *mem) spi_get_chipselect(mem->spi, 0)); } -struct aspeed_spi_window { +static int aspeed_spi_set_window(struct aspeed_spi *aspi) +{ + struct device *dev = aspi->dev; + off_t offset = 0; + phys_addr_t start; + phys_addr_t end; + void __iomem *seg_reg_base = aspi->regs + CE0_SEGMENT_ADDR_REG; + void __iomem *seg_reg; + u32 seg_val_backup; + u32 seg_val; u32 cs; - u32 offset; - u32 size; -}; + size_t window_size; + + for (cs = 0; cs < aspi->data->max_cs; cs++) { + if (aspi->chips[cs].ahb_base) { + devm_iounmap(dev, aspi->chips[cs].ahb_base); + aspi->chips[cs].ahb_base = NULL; + } + } + + for (cs = 0; cs < aspi->data->max_cs; cs++) { + seg_reg = seg_reg_base + cs * 4; + seg_val_backup = readl(seg_reg); + + start = aspi->ahb_base_phy + offset; + window_size = aspi->chips[cs].ahb_window_size; + end = start + window_size; + + seg_val = aspi->data->segment_reg(aspi, start, end); + writel(seg_val, seg_reg); + + /* + * Restore initial value if something goes wrong or the segment + * register is written protected. + */ + if (seg_val != readl(seg_reg)) { + dev_warn(dev, "CE%d expected window [ 0x%.9llx - 0x%.9llx ] %zdMB\n", + cs, (u64)start, (u64)end - 1, window_size >> 20); + writel(seg_val_backup, seg_reg); + window_size = aspi->data->segment_end(aspi, seg_val_backup) - + aspi->data->segment_start(aspi, seg_val_backup); + aspi->chips[cs].ahb_window_size = window_size; + end = start + window_size; + } + + if (window_size != 0) + dev_dbg(dev, "CE%d window [ 0x%.9llx - 0x%.9llx ] %zdMB\n", + cs, (u64)start, (u64)end - 1, window_size >> 20); + else + dev_dbg(dev, "CE%d window closed\n", cs); + + offset += window_size; + if (offset > aspi->ahb_window_size) { + dev_err(dev, "CE%d offset value 0x%llx is too large.\n", + cs, (u64)offset); + return -ENOSPC; + } + + /* + * No need to map the address deocding range when + * - window size is 0. + * - the CS is unused. + */ + if (window_size == 0 || cs >= aspi->num_cs) + continue; + + aspi->chips[cs].ahb_base = + devm_ioremap(aspi->dev, start, window_size); + if (!aspi->chips[cs].ahb_base) { + dev_err(aspi->dev, + "Fail to remap window [0x%.9llx - 0x%.9llx]\n", + (u64)start, (u64)end - 1); + return -ENOMEM; + } + } + + return 0; +} + +static const struct aspeed_spi_data ast2500_spi_data; +static const struct aspeed_spi_data ast2600_spi_data; +static const struct aspeed_spi_data ast2600_fmc_data; -static void aspeed_spi_get_windows(struct aspeed_spi *aspi, - struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS]) +static int aspeed_spi_chip_set_default_window(struct aspeed_spi *aspi) { - const struct aspeed_spi_data *data = aspi->data; - u32 reg_val; u32 cs; - for (cs = 0; cs < aspi->data->max_cs; cs++) { - reg_val = readl(aspi->regs + CE0_SEGMENT_ADDR_REG + cs * 4); - windows[cs].cs = cs; - windows[cs].size = data->segment_end(aspi, reg_val) - - data->segment_start(aspi, reg_val); - windows[cs].offset = data->segment_start(aspi, reg_val) - aspi->ahb_base_phy; - dev_vdbg(aspi->dev, "CE%d offset=0x%.8x size=0x%x\n", cs, - windows[cs].offset, windows[cs].size); + /* No segment registers for the AST2400 SPI controller */ + if (aspi->data == &ast2400_spi_data) { + aspi->chips[0].ahb_base = devm_ioremap(aspi->dev, + aspi->ahb_base_phy, + aspi->ahb_window_size); + aspi->chips[0].ahb_window_size = aspi->ahb_window_size; + return 0; } + + /* Assign the minimum window size to each CS */ + for (cs = 0; cs < aspi->num_cs; cs++) { + aspi->chips[cs].ahb_window_size = aspi->data->min_window_size; + dev_dbg(aspi->dev, "CE%d default window [ 0x%.9llx - 0x%.9llx ]", + cs, (u64)(aspi->ahb_base_phy + aspi->data->min_window_size * cs), + (u64)(aspi->ahb_base_phy + aspi->data->min_window_size * cs - 1)); + } + + /* Close unused CS */ + for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++) + aspi->chips[cs].ahb_window_size = 0; + + if (aspi->data->adjust_window) + aspi->data->adjust_window(aspi); + + return aspeed_spi_set_window(aspi); } /* - * On the AST2600, some CE windows are closed by default at reset but - * U-Boot should open all. + * As the flash size grows up, we need to trim some decoding + * size if needed for the sake of conforming the maximum + * decoding size. We trim the decoding size from the rear CS + * to avoid affecting the default boot up sequence, usually, + * from CS0. Notice, if a CS decoding size is trimmed, + * command mode may not work perfectly on that CS, but it only + * affect performance and the debug function. */ -static int aspeed_spi_chip_set_default_window(struct aspeed_spi_chip *chip) +static int aspeed_spi_trim_window_size(struct aspeed_spi *aspi) { - struct aspeed_spi *aspi = chip->aspi; - struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] = { 0 }; - struct aspeed_spi_window *win = &windows[chip->cs]; + struct aspeed_spi_chip *chips = aspi->chips; + size_t total_sz; + int cs = aspi->data->max_cs - 1; + u32 i; + bool trimmed = false; + + do { + total_sz = 0; + for (i = 0; i < aspi->data->max_cs; i++) + total_sz += chips[i].ahb_window_size; + + if (cs < 0) + return -ENOMEM; + + if (chips[cs].ahb_window_size <= aspi->data->min_window_size) { + cs--; + continue; + } - /* No segment registers for the AST2400 SPI controller */ - if (aspi->data == &ast2400_spi_data) { - win->offset = 0; - win->size = aspi->ahb_window_size; - } else { - aspeed_spi_get_windows(aspi, windows); + if (total_sz > aspi->ahb_window_size) { + chips[cs].ahb_window_size -= + aspi->data->min_window_size; + total_sz -= aspi->data->min_window_size; + /* + * If the ahb window size is ever trimmed, only user + * mode can be adopted to access the whole flash. + */ + chips[cs].force_user_mode = true; + trimmed = true; + } + } while (total_sz > aspi->ahb_window_size); + + if (trimmed) { + dev_warn(aspi->dev, "Window size after trimming:\n"); + for (cs = 0; cs < aspi->data->max_cs; cs++) { + dev_warn(aspi->dev, "CE%d: 0x%08x\n", + cs, chips[cs].ahb_window_size); + } } - chip->ahb_base = aspi->ahb_base + win->offset; - chip->ahb_window_size = win->size; + return 0; +} + +static int aspeed_adjust_window_ast2400(struct aspeed_spi *aspi) +{ + int ret; + int cs; + struct aspeed_spi_chip *chips = aspi->chips; + + /* Close unused CS. */ + for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++) + chips[cs].ahb_window_size = 0; - dev_dbg(aspi->dev, "CE%d default window [ 0x%.8x - 0x%.8x ] %dMB", - chip->cs, aspi->ahb_base_phy + win->offset, - aspi->ahb_base_phy + win->offset + win->size - 1, - win->size >> 20); + ret = aspeed_spi_trim_window_size(aspi); + if (ret != 0) + return ret; - return chip->ahb_window_size ? 0 : -1; + return 0; } -static int aspeed_spi_set_window(struct aspeed_spi *aspi, - const struct aspeed_spi_window *win) +/* + * For AST2500, the minimum address decoding size for each CS + * is 8MB. This address decoding size is mandatory for each + * CS no matter whether it will be used. This is a HW limitation. + */ +static int aspeed_adjust_window_ast2500(struct aspeed_spi *aspi) { - u32 start = aspi->ahb_base_phy + win->offset; - u32 end = start + win->size; - void __iomem *seg_reg = aspi->regs + CE0_SEGMENT_ADDR_REG + win->cs * 4; - u32 seg_val_backup = readl(seg_reg); - u32 seg_val = aspi->data->segment_reg(aspi, start, end); + int ret; + int cs, i; + u32 cum_size, rem_size; + struct aspeed_spi_chip *chips = aspi->chips; + + /* Assign min_window_sz to unused CS. */ + for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++) { + if (chips[cs].ahb_window_size < aspi->data->min_window_size) + chips[cs].ahb_window_size = + aspi->data->min_window_size; + } - if (seg_val == seg_val_backup) - return 0; + /* + * If command mode or normal mode is used by dirmap read, the start + * address of a window should be multiple of its related flash size. + * Namely, the total windows size from flash 0 to flash N should + * be multiple of the size of flash (N + 1). + */ + for (cs = aspi->num_cs - 1; cs >= 0; cs--) { + cum_size = 0; + for (i = 0; i < cs; i++) + cum_size += chips[i].ahb_window_size; + + rem_size = cum_size % chips[cs].ahb_window_size; + if (chips[cs].ahb_window_size != 0 && rem_size != 0) + chips[0].ahb_window_size += + chips[cs].ahb_window_size - rem_size; + } + + ret = aspeed_spi_trim_window_size(aspi); + if (ret != 0) + return ret; + + /* The total window size of AST2500 SPI1 CS0 and CS1 must be 128MB */ + if (aspi->data == &ast2500_spi_data) + chips[1].ahb_window_size = + 0x08000000 - chips[0].ahb_window_size; + + return 0; +} + +static int aspeed_adjust_window_ast2600(struct aspeed_spi *aspi) +{ + int ret; + int cs, i; + u32 cum_size, rem_size; + struct aspeed_spi_chip *chips = aspi->chips; - writel(seg_val, seg_reg); + /* Close unused CS. */ + for (cs = aspi->num_cs; cs < aspi->data->max_cs; cs++) + chips[cs].ahb_window_size = 0; /* - * Restore initial value if something goes wrong else we could - * loose access to the chip. + * If command mode or normal mode is used by dirmap read, the start + * address of a window should be multiple of its related flash size. + * Namely, the total windows size from flash 0 to flash N should + * be multiple of the size of flash (N + 1). */ - if (seg_val != readl(seg_reg)) { - dev_err(aspi->dev, "CE%d invalid window [ 0x%.8x - 0x%.8x ] %dMB", - win->cs, start, end - 1, win->size >> 20); - writel(seg_val_backup, seg_reg); - return -EIO; + for (cs = aspi->num_cs - 1; cs >= 0; cs--) { + cum_size = 0; + for (i = 0; i < cs; i++) + cum_size += chips[i].ahb_window_size; + + rem_size = cum_size % chips[cs].ahb_window_size; + if (chips[cs].ahb_window_size != 0 && rem_size != 0) + chips[0].ahb_window_size += + chips[cs].ahb_window_size - rem_size; } - if (win->size) - dev_dbg(aspi->dev, "CE%d new window [ 0x%.8x - 0x%.8x ] %dMB", - win->cs, start, end - 1, win->size >> 20); - else - dev_dbg(aspi->dev, "CE%d window closed", win->cs); + ret = aspeed_spi_trim_window_size(aspi); + if (ret != 0) + return ret; return 0; } @@ -469,78 +660,27 @@ static int aspeed_spi_set_window(struct aspeed_spi *aspi, * - ioremap each window, not strictly necessary since the overall window * is correct. */ -static const struct aspeed_spi_data ast2500_spi_data; -static const struct aspeed_spi_data ast2600_spi_data; -static const struct aspeed_spi_data ast2600_fmc_data; - static int aspeed_spi_chip_adjust_window(struct aspeed_spi_chip *chip, u32 local_offset, u32 size) { struct aspeed_spi *aspi = chip->aspi; - struct aspeed_spi_window windows[ASPEED_SPI_MAX_NUM_CS] = { 0 }; - struct aspeed_spi_window *win = &windows[chip->cs]; int ret; /* No segment registers for the AST2400 SPI controller */ if (aspi->data == &ast2400_spi_data) return 0; - /* - * Due to an HW issue on the AST2500 SPI controller, the CE0 - * window size should be smaller than the maximum 128MB. - */ - if (aspi->data == &ast2500_spi_data && chip->cs == 0 && size == SZ_128M) { - size = 120 << 20; - dev_info(aspi->dev, "CE%d window resized to %dMB (AST2500 HW quirk)", - chip->cs, size >> 20); - } - - /* - * The decoding size of AST2600 SPI controller should set at - * least 2MB. - */ - if ((aspi->data == &ast2600_spi_data || aspi->data == &ast2600_fmc_data) && - size < SZ_2M) { - size = SZ_2M; - dev_info(aspi->dev, "CE%d window resized to %dMB (AST2600 Decoding)", - chip->cs, size >> 20); - } - - aspeed_spi_get_windows(aspi, windows); - /* Adjust this chip window */ - win->offset += local_offset; - win->size = size; + aspi->chips[chip->cs].ahb_window_size = size; - if (win->offset + win->size > aspi->ahb_window_size) { - win->size = aspi->ahb_window_size - win->offset; - dev_warn(aspi->dev, "CE%d window resized to %dMB", chip->cs, win->size >> 20); - } + /* Adjust the overall windows size regarding each platform */ + if (aspi->data->adjust_window) + aspi->data->adjust_window(aspi); - ret = aspeed_spi_set_window(aspi, win); + ret = aspeed_spi_set_window(aspi); if (ret) return ret; - /* Update chip mapping info */ - chip->ahb_base = aspi->ahb_base + win->offset; - chip->ahb_window_size = win->size; - - /* - * Also adjust next chip window to make sure that it does not - * overlap with the current window. - */ - if (chip->cs < aspi->data->max_cs - 1) { - struct aspeed_spi_window *next = &windows[chip->cs + 1]; - - /* Change offset and size to keep the same end address */ - if ((next->offset + next->size) > (win->offset + win->size)) - next->size = (next->offset + next->size) - (win->offset + win->size); - else - next->size = 0; - next->offset = win->offset + win->size; - - aspeed_spi_set_window(aspi, next); - } return 0; } @@ -619,7 +759,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, struct aspeed_spi_chip *chip = &aspi->chips[spi_get_chipselect(desc->mem->spi, 0)]; /* Switch to USER command mode if mapping window is too small */ - if (chip->ahb_window_size < offset + len) { + if (chip->ahb_window_size < offset + len || chip->force_user_mode) { int ret; ret = aspeed_spi_read_user(chip, &desc->info.op_tmpl, offset, len, buf); @@ -677,11 +817,6 @@ static int aspeed_spi_setup(struct spi_device *spi) if (data->hastype) aspeed_spi_chip_set_type(aspi, cs, CONFIG_TYPE_SPI); - if (aspeed_spi_chip_set_default_window(chip) < 0) { - dev_warn(aspi->dev, "CE%d window invalid", cs); - return -EINVAL; - } - aspeed_spi_chip_enable(aspi, cs, true); chip->ctl_val[ASPEED_SPI_BASE] = CTRL_CE_STOP_ACTIVE | CTRL_IO_MODE_USER; @@ -734,10 +869,10 @@ static int aspeed_spi_probe(struct platform_device *pdev) if (IS_ERR(aspi->regs)) return PTR_ERR(aspi->regs); - aspi->ahb_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res); - if (IS_ERR(aspi->ahb_base)) { - dev_err(dev, "missing AHB mapping window\n"); - return PTR_ERR(aspi->ahb_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(dev, "missing AHB memory\n"); + return -EINVAL; } aspi->ahb_window_size = resource_size(res); @@ -762,9 +897,17 @@ static int aspeed_spi_probe(struct platform_device *pdev) ctlr->mem_ops = &aspeed_spi_mem_ops; ctlr->setup = aspeed_spi_setup; ctlr->cleanup = aspeed_spi_cleanup; - ctlr->num_chipselect = data->max_cs; + ctlr->num_chipselect = of_get_available_child_count(dev->of_node); ctlr->dev.of_node = dev->of_node; + aspi->num_cs = ctlr->num_chipselect; + + ret = aspeed_spi_chip_set_default_window(aspi); + if (ret) { + dev_err(&pdev->dev, "fail to set default window\n"); + return ret; + } + ret = devm_spi_register_controller(dev, ctlr); if (ret) dev_err(&pdev->dev, "spi_register_controller failed\n"); @@ -788,17 +931,18 @@ static void aspeed_spi_remove(struct platform_device *pdev) * The address range is encoded with absolute addresses in the overall * mapping window. */ -static u32 aspeed_spi_segment_start(struct aspeed_spi *aspi, u32 reg) +static phys_addr_t aspeed_spi_segment_start(struct aspeed_spi *aspi, u32 reg) { return ((reg >> 16) & 0xFF) << 23; } -static u32 aspeed_spi_segment_end(struct aspeed_spi *aspi, u32 reg) +static phys_addr_t aspeed_spi_segment_end(struct aspeed_spi *aspi, u32 reg) { return ((reg >> 24) & 0xFF) << 23; } -static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi, u32 start, u32 end) +static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi, + phys_addr_t start, phys_addr_t end) { return (((start >> 23) & 0xFF) << 16) | (((end >> 23) & 0xFF) << 24); } @@ -810,16 +954,16 @@ static u32 aspeed_spi_segment_reg(struct aspeed_spi *aspi, u32 start, u32 end) #define AST2600_SEG_ADDR_MASK 0x0ff00000 -static u32 aspeed_spi_segment_ast2600_start(struct aspeed_spi *aspi, - u32 reg) +static phys_addr_t aspeed_spi_segment_ast2600_start(struct aspeed_spi *aspi, + u32 reg) { u32 start_offset = (reg << 16) & AST2600_SEG_ADDR_MASK; return aspi->ahb_base_phy + start_offset; } -static u32 aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi, - u32 reg) +static phys_addr_t aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi, + u32 reg) { u32 end_offset = reg & AST2600_SEG_ADDR_MASK; @@ -831,7 +975,7 @@ static u32 aspeed_spi_segment_ast2600_end(struct aspeed_spi *aspi, } static u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi, - u32 start, u32 end) + phys_addr_t start, phys_addr_t end) { /* disable zero size segments */ if (start == end) @@ -841,6 +985,41 @@ static u32 aspeed_spi_segment_ast2600_reg(struct aspeed_spi *aspi, ((end - 1) & AST2600_SEG_ADDR_MASK); } +/* The Segment Registers of the AST2700 use a 64KB unit. */ +#define AST2700_SEG_ADDR_MASK 0x7fff0000 + +static phys_addr_t aspeed_spi_segment_ast2700_start(struct aspeed_spi *aspi, + u32 reg) +{ + u64 start_offset = (reg << 16) & AST2700_SEG_ADDR_MASK; + + if (!start_offset) + return aspi->ahb_base_phy; + + return aspi->ahb_base_phy + start_offset; +} + +static phys_addr_t aspeed_spi_segment_ast2700_end(struct aspeed_spi *aspi, + u32 reg) +{ + u64 end_offset = reg & AST2700_SEG_ADDR_MASK; + + if (!end_offset) + return aspi->ahb_base_phy; + + return aspi->ahb_base_phy + end_offset; +} + +static u32 aspeed_spi_segment_ast2700_reg(struct aspeed_spi *aspi, + phys_addr_t start, phys_addr_t end) +{ + if (start == end) + return 0; + + return (u32)(((start & AST2700_SEG_ADDR_MASK) >> 16) | + (end & AST2700_SEG_ADDR_MASK)); +} + /* * Read timing compensation sequences */ @@ -942,26 +1121,149 @@ static bool aspeed_spi_check_calib_data(const u8 *test_buf, u32 size) } static const u32 aspeed_spi_hclk_divs[] = { - 0xf, /* HCLK */ - 0x7, /* HCLK/2 */ - 0xe, /* HCLK/3 */ - 0x6, /* HCLK/4 */ - 0xd, /* HCLK/5 */ + /* HCLK, HCLK/2, HCLK/3, HCLK/4, HCLK/5, ..., HCLK/16 */ + 0xf, 0x7, 0xe, 0x6, 0xd, + 0x5, 0xc, 0x4, 0xb, 0x3, + 0xa, 0x2, 0x9, 0x1, 0x8, + 0x0 }; #define ASPEED_SPI_HCLK_DIV(i) \ (aspeed_spi_hclk_divs[(i) - 1] << CTRL_FREQ_SEL_SHIFT) +/* Transfer maximum clock frequency to register setting */ +static u32 aspeed_get_clk_div_ast2400(struct aspeed_spi_chip *chip, + u32 max_hz) +{ + struct device *dev = chip->aspi->dev; + u32 hclk_clk = chip->aspi->clk_freq; + u32 div_ctl = 0; + u32 i; + bool found = false; + + /* FMC/SPIR10[11:8] */ + for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) { + if (hclk_clk / i <= max_hz) { + found = true; + break; + } + } + + if (found) { + div_ctl = ASPEED_SPI_HCLK_DIV(i); + chip->clk_freq = hclk_clk / i; + } + + dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", + found ? "yes" : "no", hclk_clk, max_hz); + + if (found) { + dev_dbg(dev, "h_div: 0x%08x, speed: %d\n", + div_ctl, chip->clk_freq); + } + + return div_ctl; +} + +static u32 aspeed_get_clk_div_ast2500(struct aspeed_spi_chip *chip, + u32 max_hz) +{ + struct device *dev = chip->aspi->dev; + u32 hclk_clk = chip->aspi->clk_freq; + u32 div_ctl = 0; + u32 i; + bool found = false; + + /* FMC/SPIR10[11:8] */ + for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) { + if (hclk_clk / i <= max_hz) { + found = true; + chip->clk_freq = hclk_clk / i; + break; + } + } + + if (found) { + div_ctl = ASPEED_SPI_HCLK_DIV(i); + goto end; + } + + for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) { + if (hclk_clk / (i * 4) <= max_hz) { + found = true; + chip->clk_freq = hclk_clk / (i * 4); + break; + } + } + + if (found) + div_ctl = BIT(13) | ASPEED_SPI_HCLK_DIV(i); + +end: + dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", + found ? "yes" : "no", hclk_clk, max_hz); + + if (found) { + dev_dbg(dev, "h_div: 0x%08x, speed: %d\n", + div_ctl, chip->clk_freq); + } + + return div_ctl; +} + +static u32 aspeed_get_clk_div_ast2600(struct aspeed_spi_chip *chip, + u32 max_hz) +{ + struct device *dev = chip->aspi->dev; + u32 hclk_clk = chip->aspi->clk_freq; + u32 div_ctl = 0; + u32 i, j; + bool found = false; + + /* FMC/SPIR10[27:24] */ + for (j = 0; j < 16; j++) { + /* FMC/SPIR10[11:8] */ + for (i = 1; i <= ARRAY_SIZE(aspeed_spi_hclk_divs); i++) { + if (j == 0 && i == 1) + continue; + + if (hclk_clk / (j * 16 + i) <= max_hz) { + found = true; + break; + } + } + + if (found) { + div_ctl = ((j << 24) | ASPEED_SPI_HCLK_DIV(i)); + chip->clk_freq = hclk_clk / (j * 16 + i); + break; + } + } + + dev_dbg(dev, "found: %s, hclk: %d, max_clk: %d\n", + found ? "yes" : "no", hclk_clk, max_hz); + + if (found) { + dev_dbg(dev, "h_div: 0x%08x, speed: %d\n", + div_ctl, chip->clk_freq); + } + + return div_ctl; +} + static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip) { struct aspeed_spi *aspi = chip->aspi; const struct aspeed_spi_data *data = aspi->data; u32 ahb_freq = aspi->clk_freq; u32 max_freq = chip->clk_freq; + bool exec_calib = false; + u32 best_freq = 0; u32 ctl_val; u8 *golden_buf = NULL; u8 *test_buf = NULL; - int i, rc, best_div = -1; + int i, rc; + u32 div_ctl; dev_dbg(aspi->dev, "calculate timing compensation - AHB freq: %d MHz", ahb_freq / 1000000); @@ -982,7 +1284,7 @@ static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip) memcpy_fromio(golden_buf, chip->ahb_base, CALIBRATE_BUF_SIZE); if (!aspeed_spi_check_calib_data(golden_buf, CALIBRATE_BUF_SIZE)) { dev_info(aspi->dev, "Calibration area too uniform, using low speed"); - goto no_calib; + goto end_calib; } #if defined(VERBOSE_DEBUG) @@ -991,7 +1293,7 @@ static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip) #endif /* Now we iterate the HCLK dividers until we find our breaking point */ - for (i = ARRAY_SIZE(aspeed_spi_hclk_divs); i > data->hdiv_max - 1; i--) { + for (i = 5; i > data->hdiv_max - 1; i--) { u32 tv, freq; freq = ahb_freq / i; @@ -1004,22 +1306,33 @@ static int aspeed_spi_do_calibration(struct aspeed_spi_chip *chip) dev_dbg(aspi->dev, "Trying HCLK/%d [%08x] ...", i, tv); rc = data->calibrate(chip, i, golden_buf, test_buf); if (rc == 0) - best_div = i; + best_freq = freq; + + exec_calib = true; } - /* Nothing found ? */ - if (best_div < 0) { - dev_warn(aspi->dev, "No good frequency, using dumb slow"); +end_calib: + if (!exec_calib) { + /* calibration process is not executed */ + dev_warn(aspi->dev, "Force to dts configuration %dkHz.\n", + max_freq / 1000); + div_ctl = data->get_clk_div(chip, max_freq); + } else if (best_freq == 0) { + /* calibration process is executed, but no good frequency */ + dev_warn(aspi->dev, "No good frequency, using dumb slow\n"); + div_ctl = 0; } else { - dev_dbg(aspi->dev, "Found good read timings at HCLK/%d", best_div); + dev_dbg(aspi->dev, "Found good read timings at %dMHz.\n", + best_freq / 1000000); + div_ctl = data->get_clk_div(chip, best_freq); + } - /* Record the freq */ - for (i = 0; i < ASPEED_SPI_MAX; i++) - chip->ctl_val[i] = (chip->ctl_val[i] & data->hclk_mask) | - ASPEED_SPI_HCLK_DIV(best_div); + /* Record the freq */ + for (i = 0; i < ASPEED_SPI_MAX; i++) { + chip->ctl_val[i] = (chip->ctl_val[i] & data->hclk_mask) | + div_ctl; } -no_calib: writel(chip->ctl_val[ASPEED_SPI_READ], chip->ctl); kfree(test_buf); return 0; @@ -1027,21 +1340,57 @@ no_calib: #define TIMING_DELAY_DI BIT(3) #define TIMING_DELAY_HCYCLE_MAX 5 +#define TIMING_DELAY_INPUT_MAX 16 #define TIMING_REG_AST2600(chip) \ ((chip)->aspi->regs + (chip)->aspi->data->timing + \ (chip)->cs * 4) +/* + * This function returns the center point of the longest + * continuous "pass" interval within the buffer. The interval + * must contains the highest number of consecutive "pass" + * results and not span across multiple rows. + */ +static u32 aspeed_spi_ast2600_optimized_timing(u32 rows, u32 cols, + u8 buf[rows][cols]) +{ + int r = 0, c = 0; + int max = 0; + int i, j; + + for (i = 0; i < rows; i++) { + for (j = 0; j < cols;) { + int k = j; + + while (k < cols && buf[i][k]) + k++; + + if (k - j > max) { + max = k - j; + r = i; + c = j + (k - j) / 2; + } + + j = k + 1; + } + } + + return max > 4 ? r * cols + c : 0; +} + static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv, const u8 *golden_buf, u8 *test_buf) { struct aspeed_spi *aspi = chip->aspi; int hcycle; + int delay_ns; u32 shift = (hdiv - 2) << 3; - u32 mask = ~(0xfu << shift); + u32 mask = ~(0xffu << shift); u32 fread_timing_val = 0; + u8 calib_res[6][17] = {0}; + u32 calib_point; for (hcycle = 0; hcycle <= TIMING_DELAY_HCYCLE_MAX; hcycle++) { - int delay_ns; bool pass = false; fread_timing_val &= mask; @@ -1054,14 +1403,14 @@ static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv, " * [%08x] %d HCLK delay, DI delay none : %s", fread_timing_val, hcycle, pass ? "PASS" : "FAIL"); if (pass) - return 0; + calib_res[hcycle][0] = 1; /* Add DI input delays */ fread_timing_val &= mask; fread_timing_val |= (TIMING_DELAY_DI | hcycle) << shift; - for (delay_ns = 0; delay_ns < 0x10; delay_ns++) { - fread_timing_val &= ~(0xf << (4 + shift)); + for (delay_ns = 0; delay_ns < TIMING_DELAY_INPUT_MAX; delay_ns++) { + fread_timing_val &= ~(0xfu << (4 + shift)); fread_timing_val |= delay_ns << (4 + shift); writel(fread_timing_val, TIMING_REG_AST2600(chip)); @@ -1070,18 +1419,28 @@ static int aspeed_spi_ast2600_calibrate(struct aspeed_spi_chip *chip, u32 hdiv, " * [%08x] %d HCLK delay, DI delay %d.%dns : %s", fread_timing_val, hcycle, (delay_ns + 1) / 2, (delay_ns + 1) & 1 ? 5 : 5, pass ? "PASS" : "FAIL"); - /* - * TODO: This is optimistic. We should look - * for a working interval and save the middle - * value in the read timing register. - */ + if (pass) - return 0; + calib_res[hcycle][delay_ns + 1] = 1; } } + calib_point = aspeed_spi_ast2600_optimized_timing(6, 17, calib_res); /* No good setting for this frequency */ - return -1; + if (calib_point == 0) + return -1; + + hcycle = calib_point / 17; + delay_ns = calib_point % 17; + + fread_timing_val = (TIMING_DELAY_DI | hcycle | (delay_ns << 4)) << shift; + + dev_dbg(aspi->dev, "timing val: %08x, final hcycle: %d, delay_ns: %d\n", + fread_timing_val, hcycle, delay_ns); + + writel(fread_timing_val, TIMING_REG_AST2600(chip)); + + return 0; } /* @@ -1095,10 +1454,13 @@ static const struct aspeed_spi_data ast2400_fmc_data = { .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xfffff0ff, .hdiv_max = 1, + .min_window_size = 0x800000, .calibrate = aspeed_spi_calibrate, + .get_clk_div = aspeed_get_clk_div_ast2400, .segment_start = aspeed_spi_segment_start, .segment_end = aspeed_spi_segment_end, .segment_reg = aspeed_spi_segment_reg, + .adjust_window = aspeed_adjust_window_ast2400, }; static const struct aspeed_spi_data ast2400_spi_data = { @@ -1109,6 +1471,7 @@ static const struct aspeed_spi_data ast2400_spi_data = { .timing = 0x14, .hclk_mask = 0xfffff0ff, .hdiv_max = 1, + .get_clk_div = aspeed_get_clk_div_ast2400, .calibrate = aspeed_spi_calibrate, /* No segment registers */ }; @@ -1121,10 +1484,13 @@ static const struct aspeed_spi_data ast2500_fmc_data = { .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xffffd0ff, .hdiv_max = 1, + .min_window_size = 0x800000, + .get_clk_div = aspeed_get_clk_div_ast2500, .calibrate = aspeed_spi_calibrate, .segment_start = aspeed_spi_segment_start, .segment_end = aspeed_spi_segment_end, .segment_reg = aspeed_spi_segment_reg, + .adjust_window = aspeed_adjust_window_ast2500, }; static const struct aspeed_spi_data ast2500_spi_data = { @@ -1135,10 +1501,13 @@ static const struct aspeed_spi_data ast2500_spi_data = { .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xffffd0ff, .hdiv_max = 1, + .min_window_size = 0x800000, + .get_clk_div = aspeed_get_clk_div_ast2500, .calibrate = aspeed_spi_calibrate, .segment_start = aspeed_spi_segment_start, .segment_end = aspeed_spi_segment_end, .segment_reg = aspeed_spi_segment_reg, + .adjust_window = aspeed_adjust_window_ast2500, }; static const struct aspeed_spi_data ast2600_fmc_data = { @@ -1150,10 +1519,13 @@ static const struct aspeed_spi_data ast2600_fmc_data = { .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xf0fff0ff, .hdiv_max = 2, + .min_window_size = 0x200000, + .get_clk_div = aspeed_get_clk_div_ast2600, .calibrate = aspeed_spi_ast2600_calibrate, .segment_start = aspeed_spi_segment_ast2600_start, .segment_end = aspeed_spi_segment_ast2600_end, .segment_reg = aspeed_spi_segment_ast2600_reg, + .adjust_window = aspeed_adjust_window_ast2600, }; static const struct aspeed_spi_data ast2600_spi_data = { @@ -1165,10 +1537,47 @@ static const struct aspeed_spi_data ast2600_spi_data = { .timing = CE0_TIMING_COMPENSATION_REG, .hclk_mask = 0xf0fff0ff, .hdiv_max = 2, + .min_window_size = 0x200000, + .get_clk_div = aspeed_get_clk_div_ast2600, .calibrate = aspeed_spi_ast2600_calibrate, .segment_start = aspeed_spi_segment_ast2600_start, .segment_end = aspeed_spi_segment_ast2600_end, .segment_reg = aspeed_spi_segment_ast2600_reg, + .adjust_window = aspeed_adjust_window_ast2600, +}; + +static const struct aspeed_spi_data ast2700_fmc_data = { + .max_cs = 3, + .hastype = false, + .mode_bits = SPI_RX_QUAD | SPI_TX_QUAD, + .we0 = 16, + .ctl0 = CE0_CTRL_REG, + .timing = CE0_TIMING_COMPENSATION_REG, + .hclk_mask = 0xf0fff0ff, + .hdiv_max = 2, + .min_window_size = 0x10000, + .get_clk_div = aspeed_get_clk_div_ast2600, + .calibrate = aspeed_spi_ast2600_calibrate, + .segment_start = aspeed_spi_segment_ast2700_start, + .segment_end = aspeed_spi_segment_ast2700_end, + .segment_reg = aspeed_spi_segment_ast2700_reg, +}; + +static const struct aspeed_spi_data ast2700_spi_data = { + .max_cs = 2, + .hastype = false, + .mode_bits = SPI_RX_QUAD | SPI_TX_QUAD, + .we0 = 16, + .ctl0 = CE0_CTRL_REG, + .timing = CE0_TIMING_COMPENSATION_REG, + .hclk_mask = 0xf0fff0ff, + .hdiv_max = 2, + .min_window_size = 0x10000, + .get_clk_div = aspeed_get_clk_div_ast2600, + .calibrate = aspeed_spi_ast2600_calibrate, + .segment_start = aspeed_spi_segment_ast2700_start, + .segment_end = aspeed_spi_segment_ast2700_end, + .segment_reg = aspeed_spi_segment_ast2700_reg, }; static const struct of_device_id aspeed_spi_matches[] = { @@ -1178,6 +1587,8 @@ static const struct of_device_id aspeed_spi_matches[] = { { .compatible = "aspeed,ast2500-spi", .data = &ast2500_spi_data }, { .compatible = "aspeed,ast2600-fmc", .data = &ast2600_fmc_data }, { .compatible = "aspeed,ast2600-spi", .data = &ast2600_spi_data }, + { .compatible = "aspeed,ast2700-fmc", .data = &ast2700_fmc_data }, + { .compatible = "aspeed,ast2700-spi", .data = &ast2700_spi_data }, { } }; MODULE_DEVICE_TABLE(of, aspeed_spi_matches); diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index 2e3c62f12bef..4c549f166b0f 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -582,8 +582,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) host->auto_runtime_pm = true; bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT]; bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH]; - bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]); - bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]); + bs->tx_io = bs->regs + bs->reg_offsets[SPI_MSG_DATA]; + bs->rx_io = bs->regs + bs->reg_offsets[SPI_RX_DATA]; /* Initialize hardware */ ret = clk_prepare_enable(bs->clk); diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 5ae09b21d23a..47054da630d0 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -109,6 +109,7 @@ * @rxbuf: Pointer to the RX buffer * @tx_bytes: Number of bytes left to transfer * @rx_bytes: Number of bytes requested + * @n_bytes: Number of bytes per word * @dev_busy: Device busy flag * @is_decoded_cs: Flag for decoder property set or not * @tx_fifo_depth: Depth of the TX FIFO @@ -120,16 +121,24 @@ struct cdns_spi { struct clk *pclk; unsigned int clk_rate; u32 speed_hz; - const u8 *txbuf; - u8 *rxbuf; + const void *txbuf; + void *rxbuf; int tx_bytes; int rx_bytes; + u8 n_bytes; u8 dev_busy; u32 is_decoded_cs; unsigned int tx_fifo_depth; struct reset_control *rstc; }; +enum cdns_spi_frame_n_bytes { + CDNS_SPI_N_BYTES_NULL = 0, + CDNS_SPI_N_BYTES_U8 = 1, + CDNS_SPI_N_BYTES_U16 = 2, + CDNS_SPI_N_BYTES_U32 = 4 +}; + /* Macros for the SPI controller read/write */ static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset) { @@ -305,6 +314,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi, return 0; } +static u8 cdns_spi_n_bytes(struct spi_transfer *transfer) +{ + if (transfer->bits_per_word <= 8) + return CDNS_SPI_N_BYTES_U8; + else if (transfer->bits_per_word <= 16) + return CDNS_SPI_N_BYTES_U16; + else + return CDNS_SPI_N_BYTES_U32; +} + +static inline void cdns_spi_reader(struct cdns_spi *xspi) +{ + u32 rxw = 0; + + if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) { + pr_err("%s: rxbuf address is not aligned for %d bytes\n", + __func__, xspi->n_bytes); + return; + } + + rxw = cdns_spi_read(xspi, CDNS_SPI_RXD); + if (xspi->rxbuf) { + switch (xspi->n_bytes) { + case CDNS_SPI_N_BYTES_U8: + *(u8 *)xspi->rxbuf = rxw; + break; + case CDNS_SPI_N_BYTES_U16: + *(u16 *)xspi->rxbuf = rxw; + break; + case CDNS_SPI_N_BYTES_U32: + *(u32 *)xspi->rxbuf = rxw; + break; + default: + pr_err("%s invalid n_bytes %d\n", __func__, + xspi->n_bytes); + return; + } + xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes; + } +} + +static inline void cdns_spi_writer(struct cdns_spi *xspi) +{ + u32 txw = 0; + + if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) { + pr_err("%s: txbuf address is not aligned for %d bytes\n", + __func__, xspi->n_bytes); + return; + } + + if (xspi->txbuf) { + switch (xspi->n_bytes) { + case CDNS_SPI_N_BYTES_U8: + txw = *(u8 *)xspi->txbuf; + break; + case CDNS_SPI_N_BYTES_U16: + txw = *(u16 *)xspi->txbuf; + break; + case CDNS_SPI_N_BYTES_U32: + txw = *(u32 *)xspi->txbuf; + break; + default: + pr_err("%s invalid n_bytes %d\n", __func__, + xspi->n_bytes); + return; + } + cdns_spi_write(xspi, CDNS_SPI_TXD, txw); + xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes; + } +} + /** * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO * @xspi: Pointer to the cdns_spi structure @@ -321,23 +402,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) while (ntx || nrx) { if (nrx) { - u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); - - if (xspi->rxbuf) - *xspi->rxbuf++ = data; - + cdns_spi_reader(xspi); nrx--; } if (ntx) { - if (xspi->txbuf) - cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++); - else - cdns_spi_write(xspi, CDNS_SPI_TXD, 0); - + cdns_spi_writer(xspi); ntx--; } - } } @@ -454,6 +526,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr, if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) udelay(10); + xspi->n_bytes = cdns_spi_n_bytes(transfer); + xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); + xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); + cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); @@ -654,6 +730,9 @@ static int cdns_spi_probe(struct platform_device *pdev) ctlr->mode_bits = SPI_CPOL | SPI_CPHA; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + if (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6")) + ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32); + if (!spi_controller_is_target(ctlr)) { ctlr->mode_bits |= SPI_CS_HIGH; ctlr->set_cs = cdns_spi_chipselect; @@ -797,6 +876,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = { static const struct of_device_id cdns_spi_of_match[] = { { .compatible = "xlnx,zynq-spi-r1p6" }, + { .compatible = "cix,sky1-spi-r1p6" }, { .compatible = "cdns,spi-r1p6" }, { /* end of table */ } }; diff --git a/drivers/spi/spi-ch341.c b/drivers/spi/spi-ch341.c index 46bc208f2d05..79d2f9ab4ef0 100644 --- a/drivers/spi/spi-ch341.c +++ b/drivers/spi/spi-ch341.c @@ -78,7 +78,7 @@ static int ch341_transfer_one(struct spi_controller *host, ch341->tx_buf[0] = CH341A_CMD_SPI_STREAM; - memcpy(ch341->tx_buf + 1, trans->tx_buf, len); + memcpy(ch341->tx_buf + 1, trans->tx_buf, len - 1); ret = usb_bulk_msg(ch341->udev, ch341->write_pipe, ch341->tx_buf, len, NULL, CH341_DEFAULT_TIMEOUT); diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c index 14307dd800b7..4b6b65f450a8 100644 --- a/drivers/spi/spi-cs42l43.c +++ b/drivers/spi/spi-cs42l43.c @@ -52,20 +52,6 @@ static struct spi_board_info amp_info_template = { .mode = SPI_MODE_0, }; -static const struct software_node cs42l43_gpiochip_swnode = { - .name = "cs42l43-pinctrl", -}; - -static const struct software_node_ref_args cs42l43_cs_refs[] = { - SOFTWARE_NODE_REFERENCE(&cs42l43_gpiochip_swnode, 0, GPIO_ACTIVE_LOW), - SOFTWARE_NODE_REFERENCE(&swnode_gpio_undefined), -}; - -static const struct property_entry cs42l43_cs_props[] = { - PROPERTY_ENTRY_REF_ARRAY("cs-gpios", cs42l43_cs_refs), - {} -}; - static int cs42l43_spi_tx(struct regmap *regmap, const u8 *buf, unsigned int len) { const u8 *end = buf + len; @@ -324,11 +310,6 @@ static void cs42l43_release_of_node(void *data) fwnode_handle_put(data); } -static void cs42l43_release_sw_node(void *data) -{ - software_node_unregister(&cs42l43_gpiochip_swnode); -} - static int cs42l43_spi_probe(struct platform_device *pdev) { struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent); @@ -391,6 +372,15 @@ static int cs42l43_spi_probe(struct platform_device *pdev) fwnode_property_read_u32(xu_fwnode, "01fa-sidecar-instances", &nsidecars); if (nsidecars) { + struct software_node_ref_args args[] = { + SOFTWARE_NODE_REFERENCE(fwnode, 0, GPIO_ACTIVE_LOW), + SOFTWARE_NODE_REFERENCE(&swnode_gpio_undefined), + }; + struct property_entry props[] = { + PROPERTY_ENTRY_REF_ARRAY("cs-gpios", args), + { } + }; + ret = fwnode_property_read_u32(xu_fwnode, "01fa-spk-id-val", &spkid); if (!ret) { dev_dbg(priv->dev, "01fa-spk-id-val = %d\n", spkid); @@ -403,17 +393,7 @@ static int cs42l43_spi_probe(struct platform_device *pdev) "Failed to get spk-id-gpios\n"); } - ret = software_node_register(&cs42l43_gpiochip_swnode); - if (ret) - return dev_err_probe(priv->dev, ret, - "Failed to register gpio swnode\n"); - - ret = devm_add_action_or_reset(priv->dev, cs42l43_release_sw_node, NULL); - if (ret) - return ret; - - ret = device_create_managed_software_node(&priv->ctlr->dev, - cs42l43_cs_props, NULL); + ret = device_create_managed_software_node(&priv->ctlr->dev, props, NULL); if (ret) return dev_err_probe(priv->dev, ret, "Failed to add swnode\n"); } else { diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index a29934422356..21a14e800eed 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -9,6 +9,7 @@ #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/delay.h> +#include <linux/platform_data/edma.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/clk.h> @@ -19,8 +20,6 @@ #include <linux/spi/spi_bitbang.h> #include <linux/slab.h> -#include <linux/platform_data/spi-davinci.h> - #define CS_DEFAULT 0xFF #define SPIFMT_PHASE_MASK BIT(16) @@ -98,8 +97,69 @@ #define SPIDEF 0x4c #define SPIFMT0 0x50 +#define SPI_IO_TYPE_POLL 1 +#define SPI_IO_TYPE_DMA 2 + #define DMA_MIN_BYTES 16 +enum { + SPI_VERSION_1, /* For DM355/DM365/DM6467 */ + SPI_VERSION_2, /* For DA8xx */ +}; + +/** + * struct davinci_spi_platform_data - Platform data for SPI master device on DaVinci + * + * @version: version of the SPI IP. Different DaVinci devices have slightly + * varying versions of the same IP. + * @num_chipselect: number of chipselects supported by this SPI master + * @intr_line: interrupt line used to connect the SPI IP to the ARM interrupt + * controller withn the SoC. Possible values are 0 and 1. + * @prescaler_limit: max clock prescaler value + * @cshold_bug: set this to true if the SPI controller on your chip requires + * a write to CSHOLD bit in between transfers (like in DM355). + * @dma_event_q: DMA event queue to use if SPI_IO_TYPE_DMA is used for any + * device on the bus. + */ +struct davinci_spi_platform_data { + u8 version; + u8 num_chipselect; + u8 intr_line; + u8 prescaler_limit; + bool cshold_bug; + enum dma_event_q dma_event_q; +}; + +/** + * struct davinci_spi_config - Per-chip-select configuration for SPI slave devices + * + * @wdelay: amount of delay between transmissions. Measured in number of + * SPI module clocks. + * @odd_parity: polarity of parity flag at the end of transmit data stream. + * 0 - odd parity, 1 - even parity. + * @parity_enable: enable transmission of parity at end of each transmit + * data stream. + * @io_type: type of IO transfer. Choose between polled, interrupt and DMA. + * @timer_disable: disable chip-select timers (setup and hold) + * @c2tdelay: chip-select setup time. Measured in number of SPI module clocks. + * @t2cdelay: chip-select hold time. Measured in number of SPI module clocks. + * @t2edelay: transmit data finished to SPI ENAn pin inactive time. Measured + * in number of SPI clocks. + * @c2edelay: chip-select active to SPI ENAn signal active time. Measured in + * number of SPI clocks. + */ +struct davinci_spi_config { + u8 wdelay; + u8 odd_parity; + u8 parity_enable; + u8 io_type; + u8 timer_disable; + u8 c2tdelay; + u8 t2cdelay; + u8 t2edelay; + u8 c2edelay; +}; + /* SPI Controller driver's private data. */ struct davinci_spi { struct spi_bitbang bitbang; diff --git a/drivers/spi/spi-dw-bt1.c b/drivers/spi/spi-dw-bt1.c index 4a5be813efa7..91642e05ac60 100644 --- a/drivers/spi/spi-dw-bt1.c +++ b/drivers/spi/spi-dw-bt1.c @@ -288,7 +288,7 @@ static int dw_spi_bt1_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - ret = dw_spi_add_host(&pdev->dev, dws); + ret = dw_spi_add_controller(&pdev->dev, dws); if (ret) { pm_runtime_disable(&pdev->dev); return ret; @@ -303,7 +303,7 @@ static void dw_spi_bt1_remove(struct platform_device *pdev) { struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev); - dw_spi_remove_host(&dwsbt1->dws); + dw_spi_remove_controller(&dwsbt1->dws); pm_runtime_disable(&pdev->dev); } diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index b3b883cb9541..9ebf244294f8 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -63,7 +63,7 @@ static void dw_spi_debugfs_init(struct dw_spi *dws) { char name[32]; - snprintf(name, 32, "dw_spi%d", dws->host->bus_num); + snprintf(name, 32, "dw_spi%d", dws->ctlr->bus_num); dws->debugfs = debugfs_create_dir(name, NULL); dws->regset.regs = dw_spi_dbgfs_regs; @@ -185,25 +185,25 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw) irq_status = dw_readl(dws, DW_SPI_ISR); if (irq_status & DW_SPI_INT_RXOI) { - dev_err(&dws->host->dev, "RX FIFO overflow detected\n"); + dev_err(&dws->ctlr->dev, "RX FIFO overflow detected\n"); ret = -EIO; } if (irq_status & DW_SPI_INT_RXUI) { - dev_err(&dws->host->dev, "RX FIFO underflow detected\n"); + dev_err(&dws->ctlr->dev, "RX FIFO underflow detected\n"); ret = -EIO; } if (irq_status & DW_SPI_INT_TXOI) { - dev_err(&dws->host->dev, "TX FIFO overflow detected\n"); + dev_err(&dws->ctlr->dev, "TX FIFO overflow detected\n"); ret = -EIO; } /* Generically handle the erroneous situation */ if (ret) { dw_spi_reset_chip(dws); - if (dws->host->cur_msg) - dws->host->cur_msg->status = ret; + if (dws->ctlr->cur_msg) + dws->ctlr->cur_msg->status = ret; } return ret; @@ -215,7 +215,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) u16 irq_status = dw_readl(dws, DW_SPI_ISR); if (dw_spi_check_status(dws, false)) { - spi_finalize_current_transfer(dws->host); + spi_finalize_current_transfer(dws->ctlr); return IRQ_HANDLED; } @@ -229,7 +229,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) dw_reader(dws); if (!dws->rx_len) { dw_spi_mask_intr(dws, 0xff); - spi_finalize_current_transfer(dws->host); + spi_finalize_current_transfer(dws->ctlr); } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); } @@ -250,14 +250,14 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) static irqreturn_t dw_spi_irq(int irq, void *dev_id) { - struct spi_controller *host = dev_id; - struct dw_spi *dws = spi_controller_get_devdata(host); + struct spi_controller *ctlr = dev_id; + struct dw_spi *dws = spi_controller_get_devdata(ctlr); u16 irq_status = dw_readl(dws, DW_SPI_ISR) & DW_SPI_INT_MASK; if (!irq_status) return IRQ_NONE; - if (!host->cur_msg) { + if (!ctlr->cur_msg) { dw_spi_mask_intr(dws, 0xff); return IRQ_HANDLED; } @@ -332,6 +332,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, dw_writel(dws, DW_SPI_CTRLR0, cr0); + if (spi_controller_is_target(dws->ctlr)) + return; + if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD || cfg->tmode == DW_SPI_CTRLR0_TMOD_RO) dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0); @@ -410,11 +413,11 @@ static int dw_spi_poll_transfer(struct dw_spi *dws, return 0; } -static int dw_spi_transfer_one(struct spi_controller *host, +static int dw_spi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *transfer) { - struct dw_spi *dws = spi_controller_get_devdata(host); + struct dw_spi *dws = spi_controller_get_devdata(ctlr); struct dw_spi_cfg cfg = { .tmode = DW_SPI_CTRLR0_TMOD_TR, .dfs = transfer->bits_per_word, @@ -439,7 +442,7 @@ static int dw_spi_transfer_one(struct spi_controller *host, transfer->effective_speed_hz = dws->current_freq; /* Check if current transfer is a DMA transaction */ - dws->dma_mapped = spi_xfer_is_dma_mapped(host, spi, transfer); + dws->dma_mapped = spi_xfer_is_dma_mapped(ctlr, spi, transfer); /* For poll mode just disable all interrupts */ dw_spi_mask_intr(dws, 0xff); @@ -462,10 +465,9 @@ static int dw_spi_transfer_one(struct spi_controller *host, return 1; } -static void dw_spi_handle_err(struct spi_controller *host, - struct spi_message *msg) +static inline void dw_spi_abort(struct spi_controller *ctlr) { - struct dw_spi *dws = spi_controller_get_devdata(host); + struct dw_spi *dws = spi_controller_get_devdata(ctlr); if (dws->dma_mapped) dws->dma_ops->dma_stop(dws); @@ -473,6 +475,19 @@ static void dw_spi_handle_err(struct spi_controller *host, dw_spi_reset_chip(dws); } +static void dw_spi_handle_err(struct spi_controller *ctlr, + struct spi_message *msg) +{ + dw_spi_abort(ctlr); +} + +static int dw_spi_target_abort(struct spi_controller *ctlr) +{ + dw_spi_abort(ctlr); + + return 0; +} + static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op) { if (op->data.dir == SPI_MEM_DATA_IN) @@ -574,7 +589,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) while (len) { entries = readl_relaxed(dws->regs + DW_SPI_TXFLR); if (!entries) { - dev_err(&dws->host->dev, "CS de-assertion on Tx\n"); + dev_err(&dws->ctlr->dev, "CS de-assertion on Tx\n"); return -EIO; } room = min(dws->fifo_len - entries, len); @@ -594,7 +609,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) if (!entries) { sts = readl_relaxed(dws->regs + DW_SPI_RISR); if (sts & DW_SPI_INT_RXOI) { - dev_err(&dws->host->dev, "FIFO overflow on Rx\n"); + dev_err(&dws->ctlr->dev, "FIFO overflow on Rx\n"); return -EIO; } continue; @@ -635,7 +650,7 @@ static int dw_spi_wait_mem_op_done(struct dw_spi *dws) spi_delay_exec(&delay, NULL); if (retry < 0) { - dev_err(&dws->host->dev, "Mem op hanged up\n"); + dev_err(&dws->ctlr->dev, "Mem op hanged up\n"); return -EIO; } @@ -834,18 +849,23 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws) DW_SPI_GET_BYTE(dws->ver, 1)); } - /* - * Try to detect the number of native chip-selects if the platform - * driver didn't set it up. There can be up to 16 lines configured. - */ - if (!dws->num_cs) { - u32 ser; + if (spi_controller_is_target(dws->ctlr)) { + /* There is only one CS input signal in target mode */ + dws->num_cs = 1; + } else { + /* + * Try to detect the number of native chip-selects if the platform + * driver didn't set it up. There can be up to 16 lines configured. + */ + if (!dws->num_cs) { + u32 ser; - dw_writel(dws, DW_SPI_SER, 0xffff); - ser = dw_readl(dws, DW_SPI_SER); - dw_writel(dws, DW_SPI_SER, 0); + dw_writel(dws, DW_SPI_SER, 0xffff); + ser = dw_readl(dws, DW_SPI_SER); + dw_writel(dws, DW_SPI_SER, 0); - dws->num_cs = hweight16(ser); + dws->num_cs = hweight16(ser); + } } /* @@ -898,60 +918,72 @@ static const struct spi_controller_mem_caps dw_spi_mem_caps = { .per_op_freq = true, }; -int dw_spi_add_host(struct device *dev, struct dw_spi *dws) +int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) { - struct spi_controller *host; + struct spi_controller *ctlr; + bool target; int ret; if (!dws) return -EINVAL; - host = spi_alloc_host(dev, 0); - if (!host) + target = device_property_read_bool(dev, "spi-slave"); + if (target) + ctlr = spi_alloc_target(dev, 0); + else + ctlr = spi_alloc_host(dev, 0); + + if (!ctlr) return -ENOMEM; - device_set_node(&host->dev, dev_fwnode(dev)); + device_set_node(&ctlr->dev, dev_fwnode(dev)); - dws->host = host; + dws->ctlr = ctlr; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); - spi_controller_set_devdata(host, dws); + spi_controller_set_devdata(ctlr, dws); /* Basic HW init */ dw_spi_hw_init(dev, dws); ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), - host); + ctlr); if (ret < 0 && ret != -ENOTCONN) { dev_err(dev, "can not get IRQ\n"); - goto err_free_host; + goto err_free_ctlr; } dw_spi_init_mem_ops(dws); - host->use_gpio_descriptors = true; - host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA; if (dws->caps & DW_SPI_CAP_DFS32) - host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); - else - host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); - host->bus_num = dws->bus_num; - host->num_chipselect = dws->num_cs; - host->setup = dw_spi_setup; - host->cleanup = dw_spi_cleanup; - if (dws->set_cs) - host->set_cs = dws->set_cs; + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); else - host->set_cs = dw_spi_set_cs; - host->transfer_one = dw_spi_transfer_one; - host->handle_err = dw_spi_handle_err; - if (dws->mem_ops.exec_op) { - host->mem_ops = &dws->mem_ops; - host->mem_caps = &dw_spi_mem_caps; + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + ctlr->bus_num = dws->bus_num; + ctlr->num_chipselect = dws->num_cs; + ctlr->setup = dw_spi_setup; + ctlr->cleanup = dw_spi_cleanup; + ctlr->transfer_one = dw_spi_transfer_one; + ctlr->handle_err = dw_spi_handle_err; + ctlr->auto_runtime_pm = true; + + if (!target) { + ctlr->use_gpio_descriptors = true; + ctlr->mode_bits |= SPI_LOOP; + if (dws->set_cs) + ctlr->set_cs = dws->set_cs; + else + ctlr->set_cs = dw_spi_set_cs; + if (dws->mem_ops.exec_op) { + ctlr->mem_ops = &dws->mem_ops; + ctlr->mem_caps = &dw_spi_mem_caps; + } + ctlr->max_speed_hz = dws->max_freq; + ctlr->flags = SPI_CONTROLLER_GPIO_SS; + } else { + ctlr->target_abort = dw_spi_target_abort; } - host->max_speed_hz = dws->max_freq; - host->flags = SPI_CONTROLLER_GPIO_SS; - host->auto_runtime_pm = true; /* Get default rx sample delay */ device_property_read_u32(dev, "rx-sample-delay-ns", @@ -964,14 +996,14 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) } else if (ret) { dev_warn(dev, "DMA init failed\n"); } else { - host->can_dma = dws->dma_ops->can_dma; - host->flags |= SPI_CONTROLLER_MUST_TX; + ctlr->can_dma = dws->dma_ops->can_dma; + ctlr->flags |= SPI_CONTROLLER_MUST_TX; } } - ret = spi_register_controller(host); + ret = spi_register_controller(ctlr); if (ret) { - dev_err_probe(dev, ret, "problem registering spi host\n"); + dev_err_probe(dev, ret, "problem registering spi controller\n"); goto err_dma_exit; } @@ -983,47 +1015,47 @@ err_dma_exit: dws->dma_ops->dma_exit(dws); dw_spi_enable_chip(dws, 0); err_free_irq: - free_irq(dws->irq, host); -err_free_host: - spi_controller_put(host); + free_irq(dws->irq, ctlr); +err_free_ctlr: + spi_controller_put(ctlr); return ret; } -EXPORT_SYMBOL_NS_GPL(dw_spi_add_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_add_controller, "SPI_DW_CORE"); -void dw_spi_remove_host(struct dw_spi *dws) +void dw_spi_remove_controller(struct dw_spi *dws) { dw_spi_debugfs_remove(dws); - spi_unregister_controller(dws->host); + spi_unregister_controller(dws->ctlr); if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); dw_spi_shutdown_chip(dws); - free_irq(dws->irq, dws->host); + free_irq(dws->irq, dws->ctlr); } -EXPORT_SYMBOL_NS_GPL(dw_spi_remove_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_remove_controller, "SPI_DW_CORE"); -int dw_spi_suspend_host(struct dw_spi *dws) +int dw_spi_suspend_controller(struct dw_spi *dws) { int ret; - ret = spi_controller_suspend(dws->host); + ret = spi_controller_suspend(dws->ctlr); if (ret) return ret; dw_spi_shutdown_chip(dws); return 0; } -EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_controller, "SPI_DW_CORE"); -int dw_spi_resume_host(struct dw_spi *dws) +int dw_spi_resume_controller(struct dw_spi *dws) { - dw_spi_hw_init(&dws->host->dev, dws); - return spi_controller_resume(dws->host); + dw_spi_hw_init(&dws->ctlr->dev, dws); + return spi_controller_resume(dws->ctlr); } -EXPORT_SYMBOL_NS_GPL(dw_spi_resume_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_resume_controller, "SPI_DW_CORE"); MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>"); MODULE_DESCRIPTION("Driver for DesignWare SPI controller core"); diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c index b5bed02b7e50..65adec7c7524 100644 --- a/drivers/spi/spi-dw-dma.c +++ b/drivers/spi/spi-dw-dma.c @@ -139,8 +139,8 @@ static int dw_spi_dma_init_mfld(struct device *dev, struct dw_spi *dws) if (!dws->txchan) goto free_rxchan; - dws->host->dma_rx = dws->rxchan; - dws->host->dma_tx = dws->txchan; + dws->ctlr->dma_rx = dws->rxchan; + dws->ctlr->dma_tx = dws->txchan; init_completion(&dws->dma_completion); @@ -183,8 +183,8 @@ static int dw_spi_dma_init_generic(struct device *dev, struct dw_spi *dws) goto free_rxchan; } - dws->host->dma_rx = dws->rxchan; - dws->host->dma_tx = dws->txchan; + dws->ctlr->dma_rx = dws->rxchan; + dws->ctlr->dma_tx = dws->txchan; init_completion(&dws->dma_completion); @@ -242,10 +242,10 @@ static enum dma_slave_buswidth dw_spi_dma_convert_width(u8 n_bytes) } } -static bool dw_spi_can_dma(struct spi_controller *host, +static bool dw_spi_can_dma(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer) { - struct dw_spi *dws = spi_controller_get_devdata(host); + struct dw_spi *dws = spi_controller_get_devdata(ctlr); enum dma_slave_buswidth dma_bus_width; if (xfer->len <= dws->fifo_len) @@ -271,7 +271,7 @@ static int dw_spi_dma_wait(struct dw_spi *dws, unsigned int len, u32 speed) msecs_to_jiffies(ms)); if (ms == 0) { - dev_err(&dws->host->cur_msg->spi->dev, + dev_err(&dws->ctlr->cur_msg->spi->dev, "DMA transaction timed out\n"); return -ETIMEDOUT; } @@ -299,7 +299,7 @@ static int dw_spi_dma_wait_tx_done(struct dw_spi *dws, spi_delay_exec(&delay, xfer); if (retry < 0) { - dev_err(&dws->host->dev, "Tx hanged up\n"); + dev_err(&dws->ctlr->dev, "Tx hanged up\n"); return -EIO; } @@ -400,7 +400,7 @@ static int dw_spi_dma_wait_rx_done(struct dw_spi *dws) spi_delay_exec(&delay, NULL); if (retry < 0) { - dev_err(&dws->host->dev, "Rx hanged up\n"); + dev_err(&dws->ctlr->dev, "Rx hanged up\n"); return -EIO; } @@ -656,13 +656,13 @@ static int dw_spi_dma_transfer(struct dw_spi *dws, struct spi_transfer *xfer) if (ret) return ret; - if (dws->host->cur_msg->status == -EINPROGRESS) { + if (dws->ctlr->cur_msg->status == -EINPROGRESS) { ret = dw_spi_dma_wait_tx_done(dws, xfer); if (ret) return ret; } - if (xfer->rx_buf && dws->host->cur_msg->status == -EINPROGRESS) + if (xfer->rx_buf && dws->ctlr->cur_msg->status == -EINPROGRESS) ret = dw_spi_dma_wait_rx_done(dws); return ret; diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 7a5197586919..33239b4778cb 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -321,11 +321,6 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) struct dw_spi *dws; int ret; - if (device_property_read_bool(&pdev->dev, "spi-slave")) { - dev_warn(&pdev->dev, "spi-slave is not yet supported\n"); - return -ENODEV; - } - dwsmmio = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_mmio), GFP_KERNEL); if (!dwsmmio) @@ -382,7 +377,7 @@ static int dw_spi_mmio_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - ret = dw_spi_add_host(&pdev->dev, dws); + ret = dw_spi_add_controller(&pdev->dev, dws); if (ret) goto out; @@ -401,7 +396,7 @@ static void dw_spi_mmio_remove(struct platform_device *pdev) { struct dw_spi_mmio *dwsmmio = platform_get_drvdata(pdev); - dw_spi_remove_host(&dwsmmio->dws); + dw_spi_remove_controller(&dwsmmio->dws); pm_runtime_disable(&pdev->dev); reset_control_assert(dwsmmio->rstc); } diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index b32d6648a32e..72d9f5bc87f7 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -127,7 +127,7 @@ static int dw_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *en goto err_free_irq_vectors; } - ret = dw_spi_add_host(&pdev->dev, dws); + ret = dw_spi_add_controller(&pdev->dev, dws); if (ret) goto err_free_irq_vectors; @@ -156,7 +156,7 @@ static void dw_spi_pci_remove(struct pci_dev *pdev) pm_runtime_forbid(&pdev->dev); pm_runtime_get_noresume(&pdev->dev); - dw_spi_remove_host(dws); + dw_spi_remove_controller(dws); pci_free_irq_vectors(pdev); } @@ -165,14 +165,14 @@ static int dw_spi_pci_suspend(struct device *dev) { struct dw_spi *dws = dev_get_drvdata(dev); - return dw_spi_suspend_host(dws); + return dw_spi_suspend_controller(dws); } static int dw_spi_pci_resume(struct device *dev) { struct dw_spi *dws = dev_get_drvdata(dev); - return dw_spi_resume_host(dws); + return dw_spi_resume_controller(dws); } #endif diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index fc267c6437ae..9cc79c566a70 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -142,14 +142,14 @@ struct dw_spi_dma_ops { int (*dma_init)(struct device *dev, struct dw_spi *dws); void (*dma_exit)(struct dw_spi *dws); int (*dma_setup)(struct dw_spi *dws, struct spi_transfer *xfer); - bool (*can_dma)(struct spi_controller *host, struct spi_device *spi, + bool (*can_dma)(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *xfer); int (*dma_transfer)(struct dw_spi *dws, struct spi_transfer *xfer); void (*dma_stop)(struct dw_spi *dws); }; struct dw_spi { - struct spi_controller *host; + struct spi_controller *ctlr; u32 ip; /* Synopsys DW SSI IP-core ID */ u32 ver; /* Synopsys component version */ @@ -288,10 +288,10 @@ extern void dw_spi_set_cs(struct spi_device *spi, bool enable); extern void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, struct dw_spi_cfg *cfg); extern int dw_spi_check_status(struct dw_spi *dws, bool raw); -extern int dw_spi_add_host(struct device *dev, struct dw_spi *dws); -extern void dw_spi_remove_host(struct dw_spi *dws); -extern int dw_spi_suspend_host(struct dw_spi *dws); -extern int dw_spi_resume_host(struct dw_spi *dws); +extern int dw_spi_add_controller(struct device *dev, struct dw_spi *dws); +extern void dw_spi_remove_controller(struct dw_spi *dws); +extern int dw_spi_suspend_controller(struct dw_spi *dws); +extern int dw_spi_resume_controller(struct dw_spi *dws); #ifdef CONFIG_SPI_DW_DMA diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c index c887abb028d7..a223b4bc6e63 100644 --- a/drivers/spi/spi-fsl-qspi.c +++ b/drivers/spi/spi-fsl-qspi.c @@ -36,6 +36,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_qos.h> +#include <linux/reset.h> #include <linux/sizes.h> #include <linux/spi/spi.h> @@ -196,11 +197,17 @@ */ #define QUADSPI_QUIRK_USE_TDH_SETTING BIT(5) +/* + * Do not disable the "qspi" clock when changing its rate. + */ +#define QUADSPI_QUIRK_SKIP_CLK_DISABLE BIT(6) + struct fsl_qspi_devtype_data { unsigned int rxfifo; unsigned int txfifo; int invalid_mstrid; unsigned int ahb_buf_size; + unsigned int sfa_size; unsigned int quirks; bool little_endian; }; @@ -261,12 +268,23 @@ static const struct fsl_qspi_devtype_data ls2080a_data = { .little_endian = true, }; +static const struct fsl_qspi_devtype_data spacemit_k1_data = { + .rxfifo = SZ_128, + .txfifo = SZ_256, + .ahb_buf_size = SZ_512, + .sfa_size = SZ_1K, + .invalid_mstrid = QUADSPI_BUFXCR_INVALID_MSTRID, + .quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_SKIP_CLK_DISABLE, + .little_endian = true, +}; + struct fsl_qspi { void __iomem *iobase; void __iomem *ahb_addr; const struct fsl_qspi_devtype_data *devtype_data; struct mutex lock; struct completion c; + struct reset_control *resets; struct clk *clk, *clk_en; struct pm_qos_request pm_qos_req; struct device *dev; @@ -274,34 +292,39 @@ struct fsl_qspi { u32 memmap_phy; }; -static inline int needs_swap_endian(struct fsl_qspi *q) +static bool needs_swap_endian(struct fsl_qspi *q) { - return q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN; + return !!(q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN); } -static inline int needs_4x_clock(struct fsl_qspi *q) +static bool needs_4x_clock(struct fsl_qspi *q) { - return q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK; + return !!(q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK); } -static inline int needs_fill_txfifo(struct fsl_qspi *q) +static bool needs_fill_txfifo(struct fsl_qspi *q) { - return q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890; + return !!(q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890); } -static inline int needs_wakeup_wait_mode(struct fsl_qspi *q) +static bool needs_wakeup_wait_mode(struct fsl_qspi *q) { - return q->devtype_data->quirks & QUADSPI_QUIRK_TKT245618; + return !!(q->devtype_data->quirks & QUADSPI_QUIRK_TKT245618); } -static inline int needs_amba_base_offset(struct fsl_qspi *q) +static bool needs_amba_base_offset(struct fsl_qspi *q) { return !(q->devtype_data->quirks & QUADSPI_QUIRK_BASE_INTERNAL); } -static inline int needs_tdh_setting(struct fsl_qspi *q) +static bool needs_tdh_setting(struct fsl_qspi *q) +{ + return !!(q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING); +} + +static bool needs_clk_disable(struct fsl_qspi *q) { - return q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING; + return !(q->devtype_data->quirks & QUADSPI_QUIRK_SKIP_CLK_DISABLE); } /* @@ -534,15 +557,18 @@ static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi, if (needs_4x_clock(q)) rate *= 4; - fsl_qspi_clk_disable_unprep(q); + if (needs_clk_disable(q)) + fsl_qspi_clk_disable_unprep(q); ret = clk_set_rate(q->clk, rate); if (ret) return; - ret = fsl_qspi_clk_prep_enable(q); - if (ret) - return; + if (needs_clk_disable(q)) { + ret = fsl_qspi_clk_prep_enable(q); + if (ret) + return; + } q->selected = spi_get_chipselect(spi, 0); @@ -722,6 +748,7 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q) { void __iomem *base = q->iobase; u32 reg, addr_offset = 0; + u32 sfa_size; int ret; /* disable and unprepare clock to avoid glitch pass to controller */ @@ -780,17 +807,17 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q) * In HW there can be a maximum of four chips on two buses with * two chip selects on each bus. We use four chip selects in SW * to differentiate between the four chips. - * We use ahb_buf_size for each chip and set SFA1AD, SFA2AD, SFB1AD, - * SFB2AD accordingly. + * + * By default we write the AHB buffer size to each chip, but + * a different size can be specified with devtype_data->sfa_size. + * The SFA1AD, SFA2AD, SFB1AD, and SFB2AD registers define the + * top (end) of these four regions. */ - qspi_writel(q, q->devtype_data->ahb_buf_size + addr_offset, - base + QUADSPI_SFA1AD); - qspi_writel(q, q->devtype_data->ahb_buf_size * 2 + addr_offset, - base + QUADSPI_SFA2AD); - qspi_writel(q, q->devtype_data->ahb_buf_size * 3 + addr_offset, - base + QUADSPI_SFB1AD); - qspi_writel(q, q->devtype_data->ahb_buf_size * 4 + addr_offset, - base + QUADSPI_SFB2AD); + sfa_size = q->devtype_data->sfa_size ? : q->devtype_data->ahb_buf_size; + qspi_writel(q, addr_offset + 1 * sfa_size, base + QUADSPI_SFA1AD); + qspi_writel(q, addr_offset + 2 * sfa_size, base + QUADSPI_SFA2AD); + qspi_writel(q, addr_offset + 3 * sfa_size, base + QUADSPI_SFB1AD); + qspi_writel(q, addr_offset + 4 * sfa_size, base + QUADSPI_SFB2AD); q->selected = -1; @@ -857,6 +884,8 @@ static void fsl_qspi_cleanup(void *data) { struct fsl_qspi *q = data; + reset_control_assert(q->resets); + fsl_qspi_clk_disable_unprep(q); mutex_destroy(&q->lock); @@ -902,6 +931,10 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (!q->ahb_addr) return -ENOMEM; + q->resets = devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(q->resets)) + return PTR_ERR(q->resets); + /* find the clocks */ q->clk_en = devm_clk_get(dev, "qspi_en"); if (IS_ERR(q->clk_en)) @@ -923,6 +956,10 @@ static int fsl_qspi_probe(struct platform_device *pdev) if (ret) return ret; + ret = reset_control_deassert(q->resets); + if (ret) + return ret; + /* find the irq */ ret = platform_get_irq(pdev, 0); if (ret < 0) @@ -976,6 +1013,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = { { .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, }, { .compatible = "fsl,ls1021a-qspi", .data = &ls1021a_data, }, { .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, }, + { .compatible = "spacemit,k1-qspi", .data = &spacemit_k1_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index bbf1fd4fe1e9..b8b79bb7fec3 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -42,6 +42,7 @@ MODULE_PARM_DESC(polling_limit_us, "time in us to run a transfer in polling mode\n"); #define MXC_RPM_TIMEOUT 2000 /* 2000ms */ +#define MXC_SPI_DEFAULT_SPEED 500000 /* 500KHz */ #define MXC_CSPIRXDATA 0x00 #define MXC_CSPITXDATA 0x04 @@ -424,8 +425,15 @@ static void spi_imx_buf_tx_swap(struct spi_imx_data *spi_imx) static void mx53_ecspi_rx_target(struct spi_imx_data *spi_imx) { - u32 val = ioread32be(spi_imx->base + MXC_CSPIRXDATA); + u32 val = readl(spi_imx->base + MXC_CSPIRXDATA); +#ifdef __LITTLE_ENDIAN + unsigned int bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word); + if (bytes_per_word == 1) + swab32s(&val); + else if (bytes_per_word == 2) + swahw32s(&val); +#endif if (spi_imx->rx_buf) { int n_bytes = spi_imx->target_burst % sizeof(val); @@ -446,6 +454,9 @@ static void mx53_ecspi_tx_target(struct spi_imx_data *spi_imx) { u32 val = 0; int n_bytes = spi_imx->count % sizeof(val); +#ifdef __LITTLE_ENDIAN + unsigned int bytes_per_word; +#endif if (!n_bytes) n_bytes = sizeof(val); @@ -458,7 +469,14 @@ static void mx53_ecspi_tx_target(struct spi_imx_data *spi_imx) spi_imx->count -= n_bytes; - iowrite32be(val, spi_imx->base + MXC_CSPITXDATA); +#ifdef __LITTLE_ENDIAN + bytes_per_word = spi_imx_bytes_per_word(spi_imx->bits_per_word); + if (bytes_per_word == 1) + swab32s(&val); + else if (bytes_per_word == 2) + swahw32s(&val); +#endif + writel(val, spi_imx->base + MXC_CSPITXDATA); } /* MX51 eCSPI */ @@ -591,7 +609,7 @@ static int mx51_ecspi_prepare_message(struct spi_imx_data *spi_imx, * is not functional for imx53 Soc, config SPI burst completed when * BURST_LENGTH + 1 bits are received */ - if (spi_imx->target_mode && is_imx53_ecspi(spi_imx)) + if (spi_imx->target_mode) cfg &= ~MX51_ECSPI_CONFIG_SBBCTRL(channel); else cfg |= MX51_ECSPI_CONFIG_SBBCTRL(channel); @@ -679,7 +697,7 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx, /* Clear BL field and set the right value */ ctrl &= ~MX51_ECSPI_CTRL_BL_MASK; - if (spi_imx->target_mode && is_imx53_ecspi(spi_imx)) + if (spi_imx->target_mode) ctrl |= (spi_imx->target_burst * 8 - 1) << MX51_ECSPI_CTRL_BL_OFFSET; else { @@ -690,8 +708,11 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx, /* set clock speed */ ctrl &= ~(0xf << MX51_ECSPI_CTRL_POSTDIV_OFFSET | 0xf << MX51_ECSPI_CTRL_PREDIV_OFFSET); - ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk); - spi_imx->spi_bus_clk = clk; + + if (!spi_imx->target_mode) { + ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk); + spi_imx->spi_bus_clk = clk; + } mx51_configure_cpha(spi_imx, spi); @@ -1313,15 +1334,18 @@ static int spi_imx_setupxfer(struct spi_device *spi, if (!t) return 0; - if (!t->speed_hz) { - if (!spi->max_speed_hz) { - dev_err(&spi->dev, "no speed_hz provided!\n"); - return -EINVAL; + if (!spi_imx->target_mode) { + if (!t->speed_hz) { + if (!spi->max_speed_hz) { + dev_err(&spi->dev, "no speed_hz provided!\n"); + return -EINVAL; + } + dev_dbg(&spi->dev, "using spi->max_speed_hz!\n"); + spi_imx->spi_bus_clk = spi->max_speed_hz; + } else { + spi_imx->spi_bus_clk = t->speed_hz; } - dev_dbg(&spi->dev, "using spi->max_speed_hz!\n"); - spi_imx->spi_bus_clk = spi->max_speed_hz; - } else - spi_imx->spi_bus_clk = t->speed_hz; + } spi_imx->bits_per_word = t->bits_per_word; spi_imx->count = t->len; @@ -1365,7 +1389,7 @@ static int spi_imx_setupxfer(struct spi_device *spi, spi_imx->rx_only = ((t->tx_buf == NULL) || (t->tx_buf == spi->controller->dummy_tx)); - if (is_imx53_ecspi(spi_imx) && spi_imx->target_mode) { + if (spi_imx->target_mode) { spi_imx->rx = mx53_ecspi_rx_target; spi_imx->tx = mx53_ecspi_tx_target; spi_imx->target_burst = t->len; @@ -1641,8 +1665,7 @@ static int spi_imx_pio_transfer_target(struct spi_device *spi, struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller); int ret = 0; - if (is_imx53_ecspi(spi_imx) && - transfer->len > MX53_MAX_TRANSFER_BYTES) { + if (transfer->len > MX53_MAX_TRANSFER_BYTES) { dev_err(&spi->dev, "Transaction too big, max size is %d bytes\n", MX53_MAX_TRANSFER_BYTES); return -EMSGSIZE; @@ -1838,6 +1861,7 @@ static int spi_imx_probe(struct platform_device *pdev) controller->prepare_message = spi_imx_prepare_message; controller->unprepare_message = spi_imx_unprepare_message; controller->target_abort = spi_imx_target_abort; + spi_imx->spi_bus_clk = MXC_SPI_DEFAULT_SPEED; controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS | SPI_MOSI_IDLE_LOW; diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index 064b99204d9a..c8b2add2640e 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -12,6 +12,9 @@ #include <linux/spi/spi-mem.h> #include <linux/sched/task_stack.h> +#define CREATE_TRACE_POINTS +#include <trace/events/spi-mem.h> + #include "internals.h" #define SPI_MEM_MAX_BUSWIDTH 8 @@ -403,7 +406,9 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) if (ret) return ret; + trace_spi_mem_start_op(mem, op); ret = ctlr->mem_ops->exec_op(mem, op); + trace_spi_mem_stop_op(mem, op); spi_mem_access_end(mem); diff --git a/drivers/spi/spi-microchip-core-spi.c b/drivers/spi/spi-microchip-core-spi.c new file mode 100644 index 000000000000..98bf0e6cd00e --- /dev/null +++ b/drivers/spi/spi-microchip-core-spi.c @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: (GPL-2.0) +// +// Microchip CoreSPI controller driver +// +// Copyright (c) 2025 Microchip Technology Inc. and its subsidiaries +// +// Author: Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> + +#define MCHP_CORESPI_MAX_CS (8) +#define MCHP_CORESPI_DEFAULT_FIFO_DEPTH (4) +#define MCHP_CORESPI_DEFAULT_MOTOROLA_MODE (3) + +#define MCHP_CORESPI_CONTROL_ENABLE BIT(0) +#define MCHP_CORESPI_CONTROL_MASTER BIT(1) +#define MCHP_CORESPI_CONTROL_TX_DATA_INT BIT(3) +#define MCHP_CORESPI_CONTROL_RX_OVER_INT BIT(4) +#define MCHP_CORESPI_CONTROL_TX_UNDER_INT BIT(5) +#define MCHP_CORESPI_CONTROL_FRAMEURUN BIT(6) +#define MCHP_CORESPI_CONTROL_OENOFF BIT(7) + +#define MCHP_CORESPI_STATUS_ACTIVE BIT(7) +#define MCHP_CORESPI_STATUS_SSEL BIT(6) +#define MCHP_CORESPI_STATUS_TXFIFO_UNDERFLOW BIT(5) +#define MCHP_CORESPI_STATUS_RXFIFO_FULL BIT(4) +#define MCHP_CORESPI_STATUS_TXFIFO_FULL BIT(3) +#define MCHP_CORESPI_STATUS_RXFIFO_EMPTY BIT(2) +#define MCHP_CORESPI_STATUS_DONE BIT(1) +#define MCHP_CORESPI_STATUS_FIRSTFRAME BIT(0) + +#define MCHP_CORESPI_INT_TXDONE BIT(0) +#define MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW BIT(2) +#define MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN BIT(3) +#define MCHP_CORESPI_INT_CMDINT BIT(4) +#define MCHP_CORESPI_INT_SSEND BIT(5) +#define MCHP_CORESPI_INT_DATA_RX BIT(6) +#define MCHP_CORESPI_INT_TXRFM BIT(7) + +#define MCHP_CORESPI_CONTROL2_INTEN_TXRFMT BIT(7) +#define MCHP_CORESPI_CONTROL2_INTEN_DATA_RX BIT(6) +#define MCHP_CORESPI_CONTROL2_INTEN_SSEND BIT(5) +#define MCHP_CORESPI_CONTROL2_INTEN_CMD BIT(4) + +#define INT_ENABLE_MASK (MCHP_CORESPI_CONTROL_TX_DATA_INT | MCHP_CORESPI_CONTROL_RX_OVER_INT | \ + MCHP_CORESPI_CONTROL_TX_UNDER_INT) + +#define MCHP_CORESPI_REG_CONTROL (0x00) +#define MCHP_CORESPI_REG_INTCLEAR (0x04) +#define MCHP_CORESPI_REG_RXDATA (0x08) +#define MCHP_CORESPI_REG_TXDATA (0x0c) +#define MCHP_CORESPI_REG_INTMASK (0X10) +#define MCHP_CORESPI_REG_INTRAW (0X14) +#define MCHP_CORESPI_REG_CONTROL2 (0x18) +#define MCHP_CORESPI_REG_COMMAND (0x1c) +#define MCHP_CORESPI_REG_STAT (0x20) +#define MCHP_CORESPI_REG_SSEL (0x24) +#define MCHP_CORESPI_REG_TXDATA_LAST (0X28) +#define MCHP_CORESPI_REG_CLK_DIV (0x2c) + +struct mchp_corespi { + void __iomem *regs; + struct clk *clk; + const u8 *tx_buf; + u8 *rx_buf; + u32 clk_gen; + int irq; + unsigned int tx_len; + unsigned int rx_len; + u32 fifo_depth; +}; + +static inline void mchp_corespi_disable(struct mchp_corespi *spi) +{ + u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL); + + control &= ~MCHP_CORESPI_CONTROL_ENABLE; + + writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL); +} + +static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, u32 fifo_max) +{ + for (int i = 0; i < fifo_max; i++) { + u32 data; + + while (readb(spi->regs + MCHP_CORESPI_REG_STAT) & + MCHP_CORESPI_STATUS_RXFIFO_EMPTY) + ; + + /* On TX-only transfers always perform a dummy read */ + data = readb(spi->regs + MCHP_CORESPI_REG_RXDATA); + if (spi->rx_buf) + *spi->rx_buf++ = data; + + spi->rx_len--; + } +} + +static void mchp_corespi_enable_ints(struct mchp_corespi *spi) +{ + u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL); + + control |= INT_ENABLE_MASK; + writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL); +} + +static void mchp_corespi_disable_ints(struct mchp_corespi *spi) +{ + u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL); + + control &= ~INT_ENABLE_MASK; + writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL); +} + +static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, u32 fifo_max) +{ + for (int i = 0; i < fifo_max; i++) { + if (readb(spi->regs + MCHP_CORESPI_REG_STAT) & + MCHP_CORESPI_STATUS_TXFIFO_FULL) + break; + + /* On RX-only transfers always perform a dummy write */ + if (spi->tx_buf) + writeb(*spi->tx_buf++, spi->regs + MCHP_CORESPI_REG_TXDATA); + else + writeb(0xaa, spi->regs + MCHP_CORESPI_REG_TXDATA); + + spi->tx_len--; + } +} + +static void mchp_corespi_set_cs(struct spi_device *spi, bool disable) +{ + struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller); + u32 reg; + + reg = readb(corespi->regs + MCHP_CORESPI_REG_SSEL); + reg &= ~BIT(spi_get_chipselect(spi, 0)); + reg |= !disable << spi_get_chipselect(spi, 0); + + writeb(reg, corespi->regs + MCHP_CORESPI_REG_SSEL); +} + +static int mchp_corespi_setup(struct spi_device *spi) +{ + if (spi_get_csgpiod(spi, 0)) + return 0; + + if (spi->mode & (SPI_CS_HIGH)) { + dev_err(&spi->dev, "unable to support active-high CS in Motorola mode\n"); + return -EOPNOTSUPP; + } + + if (spi->mode & SPI_MODE_X_MASK & ~spi->controller->mode_bits) { + dev_err(&spi->dev, "incompatible CPOL/CPHA, must match controller's Motorola mode\n"); + return -EINVAL; + } + + return 0; +} + +static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi) +{ + u8 control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL); + + /* Master mode changes require core to be disabled.*/ + control = (control & ~MCHP_CORESPI_CONTROL_ENABLE) | MCHP_CORESPI_CONTROL_MASTER; + + writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL); + + mchp_corespi_enable_ints(spi); + + control = readb(spi->regs + MCHP_CORESPI_REG_CONTROL); + control |= MCHP_CORESPI_CONTROL_ENABLE; + + writeb(control, spi->regs + MCHP_CORESPI_REG_CONTROL); +} + +static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id) +{ + struct spi_controller *host = dev_id; + struct mchp_corespi *spi = spi_controller_get_devdata(host); + u8 intfield = readb(spi->regs + MCHP_CORESPI_REG_INTMASK) & 0xff; + bool finalise = false; + + /* Interrupt line may be shared and not for us at all */ + if (intfield == 0) + return IRQ_NONE; + + if (intfield & MCHP_CORESPI_INT_TXDONE) + writeb(MCHP_CORESPI_INT_TXDONE, spi->regs + MCHP_CORESPI_REG_INTCLEAR); + + if (intfield & MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW) { + writeb(MCHP_CORESPI_INT_RX_CHANNEL_OVERFLOW, + spi->regs + MCHP_CORESPI_REG_INTCLEAR); + finalise = true; + dev_err(&host->dev, + "RX OVERFLOW: rxlen: %u, txlen: %u\n", + spi->rx_len, spi->tx_len); + } + + if (intfield & MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN) { + writeb(MCHP_CORESPI_INT_TX_CHANNEL_UNDERRUN, + spi->regs + MCHP_CORESPI_REG_INTCLEAR); + finalise = true; + dev_err(&host->dev, + "TX UNDERFLOW: rxlen: %u, txlen: %u\n", + spi->rx_len, spi->tx_len); + } + + if (finalise) + spi_finalize_current_transfer(host); + + return IRQ_HANDLED; +} + +static int mchp_corespi_set_clk_div(struct mchp_corespi *spi, + unsigned long target_hz) +{ + unsigned long pclk_hz, spi_hz; + u32 clk_div; + + /* Get peripheral clock rate */ + pclk_hz = clk_get_rate(spi->clk); + if (!pclk_hz) + return -EINVAL; + + /* + * Calculate clock rate generated by SPI master + * Formula: SPICLK = PCLK / (2 * (CLK_DIV + 1)) + */ + clk_div = DIV_ROUND_UP(pclk_hz, 2 * target_hz) - 1; + + if (clk_div > 0xFF) + return -EINVAL; + + spi_hz = pclk_hz / (2 * (clk_div + 1)); + + if (spi_hz > target_hz) + return -EINVAL; + + writeb(clk_div, spi->regs + MCHP_CORESPI_REG_CLK_DIV); + + return 0; +} + +static int mchp_corespi_transfer_one(struct spi_controller *host, + struct spi_device *spi_dev, + struct spi_transfer *xfer) +{ + struct mchp_corespi *spi = spi_controller_get_devdata(host); + int ret; + + ret = mchp_corespi_set_clk_div(spi, (unsigned long)xfer->speed_hz); + if (ret) { + dev_err(&host->dev, "failed to set clock divider for target %u Hz\n", + xfer->speed_hz); + return ret; + } + + spi->tx_buf = xfer->tx_buf; + spi->rx_buf = xfer->rx_buf; + spi->tx_len = xfer->len; + spi->rx_len = xfer->len; + + while (spi->tx_len) { + unsigned int fifo_max = min(spi->tx_len, spi->fifo_depth); + + mchp_corespi_write_fifo(spi, fifo_max); + mchp_corespi_read_fifo(spi, fifo_max); + } + + spi_finalize_current_transfer(host); + return 1; +} + +static int mchp_corespi_probe(struct platform_device *pdev) +{ + const char *protocol = "motorola"; + struct device *dev = &pdev->dev; + struct spi_controller *host; + struct mchp_corespi *spi; + struct resource *res; + u32 num_cs, mode, frame_size; + bool assert_ssel; + int ret = 0; + + host = devm_spi_alloc_host(dev, sizeof(*spi)); + if (!host) + return -ENOMEM; + + platform_set_drvdata(pdev, host); + + if (of_property_read_u32(dev->of_node, "num-cs", &num_cs)) + num_cs = MCHP_CORESPI_MAX_CS; + + /* + * Protocol: CFG_MODE + * CoreSPI can be configured for Motorola, TI or NSC. + * The current driver supports only Motorola mode. + */ + ret = of_property_read_string(dev->of_node, "microchip,protocol-configuration", + &protocol); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, "Error reading protocol-configuration\n"); + if (strcmp(protocol, "motorola") != 0) + return dev_err_probe(dev, -EINVAL, + "CoreSPI: protocol '%s' not supported by this driver\n", + protocol); + + /* + * Motorola mode (0-3): CFG_MOT_MODE + * Mode is fixed in the IP configurator. + */ + ret = of_property_read_u32(dev->of_node, "microchip,motorola-mode", &mode); + if (ret) + mode = MCHP_CORESPI_DEFAULT_MOTOROLA_MODE; + else if (mode > 3) + return dev_err_probe(dev, -EINVAL, + "invalid 'microchip,motorola-mode' value %u\n", mode); + + /* + * Frame size: CFG_FRAME_SIZE + * The hardware allows frame sizes <= APB data width. + * However, this driver currently only supports 8-bit frames. + */ + ret = of_property_read_u32(dev->of_node, "microchip,frame-size", &frame_size); + if (!ret && frame_size != 8) + return dev_err_probe(dev, -EINVAL, + "CoreSPI: frame size %u not supported by this driver\n", + frame_size); + + /* + * SSEL: CFG_MOT_SSEL + * CoreSPI deasserts SSEL when the TX FIFO empties. + * To prevent CS deassertion when TX FIFO drains, the ssel-active property + * keeps CS asserted for the full SPI transfer. + */ + assert_ssel = of_property_read_bool(dev->of_node, "microchip,ssel-active"); + if (!assert_ssel) + return dev_err_probe(dev, -EINVAL, + "hardware must enable 'microchip,ssel-active' to keep CS asserted for the SPI transfer\n"); + + spi = spi_controller_get_devdata(host); + + host->num_chipselect = num_cs; + host->mode_bits = mode; + host->setup = mchp_corespi_setup; + host->use_gpio_descriptors = true; + host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); + host->transfer_one = mchp_corespi_transfer_one; + host->set_cs = mchp_corespi_set_cs; + host->dev.of_node = dev->of_node; + + ret = of_property_read_u32(dev->of_node, "fifo-depth", &spi->fifo_depth); + if (ret) + spi->fifo_depth = MCHP_CORESPI_DEFAULT_FIFO_DEPTH; + + spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(spi->regs)) + return PTR_ERR(spi->regs); + + spi->irq = platform_get_irq(pdev, 0); + if (spi->irq < 0) + return spi->irq; + + ret = devm_request_irq(dev, spi->irq, mchp_corespi_interrupt, IRQF_SHARED, + dev_name(dev), host); + if (ret) + return dev_err_probe(dev, ret, "could not request irq\n"); + + spi->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(spi->clk)) + return dev_err_probe(dev, PTR_ERR(spi->clk), "could not get clk\n"); + + mchp_corespi_init(host, spi); + + ret = devm_spi_register_controller(dev, host); + if (ret) { + mchp_corespi_disable(spi); + return dev_err_probe(dev, ret, "unable to register host for CoreSPI controller\n"); + } + + return 0; +} + +static void mchp_corespi_remove(struct platform_device *pdev) +{ + struct spi_controller *host = platform_get_drvdata(pdev); + struct mchp_corespi *spi = spi_controller_get_devdata(host); + + mchp_corespi_disable_ints(spi); + mchp_corespi_disable(spi); +} + +/* + * Platform driver data structure + */ + +#if defined(CONFIG_OF) +static const struct of_device_id mchp_corespi_dt_ids[] = { + { .compatible = "microchip,corespi-rtl-v5" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids); +#endif + +static struct platform_driver mchp_corespi_driver = { + .probe = mchp_corespi_probe, + .driver = { + .name = "microchip-corespi", + .of_match_table = of_match_ptr(mchp_corespi_dt_ids), + }, + .remove = mchp_corespi_remove, +}; +module_platform_driver(mchp_corespi_driver); +MODULE_DESCRIPTION("Microchip CoreSPI controller driver"); +MODULE_AUTHOR("Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-microchip-core.c b/drivers/spi/spi-mpfs.c index 9128b86c5366..9a14d1732a15 100644 --- a/drivers/spi/spi-microchip-core.c +++ b/drivers/spi/spi-mpfs.c @@ -99,7 +99,7 @@ #define REG_CTRL2 (0x48) #define REG_FRAMESUP (0x50) -struct mchp_corespi { +struct mpfs_spi { void __iomem *regs; struct clk *clk; const u8 *tx_buf; @@ -113,34 +113,34 @@ struct mchp_corespi { int n_bytes; }; -static inline u32 mchp_corespi_read(struct mchp_corespi *spi, unsigned int reg) +static inline u32 mpfs_spi_read(struct mpfs_spi *spi, unsigned int reg) { return readl(spi->regs + reg); } -static inline void mchp_corespi_write(struct mchp_corespi *spi, unsigned int reg, u32 val) +static inline void mpfs_spi_write(struct mpfs_spi *spi, unsigned int reg, u32 val) { writel(val, spi->regs + reg); } -static inline void mchp_corespi_disable(struct mchp_corespi *spi) +static inline void mpfs_spi_disable(struct mpfs_spi *spi) { - u32 control = mchp_corespi_read(spi, REG_CONTROL); + u32 control = mpfs_spi_read(spi, REG_CONTROL); control &= ~CONTROL_ENABLE; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); } -static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, int fifo_max) +static inline void mpfs_spi_read_fifo(struct mpfs_spi *spi, int fifo_max) { for (int i = 0; i < fifo_max; i++) { u32 data; - while (mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY) + while (mpfs_spi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY) ; - data = mchp_corespi_read(spi, REG_RX_DATA); + data = mpfs_spi_read(spi, REG_RX_DATA); spi->rx_len -= spi->n_bytes; @@ -158,34 +158,34 @@ static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, int fifo_max } } -static void mchp_corespi_enable_ints(struct mchp_corespi *spi) +static void mpfs_spi_enable_ints(struct mpfs_spi *spi) { - u32 control = mchp_corespi_read(spi, REG_CONTROL); + u32 control = mpfs_spi_read(spi, REG_CONTROL); control |= INT_ENABLE_MASK; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); } -static void mchp_corespi_disable_ints(struct mchp_corespi *spi) +static void mpfs_spi_disable_ints(struct mpfs_spi *spi) { - u32 control = mchp_corespi_read(spi, REG_CONTROL); + u32 control = mpfs_spi_read(spi, REG_CONTROL); control &= ~INT_ENABLE_MASK; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); } -static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len) +static inline void mpfs_spi_set_xfer_size(struct mpfs_spi *spi, int len) { u32 control; u32 lenpart; - u32 frames = mchp_corespi_read(spi, REG_FRAMESUP); + u32 frames = mpfs_spi_read(spi, REG_FRAMESUP); /* * Writing to FRAMECNT in REG_CONTROL will reset the frame count, taking * a shortcut requires an explicit clear. */ if (frames == len) { - mchp_corespi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT); + mpfs_spi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT); return; } @@ -208,20 +208,20 @@ static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len) * that matches the documentation. */ lenpart = len & 0xffff; - control = mchp_corespi_read(spi, REG_CONTROL); + control = mpfs_spi_read(spi, REG_CONTROL); control &= ~CONTROL_FRAMECNT_MASK; control |= lenpart << CONTROL_FRAMECNT_SHIFT; - mchp_corespi_write(spi, REG_CONTROL, control); - mchp_corespi_write(spi, REG_FRAMESUP, len); + mpfs_spi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_FRAMESUP, len); } -static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_max) +static inline void mpfs_spi_write_fifo(struct mpfs_spi *spi, int fifo_max) { int i = 0; - mchp_corespi_set_xfer_size(spi, fifo_max); + mpfs_spi_set_xfer_size(spi, fifo_max); - while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) { + while ((i < fifo_max) && !(mpfs_spi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) { u32 word; if (spi->n_bytes == 4) @@ -231,7 +231,7 @@ static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_ma else word = spi->tx_buf ? *spi->tx_buf : 0xaa; - mchp_corespi_write(spi, REG_TX_DATA, word); + mpfs_spi_write(spi, REG_TX_DATA, word); if (spi->tx_buf) spi->tx_buf += spi->n_bytes; i++; @@ -240,9 +240,9 @@ static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_ma spi->tx_len -= i * spi->n_bytes; } -static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt) +static inline void mpfs_spi_set_framesize(struct mpfs_spi *spi, int bt) { - u32 frame_size = mchp_corespi_read(spi, REG_FRAME_SIZE); + u32 frame_size = mpfs_spi_read(spi, REG_FRAME_SIZE); u32 control; if ((frame_size & FRAME_SIZE_MASK) == bt) @@ -252,25 +252,25 @@ static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt) * Disable the SPI controller. Writes to the frame size have * no effect when the controller is enabled. */ - control = mchp_corespi_read(spi, REG_CONTROL); + control = mpfs_spi_read(spi, REG_CONTROL); control &= ~CONTROL_ENABLE; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); - mchp_corespi_write(spi, REG_FRAME_SIZE, bt); + mpfs_spi_write(spi, REG_FRAME_SIZE, bt); control |= CONTROL_ENABLE; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); } -static void mchp_corespi_set_cs(struct spi_device *spi, bool disable) +static void mpfs_spi_set_cs(struct spi_device *spi, bool disable) { u32 reg; - struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller); + struct mpfs_spi *mspi = spi_controller_get_devdata(spi->controller); - reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT); + reg = mpfs_spi_read(mspi, REG_SLAVE_SELECT); reg &= ~BIT(spi_get_chipselect(spi, 0)); reg |= !disable << spi_get_chipselect(spi, 0); - corespi->pending_slave_select = reg; + mspi->pending_slave_select = reg; /* * Only deassert chip select immediately. Writing to some registers @@ -281,12 +281,12 @@ static void mchp_corespi_set_cs(struct spi_device *spi, bool disable) * doesn't see any spurious clock transitions whilst CS is enabled. */ if (((spi->mode & SPI_CS_HIGH) == 0) == disable) - mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg); + mpfs_spi_write(mspi, REG_SLAVE_SELECT, reg); } -static int mchp_corespi_setup(struct spi_device *spi) +static int mpfs_spi_setup(struct spi_device *spi) { - struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller); + struct mpfs_spi *mspi = spi_controller_get_devdata(spi->controller); u32 reg; if (spi_is_csgpiod(spi)) @@ -298,21 +298,21 @@ static int mchp_corespi_setup(struct spi_device *spi) * driving their select line low. */ if (spi->mode & SPI_CS_HIGH) { - reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT); + reg = mpfs_spi_read(mspi, REG_SLAVE_SELECT); reg |= BIT(spi_get_chipselect(spi, 0)); - corespi->pending_slave_select = reg; - mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg); + mspi->pending_slave_select = reg; + mpfs_spi_write(mspi, REG_SLAVE_SELECT, reg); } return 0; } -static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi) +static void mpfs_spi_init(struct spi_controller *host, struct mpfs_spi *spi) { unsigned long clk_hz; - u32 control = mchp_corespi_read(spi, REG_CONTROL); + u32 control = mpfs_spi_read(spi, REG_CONTROL); control &= ~CONTROL_ENABLE; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); control |= CONTROL_MASTER; control &= ~CONTROL_MODE_MASK; @@ -328,15 +328,15 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi * */ control |= CONTROL_SPS | CONTROL_BIGFIFO; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); - mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE); + mpfs_spi_set_framesize(spi, DEFAULT_FRAMESIZE); /* max. possible spi clock rate is the apb clock rate */ clk_hz = clk_get_rate(spi->clk); host->max_speed_hz = clk_hz; - mchp_corespi_enable_ints(spi); + mpfs_spi_enable_ints(spi); /* * It is required to enable direct mode, otherwise control over the chip @@ -344,34 +344,34 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi * * can deal with active high targets. */ spi->pending_slave_select = SSELOUT | SSEL_DIRECT; - mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select); + mpfs_spi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select); - control = mchp_corespi_read(spi, REG_CONTROL); + control = mpfs_spi_read(spi, REG_CONTROL); control &= ~CONTROL_RESET; control |= CONTROL_ENABLE; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); } -static inline void mchp_corespi_set_clk_gen(struct mchp_corespi *spi) +static inline void mpfs_spi_set_clk_gen(struct mpfs_spi *spi) { u32 control; - control = mchp_corespi_read(spi, REG_CONTROL); + control = mpfs_spi_read(spi, REG_CONTROL); if (spi->clk_mode) control |= CONTROL_CLKMODE; else control &= ~CONTROL_CLKMODE; - mchp_corespi_write(spi, REG_CLK_GEN, spi->clk_gen); - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CLK_GEN, spi->clk_gen); + mpfs_spi_write(spi, REG_CONTROL, control); } -static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int mode) +static inline void mpfs_spi_set_mode(struct mpfs_spi *spi, unsigned int mode) { u32 mode_val; - u32 control = mchp_corespi_read(spi, REG_CONTROL); + u32 control = mpfs_spi_read(spi, REG_CONTROL); switch (mode & SPI_MODE_X_MASK) { case SPI_MODE_0: @@ -394,22 +394,22 @@ static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int */ control &= ~CONTROL_ENABLE; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); control &= ~(SPI_MODE_X_MASK << MODE_X_MASK_SHIFT); control |= mode_val; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); control |= CONTROL_ENABLE; - mchp_corespi_write(spi, REG_CONTROL, control); + mpfs_spi_write(spi, REG_CONTROL, control); } -static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id) +static irqreturn_t mpfs_spi_interrupt(int irq, void *dev_id) { struct spi_controller *host = dev_id; - struct mchp_corespi *spi = spi_controller_get_devdata(host); - u32 intfield = mchp_corespi_read(spi, REG_MIS) & 0xf; + struct mpfs_spi *spi = spi_controller_get_devdata(host); + u32 intfield = mpfs_spi_read(spi, REG_MIS) & 0xf; bool finalise = false; /* Interrupt line may be shared and not for us at all */ @@ -417,7 +417,7 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id) return IRQ_NONE; if (intfield & INT_RX_CHANNEL_OVERFLOW) { - mchp_corespi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW); + mpfs_spi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW); finalise = true; dev_err(&host->dev, "%s: RX OVERFLOW: rxlen: %d, txlen: %d\n", __func__, @@ -425,7 +425,7 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id) } if (intfield & INT_TX_CHANNEL_UNDERRUN) { - mchp_corespi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN); + mpfs_spi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN); finalise = true; dev_err(&host->dev, "%s: TX UNDERFLOW: rxlen: %d, txlen: %d\n", __func__, @@ -438,8 +438,8 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int mchp_corespi_calculate_clkgen(struct mchp_corespi *spi, - unsigned long target_hz) +static int mpfs_spi_calculate_clkgen(struct mpfs_spi *spi, + unsigned long target_hz) { unsigned long clk_hz, spi_hz, clk_gen; @@ -475,20 +475,20 @@ static int mchp_corespi_calculate_clkgen(struct mchp_corespi *spi, return 0; } -static int mchp_corespi_transfer_one(struct spi_controller *host, - struct spi_device *spi_dev, - struct spi_transfer *xfer) +static int mpfs_spi_transfer_one(struct spi_controller *host, + struct spi_device *spi_dev, + struct spi_transfer *xfer) { - struct mchp_corespi *spi = spi_controller_get_devdata(host); + struct mpfs_spi *spi = spi_controller_get_devdata(host); int ret; - ret = mchp_corespi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz); + ret = mpfs_spi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz); if (ret) { dev_err(&host->dev, "failed to set clk_gen for target %u Hz\n", xfer->speed_hz); return ret; } - mchp_corespi_set_clk_gen(spi); + mpfs_spi_set_clk_gen(spi); spi->tx_buf = xfer->tx_buf; spi->rx_buf = xfer->rx_buf; @@ -496,45 +496,46 @@ static int mchp_corespi_transfer_one(struct spi_controller *host, spi->rx_len = xfer->len; spi->n_bytes = roundup_pow_of_two(DIV_ROUND_UP(xfer->bits_per_word, BITS_PER_BYTE)); - mchp_corespi_set_framesize(spi, xfer->bits_per_word); + mpfs_spi_set_framesize(spi, xfer->bits_per_word); - mchp_corespi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST); + mpfs_spi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST); - mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select); + mpfs_spi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select); while (spi->tx_len) { int fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes); - mchp_corespi_write_fifo(spi, fifo_max); - mchp_corespi_read_fifo(spi, fifo_max); + mpfs_spi_write_fifo(spi, fifo_max); + mpfs_spi_read_fifo(spi, fifo_max); } spi_finalize_current_transfer(host); return 1; } -static int mchp_corespi_prepare_message(struct spi_controller *host, - struct spi_message *msg) +static int mpfs_spi_prepare_message(struct spi_controller *host, + struct spi_message *msg) { struct spi_device *spi_dev = msg->spi; - struct mchp_corespi *spi = spi_controller_get_devdata(host); + struct mpfs_spi *spi = spi_controller_get_devdata(host); - mchp_corespi_set_mode(spi, spi_dev->mode); + mpfs_spi_set_mode(spi, spi_dev->mode); return 0; } -static int mchp_corespi_probe(struct platform_device *pdev) +static int mpfs_spi_probe(struct platform_device *pdev) { struct spi_controller *host; - struct mchp_corespi *spi; + struct mpfs_spi *spi; struct resource *res; u32 num_cs; int ret = 0; host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi)); if (!host) - return -ENOMEM; + return dev_err_probe(&pdev->dev, -ENOMEM, + "unable to allocate host for SPI controller\n"); platform_set_drvdata(pdev, host); @@ -544,11 +545,11 @@ static int mchp_corespi_probe(struct platform_device *pdev) host->num_chipselect = num_cs; host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; host->use_gpio_descriptors = true; - host->setup = mchp_corespi_setup; + host->setup = mpfs_spi_setup; host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); - host->transfer_one = mchp_corespi_transfer_one; - host->prepare_message = mchp_corespi_prepare_message; - host->set_cs = mchp_corespi_set_cs; + host->transfer_one = mpfs_spi_transfer_one; + host->prepare_message = mpfs_spi_prepare_message; + host->set_cs = mpfs_spi_set_cs; host->dev.of_node = pdev->dev.of_node; spi = spi_controller_get_devdata(host); @@ -561,7 +562,7 @@ static int mchp_corespi_probe(struct platform_device *pdev) if (spi->irq < 0) return spi->irq; - ret = devm_request_irq(&pdev->dev, spi->irq, mchp_corespi_interrupt, + ret = devm_request_irq(&pdev->dev, spi->irq, mpfs_spi_interrupt, IRQF_SHARED, dev_name(&pdev->dev), host); if (ret) return dev_err_probe(&pdev->dev, ret, @@ -572,11 +573,11 @@ static int mchp_corespi_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk), "could not get clk\n"); - mchp_corespi_init(host, spi); + mpfs_spi_init(host, spi); ret = devm_spi_register_controller(&pdev->dev, host); if (ret) { - mchp_corespi_disable(spi); + mpfs_spi_disable(spi); return dev_err_probe(&pdev->dev, ret, "unable to register host for SPI controller\n"); } @@ -586,13 +587,13 @@ static int mchp_corespi_probe(struct platform_device *pdev) return 0; } -static void mchp_corespi_remove(struct platform_device *pdev) +static void mpfs_spi_remove(struct platform_device *pdev) { struct spi_controller *host = platform_get_drvdata(pdev); - struct mchp_corespi *spi = spi_controller_get_devdata(host); + struct mpfs_spi *spi = spi_controller_get_devdata(host); - mchp_corespi_disable_ints(spi); - mchp_corespi_disable(spi); + mpfs_spi_disable_ints(spi); + mpfs_spi_disable(spi); } #define MICROCHIP_SPI_PM_OPS (NULL) @@ -602,23 +603,23 @@ static void mchp_corespi_remove(struct platform_device *pdev) */ #if defined(CONFIG_OF) -static const struct of_device_id mchp_corespi_dt_ids[] = { +static const struct of_device_id mpfs_spi_dt_ids[] = { { .compatible = "microchip,mpfs-spi" }, { /* sentinel */ } }; -MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids); +MODULE_DEVICE_TABLE(of, mpfs_spi_dt_ids); #endif -static struct platform_driver mchp_corespi_driver = { - .probe = mchp_corespi_probe, +static struct platform_driver mpfs_spi_driver = { + .probe = mpfs_spi_probe, .driver = { - .name = "microchip-corespi", + .name = "microchip-spi", .pm = MICROCHIP_SPI_PM_OPS, - .of_match_table = of_match_ptr(mchp_corespi_dt_ids), + .of_match_table = of_match_ptr(mpfs_spi_dt_ids), }, - .remove = mchp_corespi_remove, + .remove = mpfs_spi_remove, }; -module_platform_driver(mchp_corespi_driver); +module_platform_driver(mpfs_spi_driver); MODULE_DESCRIPTION("Microchip coreSPI SPI controller driver"); MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>"); MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); diff --git a/drivers/spi/spi-offload-trigger-pwm.c b/drivers/spi/spi-offload-trigger-pwm.c index 805ed41560df..3e8c19227edb 100644 --- a/drivers/spi/spi-offload-trigger-pwm.c +++ b/drivers/spi/spi-offload-trigger-pwm.c @@ -51,12 +51,14 @@ static int spi_offload_trigger_pwm_validate(struct spi_offload_trigger *trigger, wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, periodic->frequency_hz); /* REVISIT: 50% duty-cycle for now - may add config parameter later */ wf.duty_length_ns = wf.period_length_ns / 2; + wf.duty_offset_ns = periodic->offset_ns; ret = pwm_round_waveform_might_sleep(st->pwm, &wf); if (ret < 0) return ret; periodic->frequency_hz = DIV_ROUND_UP_ULL(NSEC_PER_SEC, wf.period_length_ns); + periodic->offset_ns = wf.duty_offset_ns; return 0; } @@ -77,6 +79,7 @@ static int spi_offload_trigger_pwm_enable(struct spi_offload_trigger *trigger, wf.period_length_ns = DIV_ROUND_UP_ULL(NSEC_PER_SEC, periodic->frequency_hz); /* REVISIT: 50% duty-cycle for now - may add config parameter later */ wf.duty_length_ns = wf.period_length_ns / 2; + wf.duty_offset_ns = periodic->offset_ns; return pwm_set_waveform_might_sleep(st->pwm, &wf, false); } diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 58ceea1ea8fb..7681a91d67d5 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -448,7 +448,7 @@ static int qcom_spi_ecc_finish_io_req_pipelined(struct nand_device *nand, return snandc->qspi->ecc_stats.bitflips; } -static struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = { +static const struct nand_ecc_engine_ops qcom_spi_ecc_engine_ops_pipelined = { .init_ctx = qcom_spi_ecc_init_ctx_pipelined, .cleanup_ctx = qcom_spi_ecc_cleanup_ctx_pipelined, .prepare_io_req = qcom_spi_ecc_prepare_io_req_pipelined, diff --git a/drivers/spi/spi-rzv2h-rspi.c b/drivers/spi/spi-rzv2h-rspi.c index dcc431ba60a9..1db7e4e5d64e 100644 --- a/drivers/spi/spi-rzv2h-rspi.c +++ b/drivers/spi/spi-rzv2h-rspi.c @@ -24,6 +24,7 @@ /* Registers */ #define RSPI_SPDR 0x00 #define RSPI_SPCR 0x08 +#define RSPI_SPPCR 0x0e #define RSPI_SSLP 0x10 #define RSPI_SPBR 0x11 #define RSPI_SPSCR 0x13 @@ -34,13 +35,18 @@ #define RSPI_SPFCR 0x6c /* Register SPCR */ +#define RSPI_SPCR_BPEN BIT(31) #define RSPI_SPCR_MSTR BIT(30) #define RSPI_SPCR_SPRIE BIT(17) #define RSPI_SPCR_SCKASE BIT(12) #define RSPI_SPCR_SPE BIT(0) +/* Register SPPCR */ +#define RSPI_SPPCR_SPLP2 BIT(1) + /* Register SPBR */ #define RSPI_SPBR_SPR_MIN 0 +#define RSPI_SPBR_SPR_PCLK_MIN 1 #define RSPI_SPBR_SPR_MAX 255 /* Register SPCMD */ @@ -58,7 +64,6 @@ /* Register SPDCR2 */ #define RSPI_SPDCR2_TTRG GENMASK(11, 8) #define RSPI_SPDCR2_RTRG GENMASK(3, 0) -#define RSPI_FIFO_SIZE 16 /* Register SPSR */ #define RSPI_SPSR_SPRF BIT(15) @@ -67,17 +72,41 @@ #define RSPI_SPSRC_CLEAR 0xfd80 #define RSPI_RESET_NUM 2 -#define RSPI_CLK_NUM 3 + +struct rzv2h_rspi_best_clock { + struct clk *clk; + unsigned long clk_rate; + unsigned long error; + u32 actual_hz; + u8 brdv; + u8 spr; +}; + +struct rzv2h_rspi_info { + void (*find_tclk_rate)(struct clk *clk, u32 hz, u8 spr_min, u8 spr_max, + struct rzv2h_rspi_best_clock *best_clk); + void (*find_pclk_rate)(struct clk *clk, u32 hz, u8 spr_low, u8 spr_high, + struct rzv2h_rspi_best_clock *best_clk); + const char *tclk_name; + unsigned int fifo_size; + unsigned int num_clks; +}; struct rzv2h_rspi_priv { struct reset_control_bulk_data resets[RSPI_RESET_NUM]; struct spi_controller *controller; + const struct rzv2h_rspi_info *info; void __iomem *base; struct clk *tclk; + struct clk *pclk; wait_queue_head_t wait; unsigned int bytes_per_word; + u32 last_speed_hz; u32 freq; u16 status; + u8 spr; + u8 brdv; + bool use_pclk; }; #define RZV2H_RSPI_TX(func, type) \ @@ -232,9 +261,112 @@ static inline u32 rzv2h_rspi_calc_bitrate(unsigned long tclk_rate, u8 spr, return DIV_ROUND_UP(tclk_rate, (2 * (spr + 1) * (1 << brdv))); } -static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) +static void rzv2h_rspi_find_rate_variable(struct clk *clk, u32 hz, + u8 spr_min, u8 spr_max, + struct rzv2h_rspi_best_clock *best) +{ + long clk_rate, clk_min_rate, clk_max_rate; + int min_rate_spr, max_rate_spr; + unsigned long error; + u32 actual_hz; + u8 brdv; + int spr; + + /* + * On T2H / N2H, the source for the SPI clock is PCLKSPIn, which is a + * 1/32, 1/30, 1/25 or 1/24 divider of PLL4, which is 2400MHz, + * resulting in either 75MHz, 80MHz, 96MHz or 100MHz. + */ + clk_min_rate = clk_round_rate(clk, 0); + if (clk_min_rate < 0) + return; + + clk_max_rate = clk_round_rate(clk, ULONG_MAX); + if (clk_max_rate < 0) + return; + + /* + * From the manual: + * Bit rate = f(PCLKSPIn) / (2 * (n + 1) * 2^N) + * + * If we adapt it to the current context, we get the following: + * hz = rate / ((spr + 1) * (1 << (brdv + 1))) + * + * This can be written in multiple forms depending on what we want to + * determine. + * + * To find the rate, having hz, spr and brdv: + * rate = hz * (spr + 1) * (1 << (brdv + 1) + * + * To find the spr, having rate, hz, and spr: + * spr = rate / (hz * (1 << (brdv + 1)) - 1 + */ + + for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) { + /* Calculate the divisor needed to find the SPR from a rate. */ + u32 rate_div = hz * (1 << (brdv + 1)); + + /* + * If the SPR for the minimum rate is greater than the maximum + * allowed value skip this BRDV. The divisor increases with each + * BRDV iteration, so the following BRDV might result in a + * minimum SPR that is in the valid range. + */ + min_rate_spr = DIV_ROUND_CLOSEST(clk_min_rate, rate_div) - 1; + if (min_rate_spr > spr_max) + continue; + + /* + * If the SPR for the maximum rate is less than the minimum + * allowed value, exit. The divisor only increases with each + * BRDV iteration, so the following BRDV cannot result in a + * maximum SPR that is in the valid range. + */ + max_rate_spr = DIV_ROUND_CLOSEST(clk_max_rate, rate_div) - 1; + if (max_rate_spr < spr_min) + break; + + if (min_rate_spr < spr_min) + min_rate_spr = spr_min; + + if (max_rate_spr > spr_max) + max_rate_spr = spr_max; + + for (spr = min_rate_spr; spr <= max_rate_spr; spr++) { + clk_rate = (spr + 1) * rate_div; + + clk_rate = clk_round_rate(clk, clk_rate); + if (clk_rate <= 0) + continue; + + actual_hz = rzv2h_rspi_calc_bitrate(clk_rate, spr, brdv); + error = abs((long)hz - (long)actual_hz); + + if (error >= best->error) + continue; + + *best = (struct rzv2h_rspi_best_clock) { + .clk = clk, + .clk_rate = clk_rate, + .error = error, + .actual_hz = actual_hz, + .brdv = brdv, + .spr = spr, + }; + + if (!error) + return; + } + } +} + +static void rzv2h_rspi_find_rate_fixed(struct clk *clk, u32 hz, + u8 spr_min, u8 spr_max, + struct rzv2h_rspi_best_clock *best) { - unsigned long tclk_rate; + unsigned long clk_rate; + unsigned long error; + u32 actual_hz; int spr; u8 brdv; @@ -247,21 +379,63 @@ static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) * * n = SPR - is RSPI_SPBR.SPR (from 0 to 255) * * N = BRDV - is RSPI_SPCMD.BRDV (from 0 to 3) */ - tclk_rate = clk_get_rate(rspi->tclk); + clk_rate = clk_get_rate(clk); for (brdv = RSPI_SPCMD_BRDV_MIN; brdv <= RSPI_SPCMD_BRDV_MAX; brdv++) { - spr = DIV_ROUND_UP(tclk_rate, hz * (1 << (brdv + 1))); + spr = DIV_ROUND_UP(clk_rate, hz * (1 << (brdv + 1))); spr--; - if (spr >= RSPI_SPBR_SPR_MIN && spr <= RSPI_SPBR_SPR_MAX) + if (spr >= spr_min && spr <= spr_max) goto clock_found; } - return 0; + return; clock_found: - rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_BRDV, brdv); - writeb(spr, rspi->base + RSPI_SPBR); + actual_hz = rzv2h_rspi_calc_bitrate(clk_rate, spr, brdv); + error = abs((long)hz - (long)actual_hz); + + if (error >= best->error) + return; + + *best = (struct rzv2h_rspi_best_clock) { + .clk = clk, + .clk_rate = clk_rate, + .error = error, + .actual_hz = actual_hz, + .brdv = brdv, + .spr = spr, + }; +} + +static u32 rzv2h_rspi_setup_clock(struct rzv2h_rspi_priv *rspi, u32 hz) +{ + struct rzv2h_rspi_best_clock best_clock = { + .error = ULONG_MAX, + }; + int ret; + + rspi->info->find_tclk_rate(rspi->tclk, hz, RSPI_SPBR_SPR_MIN, + RSPI_SPBR_SPR_MAX, &best_clock); + + /* + * T2H and N2H can also use PCLK as a source, which is 125MHz, but not + * when both SPR and BRDV are 0. + */ + if (best_clock.error && rspi->info->find_pclk_rate) + rspi->info->find_pclk_rate(rspi->pclk, hz, RSPI_SPBR_SPR_PCLK_MIN, + RSPI_SPBR_SPR_MAX, &best_clock); + + if (!best_clock.clk_rate) + return -EINVAL; - return rzv2h_rspi_calc_bitrate(tclk_rate, spr, brdv); + ret = clk_set_rate(best_clock.clk, best_clock.clk_rate); + if (ret) + return 0; + + rspi->use_pclk = best_clock.clk == rspi->pclk; + rspi->spr = best_clock.spr; + rspi->brdv = best_clock.brdv; + + return best_clock.actual_hz; } static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, @@ -274,10 +448,34 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, u8 bits_per_word; u32 conf32; u16 conf16; + u8 conf8; /* Make sure SPCR.SPE is 0 before amending the configuration */ rzv2h_rspi_spe_disable(rspi); + list_for_each_entry(xfer, &message->transfers, transfer_list) { + if (!xfer->speed_hz) + continue; + + speed_hz = min(xfer->speed_hz, speed_hz); + bits_per_word = xfer->bits_per_word; + } + + if (speed_hz == U32_MAX) + return -EINVAL; + + rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word)); + + if (speed_hz != rspi->last_speed_hz) { + rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz); + if (!rspi->freq) + return -EINVAL; + + rspi->last_speed_hz = speed_hz; + } + + writeb(rspi->spr, rspi->base + RSPI_SPBR); + /* Configure the device to work in "host" mode */ conf32 = RSPI_SPCR_MSTR; @@ -287,15 +485,24 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, /* SPI receive buffer full interrupt enable */ conf32 |= RSPI_SPCR_SPRIE; + /* Bypass synchronization circuit */ + conf32 |= FIELD_PREP(RSPI_SPCR_BPEN, rspi->use_pclk); + writel(conf32, rspi->base + RSPI_SPCR); /* Use SPCMD0 only */ writeb(0x0, rspi->base + RSPI_SPSCR); + /* Setup loopback */ + conf8 = FIELD_PREP(RSPI_SPPCR_SPLP2, !!(spi->mode & SPI_LOOP)); + writeb(conf8, rspi->base + RSPI_SPPCR); + /* Setup mode */ conf32 = FIELD_PREP(RSPI_SPCMD_CPOL, !!(spi->mode & SPI_CPOL)); conf32 |= FIELD_PREP(RSPI_SPCMD_CPHA, !!(spi->mode & SPI_CPHA)); conf32 |= FIELD_PREP(RSPI_SPCMD_LSBF, !!(spi->mode & SPI_LSB_FIRST)); + conf32 |= FIELD_PREP(RSPI_SPCMD_SPB, bits_per_word - 1); + conf32 |= FIELD_PREP(RSPI_SPCMD_BRDV, rspi->brdv); conf32 |= FIELD_PREP(RSPI_SPCMD_SSLKP, 1); conf32 |= FIELD_PREP(RSPI_SPCMD_SSLA, spi_get_chipselect(spi, 0)); writel(conf32, rspi->base + RSPI_SPCMD); @@ -305,30 +512,12 @@ static int rzv2h_rspi_prepare_message(struct spi_controller *ctlr, writeb(0, rspi->base + RSPI_SSLP); /* Setup FIFO thresholds */ - conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, RSPI_FIFO_SIZE - 1); + conf16 = FIELD_PREP(RSPI_SPDCR2_TTRG, rspi->info->fifo_size - 1); conf16 |= FIELD_PREP(RSPI_SPDCR2_RTRG, 0); writew(conf16, rspi->base + RSPI_SPDCR2); rzv2h_rspi_clear_fifos(rspi); - list_for_each_entry(xfer, &message->transfers, transfer_list) { - if (!xfer->speed_hz) - continue; - - speed_hz = min(xfer->speed_hz, speed_hz); - bits_per_word = xfer->bits_per_word; - } - - if (speed_hz == U32_MAX) - return -EINVAL; - - rspi->bytes_per_word = roundup_pow_of_two(BITS_TO_BYTES(bits_per_word)); - rzv2h_rspi_reg_rmw(rspi, RSPI_SPCMD, RSPI_SPCMD_SPB, bits_per_word - 1); - - rspi->freq = rzv2h_rspi_setup_clock(rspi, speed_hz); - if (!rspi->freq) - return -EINVAL; - rzv2h_rspi_spe_enable(rspi); return 0; @@ -350,8 +539,8 @@ static int rzv2h_rspi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rzv2h_rspi_priv *rspi; struct clk_bulk_data *clks; - unsigned long tclk_rate; int irq_rx, ret, i; + long tclk_rate; controller = devm_spi_alloc_host(dev, sizeof(*rspi)); if (!controller) @@ -362,30 +551,32 @@ static int rzv2h_rspi_probe(struct platform_device *pdev) rspi->controller = controller; + rspi->info = device_get_match_data(dev); + rspi->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rspi->base)) return PTR_ERR(rspi->base); ret = devm_clk_bulk_get_all_enabled(dev, &clks); - if (ret != RSPI_CLK_NUM) + if (ret != rspi->info->num_clks) return dev_err_probe(dev, ret >= 0 ? -EINVAL : ret, "cannot get clocks\n"); - for (i = 0; i < RSPI_CLK_NUM; i++) { - if (!strcmp(clks[i].id, "tclk")) { + for (i = 0; i < rspi->info->num_clks; i++) { + if (!strcmp(clks[i].id, rspi->info->tclk_name)) { rspi->tclk = clks[i].clk; - break; + } else if (rspi->info->find_pclk_rate && + !strcmp(clks[i].id, "pclk")) { + rspi->pclk = clks[i].clk; } } if (!rspi->tclk) return dev_err_probe(dev, -EINVAL, "Failed to get tclk\n"); - tclk_rate = clk_get_rate(rspi->tclk); - rspi->resets[0].id = "presetn"; rspi->resets[1].id = "tresetn"; - ret = devm_reset_control_bulk_get_exclusive(dev, RSPI_RESET_NUM, - rspi->resets); + ret = devm_reset_control_bulk_get_optional_exclusive(dev, RSPI_RESET_NUM, + rspi->resets); if (ret) return dev_err_probe(dev, ret, "cannot get resets\n"); @@ -407,15 +598,29 @@ static int rzv2h_rspi_probe(struct platform_device *pdev) } controller->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | - SPI_LSB_FIRST; + SPI_LSB_FIRST | SPI_LOOP; controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); controller->prepare_message = rzv2h_rspi_prepare_message; controller->unprepare_message = rzv2h_rspi_unprepare_message; controller->num_chipselect = 4; controller->transfer_one = rzv2h_rspi_transfer_one; + + tclk_rate = clk_round_rate(rspi->tclk, 0); + if (tclk_rate < 0) { + ret = tclk_rate; + goto quit_resets; + } + controller->min_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate, RSPI_SPBR_SPR_MAX, RSPI_SPCMD_BRDV_MAX); + + tclk_rate = clk_round_rate(rspi->tclk, ULONG_MAX); + if (tclk_rate < 0) { + ret = tclk_rate; + goto quit_resets; + } + controller->max_speed_hz = rzv2h_rspi_calc_bitrate(tclk_rate, RSPI_SPBR_SPR_MIN, RSPI_SPCMD_BRDV_MIN); @@ -445,8 +650,24 @@ static void rzv2h_rspi_remove(struct platform_device *pdev) reset_control_bulk_assert(RSPI_RESET_NUM, rspi->resets); } +static const struct rzv2h_rspi_info rzv2h_info = { + .find_tclk_rate = rzv2h_rspi_find_rate_fixed, + .tclk_name = "tclk", + .fifo_size = 16, + .num_clks = 3, +}; + +static const struct rzv2h_rspi_info rzt2h_info = { + .find_tclk_rate = rzv2h_rspi_find_rate_variable, + .find_pclk_rate = rzv2h_rspi_find_rate_fixed, + .tclk_name = "pclkspi", + .fifo_size = 4, + .num_clks = 2, +}; + static const struct of_device_id rzv2h_rspi_match[] = { - { .compatible = "renesas,r9a09g057-rspi" }, + { .compatible = "renesas,r9a09g057-rspi", &rzv2h_info }, + { .compatible = "renesas,r9a09g077-rspi", &rzt2h_info }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzv2h_rspi_match); diff --git a/drivers/spi/spi-sg2044-nor.c b/drivers/spi/spi-sg2044-nor.c index af48b1fcda93..37f1cfe10be4 100644 --- a/drivers/spi/spi-sg2044-nor.c +++ b/drivers/spi/spi-sg2044-nor.c @@ -42,6 +42,7 @@ #define SPIFMC_TRAN_CSR_TRAN_MODE_RX BIT(0) #define SPIFMC_TRAN_CSR_TRAN_MODE_TX BIT(1) #define SPIFMC_TRAN_CSR_FAST_MODE BIT(3) +#define SPIFMC_TRAN_CSR_BUS_WIDTH_MASK GENMASK(5, 4) #define SPIFMC_TRAN_CSR_BUS_WIDTH_1_BIT (0x00 << 4) #define SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT (0x01 << 4) #define SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT (0x02 << 4) @@ -122,8 +123,7 @@ static u32 sg2044_spifmc_init_reg(struct sg2044_spifmc *spifmc) reg = readl(spifmc->io_base + SPIFMC_TRAN_CSR); reg &= ~(SPIFMC_TRAN_CSR_TRAN_MODE_MASK | SPIFMC_TRAN_CSR_FAST_MODE | - SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT | - SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT | + SPIFMC_TRAN_CSR_BUS_WIDTH_MASK | SPIFMC_TRAN_CSR_DMA_EN | SPIFMC_TRAN_CSR_ADDR_BYTES_MASK | SPIFMC_TRAN_CSR_WITH_CMD | diff --git a/drivers/spi/spi-tegra210-quad.c b/drivers/spi/spi-tegra210-quad.c index 3be7499db21e..cdc3cb7c01f9 100644 --- a/drivers/spi/spi-tegra210-quad.c +++ b/drivers/spi/spi-tegra210-quad.c @@ -1019,13 +1019,20 @@ static void tegra_qspi_dump_regs(struct tegra_qspi *tqspi) tegra_qspi_readl(tqspi, QSPI_FIFO_STATUS)); } +static void tegra_qspi_reset(struct tegra_qspi *tqspi) +{ + if (device_reset(tqspi->dev) < 0) { + dev_warn_once(tqspi->dev, "device reset failed\n"); + tegra_qspi_mask_clear_irq(tqspi); + } +} + static void tegra_qspi_handle_error(struct tegra_qspi *tqspi) { dev_err(tqspi->dev, "error in transfer, fifo status 0x%08x\n", tqspi->status_reg); tegra_qspi_dump_regs(tqspi); tegra_qspi_flush_fifos(tqspi, true); - if (device_reset(tqspi->dev) < 0) - dev_warn_once(tqspi->dev, "device reset failed\n"); + tegra_qspi_reset(tqspi); } static void tegra_qspi_transfer_end(struct spi_device *spi) @@ -1041,6 +1048,49 @@ static void tegra_qspi_transfer_end(struct spi_device *spi) tegra_qspi_writel(tqspi, tqspi->def_command1_reg, QSPI_COMMAND1); } +static irqreturn_t handle_cpu_based_xfer(struct tegra_qspi *tqspi); +static irqreturn_t handle_dma_based_xfer(struct tegra_qspi *tqspi); + +/** + * tegra_qspi_handle_timeout - Handle transfer timeout with hardware check + * @tqspi: QSPI controller instance + * + * When a timeout occurs but hardware has completed the transfer (interrupt + * was lost or delayed), manually trigger transfer completion processing. + * This avoids failing transfers that actually succeeded. + * + * Returns: 0 if transfer was completed, -ETIMEDOUT if real timeout + */ +static int tegra_qspi_handle_timeout(struct tegra_qspi *tqspi) +{ + irqreturn_t ret; + u32 status; + + /* Check if hardware actually completed the transfer */ + status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS); + if (!(status & QSPI_RDY)) + return -ETIMEDOUT; + + /* + * Hardware completed but interrupt was lost/delayed. Manually + * process the completion by calling the appropriate handler. + */ + dev_warn_ratelimited(tqspi->dev, + "QSPI interrupt timeout, but transfer complete\n"); + + /* Clear the transfer status */ + status = tegra_qspi_readl(tqspi, QSPI_TRANS_STATUS); + tegra_qspi_writel(tqspi, status, QSPI_TRANS_STATUS); + + /* Manually trigger completion handler */ + if (!tqspi->is_curr_dma_xfer) + ret = handle_cpu_based_xfer(tqspi); + else + ret = handle_dma_based_xfer(tqspi); + + return (ret == IRQ_HANDLED) ? 0 : -EIO; +} + static u32 tegra_qspi_cmd_config(bool is_ddr, u8 bus_width, u8 len) { u32 cmd_config = 0; @@ -1072,6 +1122,30 @@ static u32 tegra_qspi_addr_config(bool is_ddr, u8 bus_width, u8 len) return addr_config; } +static void tegra_qspi_dma_stop(struct tegra_qspi *tqspi) +{ + u32 value; + + if ((tqspi->cur_direction & DATA_DIR_TX) && tqspi->tx_dma_chan) + dmaengine_terminate_all(tqspi->tx_dma_chan); + + if ((tqspi->cur_direction & DATA_DIR_RX) && tqspi->rx_dma_chan) + dmaengine_terminate_all(tqspi->rx_dma_chan); + + value = tegra_qspi_readl(tqspi, QSPI_DMA_CTL); + value &= ~QSPI_DMA_EN; + tegra_qspi_writel(tqspi, value, QSPI_DMA_CTL); +} + +static void tegra_qspi_pio_stop(struct tegra_qspi *tqspi) +{ + u32 value; + + value = tegra_qspi_readl(tqspi, QSPI_COMMAND1); + value &= ~QSPI_PIO; + tegra_qspi_writel(tqspi, value, QSPI_COMMAND1); +} + static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, struct spi_message *msg) { @@ -1079,7 +1153,7 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, struct spi_transfer *xfer; struct spi_device *spi = msg->spi; u8 transfer_phase = 0; - u32 cmd1 = 0, dma_ctl = 0; + u32 cmd1 = 0; int ret = 0; u32 address_value = 0; u32 cmd_config = 0, addr_config = 0; @@ -1146,41 +1220,28 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, QSPI_DMA_TIMEOUT); if (WARN_ON_ONCE(ret == 0)) { - dev_err_ratelimited(tqspi->dev, - "QSPI Transfer failed with timeout\n"); - if (tqspi->is_curr_dma_xfer) { - if ((tqspi->cur_direction & DATA_DIR_TX) && - tqspi->tx_dma_chan) - dmaengine_terminate_all(tqspi->tx_dma_chan); - if ((tqspi->cur_direction & DATA_DIR_RX) && - tqspi->rx_dma_chan) - dmaengine_terminate_all(tqspi->rx_dma_chan); - } - - /* Abort transfer by resetting pio/dma bit */ - if (!tqspi->is_curr_dma_xfer) { - cmd1 = tegra_qspi_readl - (tqspi, - QSPI_COMMAND1); - cmd1 &= ~QSPI_PIO; - tegra_qspi_writel - (tqspi, cmd1, - QSPI_COMMAND1); - } else { - dma_ctl = tegra_qspi_readl - (tqspi, - QSPI_DMA_CTL); - dma_ctl &= ~QSPI_DMA_EN; - tegra_qspi_writel(tqspi, dma_ctl, - QSPI_DMA_CTL); + /* + * Check if hardware completed the transfer + * even though interrupt was lost or delayed. + * If so, process the completion and continue. + */ + ret = tegra_qspi_handle_timeout(tqspi); + if (ret < 0) { + /* Real timeout - clean up and fail */ + dev_err(tqspi->dev, "transfer timeout\n"); + + /* Abort transfer by resetting pio/dma bit */ + if (tqspi->is_curr_dma_xfer) + tegra_qspi_dma_stop(tqspi); + else + tegra_qspi_pio_stop(tqspi); + + /* Reset controller if timeout happens */ + tegra_qspi_reset(tqspi); + + ret = -EIO; + goto exit; } - - /* Reset controller if timeout happens */ - if (device_reset(tqspi->dev) < 0) - dev_warn_once(tqspi->dev, - "device reset failed\n"); - ret = -EIO; - goto exit; } if (tqspi->tx_status || tqspi->rx_status) { @@ -1200,11 +1261,13 @@ static int tegra_qspi_combined_seq_xfer(struct tegra_qspi *tqspi, tegra_qspi_transfer_end(spi); spi_transfer_delay_exec(xfer); } + tqspi->curr_xfer = NULL; transfer_phase++; } ret = 0; exit: + tqspi->curr_xfer = NULL; msg->status = ret; return ret; @@ -1269,16 +1332,23 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi, ret = wait_for_completion_timeout(&tqspi->xfer_completion, QSPI_DMA_TIMEOUT); if (WARN_ON(ret == 0)) { - dev_err(tqspi->dev, "transfer timeout\n"); - if (tqspi->is_curr_dma_xfer) { - if ((tqspi->cur_direction & DATA_DIR_TX) && tqspi->tx_dma_chan) - dmaengine_terminate_all(tqspi->tx_dma_chan); - if ((tqspi->cur_direction & DATA_DIR_RX) && tqspi->rx_dma_chan) - dmaengine_terminate_all(tqspi->rx_dma_chan); + /* + * Check if hardware completed the transfer even though + * interrupt was lost or delayed. If so, process the + * completion and continue. + */ + ret = tegra_qspi_handle_timeout(tqspi); + if (ret < 0) { + /* Real timeout - clean up and fail */ + dev_err(tqspi->dev, "transfer timeout\n"); + + if (tqspi->is_curr_dma_xfer) + tegra_qspi_dma_stop(tqspi); + + tegra_qspi_handle_error(tqspi); + ret = -EIO; + goto complete_xfer; } - tegra_qspi_handle_error(tqspi); - ret = -EIO; - goto complete_xfer; } if (tqspi->tx_status || tqspi->rx_status) { @@ -1290,6 +1360,8 @@ static int tegra_qspi_non_combined_seq_xfer(struct tegra_qspi *tqspi, msg->actual_length += xfer->len + dummy_bytes; complete_xfer: + tqspi->curr_xfer = NULL; + if (ret < 0) { tegra_qspi_transfer_end(spi); spi_transfer_delay_exec(xfer); @@ -1395,6 +1467,7 @@ static irqreturn_t handle_cpu_based_xfer(struct tegra_qspi *tqspi) tegra_qspi_calculate_curr_xfer_param(tqspi, t); tegra_qspi_start_cpu_based_transfer(tqspi, t); exit: + tqspi->curr_xfer = NULL; spin_unlock_irqrestore(&tqspi->lock, flags); return IRQ_HANDLED; } @@ -1480,6 +1553,15 @@ static irqreturn_t tegra_qspi_isr_thread(int irq, void *context_data) { struct tegra_qspi *tqspi = context_data; + /* + * Occasionally the IRQ thread takes a long time to wake up (usually + * when the CPU that it's running on is excessively busy) and we have + * already reached the timeout before and cleaned up the timed out + * transfer. Avoid any processing in that case and bail out early. + */ + if (!tqspi->curr_xfer) + return IRQ_NONE; + tqspi->status_reg = tegra_qspi_readl(tqspi, QSPI_FIFO_STATUS); if (tqspi->cur_direction & DATA_DIR_TX) diff --git a/drivers/spi/spi-tle62x0.c b/drivers/spi/spi-tle62x0.c index a565352f6381..663c0136d119 100644 --- a/drivers/spi/spi-tle62x0.c +++ b/drivers/spi/spi-tle62x0.c @@ -141,7 +141,7 @@ static ssize_t tle62x0_gpio_show(struct device *dev, value = (st->gpio_state >> gpio_num) & 1; mutex_unlock(&st->lock); - return sysfs_emit(buf, "%d", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t tle62x0_gpio_store(struct device *dev, diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 5300c942a2a4..9a0160f6dc3d 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -704,6 +704,7 @@ static const struct class spidev_class = { */ static const struct spi_device_id spidev_spi_ids[] = { { .name = /* abb */ "spi-sensor" }, + { .name = /* arduino */ "unoq-mcu" }, { .name = /* cisco */ "spi-petra" }, { .name = /* dh */ "dhcom-board" }, { .name = /* elgin */ "jg10309-01" }, @@ -737,6 +738,7 @@ static int spidev_of_check(struct device *dev) static const struct of_device_id spidev_dt_ids[] = { { .compatible = "abb,spi-sensor", .data = &spidev_of_check }, + { .compatible = "arduino,unoq-mcu", .data = &spidev_of_check }, { .compatible = "cisco,spi-petra", .data = &spidev_of_check }, { .compatible = "dh,dhcom-board", .data = &spidev_of_check }, { .compatible = "elgin,jg10309-01", .data = &spidev_of_check }, |
