1de16c322SLongbin Li // SPDX-License-Identifier: GPL-2.0-only 2de16c322SLongbin Li /* 3de16c322SLongbin Li * SG2044 SPI NOR controller driver 4de16c322SLongbin Li * 5de16c322SLongbin Li * Copyright (c) 2025 Longbin Li <looong.bin@gmail.com> 6de16c322SLongbin Li */ 7de16c322SLongbin Li 8de16c322SLongbin Li #include <linux/bitfield.h> 9de16c322SLongbin Li #include <linux/clk.h> 10de16c322SLongbin Li #include <linux/iopoll.h> 11de16c322SLongbin Li #include <linux/module.h> 12de16c322SLongbin Li #include <linux/of.h> 13de16c322SLongbin Li #include <linux/platform_device.h> 14de16c322SLongbin Li #include <linux/spi/spi-mem.h> 15de16c322SLongbin Li 16de16c322SLongbin Li /* Hardware register definitions */ 17de16c322SLongbin Li #define SPIFMC_CTRL 0x00 18de16c322SLongbin Li #define SPIFMC_CTRL_CPHA BIT(12) 19de16c322SLongbin Li #define SPIFMC_CTRL_CPOL BIT(13) 20de16c322SLongbin Li #define SPIFMC_CTRL_HOLD_OL BIT(14) 21de16c322SLongbin Li #define SPIFMC_CTRL_WP_OL BIT(15) 22de16c322SLongbin Li #define SPIFMC_CTRL_LSBF BIT(20) 23de16c322SLongbin Li #define SPIFMC_CTRL_SRST BIT(21) 24de16c322SLongbin Li #define SPIFMC_CTRL_SCK_DIV_SHIFT 0 25de16c322SLongbin Li #define SPIFMC_CTRL_FRAME_LEN_SHIFT 16 26de16c322SLongbin Li #define SPIFMC_CTRL_SCK_DIV_MASK 0x7FF 27de16c322SLongbin Li 28de16c322SLongbin Li #define SPIFMC_CE_CTRL 0x04 29de16c322SLongbin Li #define SPIFMC_CE_CTRL_CEMANUAL BIT(0) 30de16c322SLongbin Li #define SPIFMC_CE_CTRL_CEMANUAL_EN BIT(1) 31de16c322SLongbin Li 32de16c322SLongbin Li #define SPIFMC_DLY_CTRL 0x08 33de16c322SLongbin Li #define SPIFMC_CTRL_FM_INTVL_MASK 0x000f 34de16c322SLongbin Li #define SPIFMC_CTRL_FM_INTVL BIT(0) 35de16c322SLongbin Li #define SPIFMC_CTRL_CET_MASK 0x0f00 36de16c322SLongbin Li #define SPIFMC_CTRL_CET BIT(8) 37de16c322SLongbin Li 38de16c322SLongbin Li #define SPIFMC_DMMR 0x0c 39de16c322SLongbin Li 40de16c322SLongbin Li #define SPIFMC_TRAN_CSR 0x10 41de16c322SLongbin Li #define SPIFMC_TRAN_CSR_TRAN_MODE_MASK GENMASK(1, 0) 42de16c322SLongbin Li #define SPIFMC_TRAN_CSR_TRAN_MODE_RX BIT(0) 43de16c322SLongbin Li #define SPIFMC_TRAN_CSR_TRAN_MODE_TX BIT(1) 44de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FAST_MODE BIT(3) 45de16c322SLongbin Li #define SPIFMC_TRAN_CSR_BUS_WIDTH_1_BIT (0x00 << 4) 46de16c322SLongbin Li #define SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT (0x01 << 4) 47de16c322SLongbin Li #define SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT (0x02 << 4) 48de16c322SLongbin Li #define SPIFMC_TRAN_CSR_DMA_EN BIT(6) 49de16c322SLongbin Li #define SPIFMC_TRAN_CSR_MISO_LEVEL BIT(7) 50de16c322SLongbin Li #define SPIFMC_TRAN_CSR_ADDR_BYTES_MASK GENMASK(10, 8) 51de16c322SLongbin Li #define SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT 8 52de16c322SLongbin Li #define SPIFMC_TRAN_CSR_WITH_CMD BIT(11) 53de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_MASK GENMASK(13, 12) 54de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE (0x00 << 12) 55de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_2_BYTE (0x01 << 12) 56de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_4_BYTE (0x02 << 12) 57de16c322SLongbin Li #define SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE (0x03 << 12) 58de16c322SLongbin Li #define SPIFMC_TRAN_CSR_GO_BUSY BIT(15) 59de16c322SLongbin Li #define SPIFMC_TRAN_CSR_ADDR4B_SHIFT 20 60de16c322SLongbin Li #define SPIFMC_TRAN_CSR_CMD4B_SHIFT 21 61de16c322SLongbin Li 62de16c322SLongbin Li #define SPIFMC_TRAN_NUM 0x14 63de16c322SLongbin Li #define SPIFMC_FIFO_PORT 0x18 64de16c322SLongbin Li #define SPIFMC_FIFO_PT 0x20 65de16c322SLongbin Li 66de16c322SLongbin Li #define SPIFMC_INT_STS 0x28 67de16c322SLongbin Li #define SPIFMC_INT_TRAN_DONE BIT(0) 68de16c322SLongbin Li #define SPIFMC_INT_RD_FIFO BIT(2) 69de16c322SLongbin Li #define SPIFMC_INT_WR_FIFO BIT(3) 70de16c322SLongbin Li #define SPIFMC_INT_RX_FRAME BIT(4) 71de16c322SLongbin Li #define SPIFMC_INT_TX_FRAME BIT(5) 72de16c322SLongbin Li 73de16c322SLongbin Li #define SPIFMC_INT_EN 0x2c 74de16c322SLongbin Li #define SPIFMC_INT_TRAN_DONE_EN BIT(0) 75de16c322SLongbin Li #define SPIFMC_INT_RD_FIFO_EN BIT(2) 76de16c322SLongbin Li #define SPIFMC_INT_WR_FIFO_EN BIT(3) 77de16c322SLongbin Li #define SPIFMC_INT_RX_FRAME_EN BIT(4) 78de16c322SLongbin Li #define SPIFMC_INT_TX_FRAME_EN BIT(5) 79de16c322SLongbin Li 80de16c322SLongbin Li #define SPIFMC_OPT 0x030 81de16c322SLongbin Li #define SPIFMC_OPT_DISABLE_FIFO_FLUSH BIT(1) 82de16c322SLongbin Li 83de16c322SLongbin Li #define SPIFMC_MAX_FIFO_DEPTH 8 84de16c322SLongbin Li 85de16c322SLongbin Li #define SPIFMC_MAX_READ_SIZE 0x10000 86de16c322SLongbin Li 87de16c322SLongbin Li struct sg2044_spifmc { 88de16c322SLongbin Li struct spi_controller *ctrl; 89de16c322SLongbin Li void __iomem *io_base; 90de16c322SLongbin Li struct device *dev; 91de16c322SLongbin Li struct mutex lock; 92de16c322SLongbin Li struct clk *clk; 93de16c322SLongbin Li }; 94de16c322SLongbin Li 95de16c322SLongbin Li static int sg2044_spifmc_wait_int(struct sg2044_spifmc *spifmc, u8 int_type) 96de16c322SLongbin Li { 97de16c322SLongbin Li u32 stat; 98de16c322SLongbin Li 99de16c322SLongbin Li return readl_poll_timeout(spifmc->io_base + SPIFMC_INT_STS, stat, 100de16c322SLongbin Li (stat & int_type), 0, 1000000); 101de16c322SLongbin Li } 102de16c322SLongbin Li 103de16c322SLongbin Li static int sg2044_spifmc_wait_xfer_size(struct sg2044_spifmc *spifmc, 104de16c322SLongbin Li int xfer_size) 105de16c322SLongbin Li { 106de16c322SLongbin Li u8 stat; 107de16c322SLongbin Li 108de16c322SLongbin Li return readl_poll_timeout(spifmc->io_base + SPIFMC_FIFO_PT, stat, 109de16c322SLongbin Li ((stat & 0xf) == xfer_size), 1, 1000000); 110de16c322SLongbin Li } 111de16c322SLongbin Li 112de16c322SLongbin Li static u32 sg2044_spifmc_init_reg(struct sg2044_spifmc *spifmc) 113de16c322SLongbin Li { 114de16c322SLongbin Li u32 reg; 115de16c322SLongbin Li 116de16c322SLongbin Li reg = readl(spifmc->io_base + SPIFMC_TRAN_CSR); 117de16c322SLongbin Li reg &= ~(SPIFMC_TRAN_CSR_TRAN_MODE_MASK | 118de16c322SLongbin Li SPIFMC_TRAN_CSR_FAST_MODE | 119de16c322SLongbin Li SPIFMC_TRAN_CSR_BUS_WIDTH_2_BIT | 120de16c322SLongbin Li SPIFMC_TRAN_CSR_BUS_WIDTH_4_BIT | 121de16c322SLongbin Li SPIFMC_TRAN_CSR_DMA_EN | 122de16c322SLongbin Li SPIFMC_TRAN_CSR_ADDR_BYTES_MASK | 123de16c322SLongbin Li SPIFMC_TRAN_CSR_WITH_CMD | 124de16c322SLongbin Li SPIFMC_TRAN_CSR_FIFO_TRG_LVL_MASK); 125de16c322SLongbin Li 126de16c322SLongbin Li writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); 127de16c322SLongbin Li 128de16c322SLongbin Li return reg; 129de16c322SLongbin Li } 130de16c322SLongbin Li 131de16c322SLongbin Li static ssize_t sg2044_spifmc_read_64k(struct sg2044_spifmc *spifmc, 132de16c322SLongbin Li const struct spi_mem_op *op, loff_t from, 133de16c322SLongbin Li size_t len, u_char *buf) 134de16c322SLongbin Li { 135de16c322SLongbin Li int xfer_size, offset; 136de16c322SLongbin Li u32 reg; 137de16c322SLongbin Li int ret; 138de16c322SLongbin Li int i; 139de16c322SLongbin Li 140de16c322SLongbin Li reg = sg2044_spifmc_init_reg(spifmc); 141de16c322SLongbin Li reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT; 142de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE; 143de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_WITH_CMD; 144de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX; 145de16c322SLongbin Li 146de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 147de16c322SLongbin Li writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT); 148de16c322SLongbin Li 149de16c322SLongbin Li for (i = op->addr.nbytes - 1; i >= 0; i--) 150de16c322SLongbin Li writeb((from >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT); 151de16c322SLongbin Li 152de16c322SLongbin Li for (i = 0; i < op->dummy.nbytes; i++) 153de16c322SLongbin Li writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT); 154de16c322SLongbin Li 155de16c322SLongbin Li writel(len, spifmc->io_base + SPIFMC_TRAN_NUM); 156de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_INT_STS); 157de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_GO_BUSY; 158de16c322SLongbin Li writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); 159de16c322SLongbin Li 160de16c322SLongbin Li ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_RD_FIFO); 161de16c322SLongbin Li if (ret < 0) 162de16c322SLongbin Li return ret; 163de16c322SLongbin Li 164de16c322SLongbin Li offset = 0; 165de16c322SLongbin Li while (offset < len) { 166de16c322SLongbin Li xfer_size = min_t(size_t, SPIFMC_MAX_FIFO_DEPTH, len - offset); 167de16c322SLongbin Li 168de16c322SLongbin Li ret = sg2044_spifmc_wait_xfer_size(spifmc, xfer_size); 169de16c322SLongbin Li if (ret < 0) 170de16c322SLongbin Li return ret; 171de16c322SLongbin Li 172de16c322SLongbin Li for (i = 0; i < xfer_size; i++) 173de16c322SLongbin Li buf[i + offset] = readb(spifmc->io_base + SPIFMC_FIFO_PORT); 174de16c322SLongbin Li 175de16c322SLongbin Li offset += xfer_size; 176de16c322SLongbin Li } 177de16c322SLongbin Li 178de16c322SLongbin Li ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE); 179de16c322SLongbin Li if (ret < 0) 180de16c322SLongbin Li return ret; 181de16c322SLongbin Li 182de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 183de16c322SLongbin Li 184de16c322SLongbin Li return len; 185de16c322SLongbin Li } 186de16c322SLongbin Li 187de16c322SLongbin Li static ssize_t sg2044_spifmc_read(struct sg2044_spifmc *spifmc, 188de16c322SLongbin Li const struct spi_mem_op *op) 189de16c322SLongbin Li { 190de16c322SLongbin Li size_t xfer_size; 191de16c322SLongbin Li size_t offset; 192de16c322SLongbin Li loff_t from = op->addr.val; 193de16c322SLongbin Li size_t len = op->data.nbytes; 194de16c322SLongbin Li int ret; 195de16c322SLongbin Li u8 *din = op->data.buf.in; 196de16c322SLongbin Li 197de16c322SLongbin Li offset = 0; 198de16c322SLongbin Li while (offset < len) { 199de16c322SLongbin Li xfer_size = min_t(size_t, SPIFMC_MAX_READ_SIZE, len - offset); 200de16c322SLongbin Li 201de16c322SLongbin Li ret = sg2044_spifmc_read_64k(spifmc, op, from, xfer_size, din); 202de16c322SLongbin Li if (ret < 0) 203de16c322SLongbin Li return ret; 204de16c322SLongbin Li 205de16c322SLongbin Li offset += xfer_size; 206de16c322SLongbin Li din += xfer_size; 207de16c322SLongbin Li from += xfer_size; 208de16c322SLongbin Li } 209de16c322SLongbin Li 210de16c322SLongbin Li return 0; 211de16c322SLongbin Li } 212de16c322SLongbin Li 213de16c322SLongbin Li static ssize_t sg2044_spifmc_write(struct sg2044_spifmc *spifmc, 214de16c322SLongbin Li const struct spi_mem_op *op) 215de16c322SLongbin Li { 216de16c322SLongbin Li size_t xfer_size; 217de16c322SLongbin Li const u8 *dout = op->data.buf.out; 218de16c322SLongbin Li int i, offset; 219*3c9403f1SQasim Ijaz int ret; 220de16c322SLongbin Li u32 reg; 221de16c322SLongbin Li 222de16c322SLongbin Li reg = sg2044_spifmc_init_reg(spifmc); 223de16c322SLongbin Li reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT; 224de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_8_BYTE; 225de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_WITH_CMD; 226de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX; 227de16c322SLongbin Li 228de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 229de16c322SLongbin Li writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT); 230de16c322SLongbin Li 231de16c322SLongbin Li for (i = op->addr.nbytes - 1; i >= 0; i--) 232de16c322SLongbin Li writeb((op->addr.val >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT); 233de16c322SLongbin Li 234de16c322SLongbin Li for (i = 0; i < op->dummy.nbytes; i++) 235de16c322SLongbin Li writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT); 236de16c322SLongbin Li 237de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_INT_STS); 238de16c322SLongbin Li writel(op->data.nbytes, spifmc->io_base + SPIFMC_TRAN_NUM); 239de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_GO_BUSY; 240de16c322SLongbin Li writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); 241de16c322SLongbin Li 242de16c322SLongbin Li ret = sg2044_spifmc_wait_xfer_size(spifmc, 0); 243de16c322SLongbin Li if (ret < 0) 244de16c322SLongbin Li return ret; 245de16c322SLongbin Li 246de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 247de16c322SLongbin Li 248de16c322SLongbin Li offset = 0; 249de16c322SLongbin Li while (offset < op->data.nbytes) { 250de16c322SLongbin Li xfer_size = min_t(size_t, SPIFMC_MAX_FIFO_DEPTH, op->data.nbytes - offset); 251de16c322SLongbin Li 252de16c322SLongbin Li ret = sg2044_spifmc_wait_xfer_size(spifmc, 0); 253de16c322SLongbin Li if (ret < 0) 254de16c322SLongbin Li return ret; 255de16c322SLongbin Li 256de16c322SLongbin Li for (i = 0; i < xfer_size; i++) 257de16c322SLongbin Li writeb(dout[i + offset], spifmc->io_base + SPIFMC_FIFO_PORT); 258de16c322SLongbin Li 259de16c322SLongbin Li offset += xfer_size; 260de16c322SLongbin Li } 261de16c322SLongbin Li 262de16c322SLongbin Li ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE); 263de16c322SLongbin Li if (ret < 0) 264de16c322SLongbin Li return ret; 265de16c322SLongbin Li 266de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 267de16c322SLongbin Li 268de16c322SLongbin Li return 0; 269de16c322SLongbin Li } 270de16c322SLongbin Li 271de16c322SLongbin Li static ssize_t sg2044_spifmc_tran_cmd(struct sg2044_spifmc *spifmc, 272de16c322SLongbin Li const struct spi_mem_op *op) 273de16c322SLongbin Li { 274de16c322SLongbin Li int i, ret; 275de16c322SLongbin Li u32 reg; 276de16c322SLongbin Li 277de16c322SLongbin Li reg = sg2044_spifmc_init_reg(spifmc); 278de16c322SLongbin Li reg |= (op->addr.nbytes + op->dummy.nbytes) << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT; 279de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE; 280de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_WITH_CMD; 281de16c322SLongbin Li 282de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 283de16c322SLongbin Li writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT); 284de16c322SLongbin Li 285de16c322SLongbin Li for (i = op->addr.nbytes - 1; i >= 0; i--) 286de16c322SLongbin Li writeb((op->addr.val >> i * 8) & 0xff, spifmc->io_base + SPIFMC_FIFO_PORT); 287de16c322SLongbin Li 288de16c322SLongbin Li for (i = 0; i < op->dummy.nbytes; i++) 289de16c322SLongbin Li writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT); 290de16c322SLongbin Li 291de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_INT_STS); 292de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_GO_BUSY; 293de16c322SLongbin Li writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); 294de16c322SLongbin Li 295de16c322SLongbin Li ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE); 296de16c322SLongbin Li if (ret < 0) 297de16c322SLongbin Li return ret; 298de16c322SLongbin Li 299de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 300de16c322SLongbin Li 301de16c322SLongbin Li return 0; 302de16c322SLongbin Li } 303de16c322SLongbin Li 304de16c322SLongbin Li static void sg2044_spifmc_trans(struct sg2044_spifmc *spifmc, 305de16c322SLongbin Li const struct spi_mem_op *op) 306de16c322SLongbin Li { 307de16c322SLongbin Li if (op->data.dir == SPI_MEM_DATA_IN) 308de16c322SLongbin Li sg2044_spifmc_read(spifmc, op); 309de16c322SLongbin Li else if (op->data.dir == SPI_MEM_DATA_OUT) 310de16c322SLongbin Li sg2044_spifmc_write(spifmc, op); 311de16c322SLongbin Li else 312de16c322SLongbin Li sg2044_spifmc_tran_cmd(spifmc, op); 313de16c322SLongbin Li } 314de16c322SLongbin Li 315de16c322SLongbin Li static ssize_t sg2044_spifmc_trans_reg(struct sg2044_spifmc *spifmc, 316de16c322SLongbin Li const struct spi_mem_op *op) 317de16c322SLongbin Li { 318de16c322SLongbin Li const u8 *dout = NULL; 319de16c322SLongbin Li u8 *din = NULL; 320de16c322SLongbin Li size_t len = op->data.nbytes; 321de16c322SLongbin Li int ret, i; 322de16c322SLongbin Li u32 reg; 323de16c322SLongbin Li 324de16c322SLongbin Li if (op->data.dir == SPI_MEM_DATA_IN) 325de16c322SLongbin Li din = op->data.buf.in; 326de16c322SLongbin Li else 327de16c322SLongbin Li dout = op->data.buf.out; 328de16c322SLongbin Li 329de16c322SLongbin Li reg = sg2044_spifmc_init_reg(spifmc); 330de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_1_BYTE; 331de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_WITH_CMD; 332de16c322SLongbin Li 333de16c322SLongbin Li if (din) { 334de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_BUS_WIDTH_1_BIT; 335de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX; 336de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX; 337de16c322SLongbin Li 338de16c322SLongbin Li writel(SPIFMC_OPT_DISABLE_FIFO_FLUSH, spifmc->io_base + SPIFMC_OPT); 339de16c322SLongbin Li } else { 340de16c322SLongbin Li /* 341de16c322SLongbin Li * If write values to the Status Register, 342de16c322SLongbin Li * configure TRAN_CSR register as the same as 343de16c322SLongbin Li * sg2044_spifmc_read_reg. 344de16c322SLongbin Li */ 345de16c322SLongbin Li if (op->cmd.opcode == 0x01) { 346de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_TRAN_MODE_RX; 347de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_TRAN_MODE_TX; 348de16c322SLongbin Li writel(len, spifmc->io_base + SPIFMC_TRAN_NUM); 349de16c322SLongbin Li } 350de16c322SLongbin Li } 351de16c322SLongbin Li 352de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 353de16c322SLongbin Li writeb(op->cmd.opcode, spifmc->io_base + SPIFMC_FIFO_PORT); 354de16c322SLongbin Li 355de16c322SLongbin Li for (i = 0; i < len; i++) { 356de16c322SLongbin Li if (din) 357de16c322SLongbin Li writeb(0xff, spifmc->io_base + SPIFMC_FIFO_PORT); 358de16c322SLongbin Li else 359de16c322SLongbin Li writeb(dout[i], spifmc->io_base + SPIFMC_FIFO_PORT); 360de16c322SLongbin Li } 361de16c322SLongbin Li 362de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_INT_STS); 363de16c322SLongbin Li writel(len, spifmc->io_base + SPIFMC_TRAN_NUM); 364de16c322SLongbin Li reg |= SPIFMC_TRAN_CSR_GO_BUSY; 365de16c322SLongbin Li writel(reg, spifmc->io_base + SPIFMC_TRAN_CSR); 366de16c322SLongbin Li 367de16c322SLongbin Li ret = sg2044_spifmc_wait_int(spifmc, SPIFMC_INT_TRAN_DONE); 368de16c322SLongbin Li if (ret < 0) 369de16c322SLongbin Li return ret; 370de16c322SLongbin Li 371de16c322SLongbin Li if (din) { 372de16c322SLongbin Li while (len--) 373de16c322SLongbin Li *din++ = readb(spifmc->io_base + SPIFMC_FIFO_PORT); 374de16c322SLongbin Li } 375de16c322SLongbin Li 376de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_FIFO_PT); 377de16c322SLongbin Li 378de16c322SLongbin Li return 0; 379de16c322SLongbin Li } 380de16c322SLongbin Li 381de16c322SLongbin Li static int sg2044_spifmc_exec_op(struct spi_mem *mem, 382de16c322SLongbin Li const struct spi_mem_op *op) 383de16c322SLongbin Li { 384de16c322SLongbin Li struct sg2044_spifmc *spifmc; 385de16c322SLongbin Li 386de16c322SLongbin Li spifmc = spi_controller_get_devdata(mem->spi->controller); 387de16c322SLongbin Li 388de16c322SLongbin Li mutex_lock(&spifmc->lock); 389de16c322SLongbin Li 390de16c322SLongbin Li if (op->addr.nbytes == 0) 391de16c322SLongbin Li sg2044_spifmc_trans_reg(spifmc, op); 392de16c322SLongbin Li else 393de16c322SLongbin Li sg2044_spifmc_trans(spifmc, op); 394de16c322SLongbin Li 395de16c322SLongbin Li mutex_unlock(&spifmc->lock); 396de16c322SLongbin Li 397de16c322SLongbin Li return 0; 398de16c322SLongbin Li } 399de16c322SLongbin Li 400de16c322SLongbin Li static const struct spi_controller_mem_ops sg2044_spifmc_mem_ops = { 401de16c322SLongbin Li .exec_op = sg2044_spifmc_exec_op, 402de16c322SLongbin Li }; 403de16c322SLongbin Li 404de16c322SLongbin Li static void sg2044_spifmc_init(struct sg2044_spifmc *spifmc) 405de16c322SLongbin Li { 406de16c322SLongbin Li u32 tran_csr; 407de16c322SLongbin Li u32 reg; 408de16c322SLongbin Li 409de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_DMMR); 410de16c322SLongbin Li 411de16c322SLongbin Li reg = readl(spifmc->io_base + SPIFMC_CTRL); 412de16c322SLongbin Li reg |= SPIFMC_CTRL_SRST; 413de16c322SLongbin Li reg &= ~(SPIFMC_CTRL_SCK_DIV_MASK); 414de16c322SLongbin Li reg |= 1; 415de16c322SLongbin Li writel(reg, spifmc->io_base + SPIFMC_CTRL); 416de16c322SLongbin Li 417de16c322SLongbin Li writel(0, spifmc->io_base + SPIFMC_CE_CTRL); 418de16c322SLongbin Li 419de16c322SLongbin Li tran_csr = readl(spifmc->io_base + SPIFMC_TRAN_CSR); 420de16c322SLongbin Li tran_csr |= (0 << SPIFMC_TRAN_CSR_ADDR_BYTES_SHIFT); 421de16c322SLongbin Li tran_csr |= SPIFMC_TRAN_CSR_FIFO_TRG_LVL_4_BYTE; 422de16c322SLongbin Li tran_csr |= SPIFMC_TRAN_CSR_WITH_CMD; 423de16c322SLongbin Li writel(tran_csr, spifmc->io_base + SPIFMC_TRAN_CSR); 424de16c322SLongbin Li } 425de16c322SLongbin Li 426de16c322SLongbin Li static int sg2044_spifmc_probe(struct platform_device *pdev) 427de16c322SLongbin Li { 428c6d94963SAndy Shevchenko struct device *dev = &pdev->dev; 429de16c322SLongbin Li struct spi_controller *ctrl; 430de16c322SLongbin Li struct sg2044_spifmc *spifmc; 431de16c322SLongbin Li int ret; 432de16c322SLongbin Li 433de16c322SLongbin Li ctrl = devm_spi_alloc_host(&pdev->dev, sizeof(*spifmc)); 434de16c322SLongbin Li if (!ctrl) 435de16c322SLongbin Li return -ENOMEM; 436de16c322SLongbin Li 437de16c322SLongbin Li spifmc = spi_controller_get_devdata(ctrl); 438de16c322SLongbin Li 439de16c322SLongbin Li spifmc->clk = devm_clk_get_enabled(&pdev->dev, NULL); 440de16c322SLongbin Li if (IS_ERR(spifmc->clk)) 441085cf53dSAndy Shevchenko return dev_err_probe(dev, PTR_ERR(spifmc->clk), "Cannot get and enable AHB clock\n"); 442de16c322SLongbin Li 443de16c322SLongbin Li spifmc->dev = &pdev->dev; 444de16c322SLongbin Li spifmc->ctrl = ctrl; 445de16c322SLongbin Li 446de16c322SLongbin Li spifmc->io_base = devm_platform_ioremap_resource(pdev, 0); 447a1d8f709SDan Carpenter if (IS_ERR(spifmc->io_base)) 448a1d8f709SDan Carpenter return PTR_ERR(spifmc->io_base); 449de16c322SLongbin Li 450de16c322SLongbin Li ctrl->num_chipselect = 1; 451de16c322SLongbin Li ctrl->dev.of_node = pdev->dev.of_node; 452de16c322SLongbin Li ctrl->bits_per_word_mask = SPI_BPW_MASK(8); 453de16c322SLongbin Li ctrl->auto_runtime_pm = false; 454de16c322SLongbin Li ctrl->mem_ops = &sg2044_spifmc_mem_ops; 455de16c322SLongbin Li ctrl->mode_bits = SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD; 456de16c322SLongbin Li 457c6d94963SAndy Shevchenko ret = devm_mutex_init(dev, &spifmc->lock); 458c6d94963SAndy Shevchenko if (ret) 459c6d94963SAndy Shevchenko return ret; 460de16c322SLongbin Li 461de16c322SLongbin Li sg2044_spifmc_init(spifmc); 462de16c322SLongbin Li sg2044_spifmc_init_reg(spifmc); 463de16c322SLongbin Li 464de16c322SLongbin Li ret = devm_spi_register_controller(&pdev->dev, ctrl); 465085cf53dSAndy Shevchenko if (ret) 466085cf53dSAndy Shevchenko return dev_err_probe(dev, ret, "spi_register_controller failed\n"); 467de16c322SLongbin Li 468de16c322SLongbin Li return 0; 469de16c322SLongbin Li } 470de16c322SLongbin Li 471de16c322SLongbin Li static const struct of_device_id sg2044_spifmc_match[] = { 472de16c322SLongbin Li { .compatible = "sophgo,sg2044-spifmc-nor" }, 473de16c322SLongbin Li { /* sentinel */ } 474de16c322SLongbin Li }; 475de16c322SLongbin Li MODULE_DEVICE_TABLE(of, sg2044_spifmc_match); 476de16c322SLongbin Li 477de16c322SLongbin Li static struct platform_driver sg2044_nor_driver = { 478de16c322SLongbin Li .driver = { 479de16c322SLongbin Li .name = "sg2044,spifmc-nor", 480de16c322SLongbin Li .of_match_table = sg2044_spifmc_match, 481de16c322SLongbin Li }, 482de16c322SLongbin Li .probe = sg2044_spifmc_probe, 483de16c322SLongbin Li }; 484de16c322SLongbin Li module_platform_driver(sg2044_nor_driver); 485de16c322SLongbin Li 486de16c322SLongbin Li MODULE_DESCRIPTION("SG2044 SPI NOR controller driver"); 487de16c322SLongbin Li MODULE_AUTHOR("Longbin Li <looong.bin@gmail.com>"); 488de16c322SLongbin Li MODULE_LICENSE("GPL"); 489