| 1 | From cbb3ade4765bc715b5c2eae4a7b6eaf3ff7ad958 Mon Sep 17 00:00:00 2001 |
| 2 | From: Gabor Juhos <juhosg@openwrt.org> |
| 3 | Date: Wed, 11 Jan 2012 20:06:35 +0100 |
| 4 | Subject: [PATCH 28/34] spi/ath79: add delay between SCK changes |
| 5 | |
| 6 | The driver uses the "as fast as it can" approach |
| 7 | to drive the SCK signal. However this does not |
| 8 | work with certain low speed SPI chips (e.g. the |
| 9 | PCF2123 RTC chip). Add per-bit slowdowns in order |
| 10 | to be able to use the driver with such chips as |
| 11 | well. |
| 12 | |
| 13 | Signed-off-by: Gabor Juhos <juhosg@openwrt.org> |
| 14 | --- |
| 15 | drivers/spi/spi-ath79.c | 44 +++++++++++++++++++++++++++++++++++++++++++- |
| 16 | 1 files changed, 43 insertions(+), 1 deletions(-) |
| 17 | |
| 18 | --- a/drivers/spi/spi-ath79.c |
| 19 | +++ b/drivers/spi/spi-ath79.c |
| 20 | @@ -24,17 +24,24 @@ |
| 21 | #include <linux/spi/spi_bitbang.h> |
| 22 | #include <linux/bitops.h> |
| 23 | #include <linux/gpio.h> |
| 24 | +#include <linux/clk.h> |
| 25 | +#include <linux/err.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 | +#define ATH79_SPI_RRW_DELAY_FACTOR 12000 |
| 33 | +#define MHZ (1000 * 1000) |
| 34 | + |
| 35 | struct ath79_spi { |
| 36 | struct spi_bitbang bitbang; |
| 37 | u32 ioc_base; |
| 38 | u32 reg_ctrl; |
| 39 | void __iomem *base; |
| 40 | + struct clk *clk; |
| 41 | + unsigned rrw_delay; |
| 42 | }; |
| 43 | |
| 44 | static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg) |
| 45 | @@ -52,6 +59,12 @@ static inline struct ath79_spi *ath79_sp |
| 46 | return spi_master_get_devdata(spi->master); |
| 47 | } |
| 48 | |
| 49 | +static inline void ath79_spi_delay(struct ath79_spi *sp, unsigned nsecs) |
| 50 | +{ |
| 51 | + if (nsecs > sp->rrw_delay) |
| 52 | + ndelay(nsecs - sp->rrw_delay); |
| 53 | +} |
| 54 | + |
| 55 | static void ath79_spi_chipselect(struct spi_device *spi, int is_active) |
| 56 | { |
| 57 | struct ath79_spi *sp = ath79_spidev_to_sp(spi); |
| 58 | @@ -184,7 +197,9 @@ static u32 ath79_spi_txrx_mode0(struct s |
| 59 | |
| 60 | /* setup MSB (to slave) on trailing edge */ |
| 61 | ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out); |
| 62 | + ath79_spi_delay(sp, nsecs); |
| 63 | ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, out | AR71XX_SPI_IOC_CLK); |
| 64 | + ath79_spi_delay(sp, nsecs); |
| 65 | |
| 66 | word <<= 1; |
| 67 | } |
| 68 | @@ -198,6 +213,7 @@ static __devinit int ath79_spi_probe(str |
| 69 | struct ath79_spi *sp; |
| 70 | struct ath79_spi_platform_data *pdata; |
| 71 | struct resource *r; |
| 72 | + unsigned long rate; |
| 73 | int ret; |
| 74 | |
| 75 | master = spi_alloc_master(&pdev->dev, sizeof(*sp)); |
| 76 | @@ -239,12 +255,36 @@ static __devinit int ath79_spi_probe(str |
| 77 | goto err_put_master; |
| 78 | } |
| 79 | |
| 80 | + sp->clk = clk_get(&pdev->dev, "ahb"); |
| 81 | + if (IS_ERR(sp->clk)) { |
| 82 | + ret = PTR_ERR(sp->clk); |
| 83 | + goto err_unmap; |
| 84 | + } |
| 85 | + |
| 86 | + ret = clk_enable(sp->clk); |
| 87 | + if (ret) |
| 88 | + goto err_clk_put; |
| 89 | + |
| 90 | + rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ); |
| 91 | + if (!rate) { |
| 92 | + ret = -EINVAL; |
| 93 | + goto err_clk_disable; |
| 94 | + } |
| 95 | + |
| 96 | + sp->rrw_delay = ATH79_SPI_RRW_DELAY_FACTOR / rate; |
| 97 | + dev_dbg(&pdev->dev, "register read/write delay is %u nsecs\n", |
| 98 | + sp->rrw_delay); |
| 99 | + |
| 100 | ret = spi_bitbang_start(&sp->bitbang); |
| 101 | if (ret) |
| 102 | - goto err_unmap; |
| 103 | + goto err_clk_disable; |
| 104 | |
| 105 | return 0; |
| 106 | |
| 107 | +err_clk_disable: |
| 108 | + clk_disable(sp->clk); |
| 109 | +err_clk_put: |
| 110 | + clk_put(sp->clk); |
| 111 | err_unmap: |
| 112 | iounmap(sp->base); |
| 113 | err_put_master: |
| 114 | @@ -259,6 +299,8 @@ static __devexit int ath79_spi_remove(st |
| 115 | struct ath79_spi *sp = platform_get_drvdata(pdev); |
| 116 | |
| 117 | spi_bitbang_stop(&sp->bitbang); |
| 118 | + clk_disable(sp->clk); |
| 119 | + clk_put(sp->clk); |
| 120 | iounmap(sp->base); |
| 121 | platform_set_drvdata(pdev, NULL); |
| 122 | spi_master_put(sp->bitbang.master); |
| 123 | |