186 lines
4.6 KiB
Diff
186 lines
4.6 KiB
Diff
--- a/drivers/spi/spi-ath79.c
|
|
+++ b/drivers/spi/spi-ath79.c
|
|
@@ -35,6 +35,11 @@
|
|
|
|
#define ATH79_SPI_CS_LINE_MAX 2
|
|
|
|
+enum ath79_spi_state {
|
|
+ ATH79_SPI_STATE_WAIT_CMD = 0,
|
|
+ ATH79_SPI_STATE_WAIT_READ,
|
|
+};
|
|
+
|
|
struct ath79_spi {
|
|
struct spi_bitbang bitbang;
|
|
u32 ioc_base;
|
|
@@ -42,6 +47,11 @@ struct ath79_spi {
|
|
void __iomem *base;
|
|
struct clk *clk;
|
|
unsigned rrw_delay;
|
|
+
|
|
+ enum ath79_spi_state state;
|
|
+ u32 clk_div;
|
|
+ unsigned long read_addr;
|
|
+ unsigned long ahb_rate;
|
|
};
|
|
|
|
static inline u32 ath79_spi_rr(struct ath79_spi *sp, unsigned reg)
|
|
@@ -109,9 +119,6 @@ static void ath79_spi_enable(struct ath7
|
|
/* 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);
|
|
}
|
|
|
|
static void ath79_spi_disable(struct ath79_spi *sp)
|
|
@@ -224,6 +231,110 @@ static u32 ath79_spi_txrx_mode0(struct s
|
|
return ath79_spi_rr(sp, AR71XX_SPI_REG_RDS);
|
|
}
|
|
|
|
+static int ath79_spi_do_read_flash_data(struct spi_device *spi,
|
|
+ struct spi_transfer *t)
|
|
+{
|
|
+ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
|
|
+
|
|
+ /* disable GPIO mode */
|
|
+ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, 0);
|
|
+
|
|
+ memcpy_fromio(t->rx_buf, sp->base + sp->read_addr, t->len);
|
|
+
|
|
+ /* enable GPIO mode */
|
|
+ ath79_spi_wr(sp, AR71XX_SPI_REG_FS, AR71XX_SPI_FS_GPIO);
|
|
+
|
|
+ /* restore IOC register */
|
|
+ ath79_spi_wr(sp, AR71XX_SPI_REG_IOC, sp->ioc_base);
|
|
+
|
|
+ return t->len;
|
|
+}
|
|
+
|
|
+static int ath79_spi_do_read_flash_cmd(struct spi_device *spi,
|
|
+ struct spi_transfer *t)
|
|
+{
|
|
+ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
|
|
+ int len;
|
|
+ const u8 *p;
|
|
+
|
|
+ sp->read_addr = 0;
|
|
+
|
|
+ len = t->len - 1;
|
|
+ p = t->tx_buf;
|
|
+
|
|
+ while (len--) {
|
|
+ p++;
|
|
+ sp->read_addr <<= 8;
|
|
+ sp->read_addr |= *p;
|
|
+ }
|
|
+
|
|
+ return t->len;
|
|
+}
|
|
+
|
|
+static bool ath79_spi_is_read_cmd(struct spi_device *spi,
|
|
+ struct spi_transfer *t)
|
|
+{
|
|
+ return t->type == SPI_TRANSFER_FLASH_READ_CMD;
|
|
+}
|
|
+
|
|
+static bool ath79_spi_is_data_read(struct spi_device *spi,
|
|
+ struct spi_transfer *t)
|
|
+{
|
|
+ return t->type == SPI_TRANSFER_FLASH_READ_DATA;
|
|
+}
|
|
+
|
|
+static int ath79_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
|
|
+{
|
|
+ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
|
|
+ int ret;
|
|
+
|
|
+ switch (sp->state) {
|
|
+ case ATH79_SPI_STATE_WAIT_CMD:
|
|
+ if (ath79_spi_is_read_cmd(spi, t)) {
|
|
+ ret = ath79_spi_do_read_flash_cmd(spi, t);
|
|
+ sp->state = ATH79_SPI_STATE_WAIT_READ;
|
|
+ } else {
|
|
+ ret = spi_bitbang_bufs(spi, t);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case ATH79_SPI_STATE_WAIT_READ:
|
|
+ if (ath79_spi_is_data_read(spi, t)) {
|
|
+ ret = ath79_spi_do_read_flash_data(spi, t);
|
|
+ } else {
|
|
+ dev_warn(&spi->dev, "flash data read expected\n");
|
|
+ ret = -EIO;
|
|
+ }
|
|
+ sp->state = ATH79_SPI_STATE_WAIT_CMD;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ BUG();
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ath79_spi_setup_transfer(struct spi_device *spi,
|
|
+ struct spi_transfer *t)
|
|
+{
|
|
+ struct ath79_spi *sp = ath79_spidev_to_sp(spi);
|
|
+ struct ath79_spi_controller_data *cdata;
|
|
+ int ret;
|
|
+
|
|
+ ret = spi_bitbang_setup_transfer(spi, t);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ cdata = spi->controller_data;
|
|
+ if (cdata->is_flash)
|
|
+ sp->bitbang.txrx_bufs = ath79_spi_txrx_bufs;
|
|
+ else
|
|
+ sp->bitbang.txrx_bufs = spi_bitbang_bufs;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
static int ath79_spi_probe(struct platform_device *pdev)
|
|
{
|
|
struct spi_master *master;
|
|
@@ -246,6 +357,8 @@ static int ath79_spi_probe(struct platfo
|
|
sp = spi_master_get_devdata(master);
|
|
platform_set_drvdata(pdev, sp);
|
|
|
|
+ sp->state = ATH79_SPI_STATE_WAIT_CMD;
|
|
+
|
|
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
|
|
master->setup = ath79_spi_setup;
|
|
master->cleanup = ath79_spi_cleanup;
|
|
@@ -255,7 +368,7 @@ static int ath79_spi_probe(struct platfo
|
|
sp->bitbang.master = master;
|
|
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.setup_transfer = ath79_spi_setup_transfer;
|
|
sp->bitbang.flags = SPI_CS_HIGH;
|
|
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
@@ -280,7 +393,8 @@ static int ath79_spi_probe(struct platfo
|
|
if (ret)
|
|
goto err_put_master;
|
|
|
|
- rate = DIV_ROUND_UP(clk_get_rate(sp->clk), MHZ);
|
|
+ sp->ahb_rate = clk_get_rate(sp->clk);
|
|
+ rate = DIV_ROUND_UP(sp->ahb_rate, MHZ);
|
|
if (!rate) {
|
|
ret = -EINVAL;
|
|
goto err_clk_disable;
|
|
--- a/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
|
|
+++ b/arch/mips/include/asm/mach-ath79/ath79_spi_platform.h
|
|
@@ -24,6 +24,7 @@ enum ath79_spi_cs_type {
|
|
struct ath79_spi_controller_data {
|
|
enum ath79_spi_cs_type cs_type;
|
|
unsigned cs_line;
|
|
+ bool is_flash;
|
|
};
|
|
|
|
#endif /* _ATH79_SPI_PLATFORM_H */
|