|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Lattice FPGA programming over slave SPI sysCONFIG interface. | 
|  | */ | 
|  |  | 
|  | #include <linux/of.h> | 
|  | #include <linux/spi/spi.h> | 
|  |  | 
|  | #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"); |