Blame view
drivers/spi/spi-ath79.c
7.4 KB
8efaef4dc SPI: Add SPI cont... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* * SPI controller driver for the Atheros AR71XX/AR724X/AR913X SoCs * * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org> * * This driver has been based on the spi-gpio.c: * Copyright (C) 2006,2008 David Brownell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include <linux/kernel.h> |
807cc4b12 spi/ath79: fix co... |
16 |
#include <linux/module.h> |
8efaef4dc SPI: Add SPI cont... |
17 18 |
#include <linux/delay.h> #include <linux/spinlock.h> |
8efaef4dc SPI: Add SPI cont... |
19 20 21 22 23 24 |
#include <linux/platform_device.h> #include <linux/io.h> #include <linux/spi/spi.h> #include <linux/spi/spi_bitbang.h> #include <linux/bitops.h> #include <linux/gpio.h> |
440114fdb spi/ath79: add de... |
25 26 |
#include <linux/clk.h> #include <linux/err.h> |
8efaef4dc SPI: Add SPI cont... |
27 28 29 30 31 |
#include <asm/mach-ath79/ar71xx_regs.h> #include <asm/mach-ath79/ath79_spi_platform.h> #define DRV_NAME "ath79-spi" |
440114fdb spi/ath79: add de... |
32 33 |
#define ATH79_SPI_RRW_DELAY_FACTOR 12000 #define MHZ (1000 * 1000) |
8efaef4dc SPI: Add SPI cont... |
34 35 36 37 38 |
struct ath79_spi { struct spi_bitbang bitbang; u32 ioc_base; u32 reg_ctrl; void __iomem *base; |
440114fdb spi/ath79: add de... |
39 |
struct clk *clk; |
da470d6ab spi/ath79: Fix ch... |
40 |
unsigned int rrw_delay; |
8efaef4dc SPI: Add SPI cont... |
41 |
}; |
da470d6ab spi/ath79: Fix ch... |
42 |
static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned int reg) |
8efaef4dc SPI: Add SPI cont... |
43 44 45 |
{ return ioread32(sp->base + reg); } |
da470d6ab spi/ath79: Fix ch... |
46 |
static inline void ath79_spi_wr(struct ath79_spi *sp, unsigned int reg, u32 val) |
8efaef4dc SPI: Add SPI cont... |
47 48 49 50 51 52 53 54 |
{ iowrite32(val, sp->base + reg); } static inline struct ath79_spi *ath79_spidev_to_sp(struct spi_device *spi) { return spi_master_get_devdata(spi->master); } |
da470d6ab spi/ath79: Fix ch... |
55 |
static inline void ath79_spi_delay(struct ath79_spi *sp, unsigned int nsecs) |
440114fdb spi/ath79: add de... |
56 57 58 59 |
{ if (nsecs > sp->rrw_delay) ndelay(nsecs - sp->rrw_delay); } |
8efaef4dc SPI: Add SPI cont... |
60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
static void ath79_spi_chipselect(struct spi_device *spi, int is_active) { struct ath79_spi *sp = ath79_spidev_to_sp(spi); int cs_high = (spi->mode & SPI_CS_HIGH) ? is_active : !is_active; if (is_active) { /* set initial clock polarity */ if (spi->mode & SPI_CPOL) sp->ioc_base |= AR71XX_SPI_IOC_CLK; else sp->ioc_base &= ~AR71XX_SPI_IOC_CLK; ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); } |
22c76326b spi: spi-ath79: s... |
74 |
if (gpio_is_valid(spi->cs_gpio)) { |
8efaef4dc SPI: Add SPI cont... |
75 |
/* SPI is normally active-low */ |
91829a9a2 spi: spi-ath79: u... |
76 |
gpio_set_value_cansleep(spi->cs_gpio, cs_high); |
8efaef4dc SPI: Add SPI cont... |
77 |
} else { |
22c76326b spi: spi-ath79: s... |
78 |
u32 cs_bit = AR71XX_SPI_IOC_CS(spi->chip_select); |
8efaef4dc SPI: Add SPI cont... |
79 |
if (cs_high) |
22c76326b spi: spi-ath79: s... |
80 |
sp->ioc_base |= cs_bit; |
8efaef4dc SPI: Add SPI cont... |
81 |
else |
22c76326b spi: spi-ath79: s... |
82 |
sp->ioc_base &= ~cs_bit; |
8efaef4dc SPI: Add SPI cont... |
83 84 85 86 87 |
ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); } } |
c4a31f430 spi/ath79: avoid ... |
88 |
static void ath79_spi_enable(struct ath79_spi *sp) |
8efaef4dc SPI: Add SPI cont... |
89 |
{ |
8efaef4dc SPI: Add SPI cont... |
90 91 92 93 94 95 96 97 98 |
/* enable GPIO mode */ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO); /* save CTRL register */ sp->reg_ctrl = ath79_spi_rr(sp, AR71XX_SPI_REG_CTRL); sp->ioc_base = ath79_spi_rr(sp, AR71XX_SPI_REG_IOC); /* TODO: setup speed? */ ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, 0x43); |
c4a31f430 spi/ath79: avoid ... |
99 100 101 102 103 104 105 106 107 108 109 110 |
} static void ath79_spi_disable(struct ath79_spi *sp) { /* restore CTRL register */ ath79_spi_wr(sp, AR71XX_SPI_REG_CTRL, sp->reg_ctrl); /* disable GPIO mode */ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0); } static int ath79_spi_setup_cs(struct spi_device *spi) { |
83f0f398a spi: spi-ath79: S... |
111 |
struct ath79_spi *sp = ath79_spidev_to_sp(spi); |
c4a31f430 spi/ath79: avoid ... |
112 |
int status; |
95d79419f spi/ath79: use gp... |
113 |
status = 0; |
22c76326b spi: spi-ath79: s... |
114 |
if (gpio_is_valid(spi->cs_gpio)) { |
95d79419f spi/ath79: use gp... |
115 116 117 118 |
unsigned long flags; flags = GPIOF_DIR_OUT; if (spi->mode & SPI_CS_HIGH) |
95d79419f spi/ath79: use gp... |
119 |
flags |= GPIOF_INIT_LOW; |
61d1cf163 spi: spi-ath79: f... |
120 121 |
else flags |= GPIOF_INIT_HIGH; |
95d79419f spi/ath79: use gp... |
122 |
|
85f62476f spi: spi-ath79: A... |
123 |
status = gpio_request_one(spi->cs_gpio, flags, |
95d79419f spi/ath79: use gp... |
124 |
dev_name(&spi->dev)); |
83f0f398a spi: spi-ath79: S... |
125 |
} else { |
22c76326b spi: spi-ath79: s... |
126 |
u32 cs_bit = AR71XX_SPI_IOC_CS(spi->chip_select); |
83f0f398a spi: spi-ath79: S... |
127 |
if (spi->mode & SPI_CS_HIGH) |
22c76326b spi: spi-ath79: s... |
128 |
sp->ioc_base &= ~cs_bit; |
83f0f398a spi: spi-ath79: S... |
129 |
else |
22c76326b spi: spi-ath79: s... |
130 |
sp->ioc_base |= cs_bit; |
83f0f398a spi: spi-ath79: S... |
131 132 |
ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base); |
8efaef4dc SPI: Add SPI cont... |
133 |
} |
95d79419f spi/ath79: use gp... |
134 |
return status; |
8efaef4dc SPI: Add SPI cont... |
135 136 137 138 |
} static void ath79_spi_cleanup_cs(struct spi_device *spi) { |
da470d6ab spi/ath79: Fix ch... |
139 |
if (gpio_is_valid(spi->cs_gpio)) |
85f62476f spi: spi-ath79: A... |
140 |
gpio_free(spi->cs_gpio); |
8efaef4dc SPI: Add SPI cont... |
141 142 143 144 145 |
} static int ath79_spi_setup(struct spi_device *spi) { int status = 0; |
8efaef4dc SPI: Add SPI cont... |
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
if (!spi->controller_state) { status = ath79_spi_setup_cs(spi); if (status) return status; } status = spi_bitbang_setup(spi); if (status && !spi->controller_state) ath79_spi_cleanup_cs(spi); return status; } static void ath79_spi_cleanup(struct spi_device *spi) { ath79_spi_cleanup_cs(spi); spi_bitbang_cleanup(spi); } |
da470d6ab spi/ath79: Fix ch... |
164 |
static u32 ath79_spi_txrx_mode0(struct spi_device *spi, unsigned int nsecs, |
8efaef4dc SPI: Add SPI cont... |
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
u32 word, u8 bits) { struct ath79_spi *sp = ath79_spidev_to_sp(spi); u32 ioc = sp->ioc_base; /* clock starts at inactive polarity */ for (word <<= (32 - bits); likely(bits); bits--) { u32 out; if (word & (1 << 31)) out = ioc | AR71XX_SPI_IOC_DO; else out = ioc & ~AR71XX_SPI_IOC_DO; /* setup MSB (to slave) on trailing edge */ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); |
440114fdb spi/ath79: add de... |
181 |
ath79_spi_delay(sp, nsecs); |
8efaef4dc SPI: Add SPI cont... |
182 |
ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK); |
440114fdb spi/ath79: add de... |
183 |
ath79_spi_delay(sp, nsecs); |
72611db0e spi/ath79: add mi... |
184 185 |
if (bits == 1) ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); |
8efaef4dc SPI: Add SPI cont... |
186 187 188 189 190 191 |
word <<= 1; } return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS); } |
fd4a319bc spi: Remove HOTPL... |
192 |
static int ath79_spi_probe(struct platform_device *pdev) |
8efaef4dc SPI: Add SPI cont... |
193 194 195 196 197 |
{ struct spi_master *master; struct ath79_spi *sp; struct ath79_spi_platform_data *pdata; struct resource *r; |
440114fdb spi/ath79: add de... |
198 |
unsigned long rate; |
8efaef4dc SPI: Add SPI cont... |
199 200 201 202 203 204 205 206 207 208 |
int ret; master = spi_alloc_master(&pdev->dev, sizeof(*sp)); if (master == NULL) { dev_err(&pdev->dev, "failed to allocate spi master "); return -ENOMEM; } sp = spi_master_get_devdata(master); |
85f62476f spi: spi-ath79: A... |
209 |
master->dev.of_node = pdev->dev.of_node; |
8efaef4dc SPI: Add SPI cont... |
210 |
platform_set_drvdata(pdev, sp); |
8074cf063 spi: use dev_get_... |
211 |
pdata = dev_get_platdata(&pdev->dev); |
8efaef4dc SPI: Add SPI cont... |
212 |
|
24778be20 spi: convert driv... |
213 |
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); |
8efaef4dc SPI: Add SPI cont... |
214 215 216 217 218 |
master->setup = ath79_spi_setup; master->cleanup = ath79_spi_cleanup; if (pdata) { master->bus_num = pdata->bus_num; master->num_chipselect = pdata->num_chipselect; |
8efaef4dc SPI: Add SPI cont... |
219 |
} |
94c69f765 spi: bitbang: Let... |
220 |
sp->bitbang.master = master; |
8efaef4dc SPI: Add SPI cont... |
221 222 223 224 225 226 |
sp->bitbang.chipselect = ath79_spi_chipselect; sp->bitbang.txrx_word[SPI_MODE_0] = ath79_spi_txrx_mode0; sp->bitbang.setup_transfer = spi_bitbang_setup_transfer; sp->bitbang.flags = SPI_CS_HIGH; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
b7a2a1c0b spi: ath79: simpl... |
227 228 229 |
sp->base = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(sp->base)) { ret = PTR_ERR(sp->base); |
8efaef4dc SPI: Add SPI cont... |
230 231 |
goto err_put_master; } |
a6f4c8e06 spi: ath79: Use d... |
232 |
sp->clk = devm_clk_get(&pdev->dev, "ahb"); |
440114fdb spi/ath79: add de... |
233 234 |
if (IS_ERR(sp->clk)) { ret = PTR_ERR(sp->clk); |
a6f4c8e06 spi: ath79: Use d... |
235 |
goto err_put_master; |
440114fdb spi/ath79: add de... |
236 |
} |
3e19acdc5 spi: spi-ath79: U... |
237 |
ret = clk_prepare_enable(sp->clk); |
440114fdb spi/ath79: add de... |
238 |
if (ret) |
a6f4c8e06 spi: ath79: Use d... |
239 |
goto err_put_master; |
440114fdb spi/ath79: add de... |
240 241 242 243 244 245 246 247 248 249 250 |
rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ); if (!rate) { ret = -EINVAL; goto err_clk_disable; } sp->rrw_delay = ATH79_SPI_RRW_DELAY_FACTOR / rate; dev_dbg(&pdev->dev, "register read/write delay is %u nsecs ", sp->rrw_delay); |
c4a31f430 spi/ath79: avoid ... |
251 |
ath79_spi_enable(sp); |
8efaef4dc SPI: Add SPI cont... |
252 253 |
ret = spi_bitbang_start(&sp->bitbang); if (ret) |
c4a31f430 spi/ath79: avoid ... |
254 |
goto err_disable; |
8efaef4dc SPI: Add SPI cont... |
255 256 |
return 0; |
c4a31f430 spi/ath79: avoid ... |
257 258 |
err_disable: ath79_spi_disable(sp); |
440114fdb spi/ath79: add de... |
259 |
err_clk_disable: |
3e19acdc5 spi: spi-ath79: U... |
260 |
clk_disable_unprepare(sp->clk); |
8efaef4dc SPI: Add SPI cont... |
261 |
err_put_master: |
8efaef4dc SPI: Add SPI cont... |
262 263 264 265 |
spi_master_put(sp->bitbang.master); return ret; } |
fd4a319bc spi: Remove HOTPL... |
266 |
static int ath79_spi_remove(struct platform_device *pdev) |
8efaef4dc SPI: Add SPI cont... |
267 268 269 270 |
{ struct ath79_spi *sp = platform_get_drvdata(pdev); spi_bitbang_stop(&sp->bitbang); |
c4a31f430 spi/ath79: avoid ... |
271 |
ath79_spi_disable(sp); |
3e19acdc5 spi: spi-ath79: U... |
272 |
clk_disable_unprepare(sp->clk); |
8efaef4dc SPI: Add SPI cont... |
273 274 275 276 |
spi_master_put(sp->bitbang.master); return 0; } |
7410e8485 spi/ath79: add sh... |
277 278 279 280 |
static void ath79_spi_shutdown(struct platform_device *pdev) { ath79_spi_remove(pdev); } |
85f62476f spi: spi-ath79: A... |
281 282 283 284 |
static const struct of_device_id ath79_spi_of_match[] = { { .compatible = "qca,ar7100-spi", }, { }, }; |
d7a32394e spi: ath79: Fix m... |
285 |
MODULE_DEVICE_TABLE(of, ath79_spi_of_match); |
85f62476f spi: spi-ath79: A... |
286 |
|
8efaef4dc SPI: Add SPI cont... |
287 288 |
static struct platform_driver ath79_spi_driver = { .probe = ath79_spi_probe, |
fd4a319bc spi: Remove HOTPL... |
289 |
.remove = ath79_spi_remove, |
7410e8485 spi/ath79: add sh... |
290 |
.shutdown = ath79_spi_shutdown, |
8efaef4dc SPI: Add SPI cont... |
291 292 |
.driver = { .name = DRV_NAME, |
85f62476f spi: spi-ath79: A... |
293 |
.of_match_table = ath79_spi_of_match, |
8efaef4dc SPI: Add SPI cont... |
294 295 |
}, }; |
940ab8896 drivercore: Add h... |
296 |
module_platform_driver(ath79_spi_driver); |
8efaef4dc SPI: Add SPI cont... |
297 298 299 300 301 |
MODULE_DESCRIPTION("SPI controller driver for Atheros AR71XX/AR724X/AR913X"); MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" DRV_NAME); |