/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ /* * drivers/spi/spifc.c * * Copyright (C) 2020 Amlogic, Inc. All rights reserved. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include DECLARE_GLOBAL_DATA_PTR; unsigned int spifc_flags = 0; #define spifc_dbg(fmt, args...) { \ if (spifc_flags & 1) \ printf("%s: " fmt, __func__, ## args); \ } #define spifc_dbg_buff(_buf, _len, _i) { \ if (spifc_flags & 2) { \ for (_i=0; _i<_len; _i++) \ printf("0x%x,", _buf[_i]); \ printf("\n%s: total %d\n", __func__, _len); \ } \ } struct spifc_regs { u32 cmd; #define SPI_FLASH_READ 31 #define SPI_FLASH_WREN 30 #define SPI_FLASH_WRDI 29 #define SPI_FLASH_RDID 28 #define SPI_FLASH_RDSR 27 #define SPI_FLASH_WRSR 26 #define SPI_FLASH_PP 25 #define SPI_FLASH_SE 24 #define SPI_FLASH_BE 23 #define SPI_FLASH_CE 22 #define SPI_FLASH_DP 21 #define SPI_FLASH_RES 20 #define SPI_HPM 19 #define SPI_FLASH_USR 18 #define SPI_FLASH_USR_ADDR 15 #define SPI_FLASH_USR_DUMMY 14 #define SPI_FLASH_USR_DIN 13 #define SPI_FLASH_USR_DOUT 12 #define SPI_FLASH_USR_DUMMY_BLEN 10 #define SPI_FLASH_USR_CMD 0 u32 addr; #define SPI_FLASH_BYTES_LEN 24 #define SPI_FLASH_ADDR_START 0 u32 ctrl; #define FAST_READ_QUAD_IO 24 #define FAST_READ_DUAL_IO 23 #define FAST_READ_QUAD_OUT 20 #define SPI_ENABLE_AHB 17 #define SPI_SST_AAI 16 #define SPI_RES_RID 15 #define FAST_READ_DUAL_OUT 14 #define SPI_READ_READ_EN 13 #define SPI_CLK_DIV0 12 #define SPI_CLKCNT_N 8 #define SPI_CLKCNT_H 4 #define SPI_CLKCNT_L 0 u32 ctrl1; u32 status; u32 ctrl2; u32 clock; #define SPI_CLK_DIV0_NEW 31 #define SPI_PRE_SCALE_DIV 18 #define SPI_CLKCNT_N_NEW 12 #define SPI_CLKCNT_H_NEW 6 #define SPI_CLKCNT_L_NEW 0 u32 user; #define USER_CMD_INCLUDE_CMD 31 #define USER_CMD_INCLUDE_ADDR 30 #define USER_CMD_INCLUDE_DUMMY 29 #define USER_CMD_INCLUDE_DIN 28 #define USER_CMD_INCLUDE_DOUT 27 #define USER_CMD_DUMMY_IDLE 26 #define USER_CMD_HIGHPART_DURING_SPI_DOUT_STAGE 25 #define USER_CMD_HIGHPART_DURING_SPI_DIN_STAGE 24 #define USER_CMD_EXT_HOLD_IN_STA_PREP 23 #define USER_CMD_EXT_HOLD_IN_STA_CMD 22 #define USER_CMD_EXT_HOLD_IN_STA_ADDR 21 #define USER_CMD_EXT_HOLD_IN_STA_DUMMY 20 #define USER_CMD_EXT_HOLD_IN_STA_DIN 19 #define USER_CMD_EXT_HOLD_IN_STA_DOUT 18 #define USER_CMD_EXT_HOLD_POLARITY 17 #define SINGLE_DIO_MODE 16 #define FAST_WRITE_QUAD_IO 15 #define FAST_WRITE_DUAL_IO 14 #define FAST_WRITE_QUAD_OUT 13 #define FAST_WRITE_DUAL_OUT 12 #define WRITE_BYTE_ORDER 11 #define READ_BYTE_ORDER 10 #define AHB_ENDIAN_MODE 8 #define MASTER_CLK_EDGE 7 #define SLAVE_CLK_EDGE 6 #define CS_VALID_IN_STA_PREP 5 #define CS_VALID_IN_STA_DONE 4 #define AHB_READ_APPLY_CONFIG 3 #define COMPATIBLE_TO_APPOLO 2 #define AHP_READ_SUPPORT_4BYTE_ADDR 1 #define EN_DIN_DURING_SPI_DOUT_STAGE 0 u32 user1; #define USER_CMD_ADDR_BITS 26 #define USER_CMD_DOUT_BITS 17 #define USER_CMD_DIN_BITS 8 #define USER_CMD_DUMMY_BITS 0 u32 user2; #define USER_CMD_CMD_BITS 28 #define USER_CMD_CMD_VALUE 0 u32 user3; u32 user4; //pin register #define CS_KEEP_ACTIVE_AFTER_TRANS 30 u32 slave; u32 slave1; u32 slave2; u32 slave3; u32 cache[8]; u32 buffer[8]; #define SPIFC_CACHE_SIZE_IN_WORD 16 #define SPIFC_CACHE_SIZE_IN_BYTE (SPIFC_CACHE_SIZE_IN_WORD<<2) }; struct spifc_priv { struct spifc_regs *regs; void *clk; void *pinctrl; unsigned int speed; unsigned int mode; unsigned int wordlen; unsigned char cmd; }; /* flash dual/quad read command */ #define FCMD_READ 0x03 #define FCMD_READ_FAST 0x0b #define FCMD_READ_DUAL_OUT 0x3b #define FCMD_READ_QUAD_OUT 0x6b #define FCMD_READ_DUAL_IO 0xbb #define FCMD_READ_QUAD_IO 0xeb /* flash quad write command */ #define FCMD_WRITE 0x02 #define FCMD_WRITE_QUAD_OUT 0x32 static void spifc_set_rx_op_mode( struct spifc_priv *priv, unsigned int slave_mode, unsigned char cmd) { unsigned int val; val = readl(&priv->regs->ctrl); val &= ~((1 << FAST_READ_DUAL_OUT) | (1 << FAST_READ_QUAD_OUT) | (1 << FAST_READ_DUAL_IO) | (1 << FAST_READ_QUAD_IO)); if (slave_mode & SPI_RX_DUAL) { if (cmd == FCMD_READ_DUAL_OUT) val |= 1<regs->ctrl); } static void spifc_set_tx_op_mode( struct spifc_priv *priv, unsigned int slave_mode, unsigned char cmd) { unsigned int val = 0; if (slave_mode & SPI_TX_QUAD) { if (cmd == FCMD_WRITE_QUAD_OUT) val |= 1 << FAST_WRITE_QUAD_OUT; } writel(val, &priv->regs->user); } static int spifc_user_cmd( struct spifc_priv *priv, u8 cmd, u8 *buf, u8 len) { struct spifc_regs *regs = priv->regs; u16 bits = len ? ((len << 3) - 1) : 0; u32 addr = 0; if (buf) addr = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; spifc_dbg("cmd=0x%x, len=%d, addr=0x%08x\n", cmd, len, addr); clrbits_le32(®s->ctrl, (1<user); writel((7 << USER_CMD_CMD_BITS) | (cmd << USER_CMD_CMD_VALUE), ®s->user2); writel(bits << USER_CMD_ADDR_BITS, ®s->user1); writel(addr << SPI_FLASH_ADDR_START, ®s->addr); writel((1 << SPI_FLASH_USR) | (cmd << SPI_FLASH_USR_CMD), ®s->cmd); while ((readl(®s->cmd) >> SPI_FLASH_USR) & 1); return 0; } static int spifc_user_cmd_dout( struct spifc_priv *priv, u8 *buf, int len, unsigned long flags) { struct spifc_regs *regs = priv->regs; volatile unsigned int *cache; u32 *p; int len32, i; p = (u32 *)buf; cache = (volatile unsigned int *)®s->cache; len32 = (len/4) + !!(len%4); for (i=0; iuser, 1 << USER_CMD_INCLUDE_DOUT); writel(0, ®s->user2); writel(((len << 3) - 1) << USER_CMD_DOUT_BITS, ®s->user1); writel(0, ®s->addr); writel(1 << SPI_FLASH_USR, ®s->cmd); while ((readl(®s->cmd) >> SPI_FLASH_USR) & 1); spifc_dbg_buff(buf, len, i); return 0; } static int spifc_user_cmd_din( struct spifc_priv *priv, u8 *buf, int len, unsigned long flags) { struct spifc_regs *regs = priv->regs; volatile unsigned int *cache; u32 *p; int len32, i; u8 temp_buf[SPIFC_CACHE_SIZE_IN_BYTE]; writel(1 << USER_CMD_INCLUDE_DIN, ®s->user); writel(0, ®s->user2); writel(((len << 3) - 1) << USER_CMD_DIN_BITS, ®s->user1); writel(0, ®s->addr); writel(1 << SPI_FLASH_USR, ®s->cmd); while ((readl(®s->cmd) >> SPI_FLASH_USR) & 1); p = (u32 *)temp_buf; cache = (volatile unsigned int *)®s->cache; len32 = (len/4) + !!(len%4); for (i=0; ipinctrl_enable) plat->pinctrl_enable(priv->pinctrl, 1); return 0; } static int spifc_release_bus(struct udevice *bus) { struct spifc_platdata *plat = dev_get_platdata(bus); struct spifc_priv *priv = dev_get_priv(bus); spifc_dbg("pinctrl disable\n"); if (plat->pinctrl_enable) plat->pinctrl_enable(priv->pinctrl, 0); return 0; } static int spifc_cs_gpios_init( struct spifc_priv *priv, struct spifc_platdata *plat) { int gpio; int i; if (!plat->cs_gpios) return 0; for (i=0; inum_chipselect; i++) { gpio = plat->cs_gpios[i]; if (gpio_request(gpio, "spifc_cs")) { printf("%s: requesting pin %u failed\n", __func__, gpio); return -1; } /* It's unsuitable to set cs high here, but unfortunately and specially for NOR flash, without this setting, it will probe failed because of the pullup resistance absence. */ gpio_direction_output(gpio, 1); } return 0; } static void spifc_chipselect(struct udevice *dev, bool select) { struct udevice *bus = dev->parent; struct spifc_platdata *plat = dev_get_platdata(bus); struct spi_slave *slave = dev_get_parentdata(dev); int cs = slave->cs; bool level = slave->mode & SPI_CS_HIGH; if (cs >= plat->num_chipselect) { printf("cs %d exceed the bus num_chipselect\n", cs); return; } spifc_dbg("%sselect %s(cs=%d)\n", select ? "" : "de", dev->name, cs); if (!select) level = !level; if (plat->cs_gpios) { int cs_gpio = plat->cs_gpios[cs]; gpio_direction_output(cs_gpio, level); spifc_dbg("set gpio %d %d\n", cs_gpio, level); } else { printf("auto chipselect TODO\n"); } } static int spifc_set_speed( struct udevice *bus, uint hz) { struct spifc_platdata *plat = dev_get_platdata(bus); struct spifc_priv *priv = dev_get_priv(bus); struct spifc_regs *regs = priv->regs; u32 div, value; if (hz == priv->speed) return 0; spifc_dbg("set speed to %d\n", hz); priv->speed = hz; if (plat->clk_enable) plat->clk_enable(priv->clk, !!hz); if (!hz) return 0; value = SPIFC_DEFAULT_CLK_RATE; if (plat->clk_get_rate) value = plat->clk_get_rate(priv->clk); div = value / hz; if (div < 2) div = 2; #ifdef CONFIG_SPIFC_COMPATIBLE_TO_APPOLO if (div > 0x10) div = 0x10; value = readl(®s->ctrl); value &= ~(0x1fff<> 1) - 1) << SPI_CLKCNT_H; value |= (div - 1) << SPI_CLKCNT_N; value |= (div - 1) << SPI_CLKCNT_L; writel(value, ®s->ctrl); #else if (div > 0x40) div = 0x40; value = ((div >> 1) - 1) << SPI_CLKCNT_H_NEW; value |= (div - 1) << SPI_CLKCNT_N_NEW; value |= (div - 1) << SPI_CLKCNT_L_NEW; writel(value, ®s->clock); #endif spifc_dbg("div=%d, value=0x%x\n", div, value); return 0; } static int spifc_set_mode( struct udevice *bus, uint mode) { struct spifc_priv *priv = dev_get_priv(bus); if (mode == priv->mode) return 0; spifc_dbg("fix mode 0 now, TODO 0x%x\n", mode); priv->mode = mode; return 0; } static int spifc_set_wordlen( struct udevice *bus, unsigned int wordlen) { struct spifc_priv *priv = dev_get_priv(bus); if (wordlen == priv->wordlen) return 0; spifc_dbg("fix 8 now, TODO %d\n", wordlen); priv->wordlen = wordlen; return 0; } static int spifc_xfer( struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { struct udevice *bus = dev->parent; struct spi_slave *slave = dev_get_parentdata(dev); struct spifc_priv *priv = dev_get_priv(bus); u8 *buf; int len = bitlen >> 3; int lening; int ret = 0; spifc_dbg("slave %u:%u bitlen %u\n", bus->seq, spi_chip_select(dev), bitlen); if (bitlen % 8) { printf("%s: error bitlen\n", __func__); return -EINVAL; } spifc_set_speed(bus, slave->max_hz); spifc_set_mode(bus, slave->mode); if (flags & SPI_XFER_BEGIN) { spifc_chipselect(dev, 1); buf = (u8 *)dout; if (!buf || (len > 5)) { printf("%s: error command\n", __func__); ret = -EINVAL; } else { spifc_user_cmd(priv, buf[0], &buf[1], len - 1); /* save the command for next xfer dual/quad setting */ priv->cmd = buf[0]; } } else if (dout && priv->cmd) { buf = (u8 *)dout; spifc_set_tx_op_mode(priv, slave->mode, priv->cmd); while (len > 0) { lening = min_t(size_t, SPIFC_CACHE_SIZE_IN_BYTE, len); ret = spifc_user_cmd_dout(priv, buf, lening, flags); if (ret) break; buf += lening; len -= lening; } } else if (din && priv->cmd) { buf = (u8 *)din; spifc_set_rx_op_mode(priv, slave->mode, priv->cmd); while (len > 0) { lening = min_t(size_t, SPIFC_CACHE_SIZE_IN_BYTE, len); ret = spifc_user_cmd_din(priv, buf, lening, flags); if (ret) break; buf += lening; len -= lening; } } if (ret || flags & SPI_XFER_END) { spifc_chipselect(dev, 0); priv->cmd = 0; } return ret; } static int spifc_probe(struct udevice *bus) { struct spifc_platdata *plat = dev_get_platdata(bus); struct spifc_priv *priv = dev_get_priv(bus); priv->regs = (struct spifc_regs *)plat->reg; if (plat->clk_get) priv->clk = plat->clk_get(bus, "spifc_clk"); if (plat->pinctrl_get) priv->pinctrl = plat->pinctrl_get(bus, "spifc_pinctrl"); spifc_cs_gpios_init(priv, plat); priv->speed = -1; priv->mode = -1; printf("%s: reg=%p, mem_map=%p\n", __func__, (void *)priv->regs, (void *)plat->mem_map); return 0; } #ifdef CONFIG_OF_CONTROL static int spifc_ofdata_to_platdata(struct udevice *bus) { spifc_dbg("step 1\n"); struct spifc_platdata *plat = dev_get_platdata(bus); const void *blob = gd->fdt_blob; int node = bus->of_offset; plat->reg = fdtdec_get_addr(blob, node, "reg"); plat->mem_map = fdtdec_get_addr(blob, node, "ahb"); /* default 5MHz */ return 0; } static const struct udevice_id spifc_ids[] = { { .compatible = "amlogic, spifc" }, { } }; #endif static const struct dm_spi_ops spifc_ops = { .claim_bus = spifc_claim_bus, .release_bus = spifc_release_bus, .xfer = spifc_xfer, .set_speed = spifc_set_speed, .set_mode = spifc_set_mode, .set_wordlen = spifc_set_wordlen, }; U_BOOT_DRIVER(spifc) = { .name = "spifc", .id = UCLASS_SPI, #ifdef CONFIG_OF_CONTROL .of_match = spifc_ids, .ofdata_to_platdata = spifc_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct spifc_platdata), #endif .priv_auto_alloc_size = sizeof(struct spifc_priv), .per_child_auto_alloc_size = sizeof(struct spi_slave), .ops= &spifc_ops, .probe = spifc_probe, };