linux/drivers/spi/spi-hisi-sfc-v3xx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2//
   3// HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets
   4//
   5// Copyright (c) 2019 HiSilicon Technologies Co., Ltd.
   6// Author: John Garry <john.garry@huawei.com>
   7
   8#include <linux/acpi.h>
   9#include <linux/bitops.h>
  10#include <linux/dmi.h>
  11#include <linux/iopoll.h>
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/slab.h>
  15#include <linux/spi/spi.h>
  16#include <linux/spi/spi-mem.h>
  17
  18#define HISI_SFC_V3XX_VERSION (0x1f8)
  19
  20#define HISI_SFC_V3XX_INT_STAT (0x120)
  21#define HISI_SFC_V3XX_INT_STAT_PP_ERR BIT(2)
  22#define HISI_SFC_V3XX_INT_STAT_ADDR_IACCES BIT(5)
  23#define HISI_SFC_V3XX_INT_CLR (0x12c)
  24#define HISI_SFC_V3XX_INT_CLR_CLEAR (0xff)
  25#define HISI_SFC_V3XX_CMD_CFG (0x300)
  26#define HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT (1 << 17)
  27#define HISI_SFC_V3XX_CMD_CFG_DUAL_IO (2 << 17)
  28#define HISI_SFC_V3XX_CMD_CFG_FULL_DIO (3 << 17)
  29#define HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT (5 << 17)
  30#define HISI_SFC_V3XX_CMD_CFG_QUAD_IO (6 << 17)
  31#define HISI_SFC_V3XX_CMD_CFG_FULL_QIO (7 << 17)
  32#define HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF 9
  33#define HISI_SFC_V3XX_CMD_CFG_RW_MSK BIT(8)
  34#define HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK BIT(7)
  35#define HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF 4
  36#define HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK BIT(3)
  37#define HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF 1
  38#define HISI_SFC_V3XX_CMD_CFG_START_MSK BIT(0)
  39#define HISI_SFC_V3XX_CMD_INS (0x308)
  40#define HISI_SFC_V3XX_CMD_ADDR (0x30c)
  41#define HISI_SFC_V3XX_CMD_DATABUF0 (0x400)
  42
  43struct hisi_sfc_v3xx_host {
  44        struct device *dev;
  45        void __iomem *regbase;
  46        int max_cmd_dword;
  47};
  48
  49#define HISI_SFC_V3XX_WAIT_TIMEOUT_US           1000000
  50#define HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US     10
  51
  52static int hisi_sfc_v3xx_wait_cmd_idle(struct hisi_sfc_v3xx_host *host)
  53{
  54        u32 reg;
  55
  56        return readl_poll_timeout(host->regbase + HISI_SFC_V3XX_CMD_CFG, reg,
  57                                  !(reg & HISI_SFC_V3XX_CMD_CFG_START_MSK),
  58                                  HISI_SFC_V3XX_WAIT_POLL_INTERVAL_US,
  59                                  HISI_SFC_V3XX_WAIT_TIMEOUT_US);
  60}
  61
  62static int hisi_sfc_v3xx_adjust_op_size(struct spi_mem *mem,
  63                                        struct spi_mem_op *op)
  64{
  65        struct spi_device *spi = mem->spi;
  66        struct hisi_sfc_v3xx_host *host;
  67        uintptr_t addr = (uintptr_t)op->data.buf.in;
  68        int max_byte_count;
  69
  70        host = spi_controller_get_devdata(spi->master);
  71
  72        max_byte_count = host->max_cmd_dword * 4;
  73
  74        if (!IS_ALIGNED(addr, 4) && op->data.nbytes >= 4)
  75                op->data.nbytes = 4 - (addr % 4);
  76        else if (op->data.nbytes > max_byte_count)
  77                op->data.nbytes = max_byte_count;
  78
  79        return 0;
  80}
  81
  82/*
  83 * memcpy_{to,from}io doesn't gurantee 32b accesses - which we require for the
  84 * DATABUF registers -so use __io{read,write}32_copy when possible. For
  85 * trailing bytes, copy them byte-by-byte from the DATABUF register, as we
  86 * can't clobber outside the source/dest buffer.
  87 *
  88 * For efficient data read/write, we try to put any start 32b unaligned data
  89 * into a separate transaction in hisi_sfc_v3xx_adjust_op_size().
  90 */
  91static void hisi_sfc_v3xx_read_databuf(struct hisi_sfc_v3xx_host *host,
  92                                       u8 *to, unsigned int len)
  93{
  94        void __iomem *from;
  95        int i;
  96
  97        from = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
  98
  99        if (IS_ALIGNED((uintptr_t)to, 4)) {
 100                int words = len / 4;
 101
 102                __ioread32_copy(to, from, words);
 103
 104                len -= words * 4;
 105                if (len) {
 106                        u32 val;
 107
 108                        to += words * 4;
 109                        from += words * 4;
 110
 111                        val = __raw_readl(from);
 112
 113                        for (i = 0; i < len; i++, val >>= 8, to++)
 114                                *to = (u8)val;
 115                }
 116        } else {
 117                for (i = 0; i < DIV_ROUND_UP(len, 4); i++, from += 4) {
 118                        u32 val = __raw_readl(from);
 119                        int j;
 120
 121                        for (j = 0; j < 4 && (j + (i * 4) < len);
 122                             to++, val >>= 8, j++)
 123                                *to = (u8)val;
 124                }
 125        }
 126}
 127
 128static void hisi_sfc_v3xx_write_databuf(struct hisi_sfc_v3xx_host *host,
 129                                        const u8 *from, unsigned int len)
 130{
 131        void __iomem *to;
 132        int i;
 133
 134        to = host->regbase + HISI_SFC_V3XX_CMD_DATABUF0;
 135
 136        if (IS_ALIGNED((uintptr_t)from, 4)) {
 137                int words = len / 4;
 138
 139                __iowrite32_copy(to, from, words);
 140
 141                len -= words * 4;
 142                if (len) {
 143                        u32 val = 0;
 144
 145                        to += words * 4;
 146                        from += words * 4;
 147
 148                        for (i = 0; i < len; i++, from++)
 149                                val |= *from << i * 8;
 150                        __raw_writel(val, to);
 151                }
 152
 153        } else {
 154                for (i = 0; i < DIV_ROUND_UP(len, 4); i++, to += 4) {
 155                        u32 val = 0;
 156                        int j;
 157
 158                        for (j = 0; j < 4 && (j + (i * 4) < len);
 159                             from++, j++)
 160                                val |= *from << j * 8;
 161                        __raw_writel(val, to);
 162                }
 163        }
 164}
 165
 166static int hisi_sfc_v3xx_generic_exec_op(struct hisi_sfc_v3xx_host *host,
 167                                         const struct spi_mem_op *op,
 168                                         u8 chip_select)
 169{
 170        int ret, len = op->data.nbytes;
 171        u32 int_stat, config = 0;
 172
 173        if (op->addr.nbytes)
 174                config |= HISI_SFC_V3XX_CMD_CFG_ADDR_EN_MSK;
 175
 176        switch (op->data.buswidth) {
 177        case 0 ... 1:
 178                break;
 179        case 2:
 180                if (op->addr.buswidth <= 1) {
 181                        config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IN_DUAL_OUT;
 182                } else if (op->addr.buswidth == 2) {
 183                        if (op->cmd.buswidth <= 1) {
 184                                config |= HISI_SFC_V3XX_CMD_CFG_DUAL_IO;
 185                        } else if (op->cmd.buswidth == 2) {
 186                                config |= HISI_SFC_V3XX_CMD_CFG_FULL_DIO;
 187                        } else {
 188                                return -EIO;
 189                        }
 190                } else {
 191                        return -EIO;
 192                }
 193                break;
 194        case 4:
 195                if (op->addr.buswidth <= 1) {
 196                        config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IN_QUAD_OUT;
 197                } else if (op->addr.buswidth == 4) {
 198                        if (op->cmd.buswidth <= 1) {
 199                                config |= HISI_SFC_V3XX_CMD_CFG_QUAD_IO;
 200                        } else if (op->cmd.buswidth == 4) {
 201                                config |= HISI_SFC_V3XX_CMD_CFG_FULL_QIO;
 202                        } else {
 203                                return -EIO;
 204                        }
 205                } else {
 206                        return -EIO;
 207                }
 208                break;
 209        default:
 210                return -EOPNOTSUPP;
 211        }
 212
 213        if (op->data.dir != SPI_MEM_NO_DATA) {
 214                config |= (len - 1) << HISI_SFC_V3XX_CMD_CFG_DATA_CNT_OFF;
 215                config |= HISI_SFC_V3XX_CMD_CFG_DATA_EN_MSK;
 216        }
 217
 218        if (op->data.dir == SPI_MEM_DATA_OUT)
 219                hisi_sfc_v3xx_write_databuf(host, op->data.buf.out, len);
 220        else if (op->data.dir == SPI_MEM_DATA_IN)
 221                config |= HISI_SFC_V3XX_CMD_CFG_RW_MSK;
 222
 223        config |= op->dummy.nbytes << HISI_SFC_V3XX_CMD_CFG_DUMMY_CNT_OFF |
 224                  chip_select << HISI_SFC_V3XX_CMD_CFG_CS_SEL_OFF |
 225                  HISI_SFC_V3XX_CMD_CFG_START_MSK;
 226
 227        writel(op->addr.val, host->regbase + HISI_SFC_V3XX_CMD_ADDR);
 228        writel(op->cmd.opcode, host->regbase + HISI_SFC_V3XX_CMD_INS);
 229
 230        writel(config, host->regbase + HISI_SFC_V3XX_CMD_CFG);
 231
 232        ret = hisi_sfc_v3xx_wait_cmd_idle(host);
 233        if (ret)
 234                return ret;
 235
 236        /*
 237         * The interrupt status register indicates whether an error occurs
 238         * after per operation. Check it, and clear the interrupts for
 239         * next time judgement.
 240         */
 241        int_stat = readl(host->regbase + HISI_SFC_V3XX_INT_STAT);
 242        writel(HISI_SFC_V3XX_INT_CLR_CLEAR,
 243               host->regbase + HISI_SFC_V3XX_INT_CLR);
 244
 245        if (int_stat & HISI_SFC_V3XX_INT_STAT_ADDR_IACCES) {
 246                dev_err(host->dev, "fail to access protected address\n");
 247                return -EIO;
 248        }
 249
 250        if (int_stat & HISI_SFC_V3XX_INT_STAT_PP_ERR) {
 251                dev_err(host->dev, "page program operation failed\n");
 252                return -EIO;
 253        }
 254
 255        if (op->data.dir == SPI_MEM_DATA_IN)
 256                hisi_sfc_v3xx_read_databuf(host, op->data.buf.in, len);
 257
 258        return 0;
 259}
 260
 261static int hisi_sfc_v3xx_exec_op(struct spi_mem *mem,
 262                                 const struct spi_mem_op *op)
 263{
 264        struct hisi_sfc_v3xx_host *host;
 265        struct spi_device *spi = mem->spi;
 266        u8 chip_select = spi->chip_select;
 267
 268        host = spi_controller_get_devdata(spi->master);
 269
 270        return hisi_sfc_v3xx_generic_exec_op(host, op, chip_select);
 271}
 272
 273static const struct spi_controller_mem_ops hisi_sfc_v3xx_mem_ops = {
 274        .adjust_op_size = hisi_sfc_v3xx_adjust_op_size,
 275        .exec_op = hisi_sfc_v3xx_exec_op,
 276};
 277
 278static int hisi_sfc_v3xx_buswidth_override_bits;
 279
 280/*
 281 * ACPI FW does not allow us to currently set the device buswidth, so quirk it
 282 * depending on the board.
 283 */
 284static int __init hisi_sfc_v3xx_dmi_quirk(const struct dmi_system_id *d)
 285{
 286        hisi_sfc_v3xx_buswidth_override_bits = SPI_RX_QUAD | SPI_TX_QUAD;
 287
 288        return 0;
 289}
 290
 291static const struct dmi_system_id hisi_sfc_v3xx_dmi_quirk_table[]  = {
 292        {
 293        .callback = hisi_sfc_v3xx_dmi_quirk,
 294        .matches = {
 295                DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
 296                DMI_MATCH(DMI_PRODUCT_NAME, "D06"),
 297        },
 298        },
 299        {
 300        .callback = hisi_sfc_v3xx_dmi_quirk,
 301        .matches = {
 302                DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
 303                DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 2280 V2"),
 304        },
 305        },
 306        {
 307        .callback = hisi_sfc_v3xx_dmi_quirk,
 308        .matches = {
 309                DMI_MATCH(DMI_SYS_VENDOR, "Huawei"),
 310                DMI_MATCH(DMI_PRODUCT_NAME, "TaiShan 200 (Model 2280)"),
 311        },
 312        },
 313        {}
 314};
 315
 316static int hisi_sfc_v3xx_probe(struct platform_device *pdev)
 317{
 318        struct device *dev = &pdev->dev;
 319        struct hisi_sfc_v3xx_host *host;
 320        struct spi_controller *ctlr;
 321        u32 version;
 322        int ret;
 323
 324        ctlr = spi_alloc_master(&pdev->dev, sizeof(*host));
 325        if (!ctlr)
 326                return -ENOMEM;
 327
 328        ctlr->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD |
 329                          SPI_TX_DUAL | SPI_TX_QUAD;
 330
 331        ctlr->buswidth_override_bits = hisi_sfc_v3xx_buswidth_override_bits;
 332
 333        host = spi_controller_get_devdata(ctlr);
 334        host->dev = dev;
 335
 336        platform_set_drvdata(pdev, host);
 337
 338        host->regbase = devm_platform_ioremap_resource(pdev, 0);
 339        if (IS_ERR(host->regbase)) {
 340                ret = PTR_ERR(host->regbase);
 341                goto err_put_master;
 342        }
 343
 344        ctlr->bus_num = -1;
 345        ctlr->num_chipselect = 1;
 346        ctlr->mem_ops = &hisi_sfc_v3xx_mem_ops;
 347
 348        version = readl(host->regbase + HISI_SFC_V3XX_VERSION);
 349
 350        switch (version) {
 351        case 0x351:
 352                host->max_cmd_dword = 64;
 353                break;
 354        default:
 355                host->max_cmd_dword = 16;
 356                break;
 357        }
 358
 359        ret = devm_spi_register_controller(dev, ctlr);
 360        if (ret)
 361                goto err_put_master;
 362
 363        dev_info(&pdev->dev, "hw version 0x%x\n", version);
 364
 365        return 0;
 366
 367err_put_master:
 368        spi_master_put(ctlr);
 369        return ret;
 370}
 371
 372#if IS_ENABLED(CONFIG_ACPI)
 373static const struct acpi_device_id hisi_sfc_v3xx_acpi_ids[] = {
 374        {"HISI0341", 0},
 375        {}
 376};
 377MODULE_DEVICE_TABLE(acpi, hisi_sfc_v3xx_acpi_ids);
 378#endif
 379
 380static struct platform_driver hisi_sfc_v3xx_spi_driver = {
 381        .driver = {
 382                .name   = "hisi-sfc-v3xx",
 383                .acpi_match_table = ACPI_PTR(hisi_sfc_v3xx_acpi_ids),
 384        },
 385        .probe  = hisi_sfc_v3xx_probe,
 386};
 387
 388static int __init hisi_sfc_v3xx_spi_init(void)
 389{
 390        dmi_check_system(hisi_sfc_v3xx_dmi_quirk_table);
 391
 392        return platform_driver_register(&hisi_sfc_v3xx_spi_driver);
 393}
 394
 395static void __exit hisi_sfc_v3xx_spi_exit(void)
 396{
 397        platform_driver_unregister(&hisi_sfc_v3xx_spi_driver);
 398}
 399
 400module_init(hisi_sfc_v3xx_spi_init);
 401module_exit(hisi_sfc_v3xx_spi_exit);
 402
 403MODULE_LICENSE("GPL");
 404MODULE_AUTHOR("John Garry <john.garry@huawei.com>");
 405MODULE_DESCRIPTION("HiSilicon SPI NOR V3XX Flash Controller Driver for hi16xx chipsets");
 406