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

[PATCH] S3C24XX: hardware SPI driver

Hardware based SPI driver for Samsung S3C24XX SoC systems

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Cc: David Brownell <david-b@pacbell.net>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Ben Dooks and committed by
Linus Torvalds
7fba5340 1fc7547d

+489
+6
drivers/spi/Kconfig
··· 108 108 # 109 109 110 110 111 + config SPI_S3C24XX 112 + tristate "Samsung S3C24XX series SPI" 113 + depends on SPI_MASTER && ARCH_S3C2410 && EXPERIMENTAL 114 + help 115 + SPI driver for Samsung S3C24XX series ARM SoCs 116 + 111 117 # 112 118 # There are lots of SPI device types, with sensors and memory 113 119 # being probably the most widely used ones.
+1
drivers/spi/Makefile
··· 16 16 obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o 17 17 obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o 18 18 obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o 19 + obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o 19 20 # ... add above this line ... 20 21 21 22 # SPI protocol drivers (device/link on bus)
+453
drivers/spi/spi_s3c24xx.c
··· 1 + /* linux/drivers/spi/spi_s3c24xx.c 2 + * 3 + * Copyright (c) 2006 Ben Dooks 4 + * Copyright (c) 2006 Simtec Electronics 5 + * Ben Dooks <ben@simtec.co.uk> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2 as 9 + * published by the Free Software Foundation. 10 + * 11 + */ 12 + 13 + 14 + //#define DEBUG 15 + 16 + #include <linux/config.h> 17 + #include <linux/init.h> 18 + #include <linux/spinlock.h> 19 + #include <linux/workqueue.h> 20 + #include <linux/interrupt.h> 21 + #include <linux/delay.h> 22 + #include <linux/errno.h> 23 + #include <linux/err.h> 24 + #include <linux/clk.h> 25 + #include <linux/platform_device.h> 26 + 27 + #include <linux/spi/spi.h> 28 + #include <linux/spi/spi_bitbang.h> 29 + 30 + #include <asm/io.h> 31 + #include <asm/dma.h> 32 + #include <asm/hardware.h> 33 + 34 + #include <asm/arch/regs-gpio.h> 35 + #include <asm/arch/regs-spi.h> 36 + #include <asm/arch/spi.h> 37 + 38 + struct s3c24xx_spi { 39 + /* bitbang has to be first */ 40 + struct spi_bitbang bitbang; 41 + struct completion done; 42 + 43 + void __iomem *regs; 44 + int irq; 45 + int len; 46 + int count; 47 + 48 + /* data buffers */ 49 + const unsigned char *tx; 50 + unsigned char *rx; 51 + 52 + struct clk *clk; 53 + struct resource *ioarea; 54 + struct spi_master *master; 55 + struct spi_device *curdev; 56 + struct device *dev; 57 + struct s3c2410_spi_info *pdata; 58 + }; 59 + 60 + #define SPCON_DEFAULT (S3C2410_SPCON_MSTR | S3C2410_SPCON_SMOD_INT) 61 + #define SPPIN_DEFAULT (S3C2410_SPPIN_KEEP) 62 + 63 + static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev) 64 + { 65 + return spi_master_get_devdata(sdev->master); 66 + } 67 + 68 + static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) 69 + { 70 + struct s3c24xx_spi *hw = to_hw(spi); 71 + unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; 72 + unsigned int spcon; 73 + 74 + switch (value) { 75 + case BITBANG_CS_INACTIVE: 76 + if (hw->pdata->set_cs) 77 + hw->pdata->set_cs(hw->pdata, value, cspol); 78 + else 79 + s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol ^ 1); 80 + break; 81 + 82 + case BITBANG_CS_ACTIVE: 83 + spcon = readb(hw->regs + S3C2410_SPCON); 84 + 85 + if (spi->mode & SPI_CPHA) 86 + spcon |= S3C2410_SPCON_CPHA_FMTB; 87 + else 88 + spcon &= ~S3C2410_SPCON_CPHA_FMTB; 89 + 90 + if (spi->mode & SPI_CPOL) 91 + spcon |= S3C2410_SPCON_CPOL_HIGH; 92 + else 93 + spcon &= ~S3C2410_SPCON_CPOL_HIGH; 94 + 95 + spcon |= S3C2410_SPCON_ENSCK; 96 + 97 + /* write new configration */ 98 + 99 + writeb(spcon, hw->regs + S3C2410_SPCON); 100 + 101 + if (hw->pdata->set_cs) 102 + hw->pdata->set_cs(hw->pdata, value, cspol); 103 + else 104 + s3c2410_gpio_setpin(hw->pdata->pin_cs, cspol); 105 + 106 + break; 107 + 108 + } 109 + } 110 + 111 + static int s3c24xx_spi_setupxfer(struct spi_device *spi, 112 + struct spi_transfer *t) 113 + { 114 + struct s3c24xx_spi *hw = to_hw(spi); 115 + unsigned int bpw; 116 + unsigned int hz; 117 + unsigned int div; 118 + 119 + bpw = t ? t->bits_per_word : spi->bits_per_word; 120 + hz = t ? t->speed_hz : spi->max_speed_hz; 121 + 122 + if (bpw != 8) { 123 + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); 124 + return -EINVAL; 125 + } 126 + 127 + div = clk_get_rate(hw->clk) / hz; 128 + 129 + /* is clk = pclk / (2 * (pre+1)), or is it 130 + * clk = (pclk * 2) / ( pre + 1) */ 131 + 132 + div = (div / 2) - 1; 133 + 134 + if (div < 0) 135 + div = 1; 136 + 137 + if (div > 255) 138 + div = 255; 139 + 140 + dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", div, hz); 141 + writeb(div, hw->regs + S3C2410_SPPRE); 142 + 143 + spin_lock(&hw->bitbang.lock); 144 + if (!hw->bitbang.busy) { 145 + hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); 146 + /* need to ndelay for 0.5 clocktick ? */ 147 + } 148 + spin_unlock(&hw->bitbang.lock); 149 + 150 + return 0; 151 + } 152 + 153 + static int s3c24xx_spi_setup(struct spi_device *spi) 154 + { 155 + int ret; 156 + 157 + if (!spi->bits_per_word) 158 + spi->bits_per_word = 8; 159 + 160 + if ((spi->mode & SPI_LSB_FIRST) != 0) 161 + return -EINVAL; 162 + 163 + ret = s3c24xx_spi_setupxfer(spi, NULL); 164 + if (ret < 0) { 165 + dev_err(&spi->dev, "setupxfer returned %d\n", ret); 166 + return ret; 167 + } 168 + 169 + dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", 170 + __FUNCTION__, spi->mode, spi->bits_per_word, 171 + spi->max_speed_hz); 172 + 173 + return 0; 174 + } 175 + 176 + static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count) 177 + { 178 + return hw->tx ? hw->tx[count] : 0xff; 179 + } 180 + 181 + static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t) 182 + { 183 + struct s3c24xx_spi *hw = to_hw(spi); 184 + 185 + dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", 186 + t->tx_buf, t->rx_buf, t->len); 187 + 188 + hw->tx = t->tx_buf; 189 + hw->rx = t->rx_buf; 190 + hw->len = t->len; 191 + hw->count = 0; 192 + 193 + /* send the first byte */ 194 + writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT); 195 + wait_for_completion(&hw->done); 196 + 197 + return hw->count; 198 + } 199 + 200 + static irqreturn_t s3c24xx_spi_irq(int irq, void *dev, struct pt_regs *regs) 201 + { 202 + struct s3c24xx_spi *hw = dev; 203 + unsigned int spsta = readb(hw->regs + S3C2410_SPSTA); 204 + unsigned int count = hw->count; 205 + 206 + if (spsta & S3C2410_SPSTA_DCOL) { 207 + dev_dbg(hw->dev, "data-collision\n"); 208 + complete(&hw->done); 209 + goto irq_done; 210 + } 211 + 212 + if (!(spsta & S3C2410_SPSTA_READY)) { 213 + dev_dbg(hw->dev, "spi not ready for tx?\n"); 214 + complete(&hw->done); 215 + goto irq_done; 216 + } 217 + 218 + hw->count++; 219 + 220 + if (hw->rx) 221 + hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT); 222 + 223 + count++; 224 + 225 + if (count < hw->len) 226 + writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT); 227 + else 228 + complete(&hw->done); 229 + 230 + irq_done: 231 + return IRQ_HANDLED; 232 + } 233 + 234 + static int s3c24xx_spi_probe(struct platform_device *pdev) 235 + { 236 + struct s3c24xx_spi *hw; 237 + struct spi_master *master; 238 + struct spi_board_info *bi; 239 + struct resource *res; 240 + int err = 0; 241 + int i; 242 + 243 + master = spi_alloc_master(&pdev->dev, sizeof(struct s3c24xx_spi)); 244 + if (master == NULL) { 245 + dev_err(&pdev->dev, "No memory for spi_master\n"); 246 + err = -ENOMEM; 247 + goto err_nomem; 248 + } 249 + 250 + hw = spi_master_get_devdata(master); 251 + memset(hw, 0, sizeof(struct s3c24xx_spi)); 252 + 253 + hw->master = spi_master_get(master); 254 + hw->pdata = pdev->dev.platform_data; 255 + hw->dev = &pdev->dev; 256 + 257 + if (hw->pdata == NULL) { 258 + dev_err(&pdev->dev, "No platform data supplied\n"); 259 + err = -ENOENT; 260 + goto err_no_pdata; 261 + } 262 + 263 + platform_set_drvdata(pdev, hw); 264 + init_completion(&hw->done); 265 + 266 + /* setup the state for the bitbang driver */ 267 + 268 + hw->bitbang.master = hw->master; 269 + hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer; 270 + hw->bitbang.chipselect = s3c24xx_spi_chipsel; 271 + hw->bitbang.txrx_bufs = s3c24xx_spi_txrx; 272 + hw->bitbang.master->setup = s3c24xx_spi_setup; 273 + 274 + dev_dbg(hw->dev, "bitbang at %p\n", &hw->bitbang); 275 + 276 + /* find and map our resources */ 277 + 278 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 279 + if (res == NULL) { 280 + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); 281 + err = -ENOENT; 282 + goto err_no_iores; 283 + } 284 + 285 + hw->ioarea = request_mem_region(res->start, (res->end - res->start)+1, 286 + pdev->name); 287 + 288 + if (hw->ioarea == NULL) { 289 + dev_err(&pdev->dev, "Cannot reserve region\n"); 290 + err = -ENXIO; 291 + goto err_no_iores; 292 + } 293 + 294 + hw->regs = ioremap(res->start, (res->end - res->start)+1); 295 + if (hw->regs == NULL) { 296 + dev_err(&pdev->dev, "Cannot map IO\n"); 297 + err = -ENXIO; 298 + goto err_no_iomap; 299 + } 300 + 301 + hw->irq = platform_get_irq(pdev, 0); 302 + if (hw->irq < 0) { 303 + dev_err(&pdev->dev, "No IRQ specified\n"); 304 + err = -ENOENT; 305 + goto err_no_irq; 306 + } 307 + 308 + err = request_irq(hw->irq, s3c24xx_spi_irq, 0, pdev->name, hw); 309 + if (err) { 310 + dev_err(&pdev->dev, "Cannot claim IRQ\n"); 311 + goto err_no_irq; 312 + } 313 + 314 + hw->clk = clk_get(&pdev->dev, "spi"); 315 + if (IS_ERR(hw->clk)) { 316 + dev_err(&pdev->dev, "No clock for device\n"); 317 + err = PTR_ERR(hw->clk); 318 + goto err_no_clk; 319 + } 320 + 321 + /* for the moment, permanently enable the clock */ 322 + 323 + clk_enable(hw->clk); 324 + 325 + /* program defaults into the registers */ 326 + 327 + writeb(0xff, hw->regs + S3C2410_SPPRE); 328 + writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN); 329 + writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON); 330 + 331 + /* setup any gpio we can */ 332 + 333 + if (!hw->pdata->set_cs) { 334 + s3c2410_gpio_setpin(hw->pdata->pin_cs, 1); 335 + s3c2410_gpio_cfgpin(hw->pdata->pin_cs, S3C2410_GPIO_OUTPUT); 336 + } 337 + 338 + /* register our spi controller */ 339 + 340 + err = spi_bitbang_start(&hw->bitbang); 341 + if (err) { 342 + dev_err(&pdev->dev, "Failed to register SPI master\n"); 343 + goto err_register; 344 + } 345 + 346 + dev_dbg(hw->dev, "shutdown=%d\n", hw->bitbang.shutdown); 347 + 348 + /* register all the devices associated */ 349 + 350 + bi = &hw->pdata->board_info[0]; 351 + for (i = 0; i < hw->pdata->board_size; i++, bi++) { 352 + dev_info(hw->dev, "registering %s\n", bi->modalias); 353 + 354 + bi->controller_data = hw; 355 + spi_new_device(master, bi); 356 + } 357 + 358 + return 0; 359 + 360 + err_register: 361 + clk_disable(hw->clk); 362 + clk_put(hw->clk); 363 + 364 + err_no_clk: 365 + free_irq(hw->irq, hw); 366 + 367 + err_no_irq: 368 + iounmap(hw->regs); 369 + 370 + err_no_iomap: 371 + release_resource(hw->ioarea); 372 + kfree(hw->ioarea); 373 + 374 + err_no_iores: 375 + err_no_pdata: 376 + spi_master_put(hw->master);; 377 + 378 + err_nomem: 379 + return err; 380 + } 381 + 382 + static int s3c24xx_spi_remove(struct platform_device *dev) 383 + { 384 + struct s3c24xx_spi *hw = platform_get_drvdata(dev); 385 + 386 + platform_set_drvdata(dev, NULL); 387 + 388 + spi_unregister_master(hw->master); 389 + 390 + clk_disable(hw->clk); 391 + clk_put(hw->clk); 392 + 393 + free_irq(hw->irq, hw); 394 + iounmap(hw->regs); 395 + 396 + release_resource(hw->ioarea); 397 + kfree(hw->ioarea); 398 + 399 + spi_master_put(hw->master); 400 + return 0; 401 + } 402 + 403 + 404 + #ifdef CONFIG_PM 405 + 406 + static int s3c24xx_spi_suspend(struct platform_device *pdev, pm_message_t msg) 407 + { 408 + struct s3c24xx_spi *hw = platform_get_drvdata(dev); 409 + 410 + clk_disable(hw->clk); 411 + return 0; 412 + } 413 + 414 + static int s3c24xx_spi_resume(struct platform_device *pdev) 415 + { 416 + struct s3c24xx_spi *hw = platform_get_drvdata(dev); 417 + 418 + clk_enable(hw->clk); 419 + return 0; 420 + } 421 + 422 + #else 423 + #define s3c24xx_spi_suspend NULL 424 + #define s3c24xx_spi_resume NULL 425 + #endif 426 + 427 + static struct platform_driver s3c24xx_spidrv = { 428 + .probe = s3c24xx_spi_probe, 429 + .remove = s3c24xx_spi_remove, 430 + .suspend = s3c24xx_spi_suspend, 431 + .resume = s3c24xx_spi_resume, 432 + .driver = { 433 + .name = "s3c2410-spi", 434 + .owner = THIS_MODULE, 435 + }, 436 + }; 437 + 438 + static int __init s3c24xx_spi_init(void) 439 + { 440 + return platform_driver_register(&s3c24xx_spidrv); 441 + } 442 + 443 + static void __exit s3c24xx_spi_exit(void) 444 + { 445 + platform_driver_unregister(&s3c24xx_spidrv); 446 + } 447 + 448 + module_init(s3c24xx_spi_init); 449 + module_exit(s3c24xx_spi_exit); 450 + 451 + MODULE_DESCRIPTION("S3C24XX SPI Driver"); 452 + MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); 453 + MODULE_LICENSE("GPL");
+29
include/asm-arm/arch-s3c2410/spi.h
··· 1 + /* linux/include/asm-arm/arch-s3c2410/spi.h 2 + * 3 + * Copyright (c) 2006 Simtec Electronics 4 + * Ben Dooks <ben@simtec.co.uk> 5 + * 6 + * S3C2410 - SPI Controller platform_device info 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #ifndef __ASM_ARCH_SPI_H 14 + #define __ASM_ARCH_SPI_H __FILE__ 15 + 16 + struct s3c2410_spi_info; 17 + struct spi_board_info; 18 + 19 + struct s3c2410_spi_info { 20 + unsigned long pin_cs; /* simple gpio cs */ 21 + 22 + unsigned long board_size; 23 + struct spi_board_info *board_info; 24 + 25 + void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); 26 + }; 27 + 28 + 29 + #endif /* __ASM_ARCH_SPI_H */