aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-orion.c
diff options
context:
space:
mode:
authorGreg Ungerer <gerg@uclinux.org>2014-09-28 09:24:04 -0400
committerMark Brown <broonie@kernel.org>2014-10-01 14:21:51 -0400
commitdf59fa7f4bca9658b75f0f5fee225b3a057475c5 (patch)
tree6c01e397e59715c8043f0de634299f2273bac18b /drivers/spi/spi-orion.c
parent7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9 (diff)
spi: orion: support armada extended baud rates
The Armada SoC family implementation of this SPI hardware module has extended the configuration register to allow for a wider range of SPI clock rates. Specifically the Serial Baud Rate Pre-selection bits in the SPI Interface Configuration Register now also use bits 6 and 7 as well. Modify the baud rate calculation to handle these differences for the Armada case. Potentially a baud rate can be setup using a number of different pre-scalar and scalar combinations. This code tries all possible pre-scalar divisors (8 in total) to try and find the most accurate set. This change introduces (and documents) a new device tree compatible device name "armada-370-spi" to support this. Signed-off-by: Greg Ungerer <gerg@uclinux.org> Tested-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com> Reviewed-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-orion.c')
-rw-r--r--drivers/spi/spi-orion.c116
1 files changed, 94 insertions, 22 deletions
diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c
index c4675fa8b645..acf8e48db16c 100644
--- a/drivers/spi/spi-orion.c
+++ b/drivers/spi/spi-orion.c
@@ -18,6 +18,7 @@
18#include <linux/module.h> 18#include <linux/module.h>
19#include <linux/pm_runtime.h> 19#include <linux/pm_runtime.h>
20#include <linux/of.h> 20#include <linux/of.h>
21#include <linux/of_device.h>
21#include <linux/clk.h> 22#include <linux/clk.h>
22#include <linux/sizes.h> 23#include <linux/sizes.h>
23#include <asm/unaligned.h> 24#include <asm/unaligned.h>
@@ -40,13 +41,27 @@
40#define ORION_SPI_MODE_CPHA (1 << 12) 41#define ORION_SPI_MODE_CPHA (1 << 12)
41#define ORION_SPI_IF_8_16_BIT_MODE (1 << 5) 42#define ORION_SPI_IF_8_16_BIT_MODE (1 << 5)
42#define ORION_SPI_CLK_PRESCALE_MASK 0x1F 43#define ORION_SPI_CLK_PRESCALE_MASK 0x1F
44#define ARMADA_SPI_CLK_PRESCALE_MASK 0xDF
43#define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \ 45#define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \
44 ORION_SPI_MODE_CPHA) 46 ORION_SPI_MODE_CPHA)
45 47
48enum orion_spi_type {
49 ORION_SPI,
50 ARMADA_SPI,
51};
52
53struct orion_spi_dev {
54 enum orion_spi_type typ;
55 unsigned int min_divisor;
56 unsigned int max_divisor;
57 u32 prescale_mask;
58};
59
46struct orion_spi { 60struct orion_spi {
47 struct spi_master *master; 61 struct spi_master *master;
48 void __iomem *base; 62 void __iomem *base;
49 struct clk *clk; 63 struct clk *clk;
64 const struct orion_spi_dev *devdata;
50}; 65};
51 66
52static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) 67static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
@@ -83,30 +98,66 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
83 u32 prescale; 98 u32 prescale;
84 u32 reg; 99 u32 reg;
85 struct orion_spi *orion_spi; 100 struct orion_spi *orion_spi;
101 const struct orion_spi_dev *devdata;
86 102
87 orion_spi = spi_master_get_devdata(spi->master); 103 orion_spi = spi_master_get_devdata(spi->master);
104 devdata = orion_spi->devdata;
88 105
89 tclk_hz = clk_get_rate(orion_spi->clk); 106 tclk_hz = clk_get_rate(orion_spi->clk);
90 107
91 /* 108 if (devdata->typ == ARMADA_SPI) {
92 * the supported rates are: 4,6,8...30 109 unsigned int clk, spr, sppr, sppr2, err;
93 * round up as we look for equal or less speed 110 unsigned int best_spr, best_sppr, best_err;
94 */ 111
95 rate = DIV_ROUND_UP(tclk_hz, speed); 112 best_err = speed;
96 rate = roundup(rate, 2); 113 best_spr = 0;
114 best_sppr = 0;
115
116 /* Iterate over the valid range looking for best fit */
117 for (sppr = 0; sppr < 8; sppr++) {
118 sppr2 = 0x1 << sppr;
119
120 spr = tclk_hz / sppr2;
121 spr = DIV_ROUND_UP(spr, speed);
122 if ((spr == 0) || (spr > 15))
123 continue;
97 124
98 /* check if requested speed is too small */ 125 clk = tclk_hz / (spr * sppr2);
99 if (rate > 30) 126 err = speed - clk;
100 return -EINVAL;
101 127
102 if (rate < 4) 128 if (err < best_err) {
103 rate = 4; 129 best_spr = spr;
130 best_sppr = sppr;
131 best_err = err;
132 }
133 }
134
135 if ((best_sppr == 0) && (best_spr == 0))
136 return -EINVAL;
137
138 prescale = ((best_sppr & 0x6) << 5) |
139 ((best_sppr & 0x1) << 4) | best_spr;
140 } else {
141 /*
142 * the supported rates are: 4,6,8...30
143 * round up as we look for equal or less speed
144 */
145 rate = DIV_ROUND_UP(tclk_hz, speed);
146 rate = roundup(rate, 2);
147
148 /* check if requested speed is too small */
149 if (rate > 30)
150 return -EINVAL;
104 151
105 /* Convert the rate to SPI clock divisor value. */ 152 if (rate < 4)
106 prescale = 0x10 + rate/2; 153 rate = 4;
154
155 /* Convert the rate to SPI clock divisor value. */
156 prescale = 0x10 + rate/2;
157 }
107 158
108 reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); 159 reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
109 reg = ((reg & ~ORION_SPI_CLK_PRESCALE_MASK) | prescale); 160 reg = ((reg & ~devdata->prescale_mask) | prescale);
110 writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG)); 161 writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
111 162
112 return 0; 163 return 0;
@@ -342,8 +393,31 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
342 return 0; 393 return 0;
343} 394}
344 395
396static const struct orion_spi_dev orion_spi_dev_data = {
397 .typ = ORION_SPI,
398 .min_divisor = 4,
399 .max_divisor = 30,
400 .prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
401};
402
403static const struct orion_spi_dev armada_spi_dev_data = {
404 .typ = ARMADA_SPI,
405 .min_divisor = 1,
406 .max_divisor = 1920,
407 .prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
408};
409
410static const struct of_device_id orion_spi_of_match_table[] = {
411 { .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, },
412 { .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, },
413 {}
414};
415MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
416
345static int orion_spi_probe(struct platform_device *pdev) 417static int orion_spi_probe(struct platform_device *pdev)
346{ 418{
419 const struct of_device_id *of_id;
420 const struct orion_spi_dev *devdata;
347 struct spi_master *master; 421 struct spi_master *master;
348 struct orion_spi *spi; 422 struct orion_spi *spi;
349 struct resource *r; 423 struct resource *r;
@@ -378,6 +452,10 @@ static int orion_spi_probe(struct platform_device *pdev)
378 spi = spi_master_get_devdata(master); 452 spi = spi_master_get_devdata(master);
379 spi->master = master; 453 spi->master = master;
380 454
455 of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
456 devdata = of_id->data;
457 spi->devdata = devdata;
458
381 spi->clk = devm_clk_get(&pdev->dev, NULL); 459 spi->clk = devm_clk_get(&pdev->dev, NULL);
382 if (IS_ERR(spi->clk)) { 460 if (IS_ERR(spi->clk)) {
383 status = PTR_ERR(spi->clk); 461 status = PTR_ERR(spi->clk);
@@ -389,8 +467,8 @@ static int orion_spi_probe(struct platform_device *pdev)
389 goto out; 467 goto out;
390 468
391 tclk_hz = clk_get_rate(spi->clk); 469 tclk_hz = clk_get_rate(spi->clk);
392 master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4); 470 master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
393 master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30); 471 master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
394 472
395 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 473 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
396 spi->base = devm_ioremap_resource(&pdev->dev, r); 474 spi->base = devm_ioremap_resource(&pdev->dev, r);
@@ -469,12 +547,6 @@ static const struct dev_pm_ops orion_spi_pm_ops = {
469 NULL) 547 NULL)
470}; 548};
471 549
472static const struct of_device_id orion_spi_of_match_table[] = {
473 { .compatible = "marvell,orion-spi", },
474 {}
475};
476MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
477
478static struct platform_driver orion_spi_driver = { 550static struct platform_driver orion_spi_driver = {
479 .driver = { 551 .driver = {
480 .name = DRIVER_NAME, 552 .name = DRIVER_NAME,