Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0=or-later
0002 /* Copyright (C) 2022 Hewlett-Packard Development Company, L.P. */
0003 
0004 #include <linux/iopoll.h>
0005 #include <linux/of.h>
0006 #include <linux/of_device.h>
0007 #include <linux/platform_device.h>
0008 #include <linux/spi/spi.h>
0009 #include <linux/spi/spi-mem.h>
0010 
0011 #define GXP_SPI0_MAX_CHIPSELECT 2
0012 #define GXP_SPI_SLEEP_TIME  1
0013 #define GXP_SPI_TIMEOUT (130 * 1000000 / GXP_SPI_SLEEP_TIME)
0014 
0015 #define MANUAL_MODE     0
0016 #define DIRECT_MODE     1
0017 #define SPILDAT_LEN     256
0018 
0019 #define OFFSET_SPIMCFG      0x0
0020 #define OFFSET_SPIMCTRL     0x4
0021 #define OFFSET_SPICMD       0x5
0022 #define OFFSET_SPIDCNT      0x6
0023 #define OFFSET_SPIADDR      0x8
0024 #define OFFSET_SPIINTSTS    0xc
0025 
0026 #define SPIMCTRL_START      0x01
0027 #define SPIMCTRL_BUSY       0x02
0028 #define SPIMCTRL_DIR        0x08
0029 
0030 struct gxp_spi;
0031 
0032 struct gxp_spi_chip {
0033     struct gxp_spi *spifi;
0034     u32 cs;
0035 };
0036 
0037 struct gxp_spi_data {
0038     u32 max_cs;
0039     u32 mode_bits;
0040 };
0041 
0042 struct gxp_spi {
0043     const struct gxp_spi_data *data;
0044     void __iomem *reg_base;
0045     void __iomem *dat_base;
0046     void __iomem *dir_base;
0047     struct device *dev;
0048     struct gxp_spi_chip chips[GXP_SPI0_MAX_CHIPSELECT];
0049 };
0050 
0051 static void gxp_spi_set_mode(struct gxp_spi *spifi, int mode)
0052 {
0053     u8 value;
0054     void __iomem *reg_base = spifi->reg_base;
0055 
0056     value = readb(reg_base + OFFSET_SPIMCTRL);
0057 
0058     if (mode == MANUAL_MODE) {
0059         writeb(0x55, reg_base + OFFSET_SPICMD);
0060         writeb(0xaa, reg_base + OFFSET_SPICMD);
0061         value &= ~0x30;
0062     } else {
0063         value |= 0x30;
0064     }
0065     writeb(value, reg_base + OFFSET_SPIMCTRL);
0066 }
0067 
0068 static int gxp_spi_read_reg(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
0069 {
0070     int ret;
0071     struct gxp_spi *spifi = chip->spifi;
0072     void __iomem *reg_base = spifi->reg_base;
0073     u32 value;
0074 
0075     value = readl(reg_base + OFFSET_SPIMCFG);
0076     value &= ~(1 << 24);
0077     value |= (chip->cs << 24);
0078     value &= ~(0x07 << 16);
0079     value &= ~(0x1f << 19);
0080     writel(value, reg_base + OFFSET_SPIMCFG);
0081 
0082     writel(0, reg_base + OFFSET_SPIADDR);
0083 
0084     writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
0085 
0086     writew(op->data.nbytes, reg_base + OFFSET_SPIDCNT);
0087 
0088     value = readb(reg_base + OFFSET_SPIMCTRL);
0089     value &= ~SPIMCTRL_DIR;
0090     value |= SPIMCTRL_START;
0091 
0092     writeb(value, reg_base + OFFSET_SPIMCTRL);
0093 
0094     ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
0095                  !(value & SPIMCTRL_BUSY),
0096                  GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
0097     if (ret) {
0098         dev_warn(spifi->dev, "read reg busy time out\n");
0099         return ret;
0100     }
0101 
0102     memcpy_fromio(op->data.buf.in, spifi->dat_base, op->data.nbytes);
0103     return ret;
0104 }
0105 
0106 static int gxp_spi_write_reg(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
0107 {
0108     int ret;
0109     struct gxp_spi *spifi = chip->spifi;
0110     void __iomem *reg_base = spifi->reg_base;
0111     u32 value;
0112 
0113     value = readl(reg_base + OFFSET_SPIMCFG);
0114     value &= ~(1 << 24);
0115     value |= (chip->cs << 24);
0116     value &= ~(0x07 << 16);
0117     value &= ~(0x1f << 19);
0118     writel(value, reg_base + OFFSET_SPIMCFG);
0119 
0120     writel(0, reg_base + OFFSET_SPIADDR);
0121 
0122     writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
0123 
0124     memcpy_toio(spifi->dat_base, op->data.buf.in, op->data.nbytes);
0125 
0126     writew(op->data.nbytes, reg_base + OFFSET_SPIDCNT);
0127 
0128     value = readb(reg_base + OFFSET_SPIMCTRL);
0129     value |= SPIMCTRL_DIR;
0130     value |= SPIMCTRL_START;
0131 
0132     writeb(value, reg_base + OFFSET_SPIMCTRL);
0133 
0134     ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
0135                  !(value & SPIMCTRL_BUSY),
0136                  GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
0137     if (ret)
0138         dev_warn(spifi->dev, "write reg busy time out\n");
0139 
0140     return ret;
0141 }
0142 
0143 static ssize_t gxp_spi_read(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
0144 {
0145     struct gxp_spi *spifi = chip->spifi;
0146     u32 offset = op->addr.val;
0147 
0148     if (chip->cs == 0)
0149         offset += 0x4000000;
0150 
0151     memcpy_fromio(op->data.buf.in, spifi->dir_base + offset, op->data.nbytes);
0152 
0153     return 0;
0154 }
0155 
0156 static ssize_t gxp_spi_write(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
0157 {
0158     struct gxp_spi *spifi = chip->spifi;
0159     void __iomem *reg_base = spifi->reg_base;
0160     u32 write_len;
0161     u32 value;
0162     int ret;
0163 
0164     write_len = op->data.nbytes;
0165     if (write_len > SPILDAT_LEN)
0166         write_len = SPILDAT_LEN;
0167 
0168     value = readl(reg_base + OFFSET_SPIMCFG);
0169     value &= ~(1 << 24);
0170     value |= (chip->cs << 24);
0171     value &= ~(0x07 << 16);
0172     value |= (op->addr.nbytes << 16);
0173     value &= ~(0x1f << 19);
0174     writel(value, reg_base + OFFSET_SPIMCFG);
0175 
0176     writel(op->addr.val, reg_base + OFFSET_SPIADDR);
0177 
0178     writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
0179 
0180     writew(write_len, reg_base + OFFSET_SPIDCNT);
0181 
0182     memcpy_toio(spifi->dat_base, op->data.buf.in, write_len);
0183 
0184     value = readb(reg_base + OFFSET_SPIMCTRL);
0185     value |= SPIMCTRL_DIR;
0186     value |= SPIMCTRL_START;
0187 
0188     writeb(value, reg_base + OFFSET_SPIMCTRL);
0189 
0190     ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
0191                  !(value & SPIMCTRL_BUSY),
0192                  GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
0193     if (ret) {
0194         dev_warn(spifi->dev, "write busy time out\n");
0195         return ret;
0196     }
0197 
0198     return write_len;
0199 }
0200 
0201 static int do_gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
0202 {
0203     struct gxp_spi *spifi = spi_controller_get_devdata(mem->spi->master);
0204     struct gxp_spi_chip *chip = &spifi->chips[mem->spi->chip_select];
0205     int ret;
0206 
0207     if (op->data.dir == SPI_MEM_DATA_IN) {
0208         if (!op->addr.nbytes)
0209             ret = gxp_spi_read_reg(chip, op);
0210         else
0211             ret = gxp_spi_read(chip, op);
0212     } else {
0213         if (!op->addr.nbytes)
0214             ret = gxp_spi_write_reg(chip, op);
0215         else
0216             ret = gxp_spi_write(chip, op);
0217     }
0218 
0219     return ret;
0220 }
0221 
0222 static int gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
0223 {
0224     int ret;
0225 
0226     ret = do_gxp_exec_mem_op(mem, op);
0227     if (ret)
0228         dev_err(&mem->spi->dev, "operation failed: %d", ret);
0229 
0230     return ret;
0231 }
0232 
0233 static const struct spi_controller_mem_ops gxp_spi_mem_ops = {
0234     .exec_op = gxp_exec_mem_op,
0235 };
0236 
0237 static int gxp_spi_setup(struct spi_device *spi)
0238 {
0239     struct gxp_spi *spifi = spi_controller_get_devdata(spi->master);
0240     unsigned int cs = spi->chip_select;
0241     struct gxp_spi_chip *chip = &spifi->chips[cs];
0242 
0243     chip->spifi = spifi;
0244     chip->cs = cs;
0245 
0246     gxp_spi_set_mode(spifi, MANUAL_MODE);
0247 
0248     return 0;
0249 }
0250 
0251 static int gxp_spifi_probe(struct platform_device *pdev)
0252 {
0253     struct device *dev = &pdev->dev;
0254     const struct gxp_spi_data *data;
0255     struct spi_controller *ctlr;
0256     struct gxp_spi *spifi;
0257     struct resource *res;
0258     int ret;
0259 
0260     data = of_device_get_match_data(&pdev->dev);
0261 
0262     ctlr = devm_spi_alloc_master(dev, sizeof(*spifi));
0263     if (!ctlr)
0264         return -ENOMEM;
0265 
0266     spifi = spi_controller_get_devdata(ctlr);
0267 
0268     platform_set_drvdata(pdev, spifi);
0269     spifi->data = data;
0270     spifi->dev = dev;
0271 
0272     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0273     spifi->reg_base = devm_ioremap_resource(&pdev->dev, res);
0274     if (IS_ERR(spifi->reg_base))
0275         return PTR_ERR(spifi->reg_base);
0276 
0277     res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
0278     spifi->dat_base = devm_ioremap_resource(&pdev->dev, res);
0279     if (IS_ERR(spifi->dat_base))
0280         return PTR_ERR(spifi->dat_base);
0281 
0282     res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
0283     spifi->dir_base = devm_ioremap_resource(&pdev->dev, res);
0284     if (IS_ERR(spifi->dir_base))
0285         return PTR_ERR(spifi->dir_base);
0286 
0287     ctlr->mode_bits = data->mode_bits;
0288     ctlr->bus_num = pdev->id;
0289     ctlr->mem_ops = &gxp_spi_mem_ops;
0290     ctlr->setup = gxp_spi_setup;
0291     ctlr->num_chipselect = data->max_cs;
0292     ctlr->dev.of_node = dev->of_node;
0293 
0294     ret = devm_spi_register_controller(dev, ctlr);
0295     if (ret) {
0296         return dev_err_probe(&pdev->dev, ret,
0297                      "failed to register spi controller\n");
0298     }
0299 
0300     return 0;
0301 }
0302 
0303 static const struct gxp_spi_data gxp_spifi_data = {
0304     .max_cs = 2,
0305     .mode_bits = 0,
0306 };
0307 
0308 static const struct of_device_id gxp_spifi_match[] = {
0309     {.compatible = "hpe,gxp-spifi", .data = &gxp_spifi_data },
0310     { /* null */ }
0311 };
0312 MODULE_DEVICE_TABLE(of, gxp_spifi_match);
0313 
0314 static struct platform_driver gxp_spifi_driver = {
0315     .probe = gxp_spifi_probe,
0316     .driver = {
0317         .name = "gxp-spifi",
0318         .of_match_table = gxp_spifi_match,
0319     },
0320 };
0321 module_platform_driver(gxp_spifi_driver);
0322 
0323 MODULE_DESCRIPTION("HPE GXP SPI Flash Interface driver");
0324 MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>");
0325 MODULE_LICENSE("GPL");