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

Configure Feed

Select the types of activity you want to include in your feed.

at v4.9-rc4 359 lines 8.8 kB view raw
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/interrupt.h> 19#include <linux/errno.h> 20#include <linux/module.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 40struct 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 int gpio_cs_count; 57 int *gpio_cs; 58}; 59 60static inline struct tiny_spi *tiny_spi_to_hw(struct spi_device *sdev) 61{ 62 return spi_master_get_devdata(sdev->master); 63} 64 65static 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 72static 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 > 0) { 77 gpio_set_value(hw->gpio_cs[spi->chip_select], 78 (spi->mode & SPI_CS_HIGH) ? is_active : !is_active); 79 } 80} 81 82static 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 97static 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 109static 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 116static 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 123static 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 interrupt 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 { 156 /* we need to tighten the transfer loop */ 157 writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA); 158 for (i = 1; i < t->len; i++) { 159 writeb(txp ? *txp++ : 0, hw->base + TINY_SPI_TXDATA); 160 161 if (rxp || (i != t->len - 1)) 162 tiny_spi_wait_txr(hw); 163 if (rxp) 164 *rxp++ = readb(hw->base + TINY_SPI_TXDATA); 165 } 166 tiny_spi_wait_txe(hw); 167 if (rxp) 168 *rxp++ = readb(hw->base + TINY_SPI_RXDATA); 169 } 170 171 return t->len; 172} 173 174static irqreturn_t tiny_spi_irq(int irq, void *dev) 175{ 176 struct tiny_spi *hw = dev; 177 178 writeb(0, hw->base + TINY_SPI_STATUS); 179 if (hw->rxc + 1 == hw->len) { 180 if (hw->rxp) 181 *hw->rxp++ = readb(hw->base + TINY_SPI_RXDATA); 182 hw->rxc++; 183 complete(&hw->done); 184 } else { 185 if (hw->rxp) 186 *hw->rxp++ = readb(hw->base + TINY_SPI_TXDATA); 187 hw->rxc++; 188 if (hw->txc < hw->len) { 189 writeb(hw->txp ? *hw->txp++ : 0, 190 hw->base + TINY_SPI_TXDATA); 191 hw->txc++; 192 writeb(TINY_SPI_STATUS_TXR, 193 hw->base + TINY_SPI_STATUS); 194 } else { 195 writeb(TINY_SPI_STATUS_TXE, 196 hw->base + TINY_SPI_STATUS); 197 } 198 } 199 return IRQ_HANDLED; 200} 201 202#ifdef CONFIG_OF 203#include <linux/of_gpio.h> 204 205static int tiny_spi_of_probe(struct platform_device *pdev) 206{ 207 struct tiny_spi *hw = platform_get_drvdata(pdev); 208 struct device_node *np = pdev->dev.of_node; 209 unsigned int i; 210 u32 val; 211 212 if (!np) 213 return 0; 214 hw->gpio_cs_count = of_gpio_count(np); 215 if (hw->gpio_cs_count > 0) { 216 hw->gpio_cs = devm_kzalloc(&pdev->dev, 217 hw->gpio_cs_count * sizeof(unsigned int), 218 GFP_KERNEL); 219 if (!hw->gpio_cs) 220 return -ENOMEM; 221 } 222 for (i = 0; i < hw->gpio_cs_count; i++) { 223 hw->gpio_cs[i] = of_get_gpio_flags(np, i, NULL); 224 if (hw->gpio_cs[i] < 0) 225 return -ENODEV; 226 } 227 hw->bitbang.master->dev.of_node = pdev->dev.of_node; 228 if (!of_property_read_u32(np, "clock-frequency", &val)) 229 hw->freq = val; 230 if (!of_property_read_u32(np, "baud-width", &val)) 231 hw->baudwidth = val; 232 return 0; 233} 234#else /* !CONFIG_OF */ 235static int tiny_spi_of_probe(struct platform_device *pdev) 236{ 237 return 0; 238} 239#endif /* CONFIG_OF */ 240 241static int tiny_spi_probe(struct platform_device *pdev) 242{ 243 struct tiny_spi_platform_data *platp = dev_get_platdata(&pdev->dev); 244 struct tiny_spi *hw; 245 struct spi_master *master; 246 struct resource *res; 247 unsigned int i; 248 int err = -ENODEV; 249 250 master = spi_alloc_master(&pdev->dev, sizeof(struct tiny_spi)); 251 if (!master) 252 return err; 253 254 /* setup the master state. */ 255 master->bus_num = pdev->id; 256 master->num_chipselect = 255; 257 master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; 258 master->setup = tiny_spi_setup; 259 260 hw = spi_master_get_devdata(master); 261 platform_set_drvdata(pdev, hw); 262 263 /* setup the state for the bitbang driver */ 264 hw->bitbang.master = master; 265 hw->bitbang.setup_transfer = tiny_spi_setup_transfer; 266 hw->bitbang.chipselect = tiny_spi_chipselect; 267 hw->bitbang.txrx_bufs = tiny_spi_txrx_bufs; 268 269 /* find and map our resources */ 270 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 271 hw->base = devm_ioremap_resource(&pdev->dev, res); 272 if (IS_ERR(hw->base)) { 273 err = PTR_ERR(hw->base); 274 goto exit; 275 } 276 /* irq is optional */ 277 hw->irq = platform_get_irq(pdev, 0); 278 if (hw->irq >= 0) { 279 init_completion(&hw->done); 280 err = devm_request_irq(&pdev->dev, hw->irq, tiny_spi_irq, 0, 281 pdev->name, hw); 282 if (err) 283 goto exit; 284 } 285 /* find platform data */ 286 if (platp) { 287 hw->gpio_cs_count = platp->gpio_cs_count; 288 hw->gpio_cs = platp->gpio_cs; 289 if (platp->gpio_cs_count && !platp->gpio_cs) { 290 err = -EBUSY; 291 goto exit; 292 } 293 hw->freq = platp->freq; 294 hw->baudwidth = platp->baudwidth; 295 } else { 296 err = tiny_spi_of_probe(pdev); 297 if (err) 298 goto exit; 299 } 300 for (i = 0; i < hw->gpio_cs_count; i++) { 301 err = gpio_request(hw->gpio_cs[i], dev_name(&pdev->dev)); 302 if (err) 303 goto exit_gpio; 304 gpio_direction_output(hw->gpio_cs[i], 1); 305 } 306 hw->bitbang.master->num_chipselect = max(1, hw->gpio_cs_count); 307 308 /* register our spi controller */ 309 err = spi_bitbang_start(&hw->bitbang); 310 if (err) 311 goto exit; 312 dev_info(&pdev->dev, "base %p, irq %d\n", hw->base, hw->irq); 313 314 return 0; 315 316exit_gpio: 317 while (i-- > 0) 318 gpio_free(hw->gpio_cs[i]); 319exit: 320 spi_master_put(master); 321 return err; 322} 323 324static int tiny_spi_remove(struct platform_device *pdev) 325{ 326 struct tiny_spi *hw = platform_get_drvdata(pdev); 327 struct spi_master *master = hw->bitbang.master; 328 unsigned int i; 329 330 spi_bitbang_stop(&hw->bitbang); 331 for (i = 0; i < hw->gpio_cs_count; i++) 332 gpio_free(hw->gpio_cs[i]); 333 spi_master_put(master); 334 return 0; 335} 336 337#ifdef CONFIG_OF 338static const struct of_device_id tiny_spi_match[] = { 339 { .compatible = "opencores,tiny-spi-rtlsvn2", }, 340 {}, 341}; 342MODULE_DEVICE_TABLE(of, tiny_spi_match); 343#endif /* CONFIG_OF */ 344 345static struct platform_driver tiny_spi_driver = { 346 .probe = tiny_spi_probe, 347 .remove = tiny_spi_remove, 348 .driver = { 349 .name = DRV_NAME, 350 .pm = NULL, 351 .of_match_table = of_match_ptr(tiny_spi_match), 352 }, 353}; 354module_platform_driver(tiny_spi_driver); 355 356MODULE_DESCRIPTION("OpenCores tiny SPI driver"); 357MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); 358MODULE_LICENSE("GPL"); 359MODULE_ALIAS("platform:" DRV_NAME);