diff options
Diffstat (limited to 'drivers/fpga/xilinx-spi.c')
-rw-r--r-- | drivers/fpga/xilinx-spi.c | 167 |
1 files changed, 119 insertions, 48 deletions
diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c index 272ee0c22822..e1a227e7ff2a 100644 --- a/drivers/fpga/xilinx-spi.c +++ b/drivers/fpga/xilinx-spi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Xilinx Spartan6 Slave Serial SPI Driver + * Xilinx Spartan6 and 7 Series Slave Serial SPI Driver * * Copyright (C) 2017 DENX Software Engineering * @@ -23,45 +23,107 @@ struct xilinx_spi_conf { struct spi_device *spi; struct gpio_desc *prog_b; + struct gpio_desc *init_b; struct gpio_desc *done; }; -static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr) +static int get_done_gpio(struct fpga_manager *mgr) { struct xilinx_spi_conf *conf = mgr->priv; + int ret; + + ret = gpiod_get_value(conf->done); + + if (ret < 0) + dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret); + + return ret; +} - if (!gpiod_get_value(conf->done)) +static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr) +{ + if (!get_done_gpio(mgr)) return FPGA_MGR_STATE_RESET; return FPGA_MGR_STATE_UNKNOWN; } +/** + * wait_for_init_b - wait for the INIT_B pin to have a given state, or wait + * a given delay if the pin is unavailable + * + * @mgr: The FPGA manager object + * @value: Value INIT_B to wait for (1 = asserted = low) + * @alt_udelay: Delay to wait if the INIT_B GPIO is not available + * + * Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if + * too much time passed waiting for that. If no INIT_B GPIO is available + * then always return 0. + */ +static int wait_for_init_b(struct fpga_manager *mgr, int value, + unsigned long alt_udelay) +{ + struct xilinx_spi_conf *conf = mgr->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + if (conf->init_b) { + while (time_before(jiffies, timeout)) { + int ret = gpiod_get_value(conf->init_b); + + if (ret == value) + return 0; + + if (ret < 0) { + dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret); + return ret; + } + + usleep_range(100, 400); + } + + dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n", + value ? "assert" : "deassert"); + return -ETIMEDOUT; + } + + udelay(alt_udelay); + + return 0; +} + static int xilinx_spi_write_init(struct fpga_manager *mgr, struct fpga_image_info *info, const char *buf, size_t count) { struct xilinx_spi_conf *conf = mgr->priv; - const size_t prog_latency_7500us = 7500; - const size_t prog_pulse_1us = 1; + int err; if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { - dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); + dev_err(&mgr->dev, "Partial reconfiguration not supported\n"); return -EINVAL; } gpiod_set_value(conf->prog_b, 1); - udelay(prog_pulse_1us); /* min is 500 ns */ + err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */ + if (err) { + gpiod_set_value(conf->prog_b, 0); + return err; + } gpiod_set_value(conf->prog_b, 0); - if (gpiod_get_value(conf->done)) { + err = wait_for_init_b(mgr, 0, 0); + if (err) + return err; + + if (get_done_gpio(mgr)) { dev_err(&mgr->dev, "Unexpected DONE pin state...\n"); return -EIO; } /* program latency */ - usleep_range(prog_latency_7500us, prog_latency_7500us + 100); + usleep_range(7500, 7600); return 0; } @@ -108,25 +170,46 @@ static int xilinx_spi_write_complete(struct fpga_manager *mgr, struct fpga_image_info *info) { struct xilinx_spi_conf *conf = mgr->priv; - unsigned long timeout; + unsigned long timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us); + bool expired = false; + int done; int ret; - if (gpiod_get_value(conf->done)) - return xilinx_spi_apply_cclk_cycles(conf); - - timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us); + /* + * This loop is carefully written such that if the driver is + * scheduled out for more than 'timeout', we still check for DONE + * before giving up and we apply 8 extra CCLK cycles in all cases. + */ + while (!expired) { + expired = time_after(jiffies, timeout); - while (time_before(jiffies, timeout)) { + done = get_done_gpio(mgr); + if (done < 0) + return done; ret = xilinx_spi_apply_cclk_cycles(conf); if (ret) return ret; - if (gpiod_get_value(conf->done)) - return xilinx_spi_apply_cclk_cycles(conf); + if (done) + return 0; + } + + if (conf->init_b) { + ret = gpiod_get_value(conf->init_b); + + if (ret < 0) { + dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret); + return ret; + } + + dev_err(&mgr->dev, + ret ? "CRC error or invalid device\n" + : "Missing sync word or incomplete bitstream\n"); + } else { + dev_err(&mgr->dev, "Timeout after config data transfer\n"); } - dev_err(&mgr->dev, "Timeout after config data transfer.\n"); return -ETIMEDOUT; } @@ -150,44 +233,33 @@ static int xilinx_spi_probe(struct spi_device *spi) /* PROGRAM_B is active low */ conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW); - if (IS_ERR(conf->prog_b)) { - dev_err(&spi->dev, "Failed to get PROGRAM_B gpio: %ld\n", - PTR_ERR(conf->prog_b)); - return PTR_ERR(conf->prog_b); - } + if (IS_ERR(conf->prog_b)) + return dev_err_probe(&spi->dev, PTR_ERR(conf->prog_b), + "Failed to get PROGRAM_B gpio\n"); - conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN); - if (IS_ERR(conf->done)) { - dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n", - PTR_ERR(conf->done)); - return PTR_ERR(conf->done); - } - - mgr = devm_fpga_mgr_create(&spi->dev, - "Xilinx Slave Serial FPGA Manager", - &xilinx_spi_ops, conf); - if (!mgr) - return -ENOMEM; - - spi_set_drvdata(spi, mgr); - - return fpga_mgr_register(mgr); -} - -static int xilinx_spi_remove(struct spi_device *spi) -{ - struct fpga_manager *mgr = spi_get_drvdata(spi); + conf->init_b = devm_gpiod_get_optional(&spi->dev, "init-b", GPIOD_IN); + if (IS_ERR(conf->init_b)) + return dev_err_probe(&spi->dev, PTR_ERR(conf->init_b), + "Failed to get INIT_B gpio\n"); - fpga_mgr_unregister(mgr); - - return 0; + conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN); + if (IS_ERR(conf->done)) + return dev_err_probe(&spi->dev, PTR_ERR(conf->done), + "Failed to get DONE gpio\n"); + + mgr = devm_fpga_mgr_register(&spi->dev, + "Xilinx Slave Serial FPGA Manager", + &xilinx_spi_ops, conf); + return PTR_ERR_OR_ZERO(mgr); } +#ifdef CONFIG_OF static const struct of_device_id xlnx_spi_of_match[] = { { .compatible = "xlnx,fpga-slave-serial", }, {} }; MODULE_DEVICE_TABLE(of, xlnx_spi_of_match); +#endif static struct spi_driver xilinx_slave_spi_driver = { .driver = { @@ -195,7 +267,6 @@ static struct spi_driver xilinx_slave_spi_driver = { .of_match_table = of_match_ptr(xlnx_spi_of_match), }, .probe = xilinx_spi_probe, - .remove = xilinx_spi_remove, }; module_spi_driver(xilinx_slave_spi_driver) |