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