File Index
Files and directories
Sign in
FILE

spi-loongson-core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/spi/spi-loongson-core.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2// Loongson SPI Support
  3// Copyright (C) 2023 Loongson Technology Corporation Limited
  4
  5#include <linux/clk.h>
  6#include <linux/delay.h>
  7#include <linux/err.h>
  8#include <linux/export.h>
  9#include <linux/init.h>
 10#include <linux/interrupt.h>
 11#include <linux/io.h>
 12#include <linux/iopoll.h>
 13#include <linux/kernel.h>
 14#include <linux/module.h>
 15#include <linux/spi/spi.h>
 16
 17#include "spi-loongson.h"
 18
 19static inline void loongson_spi_write_reg(struct loongson_spi *spi, unsigned char reg,
 20					  unsigned char data)
 21{
 22	writeb(data, spi->base + reg);
 23}
 24
 25static inline char loongson_spi_read_reg(struct loongson_spi *spi, unsigned char reg)
 26{
 27	return readb(spi->base + reg);
 28}
 29
 30static void loongson_spi_set_cs(struct spi_device *spi, bool en)
 31{
 32	int cs;
 33	unsigned char mask = (BIT(4) | BIT(0)) << spi_get_chipselect(spi, 0);
 34	unsigned char val = en ? mask :  (BIT(0) << spi_get_chipselect(spi, 0));
 35	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
 36
 37	cs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG) & ~mask;
 38	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, val | cs);
 39}
 40
 41static void loongson_spi_set_clk(struct loongson_spi *loongson_spi, unsigned int hz)
 42{
 43	unsigned char val;
 44	unsigned int div, div_tmp;
 45	static const char rdiv[12] = {0, 1, 4, 2, 3, 5, 6, 7, 8, 9, 10, 11};
 46
 47	div = clamp_val(DIV_ROUND_UP_ULL(loongson_spi->clk_rate, hz), 2, 4096);
 48	div_tmp = rdiv[fls(div - 1)];
 49	loongson_spi->spcr = (div_tmp & GENMASK(1, 0)) >> 0;
 50	loongson_spi->sper = (div_tmp & GENMASK(3, 2)) >> 2;
 51	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
 52	val &= ~GENMASK(1, 0);
 53	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val |
 54			       loongson_spi->spcr);
 55	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
 56	val &= ~GENMASK(1, 0);
 57	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, val |
 58			       loongson_spi->sper);
 59	loongson_spi->hz = hz;
 60}
 61
 62static void loongson_spi_set_mode(struct loongson_spi *loongson_spi,
 63				  struct spi_device *spi)
 64{
 65	unsigned char val;
 66
 67	val = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
 68	val &= ~(LOONGSON_SPI_SPCR_CPOL | LOONGSON_SPI_SPCR_CPHA);
 69	if (spi->mode & SPI_CPOL)
 70		val |= LOONGSON_SPI_SPCR_CPOL;
 71	if (spi->mode & SPI_CPHA)
 72		val |= LOONGSON_SPI_SPCR_CPHA;
 73
 74	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, val);
 75	loongson_spi->mode |= spi->mode;
 76}
 77
 78static int loongson_spi_update_state(struct loongson_spi *loongson_spi,
 79				struct spi_device *spi, struct spi_transfer *t)
 80{
 81	if (t && loongson_spi->hz != t->speed_hz)
 82		loongson_spi_set_clk(loongson_spi, t->speed_hz);
 83
 84	if ((spi->mode ^ loongson_spi->mode) & SPI_MODE_X_MASK)
 85		loongson_spi_set_mode(loongson_spi, spi);
 86
 87	return 0;
 88}
 89
 90static int loongson_spi_setup(struct spi_device *spi)
 91{
 92	struct loongson_spi *loongson_spi;
 93
 94	loongson_spi = spi_controller_get_devdata(spi->controller);
 95	if (spi->bits_per_word % 8)
 96		return -EINVAL;
 97
 98	if (spi_get_chipselect(spi, 0) >= spi->controller->num_chipselect)
 99		return -EINVAL;
100
101	loongson_spi->hz = 0;
102	loongson_spi_set_cs(spi, true);
103
104	return 0;
105}
106
107static int loongson_spi_write_read_8bit(struct spi_device *spi, const u8 **tx_buf,
108					u8 **rx_buf, unsigned int num)
109{
110	int ret;
111	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
112
113	if (tx_buf && *tx_buf)
114		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, *((*tx_buf)++));
115	else
116		loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_FIFO_REG, 0);
117
118	ret = readb_poll_timeout(loongson_spi->base + LOONGSON_SPI_SPSR_REG,
119				 loongson_spi->spsr, (loongson_spi->spsr &
120				 LOONGSON_SPI_SPSR_RFEMPTY) != LOONGSON_SPI_SPSR_RFEMPTY,
121				 1, USEC_PER_MSEC);
122
123	if (rx_buf && *rx_buf)
124		*(*rx_buf)++ = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_FIFO_REG);
125	else
126		loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_FIFO_REG);
127
128	return ret;
129}
130
131static int loongson_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
132{
133	int ret;
134	unsigned int count;
135	const u8 *tx = xfer->tx_buf;
136	u8 *rx = xfer->rx_buf;
137
138	count = xfer->len;
139	do {
140		ret = loongson_spi_write_read_8bit(spi, &tx, &rx, count);
141		if (ret)
142			break;
143	} while (--count);
144
145	return ret;
146}
147
148static int loongson_spi_prepare_message(struct spi_controller *ctlr, struct spi_message *m)
149{
150	struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctlr);
151
152	loongson_spi->para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
153	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para &
154			       ~LOONGSON_SPI_PARA_MEM_EN);
155
156	return 0;
157}
158
159static int loongson_spi_transfer_one(struct spi_controller *ctrl, struct spi_device *spi,
160				     struct spi_transfer *xfer)
161{
162	struct loongson_spi *loongson_spi = spi_controller_get_devdata(spi->controller);
163
164	loongson_spi_update_state(loongson_spi, spi, xfer);
165	if (xfer->len)
166		return loongson_spi_write_read(spi, xfer);
167
168	return 0;
169}
170
171static int loongson_spi_unprepare_message(struct spi_controller *ctrl, struct spi_message *m)
172{
173	struct loongson_spi *loongson_spi = spi_controller_get_devdata(ctrl);
174
175	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para);
176
177	return 0;
178}
179
180static void loongson_spi_reginit(struct loongson_spi *loongson_spi_dev)
181{
182	unsigned char val;
183
184	val = loongson_spi_read_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG);
185	val &= ~LOONGSON_SPI_SPCR_SPE;
186	loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG, val);
187
188	loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPSR_REG,
189			       (LOONGSON_SPI_SPSR_SPIF | LOONGSON_SPI_SPSR_WCOL));
190
191	val = loongson_spi_read_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG);
192	val |= LOONGSON_SPI_SPCR_SPE;
193	loongson_spi_write_reg(loongson_spi_dev, LOONGSON_SPI_SPCR_REG, val);
194}
195
196int loongson_spi_init_controller(struct device *dev, void __iomem *regs)
197{
198	struct spi_controller *controller;
199	struct loongson_spi *spi;
200	struct clk *clk;
201
202	controller = devm_spi_alloc_host(dev, sizeof(struct loongson_spi));
203	if (controller == NULL)
204		return -ENOMEM;
205
206	controller->mode_bits = SPI_MODE_X_MASK | SPI_CS_HIGH;
207	controller->setup = loongson_spi_setup;
208	controller->prepare_message = loongson_spi_prepare_message;
209	controller->transfer_one = loongson_spi_transfer_one;
210	controller->unprepare_message = loongson_spi_unprepare_message;
211	controller->set_cs = loongson_spi_set_cs;
212	controller->num_chipselect = 4;
213	device_set_node(&controller->dev, dev_fwnode(dev));
214	dev_set_drvdata(dev, controller);
215
216	spi = spi_controller_get_devdata(controller);
217	spi->base = regs;
218	spi->controller = controller;
219
220	clk = devm_clk_get_optional(dev, NULL);
221	if (IS_ERR(clk))
222		return dev_err_probe(dev, PTR_ERR(clk), "unable to get clock\n");
223
224	spi->clk_rate = clk_get_rate(clk);
225	loongson_spi_reginit(spi);
226
227	spi->mode = 0;
228
229	return devm_spi_register_controller(dev, controller);
230}
231EXPORT_SYMBOL_NS_GPL(loongson_spi_init_controller, "SPI_LOONGSON_CORE");
232
233static int __maybe_unused loongson_spi_suspend(struct device *dev)
234{
235	struct loongson_spi *loongson_spi;
236	struct spi_controller *controller;
237
238	controller = dev_get_drvdata(dev);
239	spi_controller_suspend(controller);
240
241	loongson_spi = spi_controller_get_devdata(controller);
242
243	loongson_spi->spcr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPCR_REG);
244	loongson_spi->sper = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPER_REG);
245	loongson_spi->spsr = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SPSR_REG);
246	loongson_spi->para = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_PARA_REG);
247	loongson_spi->sfcs = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_SFCS_REG);
248	loongson_spi->timi = loongson_spi_read_reg(loongson_spi, LOONGSON_SPI_TIMI_REG);
249
250	return 0;
251}
252
253static int __maybe_unused loongson_spi_resume(struct device *dev)
254{
255	struct loongson_spi *loongson_spi;
256	struct spi_controller *controller;
257
258	controller = dev_get_drvdata(dev);
259	loongson_spi = spi_controller_get_devdata(controller);
260
261	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPCR_REG, loongson_spi->spcr);
262	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPER_REG, loongson_spi->sper);
263	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SPSR_REG, loongson_spi->spsr);
264	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_PARA_REG, loongson_spi->para);
265	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_SFCS_REG, loongson_spi->sfcs);
266	loongson_spi_write_reg(loongson_spi, LOONGSON_SPI_TIMI_REG, loongson_spi->timi);
267
268	spi_controller_resume(controller);
269
270	return 0;
271}
272
273const struct dev_pm_ops loongson_spi_dev_pm_ops = {
274	.suspend = loongson_spi_suspend,
275	.resume = loongson_spi_resume,
276};
277EXPORT_SYMBOL_NS_GPL(loongson_spi_dev_pm_ops, "SPI_LOONGSON_CORE");
278
279MODULE_DESCRIPTION("Loongson SPI core driver");
280MODULE_LICENSE("GPL");
~/ Read-only file preview.