// SPDX-License-Identifier: GPL-2.0 /* * Lattice FPGA programming over slave SPI sysCONFIG interface. */ #include #include "lattice-sysconfig.h" static const u32 ecp5_spi_max_speed_hz = 60000000; static int sysconfig_spi_cmd_transfer(struct sysconfig_priv *priv, const void *tx_buf, size_t tx_len, void *rx_buf, size_t rx_len) { struct spi_device *spi = to_spi_device(priv->dev); return spi_write_then_read(spi, tx_buf, tx_len, rx_buf, rx_len); } static int sysconfig_spi_bitstream_burst_init(struct sysconfig_priv *priv) { const u8 lsc_bitstream_burst[] = SYSCONFIG_LSC_BITSTREAM_BURST; struct spi_device *spi = to_spi_device(priv->dev); struct spi_transfer xfer = {}; struct spi_message msg; size_t buf_len; void *buf; int ret; buf_len = sizeof(lsc_bitstream_burst); buf = kmemdup(lsc_bitstream_burst, buf_len, GFP_KERNEL); if (!buf) return -ENOMEM; xfer.len = buf_len; xfer.tx_buf = buf; xfer.cs_change = 1; spi_message_init_with_transfers(&msg, &xfer, 1); /* * Lock SPI bus for exclusive usage until FPGA programming is done. * SPI bus will be released in sysconfig_spi_bitstream_burst_complete(). */ spi_bus_lock(spi->controller); ret = spi_sync_locked(spi, &msg); if (ret) spi_bus_unlock(spi->controller); kfree(buf); return ret; } static int sysconfig_spi_bitstream_burst_write(struct sysconfig_priv *priv, const char *buf, size_t len) { struct spi_device *spi = to_spi_device(priv->dev); struct spi_transfer xfer = { .tx_buf = buf, .len = len, .cs_change = 1, }; struct spi_message msg; spi_message_init_with_transfers(&msg, &xfer, 1); return spi_sync_locked(spi, &msg); } static int sysconfig_spi_bitstream_burst_complete(struct sysconfig_priv *priv) { struct spi_device *spi = to_spi_device(priv->dev); /* Bitstream burst write is done, release SPI bus */ spi_bus_unlock(spi->controller); /* Toggle CS to finish bitstream write */ return spi_write(spi, NULL, 0); } static int sysconfig_spi_probe(struct spi_device *spi) { const struct spi_device_id *dev_id; struct device *dev = &spi->dev; struct sysconfig_priv *priv; const u32 *spi_max_speed; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; spi_max_speed = device_get_match_data(dev); if (!spi_max_speed) { dev_id = spi_get_device_id(spi); if (!dev_id) return -ENODEV; spi_max_speed = (const u32 *)dev_id->driver_data; } if (!spi_max_speed) return -EINVAL; if (spi->max_speed_hz > *spi_max_speed) { dev_err(dev, "SPI speed %u is too high, maximum speed is %u\n", spi->max_speed_hz, *spi_max_speed); return -EINVAL; } priv->dev = dev; priv->command_transfer = sysconfig_spi_cmd_transfer; priv->bitstream_burst_write_init = sysconfig_spi_bitstream_burst_init; priv->bitstream_burst_write = sysconfig_spi_bitstream_burst_write; priv->bitstream_burst_write_complete = sysconfig_spi_bitstream_burst_complete; return sysconfig_probe(priv); } static const struct spi_device_id sysconfig_spi_ids[] = { { .name = "sysconfig-ecp5", .driver_data = (kernel_ulong_t)&ecp5_spi_max_speed_hz, }, {}, }; MODULE_DEVICE_TABLE(spi, sysconfig_spi_ids); #if IS_ENABLED(CONFIG_OF) static const struct of_device_id sysconfig_of_ids[] = { { .compatible = "lattice,sysconfig-ecp5", .data = &ecp5_spi_max_speed_hz, }, {}, }; MODULE_DEVICE_TABLE(of, sysconfig_of_ids); #endif /* IS_ENABLED(CONFIG_OF) */ static struct spi_driver lattice_sysconfig_driver = { .probe = sysconfig_spi_probe, .id_table = sysconfig_spi_ids, .driver = { .name = "lattice_sysconfig_spi_fpga_mgr", .of_match_table = of_match_ptr(sysconfig_of_ids), }, }; module_spi_driver(lattice_sysconfig_driver); MODULE_DESCRIPTION("Lattice sysCONFIG Slave SPI FPGA Manager"); MODULE_LICENSE("GPL");