11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2e523f111SJiancheng Xue /* 3*1ac71ec0STudor Ambarus * HiSilicon FMC SPI NOR flash controller driver 4e523f111SJiancheng Xue * 5e523f111SJiancheng Xue * Copyright (c) 2015-2016 HiSilicon Technologies Co., Ltd. 6e523f111SJiancheng Xue */ 7e523f111SJiancheng Xue #include <linux/bitops.h> 8e523f111SJiancheng Xue #include <linux/clk.h> 9e523f111SJiancheng Xue #include <linux/dma-mapping.h> 10e523f111SJiancheng Xue #include <linux/iopoll.h> 11e523f111SJiancheng Xue #include <linux/module.h> 12e523f111SJiancheng Xue #include <linux/mtd/mtd.h> 13e523f111SJiancheng Xue #include <linux/mtd/spi-nor.h> 14e523f111SJiancheng Xue #include <linux/of.h> 15e523f111SJiancheng Xue #include <linux/platform_device.h> 16e523f111SJiancheng Xue #include <linux/slab.h> 17e523f111SJiancheng Xue 18e523f111SJiancheng Xue /* Hardware register offsets and field definitions */ 19e523f111SJiancheng Xue #define FMC_CFG 0x00 20e523f111SJiancheng Xue #define FMC_CFG_OP_MODE_MASK BIT_MASK(0) 21e523f111SJiancheng Xue #define FMC_CFG_OP_MODE_BOOT 0 22e523f111SJiancheng Xue #define FMC_CFG_OP_MODE_NORMAL 1 23e523f111SJiancheng Xue #define FMC_CFG_FLASH_SEL(type) (((type) & 0x3) << 1) 24e523f111SJiancheng Xue #define FMC_CFG_FLASH_SEL_MASK 0x6 25e523f111SJiancheng Xue #define FMC_ECC_TYPE(type) (((type) & 0x7) << 5) 26e523f111SJiancheng Xue #define FMC_ECC_TYPE_MASK GENMASK(7, 5) 27e523f111SJiancheng Xue #define SPI_NOR_ADDR_MODE_MASK BIT_MASK(10) 28e523f111SJiancheng Xue #define SPI_NOR_ADDR_MODE_3BYTES (0x0 << 10) 29e523f111SJiancheng Xue #define SPI_NOR_ADDR_MODE_4BYTES (0x1 << 10) 30e523f111SJiancheng Xue #define FMC_GLOBAL_CFG 0x04 31e523f111SJiancheng Xue #define FMC_GLOBAL_CFG_WP_ENABLE BIT(6) 32e523f111SJiancheng Xue #define FMC_SPI_TIMING_CFG 0x08 33e523f111SJiancheng Xue #define TIMING_CFG_TCSH(nr) (((nr) & 0xf) << 8) 34e523f111SJiancheng Xue #define TIMING_CFG_TCSS(nr) (((nr) & 0xf) << 4) 35e523f111SJiancheng Xue #define TIMING_CFG_TSHSL(nr) ((nr) & 0xf) 36e523f111SJiancheng Xue #define CS_HOLD_TIME 0x6 37e523f111SJiancheng Xue #define CS_SETUP_TIME 0x6 38e523f111SJiancheng Xue #define CS_DESELECT_TIME 0xf 39e523f111SJiancheng Xue #define FMC_INT 0x18 40e523f111SJiancheng Xue #define FMC_INT_OP_DONE BIT(0) 41e523f111SJiancheng Xue #define FMC_INT_CLR 0x20 42e523f111SJiancheng Xue #define FMC_CMD 0x24 43e523f111SJiancheng Xue #define FMC_CMD_CMD1(cmd) ((cmd) & 0xff) 44e523f111SJiancheng Xue #define FMC_ADDRL 0x2c 45e523f111SJiancheng Xue #define FMC_OP_CFG 0x30 46e523f111SJiancheng Xue #define OP_CFG_FM_CS(cs) ((cs) << 11) 47e523f111SJiancheng Xue #define OP_CFG_MEM_IF_TYPE(type) (((type) & 0x7) << 7) 48e523f111SJiancheng Xue #define OP_CFG_ADDR_NUM(addr) (((addr) & 0x7) << 4) 49e523f111SJiancheng Xue #define OP_CFG_DUMMY_NUM(dummy) ((dummy) & 0xf) 50e523f111SJiancheng Xue #define FMC_DATA_NUM 0x38 51e523f111SJiancheng Xue #define FMC_DATA_NUM_CNT(cnt) ((cnt) & GENMASK(13, 0)) 52e523f111SJiancheng Xue #define FMC_OP 0x3c 53e523f111SJiancheng Xue #define FMC_OP_DUMMY_EN BIT(8) 54e523f111SJiancheng Xue #define FMC_OP_CMD1_EN BIT(7) 55e523f111SJiancheng Xue #define FMC_OP_ADDR_EN BIT(6) 56e523f111SJiancheng Xue #define FMC_OP_WRITE_DATA_EN BIT(5) 57e523f111SJiancheng Xue #define FMC_OP_READ_DATA_EN BIT(2) 58e523f111SJiancheng Xue #define FMC_OP_READ_STATUS_EN BIT(1) 59e523f111SJiancheng Xue #define FMC_OP_REG_OP_START BIT(0) 60e523f111SJiancheng Xue #define FMC_DMA_LEN 0x40 61e523f111SJiancheng Xue #define FMC_DMA_LEN_SET(len) ((len) & GENMASK(27, 0)) 62e523f111SJiancheng Xue #define FMC_DMA_SADDR_D0 0x4c 63e523f111SJiancheng Xue #define HIFMC_DMA_MAX_LEN (4096) 64e523f111SJiancheng Xue #define HIFMC_DMA_MASK (HIFMC_DMA_MAX_LEN - 1) 65e523f111SJiancheng Xue #define FMC_OP_DMA 0x68 66e523f111SJiancheng Xue #define OP_CTRL_RD_OPCODE(code) (((code) & 0xff) << 16) 67e523f111SJiancheng Xue #define OP_CTRL_WR_OPCODE(code) (((code) & 0xff) << 8) 68e523f111SJiancheng Xue #define OP_CTRL_RW_OP(op) ((op) << 1) 69e523f111SJiancheng Xue #define OP_CTRL_DMA_OP_READY BIT(0) 70e523f111SJiancheng Xue #define FMC_OP_READ 0x0 71e523f111SJiancheng Xue #define FMC_OP_WRITE 0x1 72e523f111SJiancheng Xue #define FMC_WAIT_TIMEOUT 1000000 73e523f111SJiancheng Xue 74e523f111SJiancheng Xue enum hifmc_iftype { 75e523f111SJiancheng Xue IF_TYPE_STD, 76e523f111SJiancheng Xue IF_TYPE_DUAL, 77e523f111SJiancheng Xue IF_TYPE_DIO, 78e523f111SJiancheng Xue IF_TYPE_QUAD, 79e523f111SJiancheng Xue IF_TYPE_QIO, 80e523f111SJiancheng Xue }; 81e523f111SJiancheng Xue 82e523f111SJiancheng Xue struct hifmc_priv { 83e523f111SJiancheng Xue u32 chipselect; 84e523f111SJiancheng Xue u32 clkrate; 85e523f111SJiancheng Xue struct hifmc_host *host; 86e523f111SJiancheng Xue }; 87e523f111SJiancheng Xue 88e523f111SJiancheng Xue #define HIFMC_MAX_CHIP_NUM 2 89e523f111SJiancheng Xue struct hifmc_host { 90e523f111SJiancheng Xue struct device *dev; 91e523f111SJiancheng Xue struct mutex lock; 92e523f111SJiancheng Xue 93e523f111SJiancheng Xue void __iomem *regbase; 94e523f111SJiancheng Xue void __iomem *iobase; 95e523f111SJiancheng Xue struct clk *clk; 96e523f111SJiancheng Xue void *buffer; 97e523f111SJiancheng Xue dma_addr_t dma_buffer; 98e523f111SJiancheng Xue 99e523f111SJiancheng Xue struct spi_nor *nor[HIFMC_MAX_CHIP_NUM]; 100e523f111SJiancheng Xue u32 num_chip; 101e523f111SJiancheng Xue }; 102e523f111SJiancheng Xue 10364070249SEzequiel Garcia static inline int hisi_spi_nor_wait_op_finish(struct hifmc_host *host) 104e523f111SJiancheng Xue { 105e523f111SJiancheng Xue u32 reg; 106e523f111SJiancheng Xue 107e523f111SJiancheng Xue return readl_poll_timeout(host->regbase + FMC_INT, reg, 108e523f111SJiancheng Xue (reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT); 109e523f111SJiancheng Xue } 110e523f111SJiancheng Xue 11164070249SEzequiel Garcia static int hisi_spi_nor_get_if_type(enum spi_nor_protocol proto) 112e523f111SJiancheng Xue { 113e523f111SJiancheng Xue enum hifmc_iftype if_type; 114e523f111SJiancheng Xue 115cfc5604cSCyrille Pitchen switch (proto) { 116cfc5604cSCyrille Pitchen case SNOR_PROTO_1_1_2: 117e523f111SJiancheng Xue if_type = IF_TYPE_DUAL; 118e523f111SJiancheng Xue break; 119cfc5604cSCyrille Pitchen case SNOR_PROTO_1_2_2: 120cfc5604cSCyrille Pitchen if_type = IF_TYPE_DIO; 121cfc5604cSCyrille Pitchen break; 122cfc5604cSCyrille Pitchen case SNOR_PROTO_1_1_4: 123e523f111SJiancheng Xue if_type = IF_TYPE_QUAD; 124e523f111SJiancheng Xue break; 125cfc5604cSCyrille Pitchen case SNOR_PROTO_1_4_4: 126cfc5604cSCyrille Pitchen if_type = IF_TYPE_QIO; 127cfc5604cSCyrille Pitchen break; 128cfc5604cSCyrille Pitchen case SNOR_PROTO_1_1_1: 129e523f111SJiancheng Xue default: 130e523f111SJiancheng Xue if_type = IF_TYPE_STD; 131e523f111SJiancheng Xue break; 132e523f111SJiancheng Xue } 133e523f111SJiancheng Xue 134e523f111SJiancheng Xue return if_type; 135e523f111SJiancheng Xue } 136e523f111SJiancheng Xue 137e523f111SJiancheng Xue static void hisi_spi_nor_init(struct hifmc_host *host) 138e523f111SJiancheng Xue { 139e523f111SJiancheng Xue u32 reg; 140e523f111SJiancheng Xue 141e523f111SJiancheng Xue reg = TIMING_CFG_TCSH(CS_HOLD_TIME) 142e523f111SJiancheng Xue | TIMING_CFG_TCSS(CS_SETUP_TIME) 143e523f111SJiancheng Xue | TIMING_CFG_TSHSL(CS_DESELECT_TIME); 144e523f111SJiancheng Xue writel(reg, host->regbase + FMC_SPI_TIMING_CFG); 145e523f111SJiancheng Xue } 146e523f111SJiancheng Xue 14752bbd2dcSMichael Walle static int hisi_spi_nor_prep(struct spi_nor *nor) 148e523f111SJiancheng Xue { 149e523f111SJiancheng Xue struct hifmc_priv *priv = nor->priv; 150e523f111SJiancheng Xue struct hifmc_host *host = priv->host; 151e523f111SJiancheng Xue int ret; 152e523f111SJiancheng Xue 153e523f111SJiancheng Xue mutex_lock(&host->lock); 154e523f111SJiancheng Xue 155e523f111SJiancheng Xue ret = clk_set_rate(host->clk, priv->clkrate); 156e523f111SJiancheng Xue if (ret) 157e523f111SJiancheng Xue goto out; 158e523f111SJiancheng Xue 159e523f111SJiancheng Xue ret = clk_prepare_enable(host->clk); 160e523f111SJiancheng Xue if (ret) 161e523f111SJiancheng Xue goto out; 162e523f111SJiancheng Xue 163e523f111SJiancheng Xue return 0; 164e523f111SJiancheng Xue 165e523f111SJiancheng Xue out: 166e523f111SJiancheng Xue mutex_unlock(&host->lock); 167e523f111SJiancheng Xue return ret; 168e523f111SJiancheng Xue } 169e523f111SJiancheng Xue 17052bbd2dcSMichael Walle static void hisi_spi_nor_unprep(struct spi_nor *nor) 171e523f111SJiancheng Xue { 172e523f111SJiancheng Xue struct hifmc_priv *priv = nor->priv; 173e523f111SJiancheng Xue struct hifmc_host *host = priv->host; 174e523f111SJiancheng Xue 175e523f111SJiancheng Xue clk_disable_unprepare(host->clk); 176e523f111SJiancheng Xue mutex_unlock(&host->lock); 177e523f111SJiancheng Xue } 178e523f111SJiancheng Xue 179e523f111SJiancheng Xue static int hisi_spi_nor_op_reg(struct spi_nor *nor, 18045397787STudor Ambarus u8 opcode, size_t len, u8 optype) 181e523f111SJiancheng Xue { 182e523f111SJiancheng Xue struct hifmc_priv *priv = nor->priv; 183e523f111SJiancheng Xue struct hifmc_host *host = priv->host; 184e523f111SJiancheng Xue u32 reg; 185e523f111SJiancheng Xue 186e523f111SJiancheng Xue reg = FMC_CMD_CMD1(opcode); 187e523f111SJiancheng Xue writel(reg, host->regbase + FMC_CMD); 188e523f111SJiancheng Xue 189e523f111SJiancheng Xue reg = FMC_DATA_NUM_CNT(len); 190e523f111SJiancheng Xue writel(reg, host->regbase + FMC_DATA_NUM); 191e523f111SJiancheng Xue 192e523f111SJiancheng Xue reg = OP_CFG_FM_CS(priv->chipselect); 193e523f111SJiancheng Xue writel(reg, host->regbase + FMC_OP_CFG); 194e523f111SJiancheng Xue 195e523f111SJiancheng Xue writel(0xff, host->regbase + FMC_INT_CLR); 196e523f111SJiancheng Xue reg = FMC_OP_CMD1_EN | FMC_OP_REG_OP_START | optype; 197e523f111SJiancheng Xue writel(reg, host->regbase + FMC_OP); 198e523f111SJiancheng Xue 19964070249SEzequiel Garcia return hisi_spi_nor_wait_op_finish(host); 200e523f111SJiancheng Xue } 201e523f111SJiancheng Xue 202e523f111SJiancheng Xue static int hisi_spi_nor_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, 20345397787STudor Ambarus size_t len) 204e523f111SJiancheng Xue { 205e523f111SJiancheng Xue struct hifmc_priv *priv = nor->priv; 206e523f111SJiancheng Xue struct hifmc_host *host = priv->host; 207e523f111SJiancheng Xue int ret; 208e523f111SJiancheng Xue 209e523f111SJiancheng Xue ret = hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_READ_DATA_EN); 210e523f111SJiancheng Xue if (ret) 211e523f111SJiancheng Xue return ret; 212e523f111SJiancheng Xue 213e523f111SJiancheng Xue memcpy_fromio(buf, host->iobase, len); 214e523f111SJiancheng Xue return 0; 215e523f111SJiancheng Xue } 216e523f111SJiancheng Xue 217e523f111SJiancheng Xue static int hisi_spi_nor_write_reg(struct spi_nor *nor, u8 opcode, 21845397787STudor Ambarus const u8 *buf, size_t len) 219e523f111SJiancheng Xue { 220e523f111SJiancheng Xue struct hifmc_priv *priv = nor->priv; 221e523f111SJiancheng Xue struct hifmc_host *host = priv->host; 222e523f111SJiancheng Xue 223e523f111SJiancheng Xue if (len) 224e523f111SJiancheng Xue memcpy_toio(host->iobase, buf, len); 225e523f111SJiancheng Xue 226e523f111SJiancheng Xue return hisi_spi_nor_op_reg(nor, opcode, len, FMC_OP_WRITE_DATA_EN); 227e523f111SJiancheng Xue } 228e523f111SJiancheng Xue 229e523f111SJiancheng Xue static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off, 230e523f111SJiancheng Xue dma_addr_t dma_buf, size_t len, u8 op_type) 231e523f111SJiancheng Xue { 232e523f111SJiancheng Xue struct hifmc_priv *priv = nor->priv; 233e523f111SJiancheng Xue struct hifmc_host *host = priv->host; 234e523f111SJiancheng Xue u8 if_type = 0; 235e523f111SJiancheng Xue u32 reg; 236e523f111SJiancheng Xue 237e523f111SJiancheng Xue reg = readl(host->regbase + FMC_CFG); 238e523f111SJiancheng Xue reg &= ~(FMC_CFG_OP_MODE_MASK | SPI_NOR_ADDR_MODE_MASK); 239e523f111SJiancheng Xue reg |= FMC_CFG_OP_MODE_NORMAL; 240e523f111SJiancheng Xue reg |= (nor->addr_width == 4) ? SPI_NOR_ADDR_MODE_4BYTES 241e523f111SJiancheng Xue : SPI_NOR_ADDR_MODE_3BYTES; 242e523f111SJiancheng Xue writel(reg, host->regbase + FMC_CFG); 243e523f111SJiancheng Xue 244e523f111SJiancheng Xue writel(start_off, host->regbase + FMC_ADDRL); 245e523f111SJiancheng Xue writel(dma_buf, host->regbase + FMC_DMA_SADDR_D0); 246e523f111SJiancheng Xue writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN); 247e523f111SJiancheng Xue 248e523f111SJiancheng Xue reg = OP_CFG_FM_CS(priv->chipselect); 249cfc5604cSCyrille Pitchen if (op_type == FMC_OP_READ) 25064070249SEzequiel Garcia if_type = hisi_spi_nor_get_if_type(nor->read_proto); 251cfc5604cSCyrille Pitchen else 25264070249SEzequiel Garcia if_type = hisi_spi_nor_get_if_type(nor->write_proto); 253e523f111SJiancheng Xue reg |= OP_CFG_MEM_IF_TYPE(if_type); 254e523f111SJiancheng Xue if (op_type == FMC_OP_READ) 255e523f111SJiancheng Xue reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3); 256e523f111SJiancheng Xue writel(reg, host->regbase + FMC_OP_CFG); 257e523f111SJiancheng Xue 258e523f111SJiancheng Xue writel(0xff, host->regbase + FMC_INT_CLR); 259e523f111SJiancheng Xue reg = OP_CTRL_RW_OP(op_type) | OP_CTRL_DMA_OP_READY; 260e523f111SJiancheng Xue reg |= (op_type == FMC_OP_READ) 261e523f111SJiancheng Xue ? OP_CTRL_RD_OPCODE(nor->read_opcode) 262e523f111SJiancheng Xue : OP_CTRL_WR_OPCODE(nor->program_opcode); 263e523f111SJiancheng Xue writel(reg, host->regbase + FMC_OP_DMA); 264e523f111SJiancheng Xue 26564070249SEzequiel Garcia return hisi_spi_nor_wait_op_finish(host); 266e523f111SJiancheng Xue } 267e523f111SJiancheng Xue 268e523f111SJiancheng Xue static ssize_t hisi_spi_nor_read(struct spi_nor *nor, loff_t from, size_t len, 269e523f111SJiancheng Xue u_char *read_buf) 270e523f111SJiancheng Xue { 271e523f111SJiancheng Xue struct hifmc_priv *priv = nor->priv; 272e523f111SJiancheng Xue struct hifmc_host *host = priv->host; 273e523f111SJiancheng Xue size_t offset; 274e523f111SJiancheng Xue int ret; 275e523f111SJiancheng Xue 276e523f111SJiancheng Xue for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) { 277e523f111SJiancheng Xue size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset); 278e523f111SJiancheng Xue 279e523f111SJiancheng Xue ret = hisi_spi_nor_dma_transfer(nor, 280e523f111SJiancheng Xue from + offset, host->dma_buffer, trans, FMC_OP_READ); 281e523f111SJiancheng Xue if (ret) { 282e523f111SJiancheng Xue dev_warn(nor->dev, "DMA read timeout\n"); 283e523f111SJiancheng Xue return ret; 284e523f111SJiancheng Xue } 285e523f111SJiancheng Xue memcpy(read_buf + offset, host->buffer, trans); 286e523f111SJiancheng Xue } 287e523f111SJiancheng Xue 288e523f111SJiancheng Xue return len; 289e523f111SJiancheng Xue } 290e523f111SJiancheng Xue 291e523f111SJiancheng Xue static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to, 292e523f111SJiancheng Xue size_t len, const u_char *write_buf) 293e523f111SJiancheng Xue { 294e523f111SJiancheng Xue struct hifmc_priv *priv = nor->priv; 295e523f111SJiancheng Xue struct hifmc_host *host = priv->host; 296e523f111SJiancheng Xue size_t offset; 297e523f111SJiancheng Xue int ret; 298e523f111SJiancheng Xue 299e523f111SJiancheng Xue for (offset = 0; offset < len; offset += HIFMC_DMA_MAX_LEN) { 300e523f111SJiancheng Xue size_t trans = min_t(size_t, HIFMC_DMA_MAX_LEN, len - offset); 301e523f111SJiancheng Xue 302e523f111SJiancheng Xue memcpy(host->buffer, write_buf + offset, trans); 303e523f111SJiancheng Xue ret = hisi_spi_nor_dma_transfer(nor, 304e523f111SJiancheng Xue to + offset, host->dma_buffer, trans, FMC_OP_WRITE); 305e523f111SJiancheng Xue if (ret) { 306e523f111SJiancheng Xue dev_warn(nor->dev, "DMA write timeout\n"); 307e523f111SJiancheng Xue return ret; 308e523f111SJiancheng Xue } 309e523f111SJiancheng Xue } 310e523f111SJiancheng Xue 311e523f111SJiancheng Xue return len; 312e523f111SJiancheng Xue } 313e523f111SJiancheng Xue 31445397787STudor Ambarus static const struct spi_nor_controller_ops hisi_controller_ops = { 31545397787STudor Ambarus .prepare = hisi_spi_nor_prep, 31645397787STudor Ambarus .unprepare = hisi_spi_nor_unprep, 31745397787STudor Ambarus .read_reg = hisi_spi_nor_read_reg, 31845397787STudor Ambarus .write_reg = hisi_spi_nor_write_reg, 31945397787STudor Ambarus .read = hisi_spi_nor_read, 32045397787STudor Ambarus .write = hisi_spi_nor_write, 32145397787STudor Ambarus }; 32245397787STudor Ambarus 323e523f111SJiancheng Xue /** 324e523f111SJiancheng Xue * Get spi flash device information and register it as a mtd device. 325e523f111SJiancheng Xue */ 326e523f111SJiancheng Xue static int hisi_spi_nor_register(struct device_node *np, 327e523f111SJiancheng Xue struct hifmc_host *host) 328e523f111SJiancheng Xue { 329cfc5604cSCyrille Pitchen const struct spi_nor_hwcaps hwcaps = { 330cfc5604cSCyrille Pitchen .mask = SNOR_HWCAPS_READ | 331cfc5604cSCyrille Pitchen SNOR_HWCAPS_READ_FAST | 332cfc5604cSCyrille Pitchen SNOR_HWCAPS_READ_1_1_2 | 333cfc5604cSCyrille Pitchen SNOR_HWCAPS_READ_1_1_4 | 334cfc5604cSCyrille Pitchen SNOR_HWCAPS_PP, 335cfc5604cSCyrille Pitchen }; 336e523f111SJiancheng Xue struct device *dev = host->dev; 337e523f111SJiancheng Xue struct spi_nor *nor; 338e523f111SJiancheng Xue struct hifmc_priv *priv; 339e523f111SJiancheng Xue struct mtd_info *mtd; 340e523f111SJiancheng Xue int ret; 341e523f111SJiancheng Xue 342e523f111SJiancheng Xue nor = devm_kzalloc(dev, sizeof(*nor), GFP_KERNEL); 343e523f111SJiancheng Xue if (!nor) 344e523f111SJiancheng Xue return -ENOMEM; 345e523f111SJiancheng Xue 346e523f111SJiancheng Xue nor->dev = dev; 347e523f111SJiancheng Xue spi_nor_set_flash_node(nor, np); 348e523f111SJiancheng Xue 349e523f111SJiancheng Xue priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 350e523f111SJiancheng Xue if (!priv) 351e523f111SJiancheng Xue return -ENOMEM; 352e523f111SJiancheng Xue 353e523f111SJiancheng Xue ret = of_property_read_u32(np, "reg", &priv->chipselect); 354e523f111SJiancheng Xue if (ret) { 3551d706077SRob Herring dev_err(dev, "There's no reg property for %pOF\n", 3561d706077SRob Herring np); 357e523f111SJiancheng Xue return ret; 358e523f111SJiancheng Xue } 359e523f111SJiancheng Xue 360e523f111SJiancheng Xue ret = of_property_read_u32(np, "spi-max-frequency", 361e523f111SJiancheng Xue &priv->clkrate); 362e523f111SJiancheng Xue if (ret) { 3631d706077SRob Herring dev_err(dev, "There's no spi-max-frequency property for %pOF\n", 3641d706077SRob Herring np); 365e523f111SJiancheng Xue return ret; 366e523f111SJiancheng Xue } 367e523f111SJiancheng Xue priv->host = host; 368e523f111SJiancheng Xue nor->priv = priv; 36945397787STudor Ambarus nor->controller_ops = &hisi_controller_ops; 370e523f111SJiancheng Xue 371cfc5604cSCyrille Pitchen ret = spi_nor_scan(nor, NULL, &hwcaps); 372e523f111SJiancheng Xue if (ret) 373e523f111SJiancheng Xue return ret; 374e523f111SJiancheng Xue 375e523f111SJiancheng Xue mtd = &nor->mtd; 376e523f111SJiancheng Xue mtd->name = np->name; 377e523f111SJiancheng Xue ret = mtd_device_register(mtd, NULL, 0); 378e523f111SJiancheng Xue if (ret) 379e523f111SJiancheng Xue return ret; 380e523f111SJiancheng Xue 381e523f111SJiancheng Xue host->nor[host->num_chip] = nor; 382e523f111SJiancheng Xue host->num_chip++; 383e523f111SJiancheng Xue return 0; 384e523f111SJiancheng Xue } 385e523f111SJiancheng Xue 386e523f111SJiancheng Xue static void hisi_spi_nor_unregister_all(struct hifmc_host *host) 387e523f111SJiancheng Xue { 388e523f111SJiancheng Xue int i; 389e523f111SJiancheng Xue 390e523f111SJiancheng Xue for (i = 0; i < host->num_chip; i++) 391e523f111SJiancheng Xue mtd_device_unregister(&host->nor[i]->mtd); 392e523f111SJiancheng Xue } 393e523f111SJiancheng Xue 394e523f111SJiancheng Xue static int hisi_spi_nor_register_all(struct hifmc_host *host) 395e523f111SJiancheng Xue { 396e523f111SJiancheng Xue struct device *dev = host->dev; 397e523f111SJiancheng Xue struct device_node *np; 398e523f111SJiancheng Xue int ret; 399e523f111SJiancheng Xue 400e523f111SJiancheng Xue for_each_available_child_of_node(dev->of_node, np) { 401e523f111SJiancheng Xue ret = hisi_spi_nor_register(np, host); 402e523f111SJiancheng Xue if (ret) 403e523f111SJiancheng Xue goto fail; 404e523f111SJiancheng Xue 405e523f111SJiancheng Xue if (host->num_chip == HIFMC_MAX_CHIP_NUM) { 406e523f111SJiancheng Xue dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n"); 4077ae2227bSNishka Dasgupta of_node_put(np); 408e523f111SJiancheng Xue break; 409e523f111SJiancheng Xue } 410e523f111SJiancheng Xue } 411e523f111SJiancheng Xue 412e523f111SJiancheng Xue return 0; 413e523f111SJiancheng Xue 414e523f111SJiancheng Xue fail: 415e523f111SJiancheng Xue hisi_spi_nor_unregister_all(host); 416e523f111SJiancheng Xue return ret; 417e523f111SJiancheng Xue } 418e523f111SJiancheng Xue 419e523f111SJiancheng Xue static int hisi_spi_nor_probe(struct platform_device *pdev) 420e523f111SJiancheng Xue { 421e523f111SJiancheng Xue struct device *dev = &pdev->dev; 422e523f111SJiancheng Xue struct resource *res; 423e523f111SJiancheng Xue struct hifmc_host *host; 424e523f111SJiancheng Xue int ret; 425e523f111SJiancheng Xue 426e523f111SJiancheng Xue host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); 427e523f111SJiancheng Xue if (!host) 428e523f111SJiancheng Xue return -ENOMEM; 429e523f111SJiancheng Xue 430e523f111SJiancheng Xue platform_set_drvdata(pdev, host); 431e523f111SJiancheng Xue host->dev = dev; 432e523f111SJiancheng Xue 433e523f111SJiancheng Xue res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control"); 434e523f111SJiancheng Xue host->regbase = devm_ioremap_resource(dev, res); 435e523f111SJiancheng Xue if (IS_ERR(host->regbase)) 436e523f111SJiancheng Xue return PTR_ERR(host->regbase); 437e523f111SJiancheng Xue 438e523f111SJiancheng Xue res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory"); 439e523f111SJiancheng Xue host->iobase = devm_ioremap_resource(dev, res); 440e523f111SJiancheng Xue if (IS_ERR(host->iobase)) 441e523f111SJiancheng Xue return PTR_ERR(host->iobase); 442e523f111SJiancheng Xue 443e523f111SJiancheng Xue host->clk = devm_clk_get(dev, NULL); 444e523f111SJiancheng Xue if (IS_ERR(host->clk)) 445e523f111SJiancheng Xue return PTR_ERR(host->clk); 446e523f111SJiancheng Xue 447e523f111SJiancheng Xue ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 448e523f111SJiancheng Xue if (ret) { 449e523f111SJiancheng Xue dev_warn(dev, "Unable to set dma mask\n"); 450e523f111SJiancheng Xue return ret; 451e523f111SJiancheng Xue } 452e523f111SJiancheng Xue 453e523f111SJiancheng Xue host->buffer = dmam_alloc_coherent(dev, HIFMC_DMA_MAX_LEN, 454e523f111SJiancheng Xue &host->dma_buffer, GFP_KERNEL); 455e523f111SJiancheng Xue if (!host->buffer) 456e523f111SJiancheng Xue return -ENOMEM; 457e523f111SJiancheng Xue 4580a5165a8SAlexey Khoroshilov ret = clk_prepare_enable(host->clk); 4590a5165a8SAlexey Khoroshilov if (ret) 4600a5165a8SAlexey Khoroshilov return ret; 4610a5165a8SAlexey Khoroshilov 462e523f111SJiancheng Xue mutex_init(&host->lock); 463e523f111SJiancheng Xue hisi_spi_nor_init(host); 464e523f111SJiancheng Xue ret = hisi_spi_nor_register_all(host); 465e523f111SJiancheng Xue if (ret) 466e523f111SJiancheng Xue mutex_destroy(&host->lock); 467e523f111SJiancheng Xue 468e523f111SJiancheng Xue clk_disable_unprepare(host->clk); 469e523f111SJiancheng Xue return ret; 470e523f111SJiancheng Xue } 471e523f111SJiancheng Xue 472e523f111SJiancheng Xue static int hisi_spi_nor_remove(struct platform_device *pdev) 473e523f111SJiancheng Xue { 474e523f111SJiancheng Xue struct hifmc_host *host = platform_get_drvdata(pdev); 475e523f111SJiancheng Xue 476e523f111SJiancheng Xue hisi_spi_nor_unregister_all(host); 477e523f111SJiancheng Xue mutex_destroy(&host->lock); 478e523f111SJiancheng Xue clk_disable_unprepare(host->clk); 479e523f111SJiancheng Xue return 0; 480e523f111SJiancheng Xue } 481e523f111SJiancheng Xue 482e523f111SJiancheng Xue static const struct of_device_id hisi_spi_nor_dt_ids[] = { 483e523f111SJiancheng Xue { .compatible = "hisilicon,fmc-spi-nor"}, 484e523f111SJiancheng Xue { /* sentinel */ } 485e523f111SJiancheng Xue }; 486e523f111SJiancheng Xue MODULE_DEVICE_TABLE(of, hisi_spi_nor_dt_ids); 487e523f111SJiancheng Xue 488e523f111SJiancheng Xue static struct platform_driver hisi_spi_nor_driver = { 489e523f111SJiancheng Xue .driver = { 490e523f111SJiancheng Xue .name = "hisi-sfc", 491e523f111SJiancheng Xue .of_match_table = hisi_spi_nor_dt_ids, 492e523f111SJiancheng Xue }, 493e523f111SJiancheng Xue .probe = hisi_spi_nor_probe, 494e523f111SJiancheng Xue .remove = hisi_spi_nor_remove, 495e523f111SJiancheng Xue }; 496e523f111SJiancheng Xue module_platform_driver(hisi_spi_nor_driver); 497e523f111SJiancheng Xue 498e523f111SJiancheng Xue MODULE_LICENSE("GPL v2"); 499e523f111SJiancheng Xue MODULE_DESCRIPTION("HiSilicon SPI Nor Flash Controller Driver"); 500