linux/drivers/fpga/xilinx-spi.c
<<
>>
Prefs
   1/*
   2 * Xilinx Spartan6 Slave Serial SPI Driver
   3 *
   4 * Copyright (C) 2017 DENX Software Engineering
   5 *
   6 * Anatolij Gustschin <agust@denx.de>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms and conditions of the GNU General Public License,
  10 * version 2, as published by the Free Software Foundation.
  11 *
  12 * Manage Xilinx FPGA firmware that is loaded over SPI using
  13 * the slave serial configuration interface.
  14 */
  15
  16#include <linux/delay.h>
  17#include <linux/device.h>
  18#include <linux/fpga/fpga-mgr.h>
  19#include <linux/gpio/consumer.h>
  20#include <linux/module.h>
  21#include <linux/mod_devicetable.h>
  22#include <linux/of.h>
  23#include <linux/spi/spi.h>
  24#include <linux/sizes.h>
  25
  26struct xilinx_spi_conf {
  27        struct spi_device *spi;
  28        struct gpio_desc *prog_b;
  29        struct gpio_desc *done;
  30};
  31
  32static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
  33{
  34        struct xilinx_spi_conf *conf = mgr->priv;
  35
  36        if (!gpiod_get_value(conf->done))
  37                return FPGA_MGR_STATE_RESET;
  38
  39        return FPGA_MGR_STATE_UNKNOWN;
  40}
  41
  42static int xilinx_spi_write_init(struct fpga_manager *mgr,
  43                                 struct fpga_image_info *info,
  44                                 const char *buf, size_t count)
  45{
  46        struct xilinx_spi_conf *conf = mgr->priv;
  47        const size_t prog_latency_7500us = 7500;
  48        const size_t prog_pulse_1us = 1;
  49
  50        if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
  51                dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
  52                return -EINVAL;
  53        }
  54
  55        gpiod_set_value(conf->prog_b, 1);
  56
  57        udelay(prog_pulse_1us); /* min is 500 ns */
  58
  59        gpiod_set_value(conf->prog_b, 0);
  60
  61        if (gpiod_get_value(conf->done)) {
  62                dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
  63                return -EIO;
  64        }
  65
  66        /* program latency */
  67        usleep_range(prog_latency_7500us, prog_latency_7500us + 100);
  68        return 0;
  69}
  70
  71static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
  72                            size_t count)
  73{
  74        struct xilinx_spi_conf *conf = mgr->priv;
  75        const char *fw_data = buf;
  76        const char *fw_data_end = fw_data + count;
  77
  78        while (fw_data < fw_data_end) {
  79                size_t remaining, stride;
  80                int ret;
  81
  82                remaining = fw_data_end - fw_data;
  83                stride = min_t(size_t, remaining, SZ_4K);
  84
  85                ret = spi_write(conf->spi, fw_data, stride);
  86                if (ret) {
  87                        dev_err(&mgr->dev, "SPI error in firmware write: %d\n",
  88                                ret);
  89                        return ret;
  90                }
  91                fw_data += stride;
  92        }
  93
  94        return 0;
  95}
  96
  97static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf)
  98{
  99        struct spi_device *spi = conf->spi;
 100        const u8 din_data[1] = { 0xff };
 101        int ret;
 102
 103        ret = spi_write(conf->spi, din_data, sizeof(din_data));
 104        if (ret)
 105                dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret);
 106
 107        return ret;
 108}
 109
 110static int xilinx_spi_write_complete(struct fpga_manager *mgr,
 111                                     struct fpga_image_info *info)
 112{
 113        struct xilinx_spi_conf *conf = mgr->priv;
 114        unsigned long timeout;
 115        int ret;
 116
 117        if (gpiod_get_value(conf->done))
 118                return xilinx_spi_apply_cclk_cycles(conf);
 119
 120        timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
 121
 122        while (time_before(jiffies, timeout)) {
 123
 124                ret = xilinx_spi_apply_cclk_cycles(conf);
 125                if (ret)
 126                        return ret;
 127
 128                if (gpiod_get_value(conf->done))
 129                        return xilinx_spi_apply_cclk_cycles(conf);
 130        }
 131
 132        dev_err(&mgr->dev, "Timeout after config data transfer.\n");
 133        return -ETIMEDOUT;
 134}
 135
 136static const struct fpga_manager_ops xilinx_spi_ops = {
 137        .state = xilinx_spi_state,
 138        .write_init = xilinx_spi_write_init,
 139        .write = xilinx_spi_write,
 140        .write_complete = xilinx_spi_write_complete,
 141};
 142
 143static int xilinx_spi_probe(struct spi_device *spi)
 144{
 145        struct xilinx_spi_conf *conf;
 146        struct fpga_manager *mgr;
 147
 148        conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
 149        if (!conf)
 150                return -ENOMEM;
 151
 152        conf->spi = spi;
 153
 154        /* PROGRAM_B is active low */
 155        conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW);
 156        if (IS_ERR(conf->prog_b)) {
 157                dev_err(&spi->dev, "Failed to get PROGRAM_B gpio: %ld\n",
 158                        PTR_ERR(conf->prog_b));
 159                return PTR_ERR(conf->prog_b);
 160        }
 161
 162        conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN);
 163        if (IS_ERR(conf->done)) {
 164                dev_err(&spi->dev, "Failed to get DONE gpio: %ld\n",
 165                        PTR_ERR(conf->done));
 166                return PTR_ERR(conf->done);
 167        }
 168
 169        mgr = devm_fpga_mgr_create(&spi->dev,
 170                                   "Xilinx Slave Serial FPGA Manager",
 171                                   &xilinx_spi_ops, conf);
 172        if (!mgr)
 173                return -ENOMEM;
 174
 175        spi_set_drvdata(spi, mgr);
 176
 177        return fpga_mgr_register(mgr);
 178}
 179
 180static int xilinx_spi_remove(struct spi_device *spi)
 181{
 182        struct fpga_manager *mgr = spi_get_drvdata(spi);
 183
 184        fpga_mgr_unregister(mgr);
 185
 186        return 0;
 187}
 188
 189static const struct of_device_id xlnx_spi_of_match[] = {
 190        { .compatible = "xlnx,fpga-slave-serial", },
 191        {}
 192};
 193MODULE_DEVICE_TABLE(of, xlnx_spi_of_match);
 194
 195static struct spi_driver xilinx_slave_spi_driver = {
 196        .driver = {
 197                .name = "xlnx-slave-spi",
 198                .of_match_table = of_match_ptr(xlnx_spi_of_match),
 199        },
 200        .probe = xilinx_spi_probe,
 201        .remove = xilinx_spi_remove,
 202};
 203
 204module_spi_driver(xilinx_slave_spi_driver)
 205
 206MODULE_LICENSE("GPL v2");
 207MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
 208MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SPI");
 209