123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /* Copyright (C) 2022 Hewlett-Packard Development Company, L.P. */
- #include <linux/iopoll.h>
- #include <linux/of.h>
- #include <linux/of_device.h>
- #include <linux/platform_device.h>
- #include <linux/spi/spi.h>
- #include <linux/spi/spi-mem.h>
- #define GXP_SPI0_MAX_CHIPSELECT 2
- #define GXP_SPI_SLEEP_TIME 1
- #define GXP_SPI_TIMEOUT (130 * 1000000 / GXP_SPI_SLEEP_TIME)
- #define MANUAL_MODE 0
- #define DIRECT_MODE 1
- #define SPILDAT_LEN 256
- #define OFFSET_SPIMCFG 0x0
- #define OFFSET_SPIMCTRL 0x4
- #define OFFSET_SPICMD 0x5
- #define OFFSET_SPIDCNT 0x6
- #define OFFSET_SPIADDR 0x8
- #define OFFSET_SPIINTSTS 0xc
- #define SPIMCTRL_START 0x01
- #define SPIMCTRL_BUSY 0x02
- #define SPIMCTRL_DIR 0x08
- struct gxp_spi;
- struct gxp_spi_chip {
- struct gxp_spi *spifi;
- u32 cs;
- };
- struct gxp_spi_data {
- u32 max_cs;
- u32 mode_bits;
- };
- struct gxp_spi {
- const struct gxp_spi_data *data;
- void __iomem *reg_base;
- void __iomem *dat_base;
- void __iomem *dir_base;
- struct device *dev;
- struct gxp_spi_chip chips[GXP_SPI0_MAX_CHIPSELECT];
- };
- static void gxp_spi_set_mode(struct gxp_spi *spifi, int mode)
- {
- u8 value;
- void __iomem *reg_base = spifi->reg_base;
- value = readb(reg_base + OFFSET_SPIMCTRL);
- if (mode == MANUAL_MODE) {
- writeb(0x55, reg_base + OFFSET_SPICMD);
- writeb(0xaa, reg_base + OFFSET_SPICMD);
- value &= ~0x30;
- } else {
- value |= 0x30;
- }
- writeb(value, reg_base + OFFSET_SPIMCTRL);
- }
- static int gxp_spi_read_reg(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
- {
- int ret;
- struct gxp_spi *spifi = chip->spifi;
- void __iomem *reg_base = spifi->reg_base;
- u32 value;
- value = readl(reg_base + OFFSET_SPIMCFG);
- value &= ~(1 << 24);
- value |= (chip->cs << 24);
- value &= ~(0x07 << 16);
- value &= ~(0x1f << 19);
- writel(value, reg_base + OFFSET_SPIMCFG);
- writel(0, reg_base + OFFSET_SPIADDR);
- writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
- writew(op->data.nbytes, reg_base + OFFSET_SPIDCNT);
- value = readb(reg_base + OFFSET_SPIMCTRL);
- value &= ~SPIMCTRL_DIR;
- value |= SPIMCTRL_START;
- writeb(value, reg_base + OFFSET_SPIMCTRL);
- ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
- !(value & SPIMCTRL_BUSY),
- GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
- if (ret) {
- dev_warn(spifi->dev, "read reg busy time out\n");
- return ret;
- }
- memcpy_fromio(op->data.buf.in, spifi->dat_base, op->data.nbytes);
- return ret;
- }
- static int gxp_spi_write_reg(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
- {
- int ret;
- struct gxp_spi *spifi = chip->spifi;
- void __iomem *reg_base = spifi->reg_base;
- u32 value;
- value = readl(reg_base + OFFSET_SPIMCFG);
- value &= ~(1 << 24);
- value |= (chip->cs << 24);
- value &= ~(0x07 << 16);
- value &= ~(0x1f << 19);
- writel(value, reg_base + OFFSET_SPIMCFG);
- writel(0, reg_base + OFFSET_SPIADDR);
- writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
- memcpy_toio(spifi->dat_base, op->data.buf.in, op->data.nbytes);
- writew(op->data.nbytes, reg_base + OFFSET_SPIDCNT);
- value = readb(reg_base + OFFSET_SPIMCTRL);
- value |= SPIMCTRL_DIR;
- value |= SPIMCTRL_START;
- writeb(value, reg_base + OFFSET_SPIMCTRL);
- ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
- !(value & SPIMCTRL_BUSY),
- GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
- if (ret)
- dev_warn(spifi->dev, "write reg busy time out\n");
- return ret;
- }
- static ssize_t gxp_spi_read(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
- {
- struct gxp_spi *spifi = chip->spifi;
- u32 offset = op->addr.val;
- if (chip->cs == 0)
- offset += 0x4000000;
- memcpy_fromio(op->data.buf.in, spifi->dir_base + offset, op->data.nbytes);
- return 0;
- }
- static ssize_t gxp_spi_write(struct gxp_spi_chip *chip, const struct spi_mem_op *op)
- {
- struct gxp_spi *spifi = chip->spifi;
- void __iomem *reg_base = spifi->reg_base;
- u32 write_len;
- u32 value;
- int ret;
- write_len = op->data.nbytes;
- if (write_len > SPILDAT_LEN)
- write_len = SPILDAT_LEN;
- value = readl(reg_base + OFFSET_SPIMCFG);
- value &= ~(1 << 24);
- value |= (chip->cs << 24);
- value &= ~(0x07 << 16);
- value |= (op->addr.nbytes << 16);
- value &= ~(0x1f << 19);
- writel(value, reg_base + OFFSET_SPIMCFG);
- writel(op->addr.val, reg_base + OFFSET_SPIADDR);
- writeb(op->cmd.opcode, reg_base + OFFSET_SPICMD);
- writew(write_len, reg_base + OFFSET_SPIDCNT);
- memcpy_toio(spifi->dat_base, op->data.buf.in, write_len);
- value = readb(reg_base + OFFSET_SPIMCTRL);
- value |= SPIMCTRL_DIR;
- value |= SPIMCTRL_START;
- writeb(value, reg_base + OFFSET_SPIMCTRL);
- ret = readb_poll_timeout(reg_base + OFFSET_SPIMCTRL, value,
- !(value & SPIMCTRL_BUSY),
- GXP_SPI_SLEEP_TIME, GXP_SPI_TIMEOUT);
- if (ret) {
- dev_warn(spifi->dev, "write busy time out\n");
- return ret;
- }
- return 0;
- }
- static int do_gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
- {
- struct gxp_spi *spifi = spi_controller_get_devdata(mem->spi->master);
- struct gxp_spi_chip *chip = &spifi->chips[mem->spi->chip_select];
- int ret;
- if (op->data.dir == SPI_MEM_DATA_IN) {
- if (!op->addr.nbytes)
- ret = gxp_spi_read_reg(chip, op);
- else
- ret = gxp_spi_read(chip, op);
- } else {
- if (!op->addr.nbytes)
- ret = gxp_spi_write_reg(chip, op);
- else
- ret = gxp_spi_write(chip, op);
- }
- return ret;
- }
- static int gxp_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
- {
- int ret;
- ret = do_gxp_exec_mem_op(mem, op);
- if (ret)
- dev_err(&mem->spi->dev, "operation failed: %d", ret);
- return ret;
- }
- static const struct spi_controller_mem_ops gxp_spi_mem_ops = {
- .exec_op = gxp_exec_mem_op,
- };
- static int gxp_spi_setup(struct spi_device *spi)
- {
- struct gxp_spi *spifi = spi_controller_get_devdata(spi->master);
- unsigned int cs = spi->chip_select;
- struct gxp_spi_chip *chip = &spifi->chips[cs];
- chip->spifi = spifi;
- chip->cs = cs;
- gxp_spi_set_mode(spifi, MANUAL_MODE);
- return 0;
- }
- static int gxp_spifi_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- const struct gxp_spi_data *data;
- struct spi_controller *ctlr;
- struct gxp_spi *spifi;
- int ret;
- data = of_device_get_match_data(&pdev->dev);
- ctlr = devm_spi_alloc_master(dev, sizeof(*spifi));
- if (!ctlr)
- return -ENOMEM;
- spifi = spi_controller_get_devdata(ctlr);
- platform_set_drvdata(pdev, spifi);
- spifi->data = data;
- spifi->dev = dev;
- spifi->reg_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(spifi->reg_base))
- return PTR_ERR(spifi->reg_base);
- spifi->dat_base = devm_platform_ioremap_resource(pdev, 1);
- if (IS_ERR(spifi->dat_base))
- return PTR_ERR(spifi->dat_base);
- spifi->dir_base = devm_platform_ioremap_resource(pdev, 2);
- if (IS_ERR(spifi->dir_base))
- return PTR_ERR(spifi->dir_base);
- ctlr->mode_bits = data->mode_bits;
- ctlr->bus_num = pdev->id;
- ctlr->mem_ops = &gxp_spi_mem_ops;
- ctlr->setup = gxp_spi_setup;
- ctlr->num_chipselect = data->max_cs;
- ctlr->dev.of_node = dev->of_node;
- ret = devm_spi_register_controller(dev, ctlr);
- if (ret) {
- return dev_err_probe(&pdev->dev, ret,
- "failed to register spi controller\n");
- }
- return 0;
- }
- static const struct gxp_spi_data gxp_spifi_data = {
- .max_cs = 2,
- .mode_bits = 0,
- };
- static const struct of_device_id gxp_spifi_match[] = {
- {.compatible = "hpe,gxp-spifi", .data = &gxp_spifi_data },
- { /* null */ }
- };
- MODULE_DEVICE_TABLE(of, gxp_spifi_match);
- static struct platform_driver gxp_spifi_driver = {
- .probe = gxp_spifi_probe,
- .driver = {
- .name = "gxp-spifi",
- .of_match_table = gxp_spifi_match,
- },
- };
- module_platform_driver(gxp_spifi_driver);
- MODULE_DESCRIPTION("HPE GXP SPI Flash Interface driver");
- MODULE_AUTHOR("Nick Hawkins <[email protected]>");
- MODULE_LICENSE("GPL");
|