SPI: Add SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs

The Atheros AR71XX/AR724X/AR913X SoCs have a built-in SPI controller. This
patch implements a driver for that.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Cc: spi-devel-general@lists.sourceforge.net
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Cc: linux-mips@linux-mips.org
Cc: Imre Kaloz <kaloz@openwrt.org>
Cc: Luis R. Rodriguez <lrodriguez@atheros.com>
Cc: Cliff Holden <Cliff.Holden@Atheros.com>
Cc: Kathy Giori <Kathy.Giori@Atheros.com>
Patchwork: https://patchwork.linux-mips.org/patch/1960/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>

authored by Gabor Juhos and committed by Ralf Baechle 8efaef4d 3f348c5d

+324
+23
arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
··· 1 + /* 2 + * Platform data definition for Atheros AR71XX/AR724X/AR913X SPI controller 3 + * 4 + * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms of the GNU General Public License version 2 as published 8 + * by the Free Software Foundation. 9 + */ 10 + 11 + #ifndef _ATH79_SPI_PLATFORM_H 12 + #define _ATH79_SPI_PLATFORM_H 13 + 14 + struct ath79_spi_platform_data { 15 + unsigned bus_num; 16 + unsigned num_chipselect; 17 + }; 18 + 19 + struct ath79_spi_controller_data { 20 + unsigned gpio; 21 + }; 22 + 23 + #endif /* _ATH79_SPI_PLATFORM_H */
+8
drivers/spi/Kconfig
··· 53 53 54 54 comment "SPI Master Controller Drivers" 55 55 56 + config SPI_ATH79 57 + tristate "Atheros AR71XX/AR724X/AR913X SPI controller driver" 58 + depends on ATH79 && GENERIC_GPIO 59 + select SPI_BITBANG 60 + help 61 + This enables support for the SPI controller present on the 62 + Atheros AR71XX/AR724X/AR913X SoCs. 63 + 56 64 config SPI_ATMEL 57 65 tristate "Atmel SPI Controller" 58 66 depends on (ARCH_AT91 || AVR32)
+1
drivers/spi/Makefile
··· 10 10 11 11 # SPI master controller drivers (bus) 12 12 obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o 13 + obj-$(CONFIG_SPI_ATH79) += ath79_spi.o 13 14 obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o 14 15 obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o 15 16 obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
+292
drivers/spi/ath79_spi.c
··· 1 + /* 2 + * SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs 3 + * 4 + * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org> 5 + * 6 + * This driver has been based on the spi-gpio.c: 7 + * Copyright (C) 2006,2008 David Brownell 8 + * 9 + * This program is free software; you can redistribute it and/or modify 10 + * it under the terms of the GNU General Public License version 2 as 11 + * published by the Free Software Foundation. 12 + * 13 + */ 14 + 15 + #include <linux/kernel.h> 16 + #include <linux/init.h> 17 + #include <linux/delay.h> 18 + #include <linux/spinlock.h> 19 + #include <linux/workqueue.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/io.h> 22 + #include <linux/spi/spi.h> 23 + #include <linux/spi/spi_bitbang.h> 24 + #include <linux/bitops.h> 25 + #include <linux/gpio.h> 26 + 27 + #include <asm/mach-ath79/ar71xx_regs.h> 28 + #include <asm/mach-ath79/ath79_spi_platform.h> 29 + 30 + #define DRV_NAME "ath79-spi" 31 + 32 + struct ath79_spi { 33 + struct spi_bitbang bitbang; 34 + u32 ioc_base; 35 + u32 reg_ctrl; 36 + void __iomem *base; 37 + }; 38 + 39 + static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg) 40 + { 41 + return ioread32(sp->base + reg); 42 + } 43 + 44 + static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned reg, u32 val) 45 + { 46 + iowrite32(val, sp->base + reg); 47 + } 48 + 49 + static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi) 50 + { 51 + return spi_master_get_devdata(spi->master); 52 + } 53 + 54 + static void ath79_spi_chipselect(struct spi_device *spi, int is_active) 55 + { 56 + struct ath79_spi *sp = ath79_spidev_to_sp(spi); 57 + int cs_high = (spi->mode & SPI_CS_HIGH) ? is_active : !is_active; 58 + 59 + if (is_active) { 60 + /* set initial clock polarity */ 61 + if (spi->mode & SPI_CPOL) 62 + sp->ioc_base |= AR71XX_SPI_IOC_CLK; 63 + else 64 + sp->ioc_base &= ~AR71XX_SPI_IOC_CLK; 65 + 66 + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); 67 + } 68 + 69 + if (spi->chip_select) { 70 + struct ath79_spi_controller_data *cdata = spi->controller_data; 71 + 72 + /* SPI is normally active-low */ 73 + gpio_set_value(cdata->gpio, cs_high); 74 + } else { 75 + if (cs_high) 76 + sp->ioc_base |= AR71XX_SPI_IOC_CS0; 77 + else 78 + sp->ioc_base &= ~AR71XX_SPI_IOC_CS0; 79 + 80 + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); 81 + } 82 + 83 + } 84 + 85 + static int ath79_spi_setup_cs(struct spi_device *spi) 86 + { 87 + struct ath79_spi *sp = ath79_spidev_to_sp(spi); 88 + struct ath79_spi_controller_data *cdata; 89 + 90 + cdata = spi->controller_data; 91 + if (spi->chip_select && !cdata) 92 + return -EINVAL; 93 + 94 + /* enable GPIO mode */ 95 + ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); 96 + 97 + /* save CTRL register */ 98 + sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL); 99 + sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC); 100 + 101 + /* TODO: setup speed? */ 102 + ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43); 103 + 104 + if (spi->chip_select) { 105 + int status = 0; 106 + 107 + status = gpio_request(cdata->gpio, dev_name(&spi->dev)); 108 + if (status) 109 + return status; 110 + 111 + status = gpio_direction_output(cdata->gpio, 112 + spi->mode & SPI_CS_HIGH); 113 + if (status) { 114 + gpio_free(cdata->gpio); 115 + return status; 116 + } 117 + } else { 118 + if (spi->mode & SPI_CS_HIGH) 119 + sp->ioc_base |= AR71XX_SPI_IOC_CS0; 120 + else 121 + sp->ioc_base &= ~AR71XX_SPI_IOC_CS0; 122 + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); 123 + } 124 + 125 + return 0; 126 + } 127 + 128 + static void ath79_spi_cleanup_cs(struct spi_device *spi) 129 + { 130 + struct ath79_spi *sp = ath79_spidev_to_sp(spi); 131 + 132 + if (spi->chip_select) { 133 + struct ath79_spi_controller_data *cdata = spi->controller_data; 134 + gpio_free(cdata->gpio); 135 + } 136 + 137 + /* restore CTRL register */ 138 + ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, sp->reg_ctrl); 139 + /* disable GPIO mode */ 140 + ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0); 141 + } 142 + 143 + static int ath79_spi_setup(struct spi_device *spi) 144 + { 145 + int status = 0; 146 + 147 + if (spi->bits_per_word > 32) 148 + return -EINVAL; 149 + 150 + if (!spi->controller_state) { 151 + status = ath79_spi_setup_cs(spi); 152 + if (status) 153 + return status; 154 + } 155 + 156 + status = spi_bitbang_setup(spi); 157 + if (status && !spi->controller_state) 158 + ath79_spi_cleanup_cs(spi); 159 + 160 + return status; 161 + } 162 + 163 + static void ath79_spi_cleanup(struct spi_device *spi) 164 + { 165 + ath79_spi_cleanup_cs(spi); 166 + spi_bitbang_cleanup(spi); 167 + } 168 + 169 + static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned nsecs, 170 + u32 word, u8 bits) 171 + { 172 + struct ath79_spi *sp = ath79_spidev_to_sp(spi); 173 + u32 ioc = sp->ioc_base; 174 + 175 + /* clock starts at inactive polarity */ 176 + for (word <<= (32 - bits); likely(bits); bits--) { 177 + u32 out; 178 + 179 + if (word & (1 << 31)) 180 + out = ioc | AR71XX_SPI_IOC_DO; 181 + else 182 + out = ioc & ~AR71XX_SPI_IOC_DO; 183 + 184 + /* setup MSB (to slave) on trailing edge */ 185 + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); 186 + ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK); 187 + 188 + word <<= 1; 189 + } 190 + 191 + return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS); 192 + } 193 + 194 + static __devinit int ath79_spi_probe(struct platform_device *pdev) 195 + { 196 + struct spi_master *master; 197 + struct ath79_spi *sp; 198 + struct ath79_spi_platform_data *pdata; 199 + struct resource *r; 200 + int ret; 201 + 202 + master = spi_alloc_master(&pdev->dev, sizeof(*sp)); 203 + if (master == NULL) { 204 + dev_err(&pdev->dev, "failed to allocate spi master\n"); 205 + return -ENOMEM; 206 + } 207 + 208 + sp = spi_master_get_devdata(master); 209 + platform_set_drvdata(pdev, sp); 210 + 211 + pdata = pdev->dev.platform_data; 212 + 213 + master->setup = ath79_spi_setup; 214 + master->cleanup = ath79_spi_cleanup; 215 + if (pdata) { 216 + master->bus_num = pdata->bus_num; 217 + master->num_chipselect = pdata->num_chipselect; 218 + } else { 219 + master->bus_num = -1; 220 + master->num_chipselect = 1; 221 + } 222 + 223 + sp->bitbang.master = spi_master_get(master); 224 + sp->bitbang.chipselect = ath79_spi_chipselect; 225 + sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; 226 + sp->bitbang.setup_transfer = spi_bitbang_setup_transfer; 227 + sp->bitbang.flags = SPI_CS_HIGH; 228 + 229 + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 230 + if (r == NULL) { 231 + ret = -ENOENT; 232 + goto err_put_master; 233 + } 234 + 235 + sp->base = ioremap(r->start, r->end - r->start + 1); 236 + if (!sp->base) { 237 + ret = -ENXIO; 238 + goto err_put_master; 239 + } 240 + 241 + ret = spi_bitbang_start(&sp->bitbang); 242 + if (ret) 243 + goto err_unmap; 244 + 245 + return 0; 246 + 247 + err_unmap: 248 + iounmap(sp->base); 249 + err_put_master: 250 + platform_set_drvdata(pdev, NULL); 251 + spi_master_put(sp->bitbang.master); 252 + 253 + return ret; 254 + } 255 + 256 + static __devexit int ath79_spi_remove(struct platform_device *pdev) 257 + { 258 + struct ath79_spi *sp = platform_get_drvdata(pdev); 259 + 260 + spi_bitbang_stop(&sp->bitbang); 261 + iounmap(sp->base); 262 + platform_set_drvdata(pdev, NULL); 263 + spi_master_put(sp->bitbang.master); 264 + 265 + return 0; 266 + } 267 + 268 + static struct platform_driver ath79_spi_driver = { 269 + .probe = ath79_spi_probe, 270 + .remove = __devexit_p(ath79_spi_remove), 271 + .driver = { 272 + .name = DRV_NAME, 273 + .owner = THIS_MODULE, 274 + }, 275 + }; 276 + 277 + static __init int ath79_spi_init(void) 278 + { 279 + return platform_driver_register(&ath79_spi_driver); 280 + } 281 + module_init(ath79_spi_init); 282 + 283 + static __exit void ath79_spi_exit(void) 284 + { 285 + platform_driver_unregister(&ath79_spi_driver); 286 + } 287 + module_exit(ath79_spi_exit); 288 + 289 + MODULE_DESCRIPTION("SPI controller driver for Atheros AR71XX/AR724X/AR913X"); 290 + MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); 291 + MODULE_LICENSE("GPL v2"); 292 + MODULE_ALIAS("platform:" DRV_NAME);