* [PATCH 1/7] PM / Domains: add generic function 'pm_genpd_of_add_device_by_name'
2012-09-18 18:21 [PATCH 0/7] Add support for Exynos5 ISP SPI ports Simon Glass
@ 2012-09-18 18:21 ` Simon Glass
2012-09-18 18:21 ` [PATCH 2/7] spi: s3c64xx: Fix enum dma_data_direction warning Simon Glass
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Simon Glass @ 2012-09-18 18:21 UTC (permalink / raw)
To: linux-arm-kernel
From: Prathyush K <prathyush.k@samsung.com>
A new function pm_genpd_of_add_device_by_name is added to the pm_domain
file. This function takes three parameters - target device to be added to
genpd, a device node and the name of the property. This function reads
the "propname" property from the device node and finds the corresponding
genpd device node. The target device is then added to this genpd by
calling __pm_genpd_of_add_device.
of_property_read_u32(dev_node, propname, &val);
genpd_node = of_find_node_by_phandle(val);
__pm_genpd_of_add_device(genpd_node, dev, NULL);
Signed-off-by: Prathyush K <prathyush.k@samsung.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
Tested-by: Prathyush Kalashwaram <prathyush@chromium.org>
---
include/linux/pm_domain.h | 19 +++++++++++++++++++
1 files changed, 19 insertions(+), 0 deletions(-)
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 30f794e..c733ece 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -137,6 +137,18 @@ static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
return __pm_genpd_add_device(genpd, dev, NULL);
}
+static inline int pm_genpd_of_add_device_by_name(
+ struct device_node *dev_node,
+ struct device *dev,
+ char *propname)
+{
+ u32 val;
+ struct device_node *genpd_node;
+ of_property_read_u32(dev_node, propname, &val);
+ genpd_node = of_find_node_by_phandle(val);
+ return __pm_genpd_of_add_device(genpd_node, dev, NULL);
+}
+
static inline int pm_genpd_of_add_device(struct device_node *genpd_node,
struct device *dev)
{
@@ -184,6 +196,13 @@ static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
{
return -ENOSYS;
}
+static inline int pm_genpd_of_add_device_by_name(
+ struct device_node *dev_node,
+ struct device *dev,
+ char *propname)
+{
+ return -ENOSYS;
+}
static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
struct device *dev)
{
--
1.7.7.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/7] spi: s3c64xx: Fix enum dma_data_direction warning
2012-09-18 18:21 [PATCH 0/7] Add support for Exynos5 ISP SPI ports Simon Glass
2012-09-18 18:21 ` [PATCH 1/7] PM / Domains: add generic function 'pm_genpd_of_add_device_by_name' Simon Glass
@ 2012-09-18 18:21 ` Simon Glass
2012-09-18 18:21 ` [PATCH 3/7] spi: s3c64xx: Add support for ISP SPI ports Simon Glass
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Simon Glass @ 2012-09-18 18:21 UTC (permalink / raw)
To: linux-arm-kernel
While the effect is harmless, the SPI driver is actually using the wrong
enum and this generates warnings:
drivers/spi/spi-s3c64xx.c: In function 's3c64xx_spi_dmacb':
drivers/spi/spi-s3c64xx.c:268:21: warning: comparison between 'enum dma_data_direction' and 'enum dma_transfer_direction' [-Wenum-compare]
drivers/spi/spi-s3c64xx.c:277:21: warning: comparison between 'enum dma_data_direction' and 'enum dma_transfer_direction' [-Wenum-compare]
drivers/spi/spi-s3c64xx.c: In function 'prepare_dma':
drivers/spi/spi-s3c64xx.c:296:21: warning: comparison between 'enum dma_data_direction' and 'enum dma_transfer_direction' [-Wenum-compare]
Signed-off-by: Simon Glass <sjg@chromium.org>
---
drivers/spi/spi-s3c64xx.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 646a765..3152659 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -260,7 +260,7 @@ static void s3c64xx_spi_dmacb(void *data)
struct s3c64xx_spi_dma_data *dma = data;
unsigned long flags;
- if (dma->direction == DMA_DEV_TO_MEM)
+ if (dma->direction == DMA_FROM_DEVICE)
sdd = container_of(data,
struct s3c64xx_spi_driver_data, rx_dma);
else
@@ -269,7 +269,7 @@ static void s3c64xx_spi_dmacb(void *data)
spin_lock_irqsave(&sdd->lock, flags);
- if (dma->direction == DMA_DEV_TO_MEM) {
+ if (dma->direction == DMA_FROM_DEVICE) {
sdd->state &= ~RXBUSY;
if (!(sdd->state & TXBUSY))
complete(&sdd->xfer_completion);
@@ -289,7 +289,7 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
struct samsung_dma_prep info;
struct samsung_dma_config config;
- if (dma->direction == DMA_DEV_TO_MEM) {
+ if (dma->direction == DMA_FROM_DEVICE) {
sdd = container_of((void *)dma,
struct s3c64xx_spi_driver_data, rx_dma);
config.direction = sdd->rx_dma.direction;
--
1.7.7.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/7] spi: s3c64xx: Add support for ISP SPI ports
2012-09-18 18:21 [PATCH 0/7] Add support for Exynos5 ISP SPI ports Simon Glass
2012-09-18 18:21 ` [PATCH 1/7] PM / Domains: add generic function 'pm_genpd_of_add_device_by_name' Simon Glass
2012-09-18 18:21 ` [PATCH 2/7] spi: s3c64xx: Fix enum dma_data_direction warning Simon Glass
@ 2012-09-18 18:21 ` Simon Glass
2012-09-18 18:21 ` [PATCH 4/7] spi: s3c64xx: Use jiffies instead of loops for timeout Simon Glass
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Simon Glass @ 2012-09-18 18:21 UTC (permalink / raw)
To: linux-arm-kernel
The ISP has two SPI ports which can be used for general SPI activities.
Add support for these for:
- clocks and clock gating
- SPI FIFO size for these ports
- support for 'samsung,pd' node in SPI so we can mark these ports as
dependent on the ISP power domain
Signed-off-by: Simon Glass <sjg@chromium.org>
---
arch/arm/mach-exynos/clock-exynos5.c | 69 ++++++++++++++++++++++++
arch/arm/mach-exynos/include/mach/map.h | 2 +
arch/arm/mach-exynos/include/mach/regs-clock.h | 1 +
arch/arm/mach-exynos/mach-exynos5-dt.c | 4 ++
drivers/spi/spi-s3c64xx.c | 13 ++++-
5 files changed, 87 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-exynos/clock-exynos5.c b/arch/arm/mach-exynos/clock-exynos5.c
index 774533c..f40eaaf 100644
--- a/arch/arm/mach-exynos/clock-exynos5.c
+++ b/arch/arm/mach-exynos/clock-exynos5.c
@@ -186,6 +186,11 @@ static int exynos5_clk_ip_peris_ctrl(struct clk *clk, int enable)
return s5p_gatectrl(EXYNOS5_CLKGATE_IP_PERIS, clk, enable);
}
+static int exynos5_clksrc_mask_isp_ctrl(struct clk *clk, int enable)
+{
+ return s5p_gatectrl(EXYNOS5_SCLK_SRC_MASK_ISP, clk, enable);
+}
+
static int exynos5_clk_ip_gscl_ctrl(struct clk *clk, int enable)
{
return s5p_gatectrl(EXYNOS5_CLKGATE_IP_GSCL, clk, enable);
@@ -764,6 +769,18 @@ static struct clk exynos5_init_clocks_off[] = {
.enable = exynos5_clk_ip_peric_ctrl,
.ctrlbit = (1 << 18),
}, {
+ .name = "spi",
+ .devname = "exynos4210-spi.3",
+ .parent = &exynos5_clk_aclk_66.clk,
+ .enable = exynos5_clk_ip_isp1_ctrl,
+ .ctrlbit = (1 << 12),
+ }, {
+ .name = "spi",
+ .devname = "exynos4210-spi.4",
+ .parent = &exynos5_clk_aclk_66.clk,
+ .enable = exynos5_clk_ip_isp1_ctrl,
+ .ctrlbit = (1 << 13),
+ }, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(mfc_l, 0),
.enable = &exynos5_clk_ip_mfc_ctrl,
@@ -1120,6 +1137,50 @@ static struct clksrc_clk exynos5_clk_sclk_spi2 = {
.reg_div = { .reg = EXYNOS5_CLKDIV_PERIC2, .shift = 8, .size = 8 },
};
+static struct clksrc_clk exynos5_clk_mdout_spi3 = {
+ .clk = {
+ .name = "sclk_spi_mdout",
+ .devname = "exynos4210-spi.3",
+ },
+ .sources = &exynos5_clkset_group,
+ .reg_src = { .reg = EXYNOS5_SCLK_SRC_ISP, .shift = 0, .size = 4 },
+ .reg_div = { .reg = EXYNOS5_SCLK_DIV_ISP, .shift = 0, .size = 4 },
+
+};
+
+static struct clksrc_clk exynos5_clk_sclk_spi3 = {
+ .clk = {
+ .name = "sclk_spi",
+ .devname = "exynos4210-spi.3",
+ .parent = &exynos5_clk_mdout_spi3.clk,
+ .enable = exynos5_clksrc_mask_isp_ctrl,
+ .ctrlbit = (1 << 0),
+ },
+ .reg_div = { .reg = EXYNOS5_SCLK_DIV_ISP, .shift = 4, .size = 8 },
+};
+
+static struct clksrc_clk exynos5_clk_mdout_spi4 = {
+ .clk = {
+ .name = "sclk_spi_mdout",
+ .devname = "exynos4210-spi.4",
+ },
+ .sources = &exynos5_clkset_group,
+ .reg_src = { .reg = EXYNOS5_SCLK_SRC_ISP, .shift = 4, .size = 4 },
+ .reg_div = { .reg = EXYNOS5_SCLK_DIV_ISP, .shift = 12, .size = 4 },
+
+};
+
+static struct clksrc_clk exynos5_clk_sclk_spi4 = {
+ .clk = {
+ .name = "sclk_spi",
+ .devname = "exynos4210-spi.4",
+ .parent = &exynos5_clk_mdout_spi4.clk,
+ .enable = exynos5_clksrc_mask_isp_ctrl,
+ .ctrlbit = (1 << 4),
+ },
+ .reg_div = { .reg = EXYNOS5_SCLK_DIV_ISP, .shift = 16, .size = 8 },
+};
+
static struct clksrc_clk exynos5_clksrcs[] = {
{
.clk = {
@@ -1237,9 +1298,13 @@ static struct clksrc_clk *exynos5_sysclks[] = {
&exynos5_clk_sclk_spi0,
&exynos5_clk_sclk_spi1,
&exynos5_clk_sclk_spi2,
+ &exynos5_clk_sclk_spi3,
+ &exynos5_clk_sclk_spi4,
&exynos5_clk_mdout_spi0,
&exynos5_clk_mdout_spi1,
&exynos5_clk_mdout_spi2,
+ &exynos5_clk_mdout_spi3,
+ &exynos5_clk_mdout_spi4,
};
static struct clk *exynos5_clk_cdev[] = {
@@ -1271,6 +1336,10 @@ static struct clk_lookup exynos5_clk_lookup[] = {
CLKDEV_INIT("exynos4210-spi.0", "spi_busclk0", &exynos5_clk_sclk_spi0.clk),
CLKDEV_INIT("exynos4210-spi.1", "spi_busclk0", &exynos5_clk_sclk_spi1.clk),
CLKDEV_INIT("exynos4210-spi.2", "spi_busclk0", &exynos5_clk_sclk_spi2.clk),
+ CLKDEV_INIT("exynos4210-spi.3", "spi_busclk0",
+ &exynos5_clk_sclk_spi3.clk),
+ CLKDEV_INIT("exynos4210-spi.4", "spi_busclk0",
+ &exynos5_clk_sclk_spi4.clk),
CLKDEV_INIT("dma-pl330.0", "apb_pclk", &exynos5_clk_pdma0),
CLKDEV_INIT("dma-pl330.1", "apb_pclk", &exynos5_clk_pdma1),
CLKDEV_INIT("dma-pl330.2", "apb_pclk", &exynos5_clk_mdma1),
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index c72b675..4d46a5f 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -157,6 +157,8 @@
#define EXYNOS5_PA_SPI0 0x12D20000
#define EXYNOS5_PA_SPI1 0x12D30000
#define EXYNOS5_PA_SPI2 0x12D40000
+#define EXYNOS5_PA_SPI3 0x131A0000
+#define EXYNOS5_PA_SPI4 0x131B0000
#define EXYNOS4_PA_GPIO1 0x11400000
#define EXYNOS4_PA_GPIO2 0x11000000
diff --git a/arch/arm/mach-exynos/include/mach/regs-clock.h b/arch/arm/mach-exynos/include/mach/regs-clock.h
index 8c9b38c..d03f83d 100644
--- a/arch/arm/mach-exynos/include/mach/regs-clock.h
+++ b/arch/arm/mach-exynos/include/mach/regs-clock.h
@@ -301,6 +301,7 @@
#define EXYNOS5_CLKSRC_MASK_FSYS EXYNOS_CLKREG(0x10340)
#define EXYNOS5_CLKSRC_MASK_PERIC0 EXYNOS_CLKREG(0x10350)
#define EXYNOS5_CLKSRC_MASK_PERIC1 EXYNOS_CLKREG(0x10354)
+#define EXYNOS5_SCLK_SRC_MASK_ISP EXYNOS_CLKREG(0x10370)
#define EXYNOS5_CLKDIV_TOP0 EXYNOS_CLKREG(0x10510)
#define EXYNOS5_CLKDIV_TOP1 EXYNOS_CLKREG(0x10514)
diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c
index ef770bc..76c5126 100644
--- a/arch/arm/mach-exynos/mach-exynos5-dt.c
+++ b/arch/arm/mach-exynos/mach-exynos5-dt.c
@@ -53,6 +53,10 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = {
"exynos4210-spi.1", NULL),
OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS5_PA_SPI2,
"exynos4210-spi.2", NULL),
+ OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS5_PA_SPI3,
+ "exynos4210-spi.3", NULL),
+ OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS5_PA_SPI4,
+ "exynos4210-spi.4", NULL),
OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA0, "dma-pl330.0", NULL),
OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA1, "dma-pl330.1", NULL),
OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_MDMA1, "dma-pl330.2", NULL),
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 3152659..95a1bfc 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -30,11 +30,12 @@
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/pm_domain.h>
#include <mach/dma.h>
#include <plat/s3c64xx-spi.h>
-#define MAX_SPI_PORTS 3
+#define MAX_SPI_PORTS 5
/* Registers and bit-fields */
@@ -1345,6 +1346,14 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
goto err8;
}
+#ifdef CONFIG_PM
+ if (pdev->dev.of_node) {
+ if (pm_genpd_of_add_device_by_name(pdev->dev.of_node,
+ &pdev->dev, "samsung,pd"))
+ dev_err(&pdev->dev, "failed to add to genpd\n");
+ }
+#endif
+
dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d "
"with %d Slaves attached\n",
sdd->port_id, master->num_chipselect);
@@ -1513,7 +1522,7 @@ struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
};
struct s3c64xx_spi_port_config exynos4_spi_port_config = {
- .fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F },
+ .fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F, 0x1ff, 0x1ff },
.rx_lvl_offset = 15,
.tx_st_done = 25,
.high_speed = true,
--
1.7.7.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/7] spi: s3c64xx: Use jiffies instead of loops for timeout
2012-09-18 18:21 [PATCH 0/7] Add support for Exynos5 ISP SPI ports Simon Glass
` (2 preceding siblings ...)
2012-09-18 18:21 ` [PATCH 3/7] spi: s3c64xx: Add support for ISP SPI ports Simon Glass
@ 2012-09-18 18:21 ` Simon Glass
2012-09-18 18:21 ` [PATCH 5/7] spi: s3c64xx: Allow use with SPI ports without dma Simon Glass
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Simon Glass @ 2012-09-18 18:21 UTC (permalink / raw)
To: linux-arm-kernel
The current timeout uses loops, but does not actually use an empty loop. In
fact it checks SPI registers which are pretty slow to read. As a result the
timeout ends up being several seconds most of the time.
Change this to use jiffies instead.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
drivers/spi/spi-s3c64xx.c | 37 ++++++++++++++++++++++---------------
1 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 95a1bfc..db79d87 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -427,6 +427,7 @@ static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd,
static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer, int dma_mode)
{
+ struct device *dev = &sdd->pdev->dev;
void __iomem *regs = sdd->regs;
unsigned long val;
int ms;
@@ -439,16 +440,21 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
val = msecs_to_jiffies(ms) + 10;
val = wait_for_completion_timeout(&sdd->xfer_completion, val);
} else {
+ ulong deadline;
u32 status;
- val = msecs_to_loops(ms);
+
+ deadline = jiffies + msecs_to_jiffies(ms);
do {
status = readl(regs + S3C64XX_SPI_STATUS);
- } while (RX_FIFO_LVL(status, sdd) < xfer->len && --val);
+ if (time_after(jiffies, deadline)) {
+ dev_warn(dev, "RX timeout level=%d, need=%d\n",
+ RX_FIFO_LVL(status, sdd), xfer->len);
+ return -EIO;
+ }
+ cpu_relax();
+ } while (RX_FIFO_LVL(status, sdd) < xfer->len);
}
- if (!val)
- return -EIO;
-
if (dma_mode) {
u32 status;
@@ -460,17 +466,18 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
* Xfer involved Rx(with or without Tx).
*/
if (xfer->rx_buf == NULL) {
- val = msecs_to_loops(10);
- status = readl(regs + S3C64XX_SPI_STATUS);
- while ((TX_FIFO_LVL(status, sdd)
- || !S3C64XX_SPI_ST_TX_DONE(status, sdd))
- && --val) {
- cpu_relax();
- status = readl(regs + S3C64XX_SPI_STATUS);
- }
+ ulong deadline;
- if (!val)
- return -EIO;
+ deadline = jiffies + msecs_to_jiffies(10);
+ do {
+ status = readl(regs + S3C64XX_SPI_STATUS);
+ if (time_after(jiffies, deadline)) {
+ dev_warn(dev, "TX timeout level=%d\n",
+ TX_FIFO_LVL(status, sdd));
+ return -EIO;
+ }
+ } while (TX_FIFO_LVL(status, sdd) ||
+ !S3C64XX_SPI_ST_TX_DONE(status, sdd));
}
} else {
/* If it was only Tx */
--
1.7.7.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/7] spi: s3c64xx: Allow use with SPI ports without dma
2012-09-18 18:21 [PATCH 0/7] Add support for Exynos5 ISP SPI ports Simon Glass
` (3 preceding siblings ...)
2012-09-18 18:21 ` [PATCH 4/7] spi: s3c64xx: Use jiffies instead of loops for timeout Simon Glass
@ 2012-09-18 18:21 ` Simon Glass
2012-09-18 18:21 ` [PATCH 6/7] spi: s3c64xx: Tidy up SPI chip select handling Simon Glass
2012-09-18 18:21 ` [PATCH 7/7] spi: s3c64xx: Write to PACKET_CNT after reset Simon Glass
6 siblings, 0 replies; 8+ messages in thread
From: Simon Glass @ 2012-09-18 18:21 UTC (permalink / raw)
To: linux-arm-kernel
The ISP SPI ports appear not to support dma. Allow these to work
to some extent.
The current driver will not permit transfers larger than the FIFO
size, which is 256 bytes in the case of the ISP SPI ports, unless dma
is enabled.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
drivers/spi/spi-s3c64xx.c | 54 ++++++++++++++++++++++++++++----------------
1 files changed, 34 insertions(+), 20 deletions(-)
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index db79d87..ed12872 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -675,6 +675,7 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
int status = 0, cs_toggle = 0;
u32 speed;
u8 bpw;
+ bool have_dma;
/* If Master's(controller) state differs from that needed by Slave */
if (sdd->cur_speed != spi->max_speed_hz
@@ -686,12 +687,14 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
s3c64xx_spi_config(sdd);
}
+ have_dma = (sdd->rx_dma.direction != DMA_NONE);
+
/* Map all the transfers if needed */
- if (s3c64xx_spi_map_mssg(sdd, msg)) {
+ if (have_dma && s3c64xx_spi_map_mssg(sdd, msg)) {
dev_err(&spi->dev,
"Xfer: Unable to map message buffers!\n");
status = -ENOMEM;
- goto out;
+ goto out_nomap;
}
/* Configure feedback delay */
@@ -722,8 +725,12 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
s3c64xx_spi_config(sdd);
}
- /* Polling method for xfers not bigger than FIFO capacity */
- if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1))
+ /*
+ * Polling method if we have no DMA support and for xfers
+ * not bigger than FIFO capacity
+ */
+ if (xfer->len <= ((FIFO_LVL_MASK(sdd) >> 1) + 1) ||
+ sdd->rx_dma.direction == DMA_NONE)
use_dma = 0;
else
use_dma = 1;
@@ -794,8 +801,9 @@ out:
else
sdd->tgl_spi = spi;
- s3c64xx_spi_unmap_mssg(sdd, msg);
-
+ if (have_dma)
+ s3c64xx_spi_unmap_mssg(sdd, msg);
+out_nomap:
msg->status = status;
spi_finalize_current_message(master);
@@ -807,9 +815,11 @@ static int s3c64xx_spi_prepare_transfer(struct spi_master *spi)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
- /* Acquire DMA channels */
- while (!acquire_dma(sdd))
- msleep(10);
+ if (sdd->rx_dma.direction != DMA_NONE) {
+ /* Acquire DMA channels */
+ while (!acquire_dma(sdd))
+ msleep(10);
+ }
pm_runtime_get_sync(&sdd->pdev->dev);
@@ -820,9 +830,11 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi);
- /* Free DMA channels */
- sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
- sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
+ if (sdd->rx_dma.direction != DMA_NONE) {
+ /* Free DMA channels */
+ sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client);
+ sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client);
+ }
pm_runtime_put(&sdd->pdev->dev);
@@ -1095,8 +1107,9 @@ static int __devinit s3c64xx_spi_get_dmares(
sprintf(prop_name, "%s-dma-channel", chan_str);
prop = of_find_property(pdev->dev.of_node, prop_name, NULL);
if (!prop) {
- dev_err(&pdev->dev, "%s dma channel property not specified\n",
- chan_str);
+ dma_data->direction = DMA_NONE;
+ dev_warn(&pdev->dev, "%s no dma channel property"
+ " - falling back to PIO\n", chan_str);
return -ENXIO;
}
@@ -1209,7 +1222,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci = pdev->dev.platform_data;
struct spi_master *master;
- int ret, irq;
+ int ret, irq, ret_tx;
char clk_name[16];
if (!sci && pdev->dev.of_node) {
@@ -1264,13 +1277,14 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
sdd->cur_bpw = 8;
- ret = s3c64xx_spi_get_dmares(sdd, true);
- if (ret)
- goto err0;
-
+ /* These may fail, in which case we fall back to PIO */
+ ret_tx = s3c64xx_spi_get_dmares(sdd, true);
ret = s3c64xx_spi_get_dmares(sdd, false);
- if (ret)
+ if (!!ret_tx != !!ret) {
+ dev_err(&pdev->dev, "dma rx/tx channels must either both be"
+ "enabled or disabled\n");
goto err0;
+ }
master->dev.of_node = pdev->dev.of_node;
master->bus_num = sdd->port_id;
--
1.7.7.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6/7] spi: s3c64xx: Tidy up SPI chip select handling
2012-09-18 18:21 [PATCH 0/7] Add support for Exynos5 ISP SPI ports Simon Glass
` (4 preceding siblings ...)
2012-09-18 18:21 ` [PATCH 5/7] spi: s3c64xx: Allow use with SPI ports without dma Simon Glass
@ 2012-09-18 18:21 ` Simon Glass
2012-09-18 18:21 ` [PATCH 7/7] spi: s3c64xx: Write to PACKET_CNT after reset Simon Glass
6 siblings, 0 replies; 8+ messages in thread
From: Simon Glass @ 2012-09-18 18:21 UTC (permalink / raw)
To: linux-arm-kernel
While it is possible to use a GPIO, it is useful to be able to use the
SPI peripheral's built-in chip select feature. This should be supported.
The current driver is broken in that it doesn't properly support
the cs_change property in the message. To fix this, the chip select
should be changed in one place (enable/disable_cs()) instead of using the
macro separately in a different place. Also we need to make sure that
the FIFOs are cleared even in the event of an empty transaction or
error.
(This adds a proposed new fdt binding for Samsung SPI)
Signed-off-by: Simon Glass <sjg@chromium.org>
---
.../devicetree/bindings/spi/spi-samsung.txt | 7 ++++
arch/arm/plat-samsung/include/plat/s3c64xx-spi.h | 6 ++-
drivers/spi/spi-s3c64xx.c | 34 +++++++++++++------
3 files changed, 34 insertions(+), 13 deletions(-)
diff --git a/Documentation/devicetree/bindings/spi/spi-samsung.txt b/Documentation/devicetree/bindings/spi/spi-samsung.txt
index 59bfc4f..74d67e1 100644
--- a/Documentation/devicetree/bindings/spi/spi-samsung.txt
+++ b/Documentation/devicetree/bindings/spi/spi-samsung.txt
@@ -60,6 +60,13 @@ SPI Controller specific data in SPI slave nodes:
- 2: 180 degree phase shift sampling.
- 3: 270 degree phase shift sampling.
+- The spi slave nodes may provide the following optional properties:
+
+ - samsung,spi-cs: (boolean property). If present, uses the SPI controller's
+ built-in chip select feature, rather than toggling GPIOs manually. The
+ correct chip select line must still be specified in cs-gpio, and in this
+ case should be assigned to the SPI controller.
+
Aliases:
- All the SPI controller nodes should be represented in the aliases node using
diff --git a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h
index ceba18d..7103f71 100644
--- a/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h
+++ b/arch/arm/plat-samsung/include/plat/s3c64xx-spi.h
@@ -17,7 +17,8 @@ struct platform_device;
* struct s3c64xx_spi_csinfo - ChipSelect description
* @fb_delay: Slave specific feedback delay.
* Refer to FB_CLK_SEL register definition in SPI chapter.
- * @line: Custom 'identity' of the CS line.
+ * @line: Custom 'identity' of the CS line (-1 to use SPI controller)
+ * @use_spi_cs: Use SPI's chip select toggle instead of GPIO
*
* This is per SPI-Slave Chipselect information.
* Allocate and initialize one in machine init code and make the
@@ -25,7 +26,8 @@ struct platform_device;
*/
struct s3c64xx_spi_csinfo {
u8 fb_delay;
- unsigned line;
+ int line;
+ bool only_spi_cs;
};
/**
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index ed12872..c34ef8f 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -414,14 +414,21 @@ static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd,
if (sdd->tgl_spi != spi) { /* if last mssg on diff device */
/* Deselect the last toggled device */
cs = sdd->tgl_spi->controller_data;
- gpio_set_value(cs->line,
- spi->mode & SPI_CS_HIGH ? 0 : 1);
+ writel(S3C64XX_SPI_SLAVE_SIG_INACT,
+ sdd->regs + S3C64XX_SPI_SLAVE_SEL);
+ if (!cs->only_spi_cs) {
+ gpio_set_value(cs->line,
+ spi->mode & SPI_CS_HIGH ? 0 : 1);
+ }
}
sdd->tgl_spi = NULL;
}
+ /* Start the signals */
cs = spi->controller_data;
- gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0);
+ if (!cs->only_spi_cs)
+ gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0);
+ writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
}
static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
@@ -514,7 +521,10 @@ static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd,
if (sdd->tgl_spi == spi)
sdd->tgl_spi = NULL;
- gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1);
+ /* Quiese the signals */
+ writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
+ if (!cs->only_spi_cs)
+ gpio_set_value(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1);
}
static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
@@ -746,17 +756,10 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
/* Slave Select */
enable_cs(sdd, spi);
- /* Start the signals */
- writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL);
-
spin_unlock_irqrestore(&sdd->lock, flags);
status = wait_for_xfer(sdd, xfer, use_dma);
- /* Quiese the signals */
- writel(S3C64XX_SPI_SLAVE_SIG_INACT,
- sdd->regs + S3C64XX_SPI_SLAVE_SEL);
-
if (status) {
dev_err(&spi->dev, "I/O Error: "
"rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
@@ -801,6 +804,9 @@ out:
else
sdd->tgl_spi = spi;
+ /* Make sure empty transactions and error clean up the state */
+ flush_fifo(sdd);
+
if (have_dma)
s3c64xx_spi_unmap_mssg(sdd, msg);
out_nomap:
@@ -878,6 +884,12 @@ static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata(
return ERR_PTR(-EINVAL);
}
+ /*
+ * Rather than manually toggle GPIOs, we can use the SPI chip select
+ * feature.
+ */
+ cs->only_spi_cs = of_property_read_bool(data_np, "samsung,spi-cs");
+
of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay);
cs->fb_delay = fb_delay;
return cs;
--
1.7.7.3
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 7/7] spi: s3c64xx: Write to PACKET_CNT after reset
2012-09-18 18:21 [PATCH 0/7] Add support for Exynos5 ISP SPI ports Simon Glass
` (5 preceding siblings ...)
2012-09-18 18:21 ` [PATCH 6/7] spi: s3c64xx: Tidy up SPI chip select handling Simon Glass
@ 2012-09-18 18:21 ` Simon Glass
6 siblings, 0 replies; 8+ messages in thread
From: Simon Glass @ 2012-09-18 18:21 UTC (permalink / raw)
To: linux-arm-kernel
The Exynos5 datasheet specifically says that a software reset of SPI
must be performed before writing to the PACKET_CNT register. Also,
the current code sometimes causes an extra transfer or two to be
performed.
Adjust the code slightly to fix this problem.
Test flashrom operation on snow.
See that SPI operation with ChromeOS EC is reliable now.
Signed-off-by: Simon Glass <sjg@chromium.org>
---
drivers/spi/spi-s3c64xx.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index c34ef8f..3716825 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -213,7 +213,9 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
unsigned long loops;
u32 val;
- writel(0, regs + S3C64XX_SPI_PACKET_CNT);
+ val = readl(regs + S3C64XX_SPI_CH_CFG);
+ val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON);
+ writel(val, regs + S3C64XX_SPI_CH_CFG);
val = readl(regs + S3C64XX_SPI_CH_CFG);
val |= S3C64XX_SPI_CH_SW_RST;
@@ -250,9 +252,7 @@ static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
writel(val, regs + S3C64XX_SPI_MODE_CFG);
- val = readl(regs + S3C64XX_SPI_CH_CFG);
- val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON);
- writel(val, regs + S3C64XX_SPI_CH_CFG);
+ writel(0, regs + S3C64XX_SPI_PACKET_CNT);
}
static void s3c64xx_spi_dmacb(void *data)
--
1.7.7.3
^ permalink raw reply related [flat|nested] 8+ messages in thread