aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ramips/patches-3.18/0050-SPI-ralink-add-Ralink-SoC-spi-driver.patch
blob: cb691f336544621d2ac4ab02b4f32de3f43c12f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
From a4d005c91d403d9f3d0272db6cc46202c06ec774 Mon Sep 17 00:00:00 2001
From: Axel Gembe <ago@bastart.eu.org>
Date: Mon, 12 May 2008 18:54:09 +0200
Subject: [PATCH] bcm963xx: flashmap support

Signed-off-by: Axel Gembe <ago@bastart.eu.org>
---
 arch/mips/bcm63xx/boards/board_bcm963xx.c |   19 +----------------
 drivers/mtd/maps/bcm963xx-flash.c         |   32 ++++++++++++++++++++++++----
 drivers/mtd/redboot.c                     |   13 +++++++++--
 3 files changed, 38 insertions(+), 26 deletions(-)

--- a/arch/mips/bcm63xx/boards/board_bcm963xx.c
+++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c
@@ -816,20 +816,6 @@ void __init board_setup(void)
 		panic("unexpected CPU for bcm963xx board");
 }
 
-static struct mtd_partition mtd_partitions[] = {
-	{
-		.name		= "cfe",
-		.offset		= 0x0,
-		.size		= 0x40000,
-	}
-};
-
-static struct physmap_flash_data flash_data = {
-	.width			= 2,
-	.nr_parts		= ARRAY_SIZE(mtd_partitions),
-	.parts			= mtd_partitions,
-};
-
 static struct resource mtd_resources[] = {
 	{
 		.start		= 0,	/* filled at runtime */
@@ -839,12 +825,9 @@ static struct resource mtd_resources[] =
 };
 
 static struct platform_device mtd_dev = {
-	.name			= "physmap-flash",
+	.name			= "bcm963xx-flash",
 	.resource		= mtd_resources,
 	.num_resources		= ARRAY_SIZE(mtd_resources),
-	.dev			= {
-		.platform_data	= &flash_data,
-	},
 };
 
 static struct gpio_led_platform_data bcm63xx_led_data;
--- a/drivers/mtd/maps/bcm963xx-flash.c
+++ b/drivers/mtd/maps/bcm963xx-flash.c
@@ -27,6 +27,8 @@
 #include <linux/vmalloc.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/magic.h>
+#include <linux/jffs2.h>
 
 #include <asm/mach-bcm63xx/bcm963xx_tag.h>
 
@@ -35,6 +37,14 @@
 
 #define PFX KBUILD_MODNAME ": "
 
+struct squashfs_super_block {
+	__le32 s_magic;
+	__le32 pad0[9];
+	__le64 bytes_used;
+};
+
+extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts, unsigned long fis_origin);
+
 static struct mtd_partition *parsed_parts;
 
 static struct mtd_info *bcm963xx_mtd_info;
