brintos

brintos / linux-shallow public Read only

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