1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * OpenCores tiny SPI host driver 4 * 5 * https://opencores.org/project,tiny_spi 6 * 7 * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw> 8 * 9 * Based on spi_s3c24xx.c, which is: 10 * Copyright (c) 2006 Ben Dooks 11 * Copyright (c) 2006 Simtec Electronics 12 * Ben Dooks <ben@simtec.co.uk> 13 */ 14 15 #include <linux/interrupt.h> 16 #include <linux/errno.h> 17 #include <linux/module.h> 18 #include <linux/platform_device.h> 19 #include <linux/spi/spi.h> 20 #include <linux/spi/spi_bitbang.h> 21 #include <linux/spi/spi_oc_tiny.h> 22 #include <linux/io.h> 23 #include <linux/of.h> 24 25 #define DRV_NAME "spi_oc_tiny" 26 27 #define TINY_SPI_RXDATA 0 28 #define TINY_SPI_TXDATA 4 29 #define TINY_SPI_STATUS 8 30 #define TINY_SPI_CONTROL 12 31 #define TINY_SPI_BAUD 16 32 33 #define TINY_SPI_STATUS_TXE 0x1 34 #define TINY_SPI_STATUS_TXR 0x2 35 36 struct tiny_spi { 37 /* bitbang has to be first */ 38 struct spi_bitbang bitbang; 39 struct completion done; 40 41 void __iomem *base; 42 int irq; 43 unsigned int freq; 44 unsigned int baudwidth; 45 unsigned int baud; 46 unsigned int speed_hz; 47 unsigned int mode; 48 unsigned int len; 49 unsigned int txc, rxc; 50 const u8 *txp; 51 u8 *rxp; 52 }; 53 54 static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev) 55 { 56 return spi_controller_get_devdata(sdev->controller); 57 } 58 59 static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz) 60 { 61 struct tiny_spi *hw = tiny_spi_to_hw(spi); 62 63 return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1; 64 } 65 66 static int tiny_spi_setup_transfer(struct spi_device *spi, 67 struct spi_transfer *t) 68 { 69 struct tiny_spi *hw = tiny_spi_to_hw(spi); 70 unsigned int baud = hw->baud; 71 72 if (t) { 73 if (t->speed_hz && t->speed_hz != hw->speed_hz) 74 baud = tiny_spi_baud(spi, t->speed_hz); 75 } 76 writel(baud, hw->base + TINY_SPI_BAUD); 77 writel(hw->mode, hw->base + TINY_SPI_CONTROL); 78 return 0; 79 } 80 81 static int tiny_spi_setup(struct spi_device *spi) 82 { 83 struct tiny_spi *hw = tiny_spi_to_hw(spi); 84 85 if (spi->max_speed_hz != hw->speed_hz) { 86 hw->speed_hz = spi->max_speed_hz; 87 hw->baud = tiny_spi_baud(spi, hw->speed_hz); 88 } 89 hw->mode = spi->mode & SPI_MODE_X_MASK; 90 return 0; 91 } 92 93 static inline void tiny_spi_wait_txr(struct tiny_spi *hw) 94 { 95 while (!(readb(hw->base + TINY_SPI_STATUS) & 96 TINY_SPI_STATUS_TXR)) 97 cpu_relax(); 98 } 99 100 static inline void tiny_spi_wait_txe(struct tiny_spi *hw) 101 { 102 while (!(readb(hw->base + TINY_SPI_STATUS) & 103 TINY_SPI_STATUS_TXE)) 104 cpu_relax(); 105 } 106 107 static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) 108 { 109 struct tiny_spi *hw = tiny_spi_to_hw(spi); 110 const u8 *txp = t->tx_buf; 111 u8 *rxp = t->rx_buf; 112 unsigned int i; 113 114 if (hw->irq >= 0) { 115 /* use interrupt driven data transfer */ 116 hw->len = t->len; 117 hw->txp = t->tx_buf; 118 hw->rxp = t->rx_buf; 119 hw->txc = 0; 120 hw->rxc = 0; 121 122 /* send the first byte */ 123 if (t->len > 1) { 124 writeb(hw->txp ? *hw->txp++ : 0, 125 hw->base + TINY_SPI_TXDATA); 126 hw->txc++; 127 writeb(hw->txp ? *hw->txp++ : 0, 128 hw->base + TINY_SPI_TXDATA); 129 hw->txc++; 130 writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS); 131 } else { 132 writeb(hw->txp ? *hw->txp++ : 0, 133 hw->base + TINY_SPI_TXDATA); 134 hw->txc++; 135 writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS); 136 } 137 138 wait_for_completion(&hw->done); 139 } else { 140 /* we need to tighten the transfer loop */ 141 writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA); 142 for (i = 1; i < t->len; i++) { 143 writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA); 144 145 if (rxp || (i != t->len - 1)) 146 tiny_spi_wait_txr(hw); 147 if (rxp) 148 *rxp++ = readb(hw->base + TINY_SPI_TXDATA); 149 } 150 tiny_spi_wait_txe(hw); 151 if (rxp) 152 *rxp++ = readb(hw->base + TINY_SPI_RXDATA); 153 } 154 155 return t->len; 156 } 157 158 static irqreturn_t tiny_spi_irq(int irq, void *dev) 159 { 160 struct tiny_spi *hw = dev; 161 162 writeb(0, hw->base + TINY_SPI_STATUS); 163 if (hw->rxc + 1 == hw->len) { 164 if (hw->rxp) 165 *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA); 166 hw->rxc++; 167 complete(&hw->done); 168 } else { 169 if (hw->rxp) 170 *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA); 171 hw->rxc++; 172 if (hw->txc < hw->len) { 173 writeb(hw->txp ? *hw->txp++ : 0, 174 hw->base + TINY_SPI_TXDATA); 175 hw->txc++; 176 writeb(TINY_SPI_STATUS_TXR, 177 hw->base + TINY_SPI_STATUS); 178 } else { 179 writeb(TINY_SPI_STATUS_TXE, 180 hw->base + TINY_SPI_STATUS); 181 } 182 } 183 return IRQ_HANDLED; 184 } 185 186 #ifdef CONFIG_OF 187 #include <linux/of_gpio.h> 188 189 static int tiny_spi_of_probe(struct platform_device *pdev) 190 { 191 struct tiny_spi *hw = platform_get_drvdata(pdev); 192 struct device_node *np = pdev->dev.of_node; 193 u32 val; 194 195 if (!np) 196 return 0; 197 hw->bitbang.ctlr->dev.of_node = pdev->dev.of_node; 198 if (!of_property_read_u32(np, "clock-frequency", &val)) 199 hw->freq = val; 200 if (!of_property_read_u32(np, "baud-width", &val)) 201 hw->baudwidth = val; 202 return 0; 203 } 204 #else /* !CONFIG_OF */ 205 static int tiny_spi_of_probe(struct platform_device *pdev) 206 { 207 return 0; 208 } 209 #endif /* CONFIG_OF */ 210 211 static int tiny_spi_probe(struct platform_device *pdev) 212 { 213 struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev); 214 struct tiny_spi *hw; 215 struct spi_controller *host; 216 int err = -ENODEV; 217 218 host = spi_alloc_host(&pdev->dev, sizeof(struct tiny_spi)); 219 if (!host) 220 return err; 221 222 /* setup the host state. */ 223 host->bus_num = pdev->id; 224 host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 225 host->setup = tiny_spi_setup; 226 host->use_gpio_descriptors = true; 227 228 hw = spi_controller_get_devdata(host); 229 platform_set_drvdata(pdev, hw); 230 231 /* setup the state for the bitbang driver */ 232 hw->bitbang.ctlr = host; 233 hw->bitbang.setup_transfer = tiny_spi_setup_transfer; 234 hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs; 235 236 /* find and map our resources */ 237 hw->base = devm_platform_ioremap_resource(pdev, 0); 238 if (IS_ERR(hw->base)) { 239 err = PTR_ERR(hw->base); 240 goto exit; 241 } 242 /* irq is optional */ 243 hw->irq = platform_get_irq(pdev, 0); 244 if (hw->irq >= 0) { 245 init_completion(&hw->done); 246 err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0, 247 pdev->name, hw); 248 if (err) 249 goto exit; 250 } 251 /* find platform data */ 252 if (platp) { 253 hw->freq = platp->freq; 254 hw->baudwidth = platp->baudwidth; 255 } else { 256 err = tiny_spi_of_probe(pdev); 257 if (err) 258 goto exit; 259 } 260 261 /* register our spi controller */ 262 err = spi_bitbang_start(&hw->bitbang); 263 if (err) 264 goto exit; 265 dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); 266 267 return 0; 268 269 exit: 270 spi_controller_put(host); 271 return err; 272 } 273 274 static void tiny_spi_remove(struct platform_device *pdev) 275 { 276 struct tiny_spi *hw = platform_get_drvdata(pdev); 277 struct spi_controller *host = hw->bitbang.ctlr; 278 279 spi_bitbang_stop(&hw->bitbang); 280 spi_controller_put(host); 281 } 282 283 #ifdef CONFIG_OF 284 static const struct of_device_id tiny_spi_match[] = { 285 { .compatible = "opencores,tiny-spi-rtlsvn2", }, 286 {}, 287 }; 288 MODULE_DEVICE_TABLE(of, tiny_spi_match); 289 #endif /* CONFIG_OF */ 290 291 static struct platform_driver tiny_spi_driver = { 292 .probe = tiny_spi_probe, 293 .remove_new = tiny_spi_remove, 294 .driver = { 295 .name = DRV_NAME, 296 .pm = NULL, 297 .of_match_table = of_match_ptr(tiny_spi_match), 298 }, 299 }; 300 module_platform_driver(tiny_spi_driver); 301 302 MODULE_DESCRIPTION("OpenCores tiny SPI driver"); 303 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); 304 MODULE_LICENSE("GPL"); 305 MODULE_ALIAS("platform:" DRV_NAME); 306