@@ -219,9 +229,21 @@ probe_ok:
 			}
 		}
 	} else {
-		dev_info(&pdev->dev, "unsupported bootloader\n");
-		err = -ENODEV;
-		goto err_probe;
+		printk(KERN_INFO PFX "assuming RedBoot bootloader\n");
+		if (bcm963xx_mtd_info->size > 0x00400000) {
+			printk(KERN_INFO PFX "Support for extended flash memory size : 0x%lx ; ONLY 64MBIT SUPPORT\n", bcm963xx_mtd_info->size);
+			bcm963xx_map.virt = (u32)(BCM63XX_EXTENDED_SIZE);
+		}
+
+#ifdef CONFIG_MTD_REDBOOT_PARTS
+		if (parsed_nr_parts == 0) {
+			int ret = parse_redboot_partitions(bcm963xx_mtd_info, &parsed_parts, 0);
+			if (ret > 0) {
+				part_type = "RedBoot";
+				parsed_nr_parts = ret;
+			}
+		}
+#endif
 	}
 
 	return add_mtd_partitions(bcm963xx_mtd_info, parsed_parts,
--- a/drivers/mtd/redboot.c
+++ b/drivers/mtd/redboot.c
@@ -57,7 +57,7 @@ static inline int redboot_checksum(struc
 	return 1;
 }
 
-static int parse_redboot_partitions(struct mtd_info *master,
+int parse_redboot_partitions(struct mtd_info *master,
                              struct mtd_partition **pparts,
                              unsigned long fis_origin)
 {
@@ -180,6 +180,14 @@ static int parse_redboot_partitions(stru
 		goto out;
 	}
 
+	if (!fis_origin) {
+		for (i = 0; i < numslots; i++) {
+			if (!strncmp(buf[i].name, "RedBoot", 8)) {
+				fis_origin = (buf[i].flash_base & (master->size << 1) - 1);
+			}
+		}
+	}
+
 	for (i = 0; i < numslots; i++) {
 		struct fis_list *new_fl, **prev;
 
@@ -202,9 +210,8 @@ static int parse_redboot_partitions(stru
 		new_fl->img = &buf[i];
                 if (fis_origin) {
                         buf[i].flash_base -= fis_origin;
-                } else {
-                        buf[i].flash_base &= master->size-1;
                 }
+		buf[i].flash_base &= (master->size << 1) - 1;
 
 		/* I'm sure the JFFS2 code has done me permanent damage.
 		 * I now think the following is _normal_
69'>469 470 471 472 473 474 475 476
From fc006d0622ab8c43086b2c9018c03012db332033 Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 27 Jul 2014 11:15:12 +0100
Subject: [PATCH 50/57] SPI: ralink: add Ralink SoC spi driver

Add the driver needed to make SPI work on Ralink SoC.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Acked-by: John Crispin <blogic@openwrt.org>
---
 drivers/spi/Kconfig      |    6 +
 drivers/spi/Makefile     |    1 +
 drivers/spi/spi-rt2880.c |  432 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 439 insertions(+)
 create mode 100644 drivers/spi/spi-rt2880.c

--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -433,6 +433,12 @@ config SPI_QUP
 	  This driver can also be built as a module.  If so, the module
 	  will be called spi_qup.
 
+config SPI_RT2880
+	tristate "Ralink RT288x SPI Controller"
+	depends on RALINK
+	help
+	  This selects a driver for the Ralink RT288x/RT305x SPI Controller.
+
 config SPI_S3C24XX
 	tristate "Samsung S3C24XX series SPI"
 	depends on ARCH_S3C24XX
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_SPI_PXA2XX_PCI)		+= spi-pxa
 obj-$(CONFIG_SPI_QUP)			+= spi-qup.o
 obj-$(CONFIG_SPI_ROCKCHIP)		+= spi-rockchip.o
 obj-$(CONFIG_SPI_RSPI)			+= spi-rspi.o
+obj-$(CONFIG_SPI_RT2880)		+= spi-rt2880.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi-s3c24xx-hw.o
 spi-s3c24xx-hw-y			:= spi-s3c24xx.o
 spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
--- /dev/null
+++ b/drivers/spi/spi-rt2880.c
@@ -0,0 +1,432 @@
+/*
+ * spi-rt2880.c -- Ralink RT288x/RT305x SPI controller driver
+ *
+ * Copyright (C) 2011 Sergiy <piratfm@gmail.com>
+ * Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * Some parts are based on spi-orion.c:
+ *   Author: Shadi Ammouri <shadi@marvell.com>
+ *   Copyright (C) 2007-2008 Marvell Ltd.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME			"spi-rt2880"
+/* only one slave is supported*/
+#define RALINK_NUM_CHIPSELECTS		1
+/* in usec */
+#define RALINK_SPI_WAIT_MAX_LOOP	2000
+
+#define RAMIPS_SPI_STAT			0x00
+#define RAMIPS_SPI_CFG			0x10
+#define RAMIPS_SPI_CTL			0x14
+#define RAMIPS_SPI_DATA			0x20
+#define RAMIPS_SPI_FIFO_STAT		0x38
+
+/* SPISTAT register bit field */
+#define SPISTAT_BUSY			BIT(0)
+
+/* SPICFG register bit field */
+#define SPICFG_LSBFIRST			0
+#define SPICFG_MSBFIRST			BIT(8)
+#define SPICFG_SPICLKPOL		BIT(6)
+#define SPICFG_RXCLKEDGE_FALLING	BIT(5)
+#define SPICFG_TXCLKEDGE_FALLING	BIT(4)
+#define SPICFG_SPICLK_PRESCALE_MASK	0x7
+#define SPICFG_SPICLK_DIV2		0
+#define SPICFG_SPICLK_DIV4		1
+#define SPICFG_SPICLK_DIV8		2
+#define SPICFG_SPICLK_DIV16		3
+#define SPICFG_SPICLK_DIV32		4
+#define SPICFG_SPICLK_DIV64		5
+#define SPICFG_SPICLK_DIV128		6
+#define SPICFG_SPICLK_DISABLE		7
+
+/* SPICTL register bit field */
+#define SPICTL_HIZSDO			BIT(3)
+#define SPICTL_STARTWR			BIT(2)
+#define SPICTL_STARTRD			BIT(1)
+#define SPICTL_SPIENA			BIT(0)
+
+/* SPIFIFOSTAT register bit field */
+#define SPIFIFOSTAT_TXFULL		BIT(17)
+
+struct rt2880_spi {
+	struct spi_master	*master;
+	void __iomem		*base;
+	unsigned int		sys_freq;
+	unsigned int		speed;
+	struct clk		*clk;
+	spinlock_t		lock;
+};
+
+static inline struct rt2880_spi *spidev_to_rt2880_spi(struct spi_device *spi)
+{
+	return spi_master_get_devdata(spi->master);
+}
+
+static inline u32 rt2880_spi_read(struct rt2880_spi *rs, u32 reg)
+{
+	return ioread32(rs->base + reg);
+}
+
+static inline void rt2880_spi_write(struct rt2880_spi *rs, u32 reg, u32 val)
+{
+	iowrite32(val, rs->base + reg);
+}
+
+static inline void rt2880_spi_setbits(struct rt2880_spi *rs, u32 reg, u32 mask)
+{
+	void __iomem *addr = rs->base + reg;
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&rs->lock, flags);
+	val = ioread32(addr);
+	val |= mask;
+	iowrite32(val, addr);
+	spin_unlock_irqrestore(&rs->lock, flags);
+}
+
+static inline void rt2880_spi_clrbits(struct rt2880_spi *rs, u32 reg, u32 mask)
+{
+	void __iomem *addr = rs->base + reg;
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&rs->lock, flags);
+	val = ioread32(addr);
+	val &= ~mask;
+	iowrite32(val, addr);
+	spin_unlock_irqrestore(&rs->lock, flags);
+}
+
+static int rt2880_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
+{
+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
+	u32 rate;
+	u32 prescale;
+	u32 reg;
+
+	dev_dbg(&spi->dev, "speed:%u\n", speed);
+
+	/*
+	 * the supported rates are: 2, 4, 8, ... 128
+	 * round up as we look for equal or less speed
+	 */
+	rate = DIV_ROUND_UP(rs->sys_freq, speed);
+	dev_dbg(&spi->dev, "rate-1:%u\n", rate);
+	rate = roundup_pow_of_two(rate);
+	dev_dbg(&spi->dev, "rate-2:%u\n", rate);
+
+	/* check if requested speed is too small */
+	if (rate > 128)
+		return -EINVAL;
+
+	if (rate < 2)
+		rate = 2;
+
+	/* Convert the rate to SPI clock divisor value.	*/
+	prescale = ilog2(rate / 2);
+	dev_dbg(&spi->dev, "prescale:%u\n", prescale);
+
+	reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
+	reg = ((reg & ~SPICFG_SPICLK_PRESCALE_MASK) | prescale);
+	rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
+	rs->speed = speed;
+	return 0;
+}
+
+/*
+ * called only when no transfer is active on the bus
+ */
+static int
+rt2880_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
+{
+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
+	unsigned int speed = spi->max_speed_hz;
+	int rc;
+
+	if ((t != NULL) && t->speed_hz)
+		speed = t->speed_hz;
+
+	if (rs->speed != speed) {
+		dev_dbg(&spi->dev, "speed_hz:%u\n", speed);
+		rc = rt2880_spi_baudrate_set(spi, speed);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+static void rt2880_spi_set_cs(struct rt2880_spi *rs, int enable)
+{
+	if (enable)
+		rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
+	else
+		rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
+}
+
+static inline int rt2880_spi_wait_till_ready(struct rt2880_spi *rs)
+{
+	int i;
+
+	for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) {
+		u32 status;
+
+		status = rt2880_spi_read(rs, RAMIPS_SPI_STAT);
+		if ((status & SPISTAT_BUSY) == 0)
+			return 0;
+
+		cpu_relax();
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static unsigned int
+rt2880_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
+{
+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
+	unsigned count = 0;
+	u8 *rx = xfer->rx_buf;
+	const u8 *tx = xfer->tx_buf;
+	int err;
+
+	dev_dbg(&spi->dev, "read (%d): %s %s\n", xfer->len,
+		  (tx != NULL) ? "tx" : "  ",
+		  (rx != NULL) ? "rx" : "  ");
+
+	if (tx) {
+		for (count = 0; count < xfer->len; count++) {
+			rt2880_spi_write(rs, RAMIPS_SPI_DATA, tx[count]);
+			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR);
+			err = rt2880_spi_wait_till_ready(rs);
+			if (err) {
+				dev_err(&spi->dev, "TX failed, err=%d\n", err);
+				goto out;
+			}
+		}
+	}
+
+	if (rx) {
+		for (count = 0; count < xfer->len; count++) {
+			rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD);
+			err = rt2880_spi_wait_till_ready(rs);
+			if (err) {
+				dev_err(&spi->dev, "RX failed, err=%d\n", err);
+				goto out;
+			}
+			rx[count] = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA);
+		}
+	}
+
+out:
+	return count;
+}
+
+static int rt2880_spi_transfer_one_message(struct spi_master *master,
+					   struct spi_message *m)
+{
+	struct rt2880_spi *rs = spi_master_get_devdata(master);
+	struct spi_device *spi = m->spi;
+	struct spi_transfer *t = NULL;
+	int par_override = 0;
+	int status = 0;
+	int cs_active = 0;
+
+	/* Load defaults */
+	status = rt2880_spi_setup_transfer(spi, NULL);
+	if (status < 0)
+		goto msg_done;
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
+			dev_err(&spi->dev,
+				"message rejected: invalid transfer data buffers\n");
+			status = -EIO;
+			goto msg_done;
+		}
+
+		if (t->speed_hz && t->speed_hz < (rs->sys_freq / 128)) {
+			dev_err(&spi->dev,
+				"message rejected: device min speed (%d Hz) exceeds required transfer speed (%d Hz)\n",
+				(rs->sys_freq / 128), t->speed_hz);
+			status = -EIO;
+			goto msg_done;
+		}
+
+		if (par_override || t->speed_hz || t->bits_per_word) {
+			par_override = 1;
+			status = rt2880_spi_setup_transfer(spi, t);
+			if (status < 0)
+				goto msg_done;
+			if (!t->speed_hz && !t->bits_per_word)
+				par_override = 0;
+		}
+
+		if (!cs_active) {
+			rt2880_spi_set_cs(rs, 1);
+			cs_active = 1;
+		}
+
+		if (t->len)
+			m->actual_length += rt2880_spi_write_read(spi, t);
+
+		if (t->delay_usecs)
+			udelay(t->delay_usecs);
+
+		if (t->cs_change) {
+			rt2880_spi_set_cs(rs, 0);
+			cs_active = 0;
+		}
+	}
+
+msg_done:
+	if (cs_active)
+		rt2880_spi_set_cs(rs, 0);
+
+	m->status = status;
+	spi_finalize_current_message(master);
+
+	return 0;
+}
+
+static int rt2880_spi_setup(struct spi_device *spi)
+{
+	struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
+
+	if ((spi->max_speed_hz == 0) ||
+	    (spi->max_speed_hz > (rs->sys_freq / 2)))
+		spi->max_speed_hz = (rs->sys_freq / 2);
+
+	if (spi->max_speed_hz < (rs->sys_freq / 128)) {
+		dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n",
+			spi->max_speed_hz);
+		return -EINVAL;
+	}
+
+	/*
+	 * baudrate & width will be set rt2880_spi_setup_transfer
+	 */
+	return 0;
+}
+
+static void rt2880_spi_reset(struct rt2880_spi *rs)
+{
+	rt2880_spi_write(rs, RAMIPS_SPI_CFG,
+			 SPICFG_MSBFIRST | SPICFG_TXCLKEDGE_FALLING |
+			 SPICFG_SPICLK_DIV16 | SPICFG_SPICLKPOL);
+	rt2880_spi_write(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO | SPICTL_SPIENA);
+}
+
+static int rt2880_spi_probe(struct platform_device *pdev)
+{
+	struct spi_master *master;
+	struct rt2880_spi *rs;
+	unsigned long flags;
+	void __iomem *base;
+	struct resource *r;
+	int status = 0;
+	struct clk *clk;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, r);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n",
+			status);
+		return PTR_ERR(clk);
+	}
+
+	status = clk_prepare_enable(clk);
+	if (status)
+		return status;
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*rs));
+	if (master == NULL) {
+		dev_dbg(&pdev->dev, "master allocation failed\n");
+		return -ENOMEM;
+	}
+
+	/* we support only mode 0, and no options */
+	master->mode_bits = 0;
+
+	master->setup = rt2880_spi_setup;
+	master->transfer_one_message = rt2880_spi_transfer_one_message;
+	master->num_chipselect = RALINK_NUM_CHIPSELECTS;
+	master->bits_per_word_mask = SPI_BPW_MASK(8);
+	master->dev.of_node = pdev->dev.of_node;
+
+	dev_set_drvdata(&pdev->dev, master);
+
+	rs = spi_master_get_devdata(master);
+	rs->base = base;
+	rs->clk = clk;
+	rs->master = master;
+	rs->sys_freq = clk_get_rate(rs->clk);
+	dev_dbg(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
+	spin_lock_irqsave(&rs->lock, flags);
+
+	device_reset(&pdev->dev);
+
+	rt2880_spi_reset(rs);
+
+	return spi_register_master(master);
+}
+
+static int rt2880_spi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master;
+	struct rt2880_spi *rs;
+
+	master = dev_get_drvdata(&pdev->dev);
+	rs = spi_master_get_devdata(master);
+
+	clk_disable(rs->clk);
+	spi_unregister_master(master);
+
+	return 0;
+}
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
+static const struct of_device_id rt2880_spi_match[] = {
+	{ .compatible = "ralink,rt2880-spi" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rt2880_spi_match);
+
+static struct platform_driver rt2880_spi_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = rt2880_spi_match,
+	},
+	.probe = rt2880_spi_probe,
+	.remove = rt2880_spi_remove,
+};
+
+module_platform_driver(rt2880_spi_driver);
+
+MODULE_DESCRIPTION("Ralink SPI driver");
+MODULE_AUTHOR("Sergiy <piratfm@gmail.com>");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL");