Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

spi: add OpenCores tiny SPI driver

This patch adds support of OpenCores tiny SPI driver.

http://opencores.org/project,tiny_spi

Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>

authored by

Thomas Chou and committed by
Grant Likely
ce792580 9bde36af

+465
+12
Documentation/devicetree/bindings/spi/spi_oc_tiny.txt
··· 1 + OpenCores tiny SPI 2 + 3 + Required properties: 4 + - compatible : should be "opencores,tiny-spi-rtlsvn2". 5 + - gpios : should specify GPIOs used for chipselect. 6 + Optional properties: 7 + - clock-frequency : input clock frequency to the core. 8 + - baud-width: width, in bits, of the programmable divider used to scale 9 + the input clock to SCLK. 10 + 11 + The clock-frequency and baud-width properties are needed only if the divider 12 + is programmable. They are not needed if the divider is fixed.
+7
drivers/spi/Kconfig
··· 231 231 From MPC8536, 85xx platform uses the controller, and all P10xx, 232 232 P20xx, P30xx,P40xx, P50xx uses this controller. 233 233 234 + config SPI_OC_TINY 235 + tristate "OpenCores tiny SPI" 236 + depends on GENERIC_GPIO 237 + select SPI_BITBANG 238 + help 239 + This is the driver for OpenCores tiny SPI master controller. 240 + 234 241 config SPI_OMAP_UWIRE 235 242 tristate "OMAP1 MicroWire" 236 243 depends on ARCH_OMAP1
+1
drivers/spi/Makefile
··· 27 27 obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o 28 28 obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o 29 29 obj-$(CONFIG_SPI_PXA2XX_PCI) += pxa2xx_spi_pci.o 30 + obj-$(CONFIG_SPI_OC_TINY) += spi_oc_tiny.o 30 31 obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o 31 32 obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o 32 33 obj-$(CONFIG_SPI_OMAP_100K) += omap_spi_100k.o
+425
drivers/spi/spi_oc_tiny.c
··· 1 + /* 2 + * OpenCores tiny SPI master driver 3 + * 4 + * http://opencores.org/project,tiny_spi 5 + * 6 + * Copyright (C) 2011 Thomas Chou <thomas@wytron.com.tw> 7 + * 8 + * Based on spi_s3c24xx.c, which is: 9 + * Copyright (c) 2006 Ben Dooks 10 + * Copyright (c) 2006 Simtec Electronics 11 + * Ben Dooks <ben@simtec.co.uk> 12 + * 13 + * This program is free software; you can redistribute it and/or modify 14 + * it under the terms of the GNU General Public License version 2 as 15 + * published by the Free Software Foundation. 16 + */ 17 + 18 + #include <linux/init.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/errno.h> 21 + #include <linux/platform_device.h> 22 + #include <linux/spi/spi.h> 23 + #include <linux/spi/spi_bitbang.h> 24 + #include <linux/spi/spi_oc_tiny.h> 25 + #include <linux/io.h> 26 + #include <linux/gpio.h> 27 + #include <linux/of.h> 28 + 29 + #define DRV_NAME "spi_oc_tiny" 30 + 31 + #define TINY_SPI_RXDATA 0 32 + #define TINY_SPI_TXDATA 4 33 + #define TINY_SPI_STATUS 8 34 + #define TINY_SPI_CONTROL 12 35 + #define TINY_SPI_BAUD 16 36 + 37 + #define TINY_SPI_STATUS_TXE 0x1 38 + #define TINY_SPI_STATUS_TXR 0x2 39 + 40 + struct tiny_spi { 41 + /* bitbang has to be first */ 42 + struct spi_bitbang bitbang; 43 + struct completion done; 44 + 45 + void __iomem *base; 46 + int irq; 47 + unsigned int freq; 48 + unsigned int baudwidth; 49 + unsigned int baud; 50 + unsigned int speed_hz; 51 + unsigned int mode; 52 + unsigned int len; 53 + unsigned int txc, rxc; 54 + const u8 *txp; 55 + u8 *rxp; 56 + unsigned int gpio_cs_count; 57 + int *gpio_cs; 58 + }; 59 + 60 + static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev) 61 + { 62 + return spi_master_get_devdata(sdev->master); 63 + } 64 + 65 + static unsigned int tiny_spi_baud(struct spi_device *spi, unsigned int hz) 66 + { 67 + struct tiny_spi *hw = tiny_spi_to_hw(spi); 68 + 69 + return min(DIV_ROUND_UP(hw->freq, hz * 2), (1U << hw->baudwidth)) - 1; 70 + } 71 + 72 + static void tiny_spi_chipselect(struct spi_device *spi, int is_active) 73 + { 74 + struct tiny_spi *hw = tiny_spi_to_hw(spi); 75 + 76 + if (hw->gpio_cs_count) { 77 + gpio_set_value(hw->gpio_cs[spi->chip_select], 78 + (spi->mode & SPI_CS_HIGH) ? is_active : !is_active); 79 + } 80 + } 81 + 82 + static int tiny_spi_setup_transfer(struct spi_device *spi, 83 + struct spi_transfer *t) 84 + { 85 + struct tiny_spi *hw = tiny_spi_to_hw(spi); 86 + unsigned int baud = hw->baud; 87 + 88 + if (t) { 89 + if (t->speed_hz && t->speed_hz != hw->speed_hz) 90 + baud = tiny_spi_baud(spi, t->speed_hz); 91 + } 92 + writel(baud, hw->base + TINY_SPI_BAUD); 93 + writel(hw->mode, hw->base + TINY_SPI_CONTROL); 94 + return 0; 95 + } 96 + 97 + static int tiny_spi_setup(struct spi_device *spi) 98 + { 99 + struct tiny_spi *hw = tiny_spi_to_hw(spi); 100 + 101 + if (spi->max_speed_hz != hw->speed_hz) { 102 + hw->speed_hz = spi->max_speed_hz; 103 + hw->baud = tiny_spi_baud(spi, hw->speed_hz); 104 + } 105 + hw->mode = spi->mode & (SPI_CPOL | SPI_CPHA); 106 + return 0; 107 + } 108 + 109 + static inline void tiny_spi_wait_txr(struct tiny_spi *hw) 110 + { 111 + while (!(readb(hw->base + TINY_SPI_STATUS) & 112 + TINY_SPI_STATUS_TXR)) 113 + cpu_relax(); 114 + } 115 + 116 + static inline void tiny_spi_wait_txe(struct tiny_spi *hw) 117 + { 118 + while (!(readb(hw->base + TINY_SPI_STATUS) & 119 + TINY_SPI_STATUS_TXE)) 120 + cpu_relax(); 121 + } 122 + 123 + static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) 124 + { 125 + struct tiny_spi *hw = tiny_spi_to_hw(spi); 126 + const u8 *txp = t->tx_buf; 127 + u8 *rxp = t->rx_buf; 128 + unsigned int i; 129 + 130 + if (hw->irq >= 0) { 131 + /* use intrrupt driven data transfer */ 132 + hw->len = t->len; 133 + hw->txp = t->tx_buf; 134 + hw->rxp = t->rx_buf; 135 + hw->txc = 0; 136 + hw->rxc = 0; 137 + 138 + /* send the first byte */ 139 + if (t->len > 1) { 140 + writeb(hw->txp ? *hw->txp++ : 0, 141 + hw->base + TINY_SPI_TXDATA); 142 + hw->txc++; 143 + writeb(hw->txp ? *hw->txp++ : 0, 144 + hw->base + TINY_SPI_TXDATA); 145 + hw->txc++; 146 + writeb(TINY_SPI_STATUS_TXR, hw->base + TINY_SPI_STATUS); 147 + } else { 148 + writeb(hw->txp ? *hw->txp++ : 0, 149 + hw->base + TINY_SPI_TXDATA); 150 + hw->txc++; 151 + writeb(TINY_SPI_STATUS_TXE, hw->base + TINY_SPI_STATUS); 152 + } 153 + 154 + wait_for_completion(&hw->done); 155 + } else if (txp && rxp) { 156 + /* we need to tighten the transfer loop */ 157 + writeb(*txp++, hw->base + TINY_SPI_TXDATA); 158 + if (t->len > 1) { 159 + writeb(*txp++, hw->base + TINY_SPI_TXDATA); 160 + for (i = 2; i < t->len; i++) { 161 + u8 rx, tx = *txp++; 162 + tiny_spi_wait_txr(hw); 163 + rx = readb(hw->base + TINY_SPI_TXDATA); 164 + writeb(tx, hw->base + TINY_SPI_TXDATA); 165 + *rxp++ = rx; 166 + } 167 + tiny_spi_wait_txr(hw); 168 + *rxp++ = readb(hw->base + TINY_SPI_TXDATA); 169 + } 170 + tiny_spi_wait_txe(hw); 171 + *rxp++ = readb(hw->base + TINY_SPI_RXDATA); 172 + } else if (rxp) { 173 + writeb(0, hw->base + TINY_SPI_TXDATA); 174 + if (t->len > 1) { 175 + writeb(0, 176 + hw->base + TINY_SPI_TXDATA); 177 + for (i = 2; i < t->len; i++) { 178 + u8 rx; 179 + tiny_spi_wait_txr(hw); 180 + rx = readb(hw->base + TINY_SPI_TXDATA); 181 + writeb(0, hw->base + TINY_SPI_TXDATA); 182 + *rxp++ = rx; 183 + } 184 + tiny_spi_wait_txr(hw); 185 + *rxp++ = readb(hw->base + TINY_SPI_TXDATA); 186 + } 187 + tiny_spi_wait_txe(hw); 188 + *rxp++ = readb(hw->base + TINY_SPI_RXDATA); 189 + } else if (txp) { 190 + writeb(*txp++, hw->base + TINY_SPI_TXDATA); 191 + if (t->len > 1) { 192 + writeb(*txp++, hw->base + TINY_SPI_TXDATA); 193 + for (i = 2; i < t->len; i++) { 194 + u8 tx = *txp++; 195 + tiny_spi_wait_txr(hw); 196 + writeb(tx, hw->base + TINY_SPI_TXDATA); 197 + } 198 + } 199 + tiny_spi_wait_txe(hw); 200 + } else { 201 + writeb(0, hw->base + TINY_SPI_TXDATA); 202 + if (t->len > 1) { 203 + writeb(0, hw->base + TINY_SPI_TXDATA); 204 + for (i = 2; i < t->len; i++) { 205 + tiny_spi_wait_txr(hw); 206 + writeb(0, hw->base + TINY_SPI_TXDATA); 207 + } 208 + } 209 + tiny_spi_wait_txe(hw); 210 + } 211 + return t->len; 212 + } 213 + 214 + static irqreturn_t tiny_spi_irq(int irq, void *dev) 215 + { 216 + struct tiny_spi *hw = dev; 217 + 218 + writeb(0, hw->base + TINY_SPI_STATUS); 219 + if (hw->rxc + 1 == hw->len) { 220 + if (hw->rxp) 221 + *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA); 222 + hw->rxc++; 223 + complete(&hw->done); 224 + } else { 225 + if (hw->rxp) 226 + *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA); 227 + hw->rxc++; 228 + if (hw->txc < hw->len) { 229 + writeb(hw->txp ? *hw->txp++ : 0, 230 + hw->base + TINY_SPI_TXDATA); 231 + hw->txc++; 232 + writeb(TINY_SPI_STATUS_TXR, 233 + hw->base + TINY_SPI_STATUS); 234 + } else { 235 + writeb(TINY_SPI_STATUS_TXE, 236 + hw->base + TINY_SPI_STATUS); 237 + } 238 + } 239 + return IRQ_HANDLED; 240 + } 241 + 242 + #ifdef CONFIG_OF 243 + #include <linux/of_gpio.h> 244 + 245 + static int __devinit tiny_spi_of_probe(struct platform_device *pdev) 246 + { 247 + struct tiny_spi *hw = platform_get_drvdata(pdev); 248 + struct device_node *np = pdev->dev.of_node; 249 + unsigned int i; 250 + const __be32 *val; 251 + int len; 252 + 253 + if (!np) 254 + return 0; 255 + hw->gpio_cs_count = of_gpio_count(np); 256 + if (hw->gpio_cs_count) { 257 + hw->gpio_cs = devm_kzalloc(&pdev->dev, 258 + hw->gpio_cs_count * sizeof(unsigned int), 259 + GFP_KERNEL); 260 + if (!hw->gpio_cs) 261 + return -ENOMEM; 262 + } 263 + for (i = 0; i < hw->gpio_cs_count; i++) { 264 + hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL); 265 + if (hw->gpio_cs[i] < 0) 266 + return -ENODEV; 267 + } 268 + hw->bitbang.master->dev.of_node = pdev->dev.of_node; 269 + val = of_get_property(pdev->dev.of_node, 270 + "clock-frequency", &len); 271 + if (val && len >= sizeof(__be32)) 272 + hw->freq = be32_to_cpup(val); 273 + val = of_get_property(pdev->dev.of_node, "baud-width", &len); 274 + if (val && len >= sizeof(__be32)) 275 + hw->baudwidth = be32_to_cpup(val); 276 + return 0; 277 + } 278 + #else /* !CONFIG_OF */ 279 + static int __devinit tiny_spi_of_probe(struct platform_device *pdev) 280 + { 281 + return 0; 282 + } 283 + #endif /* CONFIG_OF */ 284 + 285 + static int __devinit tiny_spi_probe(struct platform_device *pdev) 286 + { 287 + struct tiny_spi_platform_data *platp = pdev->dev.platform_data; 288 + struct tiny_spi *hw; 289 + struct spi_master *master; 290 + struct resource *res; 291 + unsigned int i; 292 + int err = -ENODEV; 293 + 294 + master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi)); 295 + if (!master) 296 + return err; 297 + 298 + /* setup the master state. */ 299 + master->bus_num = pdev->id; 300 + master->num_chipselect = 255; 301 + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 302 + master->setup = tiny_spi_setup; 303 + 304 + hw = spi_master_get_devdata(master); 305 + platform_set_drvdata(pdev, hw); 306 + 307 + /* setup the state for the bitbang driver */ 308 + hw->bitbang.master = spi_master_get(master); 309 + if (!hw->bitbang.master) 310 + return err; 311 + hw->bitbang.setup_transfer = tiny_spi_setup_transfer; 312 + hw->bitbang.chipselect = tiny_spi_chipselect; 313 + hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs; 314 + 315 + /* find and map our resources */ 316 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 317 + if (!res) 318 + goto exit_busy; 319 + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), 320 + pdev->name)) 321 + goto exit_busy; 322 + hw->base = devm_ioremap_nocache(&pdev->dev, res->start, 323 + resource_size(res)); 324 + if (!hw->base) 325 + goto exit_busy; 326 + /* irq is optional */ 327 + hw->irq = platform_get_irq(pdev, 0); 328 + if (hw->irq >= 0) { 329 + init_completion(&hw->done); 330 + err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0, 331 + pdev->name, hw); 332 + if (err) 333 + goto exit; 334 + } 335 + /* find platform data */ 336 + if (platp) { 337 + hw->gpio_cs_count = platp->gpio_cs_count; 338 + hw->gpio_cs = platp->gpio_cs; 339 + if (platp->gpio_cs_count && !platp->gpio_cs) 340 + goto exit_busy; 341 + hw->freq = platp->freq; 342 + hw->baudwidth = platp->baudwidth; 343 + } else { 344 + err = tiny_spi_of_probe(pdev); 345 + if (err) 346 + goto exit; 347 + } 348 + for (i = 0; i < hw->gpio_cs_count; i++) { 349 + err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev)); 350 + if (err) 351 + goto exit_gpio; 352 + gpio_direction_output(hw->gpio_cs[i], 1); 353 + } 354 + hw->bitbang.master->num_chipselect = max(1U, hw->gpio_cs_count); 355 + 356 + /* register our spi controller */ 357 + err = spi_bitbang_start(&hw->bitbang); 358 + if (err) 359 + goto exit; 360 + dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); 361 + 362 + return 0; 363 + 364 + exit_gpio: 365 + while (i-- > 0) 366 + gpio_free(hw->gpio_cs[i]); 367 + exit_busy: 368 + err = -EBUSY; 369 + exit: 370 + platform_set_drvdata(pdev, NULL); 371 + spi_master_put(master); 372 + return err; 373 + } 374 + 375 + static int __devexit tiny_spi_remove(struct platform_device *pdev) 376 + { 377 + struct tiny_spi *hw = platform_get_drvdata(pdev); 378 + struct spi_master *master = hw->bitbang.master; 379 + unsigned int i; 380 + 381 + spi_bitbang_stop(&hw->bitbang); 382 + for (i = 0; i < hw->gpio_cs_count; i++) 383 + gpio_free(hw->gpio_cs[i]); 384 + platform_set_drvdata(pdev, NULL); 385 + spi_master_put(master); 386 + return 0; 387 + } 388 + 389 + #ifdef CONFIG_OF 390 + static const struct of_device_id tiny_spi_match[] = { 391 + { .compatible = "opencores,tiny-spi-rtlsvn2", }, 392 + {}, 393 + }; 394 + MODULE_DEVICE_TABLE(of, tiny_spi_match); 395 + #else /* CONFIG_OF */ 396 + #define tiny_spi_match NULL 397 + #endif /* CONFIG_OF */ 398 + 399 + static struct platform_driver tiny_spi_driver = { 400 + .probe = tiny_spi_probe, 401 + .remove = __devexit_p(tiny_spi_remove), 402 + .driver = { 403 + .name = DRV_NAME, 404 + .owner = THIS_MODULE, 405 + .pm = NULL, 406 + .of_match_table = tiny_spi_match, 407 + }, 408 + }; 409 + 410 + static int __init tiny_spi_init(void) 411 + { 412 + return platform_driver_register(&tiny_spi_driver); 413 + } 414 + module_init(tiny_spi_init); 415 + 416 + static void __exit tiny_spi_exit(void) 417 + { 418 + platform_driver_unregister(&tiny_spi_driver); 419 + } 420 + module_exit(tiny_spi_exit); 421 + 422 + MODULE_DESCRIPTION("OpenCores tiny SPI driver"); 423 + MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); 424 + MODULE_LICENSE("GPL"); 425 + MODULE_ALIAS("platform:" DRV_NAME);
+20
include/linux/spi/spi_oc_tiny.h
··· 1 + #ifndef _LINUX_SPI_SPI_OC_TINY_H 2 + #define _LINUX_SPI_SPI_OC_TINY_H 3 + 4 + /** 5 + * struct tiny_spi_platform_data - platform data of the OpenCores tiny SPI 6 + * @freq: input clock freq to the core. 7 + * @baudwidth: baud rate divider width of the core. 8 + * @gpio_cs_count: number of gpio pins used for chipselect. 9 + * @gpio_cs: array of gpio pins used for chipselect. 10 + * 11 + * freq and baudwidth are used only if the divider is programmable. 12 + */ 13 + struct tiny_spi_platform_data { 14 + unsigned int freq; 15 + unsigned int baudwidth; 16 + unsigned int gpio_cs_count; 17 + int *gpio_cs; 18 + }; 19 + 20 + #endif /* _LINUX_SPI_SPI_OC_TINY_H */