1463dd43bSIvan Bornyakov // SPDX-License-Identifier: GPL-2.0
2463dd43bSIvan Bornyakov /*
3463dd43bSIvan Bornyakov  * Lattice FPGA programming over slave SPI sysCONFIG interface.
4463dd43bSIvan Bornyakov  */
5463dd43bSIvan Bornyakov 
6*2febc5ddSRob Herring #include <linux/of.h>
7463dd43bSIvan Bornyakov #include <linux/spi/spi.h>
8463dd43bSIvan Bornyakov 
9463dd43bSIvan Bornyakov #include "lattice-sysconfig.h"
10463dd43bSIvan Bornyakov 
11463dd43bSIvan Bornyakov static const u32 ecp5_spi_max_speed_hz = 60000000;
12463dd43bSIvan Bornyakov 
sysconfig_spi_cmd_transfer(struct sysconfig_priv * priv,const void * tx_buf,size_t tx_len,void * rx_buf,size_t rx_len)13463dd43bSIvan Bornyakov static int sysconfig_spi_cmd_transfer(struct sysconfig_priv *priv,
14463dd43bSIvan Bornyakov 				      const void *tx_buf, size_t tx_len,
15463dd43bSIvan Bornyakov 				      void *rx_buf, size_t rx_len)
16463dd43bSIvan Bornyakov {
17463dd43bSIvan Bornyakov 	struct spi_device *spi = to_spi_device(priv->dev);
18463dd43bSIvan Bornyakov 
19463dd43bSIvan Bornyakov 	return spi_write_then_read(spi, tx_buf, tx_len, rx_buf, rx_len);
20463dd43bSIvan Bornyakov }
21463dd43bSIvan Bornyakov 
sysconfig_spi_bitstream_burst_init(struct sysconfig_priv * priv)22463dd43bSIvan Bornyakov static int sysconfig_spi_bitstream_burst_init(struct sysconfig_priv *priv)
23463dd43bSIvan Bornyakov {
24463dd43bSIvan Bornyakov 	const u8 lsc_bitstream_burst[] = SYSCONFIG_LSC_BITSTREAM_BURST;
25463dd43bSIvan Bornyakov 	struct spi_device *spi = to_spi_device(priv->dev);
26463dd43bSIvan Bornyakov 	struct spi_transfer xfer = {};
27463dd43bSIvan Bornyakov 	struct spi_message msg;
28463dd43bSIvan Bornyakov 	size_t buf_len;
29463dd43bSIvan Bornyakov 	void *buf;
30463dd43bSIvan Bornyakov 	int ret;
31463dd43bSIvan Bornyakov 
32463dd43bSIvan Bornyakov 	buf_len = sizeof(lsc_bitstream_burst);
33463dd43bSIvan Bornyakov 
34463dd43bSIvan Bornyakov 	buf = kmemdup(lsc_bitstream_burst, buf_len, GFP_KERNEL);
35463dd43bSIvan Bornyakov 	if (!buf)
36463dd43bSIvan Bornyakov 		return -ENOMEM;
37463dd43bSIvan Bornyakov 
38463dd43bSIvan Bornyakov 	xfer.len = buf_len;
39463dd43bSIvan Bornyakov 	xfer.tx_buf = buf;
40463dd43bSIvan Bornyakov 	xfer.cs_change = 1;
41463dd43bSIvan Bornyakov 
42463dd43bSIvan Bornyakov 	spi_message_init_with_transfers(&msg, &xfer, 1);
43463dd43bSIvan Bornyakov 
44463dd43bSIvan Bornyakov 	/*
45463dd43bSIvan Bornyakov 	 * Lock SPI bus for exclusive usage until FPGA programming is done.
46463dd43bSIvan Bornyakov 	 * SPI bus will be released in sysconfig_spi_bitstream_burst_complete().
47463dd43bSIvan Bornyakov 	 */
48463dd43bSIvan Bornyakov 	spi_bus_lock(spi->controller);
49463dd43bSIvan Bornyakov 
50463dd43bSIvan Bornyakov 	ret = spi_sync_locked(spi, &msg);
51463dd43bSIvan Bornyakov 	if (ret)
52463dd43bSIvan Bornyakov 		spi_bus_unlock(spi->controller);
53463dd43bSIvan Bornyakov 
54463dd43bSIvan Bornyakov 	kfree(buf);
55463dd43bSIvan Bornyakov 
56463dd43bSIvan Bornyakov 	return ret;
57463dd43bSIvan Bornyakov }
58463dd43bSIvan Bornyakov 
sysconfig_spi_bitstream_burst_write(struct sysconfig_priv * priv,const char * buf,size_t len)59463dd43bSIvan Bornyakov static int sysconfig_spi_bitstream_burst_write(struct sysconfig_priv *priv,
60463dd43bSIvan Bornyakov 					       const char *buf, size_t len)
61463dd43bSIvan Bornyakov {
62463dd43bSIvan Bornyakov 	struct spi_device *spi = to_spi_device(priv->dev);
63463dd43bSIvan Bornyakov 	struct spi_transfer xfer = {
64463dd43bSIvan Bornyakov 		.tx_buf = buf,
65463dd43bSIvan Bornyakov 		.len = len,
66463dd43bSIvan Bornyakov 		.cs_change = 1,
67463dd43bSIvan Bornyakov 	};
68463dd43bSIvan Bornyakov 	struct spi_message msg;
69463dd43bSIvan Bornyakov 
70463dd43bSIvan Bornyakov 	spi_message_init_with_transfers(&msg, &xfer, 1);
71463dd43bSIvan Bornyakov 
72463dd43bSIvan Bornyakov 	return spi_sync_locked(spi, &msg);
73463dd43bSIvan Bornyakov }
74463dd43bSIvan Bornyakov 
sysconfig_spi_bitstream_burst_complete(struct sysconfig_priv * priv)75463dd43bSIvan Bornyakov static int sysconfig_spi_bitstream_burst_complete(struct sysconfig_priv *priv)
76463dd43bSIvan Bornyakov {
77463dd43bSIvan Bornyakov 	struct spi_device *spi = to_spi_device(priv->dev);
78463dd43bSIvan Bornyakov 
79463dd43bSIvan Bornyakov 	/* Bitstream burst write is done, release SPI bus */
80463dd43bSIvan Bornyakov 	spi_bus_unlock(spi->controller);
81463dd43bSIvan Bornyakov 
82463dd43bSIvan Bornyakov 	/* Toggle CS to finish bitstream write */
83463dd43bSIvan Bornyakov 	return spi_write(spi, NULL, 0);
84463dd43bSIvan Bornyakov }
85463dd43bSIvan Bornyakov 
sysconfig_spi_probe(struct spi_device * spi)86463dd43bSIvan Bornyakov static int sysconfig_spi_probe(struct spi_device *spi)
87463dd43bSIvan Bornyakov {
88463dd43bSIvan Bornyakov 	const struct spi_device_id *dev_id;
89463dd43bSIvan Bornyakov 	struct device *dev = &spi->dev;
90463dd43bSIvan Bornyakov 	struct sysconfig_priv *priv;
91463dd43bSIvan Bornyakov 	const u32 *spi_max_speed;
92463dd43bSIvan Bornyakov 
93463dd43bSIvan Bornyakov 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
94463dd43bSIvan Bornyakov 	if (!priv)
95463dd43bSIvan Bornyakov 		return -ENOMEM;
96463dd43bSIvan Bornyakov 
97463dd43bSIvan Bornyakov 	spi_max_speed = device_get_match_data(dev);
98463dd43bSIvan Bornyakov 	if (!spi_max_speed) {
99463dd43bSIvan Bornyakov 		dev_id = spi_get_device_id(spi);
100463dd43bSIvan Bornyakov 		if (!dev_id)
101463dd43bSIvan Bornyakov 			return -ENODEV;
102463dd43bSIvan Bornyakov 
103463dd43bSIvan Bornyakov 		spi_max_speed = (const u32 *)dev_id->driver_data;
104463dd43bSIvan Bornyakov 	}
105463dd43bSIvan Bornyakov 
106463dd43bSIvan Bornyakov 	if (!spi_max_speed)
107463dd43bSIvan Bornyakov 		return -EINVAL;
108463dd43bSIvan Bornyakov 
109463dd43bSIvan Bornyakov 	if (spi->max_speed_hz > *spi_max_speed) {
110463dd43bSIvan Bornyakov 		dev_err(dev, "SPI speed %u is too high, maximum speed is %u\n",
111463dd43bSIvan Bornyakov 			spi->max_speed_hz, *spi_max_speed);
112463dd43bSIvan Bornyakov 		return -EINVAL;
113463dd43bSIvan Bornyakov 	}
114463dd43bSIvan Bornyakov 
115463dd43bSIvan Bornyakov 	priv->dev = dev;
116463dd43bSIvan Bornyakov 	priv->command_transfer = sysconfig_spi_cmd_transfer;
117463dd43bSIvan Bornyakov 	priv->bitstream_burst_write_init = sysconfig_spi_bitstream_burst_init;
118463dd43bSIvan Bornyakov 	priv->bitstream_burst_write = sysconfig_spi_bitstream_burst_write;
119463dd43bSIvan Bornyakov 	priv->bitstream_burst_write_complete = sysconfig_spi_bitstream_burst_complete;
120463dd43bSIvan Bornyakov 
121463dd43bSIvan Bornyakov 	return sysconfig_probe(priv);
122463dd43bSIvan Bornyakov }
123463dd43bSIvan Bornyakov 
124463dd43bSIvan Bornyakov static const struct spi_device_id sysconfig_spi_ids[] = {
125463dd43bSIvan Bornyakov 	{
126463dd43bSIvan Bornyakov 		.name = "sysconfig-ecp5",
127463dd43bSIvan Bornyakov 		.driver_data = (kernel_ulong_t)&ecp5_spi_max_speed_hz,
128463dd43bSIvan Bornyakov 	}, {},
129463dd43bSIvan Bornyakov };
130463dd43bSIvan Bornyakov MODULE_DEVICE_TABLE(spi, sysconfig_spi_ids);
131463dd43bSIvan Bornyakov 
132463dd43bSIvan Bornyakov #if IS_ENABLED(CONFIG_OF)
133463dd43bSIvan Bornyakov static const struct of_device_id sysconfig_of_ids[] = {
134463dd43bSIvan Bornyakov 	{
135463dd43bSIvan Bornyakov 		.compatible = "lattice,sysconfig-ecp5",
136463dd43bSIvan Bornyakov 		.data = &ecp5_spi_max_speed_hz,
137463dd43bSIvan Bornyakov 	}, {},
138463dd43bSIvan Bornyakov };
139463dd43bSIvan Bornyakov MODULE_DEVICE_TABLE(of, sysconfig_of_ids);
140463dd43bSIvan Bornyakov #endif /* IS_ENABLED(CONFIG_OF) */
141463dd43bSIvan Bornyakov 
142463dd43bSIvan Bornyakov static struct spi_driver lattice_sysconfig_driver = {
143463dd43bSIvan Bornyakov 	.probe = sysconfig_spi_probe,
144463dd43bSIvan Bornyakov 	.id_table = sysconfig_spi_ids,
145463dd43bSIvan Bornyakov 	.driver = {
146463dd43bSIvan Bornyakov 		.name = "lattice_sysconfig_spi_fpga_mgr",
147463dd43bSIvan Bornyakov 		.of_match_table = of_match_ptr(sysconfig_of_ids),
148463dd43bSIvan Bornyakov 	},
149463dd43bSIvan Bornyakov };
150463dd43bSIvan Bornyakov module_spi_driver(lattice_sysconfig_driver);
151463dd43bSIvan Bornyakov 
152463dd43bSIvan Bornyakov MODULE_DESCRIPTION("Lattice sysCONFIG Slave SPI FPGA Manager");
153463dd43bSIvan Bornyakov MODULE_LICENSE("GPL");
154