From 4504043cad841ed69c4d25b8d2a77452cc8c113f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chuangyifeng=E2=80=9D?= Date: Tue, 21 May 2024 10:39:21 +0800 Subject: [PATCH 011/219] feat(bootspi):Add bootspi flash driver Changelogs: 1.Added bootspi flash driver --- arch/riscv/configs/win2030_defconfig | 1 + drivers/spi/Kconfig | 6 + drivers/spi/Makefile | 1 + drivers/spi/spi-eswin-bootspi.c | 658 +++++++++++++++++++++++++++ 4 files changed, 666 insertions(+) create mode 100644 drivers/spi/spi-eswin-bootspi.c diff --git a/arch/riscv/configs/win2030_defconfig b/arch/riscv/configs/win2030_defconfig index 725ad2400def..6733030403b9 100644 --- a/arch/riscv/configs/win2030_defconfig +++ b/arch/riscv/configs/win2030_defconfig @@ -139,6 +139,7 @@ CONFIG_SPI=y CONFIG_SPI_DESIGNWARE=y CONFIG_SPI_DW_DMA=y CONFIG_SPI_DW_MMIO=y +CONFIG_SPI_ESWIN_BOOTSPI=y # CONFIG_PTP_1588_CLOCK is not set CONFIG_PINCTRL=y CONFIG_PINCTRL_EIC7700=y diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 3ce0fd5df8e9..07e874de0ebf 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -360,6 +360,12 @@ config SPI_EP93XX This enables using the Cirrus EP93xx SPI controller in master mode. +config SPI_ESWIN_BOOTSPI + tristate "Eswin Computing Boot SPI controller" + help + This enables using the Eswin Computing Boot SPI controller in master + mode. + config SPI_FALCON bool "Falcon SPI controller support" depends on SOC_FALCON diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 6af54842b9fa..dc4c98c2dd90 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_SPI_DW_BT1) += spi-dw-bt1.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-pci.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o +obj-$(CONFIG_SPI_ESWIN_BOOTSPI) += spi-eswin-bootspi.o obj-$(CONFIG_SPI_FALCON) += spi-falcon.o obj-$(CONFIG_SPI_FSI) += spi-fsi.o obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o diff --git a/drivers/spi/spi-eswin-bootspi.c b/drivers/spi/spi-eswin-bootspi.c new file mode 100644 index 000000000000..c8fbc1b3fc91 --- /dev/null +++ b/drivers/spi/spi-eswin-bootspi.c @@ -0,0 +1,658 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ESWIN BootSpi Driver + * + * Copyright 2024, Beijing ESWIN Computing Technology Co., Ltd.. All rights reserved. + * SPDX-License-Identifier: GPL-2.0 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: HuangYiFeng + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register offsets */ +#define ES_SPI_CSR_00 0x00 /*WRITE_STATUS_REG_TIME*/ +#define ES_SPI_CSR_01 0x04 /*SPI_BUS_MODE*/ +#define ES_SPI_CSR_02 0x08 /*ERASE_COUNTER_TAP*/ +#define ES_SPI_CSR_03 0x0c /*DMA_EN_HCLK_STATUS*/ +#define ES_SPI_CSR_04 0x10 /*FAST_READ_CONTROL*/ +#define ES_SPI_CSR_05 0x14 /*SPI_FLASH_WR_NUM*/ +#define ES_SPI_CSR_06 0x18 /*SPI_FLASH_COMMAND*/ +#define ES_SPI_CSR_07 0x1c /*INTERRUPT_CONTROL*/ +#define ES_SPI_CSR_08 0x20 /*DMA_REQUEST_TAP*/ +#define ES_SPI_CSR_09 0x24 /*SPI_FLASH_WR_ADDRESS*/ +#define ES_SPI_CSR_10 0x28 /*PAGE_PROGRAM_TIME*/ +#define ES_SPI_CSR_11 0x2c /*SECTOR_ERASE_TIME*/ +#define ES_SPI_CSR_12 0x30 /*SMALL_BLOCK_ERASE_TIME*/ +#define ES_SPI_CSR_13 0x34 /*LARGE_BLOCK_ERASE_TIME*/ +#define ES_SPI_CSR_14 0x38 /*CHIP_ERASE_TIME*/ +#define ES_SPI_CSR_15 0x3c /*CHIP_DESELECT_TIME*/ +#define ES_SPI_CSR_16 0x40 /*POWER_DOWN_TIME*/ + +#define ES_SYSCSR_SPIMODECFG 0x340 + +#define ES_CONCSR_SPI_INTSEL 0x3c0 + +#define SPI_COMMAND_VALID 0x01 +#define SPI_COMMAND_MOVE_VALUE 0x00 +#define SPI_COMMAND_CODE_FIELD_POSITION 0X06 +#define SPI_COMMAND_MOVE_FIELD_POSITION 0X05 +#define SPI_COMMAND_TYPE_FIELD_POSITION 0X01 + +/* Bit fields in CTRLR0 */ +/* + * Only present when SSI_MAX_XFER_SIZE=16. This is the default, and the only + * option before version 3.23a. + */ +#define SPI_INTSEL_MASK GENMASK(11, 10) +#define INT_ROUTED_U84 0x0 +#define INT_ROUTED_LPCPU 0x1 +#define INT_ROUTED_SCPU 0x3u + +#define RX_TIMEOUT 5000 /* timeout in ms */ + +#define SPI_COMMAND_INIT_VALUE 0XFFFFC000 +#define FLASH_PAGE_SIZE 0x100 + +typedef enum { + SPI_FLASH_WR_BYTE = 1, + SPI_FLASH_WR_2BYTE = 2, + SPI_FLASH_WR_WORD = 4, +} SPI_FLASH_WR_NUM_T; + +typedef enum { + SPI_FAST_READ_DEFAULT = 0, + SPI_FAST_READ_ENABLE = 3 /*WHEN SPI QUAD0 OR DUAL MODE*/ +} SPI_FAST_READ_CTL_T; + +typedef enum { STANDARD_SPI = 0, DUAL_SPI, QUAD_SPI } SPI_BUS_MODE_T; + +typedef enum { + SPIC_CMD_TYPE_SPI_PROGRAM = 0, + SPIC_CMD_TYPE_WRITE_STATUS_REGISTER, + SPIC_CMD_TYPE_READ_STATUS_REGISTER, + SPIC_CMD_TYPE_SECTOR_ERASE, + SPIC_CMD_TYPE_BLOCK_ERASE_TYPE1, + SPIC_CMD_TYPE_BLOCK_ERASE_TYPE2, + SPIC_CMD_TYPE_CHIP_ERASE, + SPIC_CMD_TYPE_POWER_DOWN, + SPIC_CMD_TYPE_RELEASE_POWER_DOWM, + SPIC_CMD_TYPE_ENTER_OR_EXIT_32BIT_MODE, + SPIC_CMD_TYPE_READ_SECURITY_REG, + SPIC_CMD_TYPE_ERASE_SECURITY_REG, + SPIC_CMD_TYPE_WRITE_SECURITY_REG, + SPIC_CMD_TYPE_READ_DATA, + SPIC_CMD_TYPE_READ_MANUFACTURED_ID, + SPIC_CMD_TYPE_READ_JEDEC_ID +} SPI_FLASH_COMMAND_TYPE_T; + +#define SPIC_CMD_CODE_POWER_DOWN 0xb9 +#define SPIC_CMD_CODE_RELEASE_POWER_DOWN 0xab +#define SPIC_CMD_CODE_ENABLE_RESET 0x66 +#define SPIC_CMD_CODE_RESET 0x99 + +struct es_spi_priv { + struct clk *cfg_clk; + struct clk *clk; + struct reset_control *rstc; + struct gpio_desc *cs_gpio; /* External chip-select gpio */ + + void __iomem *regs; + void __iomem *sys_regs; + void __iomem *flash_base; + unsigned int freq; /* Default frequency */ + unsigned int mode; + + const void *tx; + u32 opcode; + u32 cmd_type; + u64 addr; + void *rx; + u32 fifo_len; /* depth of the FIFO buffer */ + u32 max_xfer; /* Maximum transfer size (in bits) */ + + int bits_per_word; + int len; + u8 cs; /* chip select pin */ + u8 tmode; /* TR/TO/RO/EEPROM */ + u8 type; /* SPI/SSP/MicroWire */ + struct spi_controller *master; + struct device *dev; + int irq; +}; + +static inline u32 eswin_bootspi_read(struct es_spi_priv *priv, u32 offset) +{ + return readl(priv->regs + offset); +} + +static inline void eswin_bootspi_write(struct es_spi_priv *priv, u32 offset, u32 val) +{ + writel(val, priv->regs + offset); +} + +static inline u32 eswin_bootspi_data_read(struct es_spi_priv *priv, u32 offset) +{ + return readl(priv->flash_base + offset); +} + +static inline void eswin_bootspi_data_write(struct es_spi_priv *priv, u32 offset, u32 val) +{ + writel(val, priv->flash_base + offset); +} + +static int eswin_bootspi_wait_over(struct es_spi_priv *priv) +{ + u32 val; + struct device *dev = priv->dev; + + if (readl_poll_timeout(priv->regs + ES_SPI_CSR_06, val, + (!(val & 0x1)), 10, RX_TIMEOUT * 1000)) { + dev_err(dev, "eswin_bootspi_wait_over : timeout!!\n"); + return -ETIMEDOUT; + } + return 0; +} + +/** + * @brief spi read and write cfg + */ +static void eswin_bootspi_read_write_cfg(struct es_spi_priv *priv, u32 byte, u32 addr) +{ + eswin_bootspi_write(priv, ES_SPI_CSR_09, addr); + eswin_bootspi_write(priv, ES_SPI_CSR_05, byte); + eswin_bootspi_write(priv, ES_SPI_CSR_04, SPI_FAST_READ_DEFAULT); + eswin_bootspi_write(priv, ES_SPI_CSR_01, STANDARD_SPI); +} + +/** + * @brief write data from dest address to flash + */ +static void eswin_bootspi_send_data(struct es_spi_priv *priv, + const u8 *dest, u32 size) +{ + u32 offset = 0; + u8 *buff = (u8 *)dest; + u32 data = 0; + int i; + struct device *dev = priv->dev; + + dev_dbg(dev,"wrtie spi data\n"); + while (size >= SPI_FLASH_WR_WORD) { + data = (buff[0]) | (buff[1] << 8) |(buff[2] << 16) | (buff[3] << 24); + for (i = 0; i < 4; i++) { + dev_dbg(dev,"0x%x ", buff[i]); + } + dev_dbg(dev,"0x%x ", data); + eswin_bootspi_data_write(priv, offset, data); + offset = offset + SPI_FLASH_WR_WORD; + size = size - SPI_FLASH_WR_WORD; + buff = buff + SPI_FLASH_WR_WORD; + } + data = 0; + if (size != 0) { + for (i = 0; i < size; i++) { + data |=buff[i] << (8 * i); + dev_dbg(dev,"0x%x ", buff[i]); + } + dev_dbg(dev,"0x%x ", data); + eswin_bootspi_data_write(priv, offset, data); + } +} + +/** + * @brief Read data from flash to dest address + */ +static void eswin_bootspi_recv_data(struct es_spi_priv *priv, u8 *dest, u32 size) +{ + u32 offset = 0; + u8 *buff = NULL; + u32 data = 0xFFFFFFFF; + int i; + struct device *dev = priv->dev; + + dev_dbg(dev,"read spi data\n"); + while (size >= SPI_FLASH_WR_WORD) { + buff = (u8 *)dest; + data = eswin_bootspi_data_read(priv, offset); + dev_dbg(dev,"0x%x ", data); + for (i = 0; i < SPI_FLASH_WR_WORD; i++) { + *buff = (u8)(data >> (8 * i)); + dev_dbg(dev,"0x%x ", *buff); + buff++; + } + dest = dest + SPI_FLASH_WR_WORD; + offset = offset + SPI_FLASH_WR_WORD; + size = size - SPI_FLASH_WR_WORD; + } + if (size != 0) { + buff = (u8 *)dest; + data = eswin_bootspi_data_read(priv, offset); + for (i = 0; i < size; i++) { + *buff = (u8)(data >> (8 * i)); + dev_dbg(dev,"0x%x ", *buff); + buff++; + } + } + dev_dbg(dev,"\n"); +} + + +/** + * @brief spi send command + */ +static void eswin_bootspi_cmd_cfg(struct es_spi_priv *priv, u32 code, u32 type) +{ + u32 command = eswin_bootspi_read(priv, ES_SPI_CSR_06); + struct device *dev = priv->dev; + + command &= ~((0xFF << 6) | (0x1 << 5) | (0xF << 1) | 0x1); + command |= ((code << SPI_COMMAND_CODE_FIELD_POSITION) | + (SPI_COMMAND_MOVE_VALUE << SPI_COMMAND_MOVE_FIELD_POSITION) | + (type << SPI_COMMAND_TYPE_FIELD_POSITION) | SPI_COMMAND_VALID); + + eswin_bootspi_write(priv, ES_SPI_CSR_06, command); + dev_dbg(dev, "[%s %d]: write command 0x%x, read back command 0x%x\n", + __func__,__LINE__, command, eswin_bootspi_read(priv, ES_SPI_CSR_06)); +} +/** + * @brief spi write flash + * @param [in] offset: address of flash to be write + * @param [in] wr_dest: Address of data to be sent + * @param [in] size: size of flash to be write + */ +void eswin_bootspi_writer(struct es_spi_priv *priv) +{ + u32 write_size = 0, offset, cmd_code; + u32 cmd_type = priv->cmd_type; + const u8 *wr_dest = priv->tx; + int size = priv->len; + + offset = priv->addr; + cmd_code = priv->opcode; + + if (size == 0) { + // if(SPIC_CMD_TYPE_SECTOR_ERASE == cmd_type) + { + eswin_bootspi_read_write_cfg(priv, write_size, offset); + eswin_bootspi_cmd_cfg(priv, cmd_code, cmd_type); + eswin_bootspi_wait_over(priv); + } + } + while (size > 0) { + write_size = size; + if (write_size > FLASH_PAGE_SIZE) { + write_size = FLASH_PAGE_SIZE; + } + eswin_bootspi_read_write_cfg(priv, write_size, offset); + eswin_bootspi_send_data(priv, wr_dest, write_size); + eswin_bootspi_cmd_cfg(priv, cmd_code, cmd_type); + eswin_bootspi_wait_over(priv); + wr_dest += write_size; + offset += write_size; + size = size - write_size; + } +} + +static void eswin_bootspi_reader(struct es_spi_priv *priv) +{ + int read_size = 0; + u32 offset = priv->addr; + u32 cmd_code = priv->opcode; + u32 cmd_type = priv->cmd_type; + u8 *mem_dest = priv->rx; + int size = priv->len; + + while (size > 0) { + read_size = size; + if (read_size > FLASH_PAGE_SIZE) { + read_size = FLASH_PAGE_SIZE; + } + + eswin_bootspi_read_write_cfg(priv, read_size, offset); + eswin_bootspi_cmd_cfg(priv, cmd_code, cmd_type); + eswin_bootspi_wait_over(priv); + eswin_bootspi_recv_data(priv, mem_dest, read_size); + mem_dest += read_size; + offset += read_size; + size = size - read_size; + } +} + +/* + * We define external_cs_manage function as 'weak' as some targets + * (like MSCC Ocelot) don't control the external CS pin using a GPIO + * controller. These SoCs use specific registers to control by + * software the SPI pins (and especially the CS). + */ + +static void external_cs_manage(struct es_spi_priv *priv, bool on) +{ + gpiod_set_value(priv->cs_gpio, on ? 1 : 0); +} + +/* The size of ctrl1 limits data transfers to 64K */ +static int eswin_bootspi_adjust_op_size(struct spi_mem *mem, + struct spi_mem_op *op) +{ + op->data.nbytes = min(op->data.nbytes, (unsigned int)SZ_64K); + + return 0; +} + +/* + * The controller only supports Standard SPI mode, Duall mode and + * Quad mode. Double sanitize the ops here to avoid OOB access. + */ +static bool eswin_bootspi_supports_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + return spi_mem_default_supports_op(mem, op); +} + +static int eswin_bootspi_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op) +{ + bool read = op->data.dir == SPI_MEM_DATA_IN; + int ret = 0; + struct es_spi_priv *priv = spi_master_get_devdata(mem->spi->master); + struct device *dev = priv->dev; + + priv->addr = op->addr.val; + priv->opcode = op->cmd.opcode; + + dev_dbg(dev, "\n[%s %d]: addr=0x%llx opcode=0x%x\n", __func__,__LINE__, + priv->addr, priv->opcode); + + if ( priv->opcode == SPINOR_OP_WREN + || priv->opcode == SPINOR_OP_WRDI) + return 0; + + switch(priv->opcode) { + case SPINOR_OP_RDID: + case SPINOR_OP_RDSFDP: + priv->cmd_type = SPIC_CMD_TYPE_READ_JEDEC_ID; + break; + case SPINOR_OP_BE_4K: + case SPINOR_OP_BE_4K_PMC: + priv->opcode = SPINOR_OP_BE_4K; + priv->cmd_type = SPIC_CMD_TYPE_SECTOR_ERASE; + break; + case SPINOR_OP_BE_32K: + priv->cmd_type = SPIC_CMD_TYPE_BLOCK_ERASE_TYPE1; + break; + case SPINOR_OP_SE: + priv->cmd_type = SPIC_CMD_TYPE_BLOCK_ERASE_TYPE2; + break; + case SPINOR_OP_CHIP_ERASE: + priv->cmd_type = SPIC_CMD_TYPE_CHIP_ERASE; + break; + case SPINOR_OP_PP: + case SPINOR_OP_PP_1_1_4: + case SPINOR_OP_PP_1_4_4: + case SPINOR_OP_PP_1_1_8: + case SPINOR_OP_PP_1_8_8: + priv->opcode = SPINOR_OP_PP; + priv->cmd_type = SPIC_CMD_TYPE_SPI_PROGRAM; + break; + case SPINOR_OP_READ: + case SPINOR_OP_READ_FAST: + case SPINOR_OP_READ_1_1_2: + case SPINOR_OP_READ_1_2_2: + case SPINOR_OP_READ_1_1_4: + case SPINOR_OP_READ_1_4_4: + case SPINOR_OP_READ_1_1_8: + case SPINOR_OP_READ_1_8_8: + priv->opcode = SPINOR_OP_READ; + priv->cmd_type = SPIC_CMD_TYPE_READ_DATA; + break; + case SPINOR_OP_RDSR: + case SPINOR_OP_RDSR2: + priv->cmd_type = SPIC_CMD_TYPE_READ_STATUS_REGISTER; + break; + case SPINOR_OP_WRSR: + case SPINOR_OP_WRSR2: + priv->cmd_type = SPIC_CMD_TYPE_WRITE_STATUS_REGISTER; + break; + case SPIC_CMD_CODE_POWER_DOWN: + priv->cmd_type = SPIC_CMD_TYPE_POWER_DOWN; + break; + case SPIC_CMD_CODE_RELEASE_POWER_DOWN: + priv->cmd_type = SPIC_CMD_TYPE_RELEASE_POWER_DOWM; + break; + case SPIC_CMD_CODE_ENABLE_RESET: + case SPIC_CMD_CODE_RESET: + priv->cmd_type = SPIC_CMD_TYPE_SPI_PROGRAM; + break; + default: + dev_warn(dev, "[%s %d]: unsupport opcode = 0x%x, return sucess directly!\n", + __func__,__LINE__, priv->opcode); + return 0; + } + + dev_dbg(dev, "[%s %d]: data direction=%d, opcode = 0x%x, cmd_type 0x%x\n", + __func__,__LINE__, op->data.dir, priv->opcode, priv->cmd_type); + external_cs_manage(priv, false); + + if (read) { + priv->rx = op->data.buf.in; + priv->len = op->data.nbytes; + dev_dbg(dev, "[%s %d]: read len = %u\n", __func__,__LINE__, op->data.nbytes); + eswin_bootspi_reader(priv); + } else { + priv->tx = op->data.buf.out; + priv->len = op->data.nbytes; + /* Fill up the write fifo before starting the transfer */ + dev_dbg(dev, "[%s %d]: write len = 0x%x tx_addr 0x%px\n", __func__,__LINE__, + op->data.nbytes, priv->tx); + eswin_bootspi_writer(priv); + if (eswin_bootspi_wait_over(priv) < 0) { + dev_err(dev, "eswin_bootspi_wait_over ETIMEDOUT\n"); + ret = -ETIMEDOUT; + } + } + external_cs_manage(priv, true); + dev_dbg(dev, "%u bytes xfered\n", op->data.nbytes); + return ret; +} + +static const struct spi_controller_mem_ops eswin_bootspi_mem_ops = { + .adjust_op_size = eswin_bootspi_adjust_op_size, + .supports_op = eswin_bootspi_supports_op, + .exec_op = eswin_bootspi_exec_op, +}; + +static int eswin_bootspi_setup(struct spi_device *spi) +{ + struct es_spi_priv *priv = spi_master_get_devdata(spi->master); + struct device *dev = priv->dev; + int vaule = 0; + int ret; + + ret = clk_prepare_enable(priv->cfg_clk); + if (ret) { + dev_err(dev, "could not enable cfg clock: %d\n", ret); + goto err_cfg_clk; + } + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "could not enable clock: %d\n", ret); + goto err_clk; + } + /* set rate to 50M*/ + ret = clk_set_rate(priv->clk, 50000000); + if (ret) { + dev_err(dev, "could not enable clock: %d\n", ret); + goto err_clk; + } + + reset_control_deassert(priv->rstc); + + /* switch bootspi to cpu mode*/ + vaule = readl(priv->sys_regs + ES_SYSCSR_SPIMODECFG); + vaule |= 0x1; + writel(vaule, priv->sys_regs + ES_SYSCSR_SPIMODECFG); + + /* Basic HW init */ + eswin_bootspi_write(priv, ES_SPI_CSR_08, 0x0); + return ret; + +err_clk: + clk_disable(priv->cfg_clk); +err_cfg_clk: + return ret; +} + +static int eswin_bootspi_probe(struct platform_device *pdev) +{ + struct es_spi_priv *priv; + struct spi_controller *master; + int ret = 0; + struct device *dev = &pdev->dev; + + master = spi_alloc_master(&pdev->dev, sizeof(*priv)); + if (!master) + return -ENOMEM; + + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->setup = eswin_bootspi_setup; + master->dev.of_node = pdev->dev.of_node; + master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(8); + master->mem_ops = &eswin_bootspi_mem_ops; + master->num_chipselect = 1; + + priv = spi_master_get_devdata(master); + priv->master = master; + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + priv->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->regs)) { + dev_err(dev, "%s %d: failed to map registers\n", __func__,__LINE__); + return PTR_ERR(priv->regs); + } + + priv->sys_regs = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(priv->sys_regs)) { + dev_err(dev,"%s %d: failed to map sys registers\n", __func__, __LINE__); + return PTR_ERR(priv->sys_regs); + } + + priv->flash_base = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(priv->flash_base)) { + dev_err(dev,"%s %d: failed to map sys registers\n", __func__, __LINE__); + return PTR_ERR(priv->flash_base); + } + + priv->cfg_clk = devm_clk_get(dev, "cfg_clk"); + if (IS_ERR(priv->cfg_clk)) { + dev_err(dev, "%s %d:could not get cfg clk: %ld\n", __func__,__LINE__, + PTR_ERR(priv->cfg_clk)); + return PTR_ERR(priv->cfg_clk); + } + + priv->clk = devm_clk_get(dev, "clk"); + if (IS_ERR(priv->clk)) { + dev_err(dev, "%s %d:could not get clk: %ld\n",__func__,__LINE__, PTR_ERR(priv->rstc)); + return PTR_ERR(priv->clk); + } + priv->rstc = devm_reset_control_get_optional_exclusive(dev, "rst"); + if (IS_ERR(priv->rstc)) { + dev_err(dev, "%s %d:could not get rst: %ld\n", __func__,__LINE__, PTR_ERR(priv->rstc)); + return PTR_ERR(priv->rstc); + } + + priv->cs_gpio = devm_gpiod_get(dev, "cs", GPIOD_OUT_LOW); + if (IS_ERR(priv->cs_gpio)) { + dev_err(dev, "%s %d: couldn't request gpio! (error %ld)\n", __func__,__LINE__, + PTR_ERR(priv->cs_gpio)); + return PTR_ERR(priv->cs_gpio); + } + + priv->max_xfer = 32; + dev_info(dev, "ssi_max_xfer_size=%u\n", priv->max_xfer); + + /* Currently only bits_per_word == 8 supported */ + priv->bits_per_word = 8; + priv->tmode = 0; /* Tx & Rx */ + + if (!priv->fifo_len) { + priv->fifo_len = 256; + } + ret = devm_spi_register_controller(dev, master); + if (ret) + goto err_put_master; + + dev_info(&pdev->dev, "fifo_len %d, %s mode.\n", priv->fifo_len, priv->irq ? "irq" : "polling"); + return 0; + +err_put_master: + spi_master_put(master); + return ret; +} + +static int eswin_bootspi_remove(struct platform_device *pdev) +{ + struct es_spi_priv *priv = platform_get_drvdata(pdev); + struct spi_controller *master = priv->master; + + spi_master_put(master); + return 0; +} + +static const struct of_device_id eswin_bootspi_of_match[] = { + { .compatible = "eswin,bootspi", .data = NULL}, + { /* end of table */} +}; +MODULE_DEVICE_TABLE(of, eswin_bootspi_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id eswin_bootspi_acpi_match[] = { + {"eswin,bootspi", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, eswin_bootspi_acpi_match); +#endif + +static struct platform_driver eswin_bootspi_driver = { + .probe = eswin_bootspi_probe, + .remove = eswin_bootspi_remove, + .driver = { + .name = "eswin-bootspi", + .of_match_table = eswin_bootspi_of_match, +#ifdef CONFIG_ACPI + .acpi_match_table = eswin_bootspi_acpi_match, +#endif + }, +}; +module_platform_driver(eswin_bootspi_driver); + +MODULE_AUTHOR("Huangyifeng "); +MODULE_DESCRIPTION("Eswin Boot SPI Controller Driver for EIC770X SoCs"); +MODULE_LICENSE("GPL v2"); -- 2.47.0