diff options
| -rw-r--r-- | Documentation/devicetree/bindings/spi/spi-orion.txt | 2 | ||||
| -rw-r--r-- | drivers/spi/spi-orion.c | 116 |
2 files changed, 95 insertions, 23 deletions
diff --git a/Documentation/devicetree/bindings/spi/spi-orion.txt b/Documentation/devicetree/bindings/spi/spi-orion.txt index a3ff50fc76fb..50c3a3de61c1 100644 --- a/Documentation/devicetree/bindings/spi/spi-orion.txt +++ b/Documentation/devicetree/bindings/spi/spi-orion.txt | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | Marvell Orion SPI device | 1 | Marvell Orion SPI device |
| 2 | 2 | ||
| 3 | Required properties: | 3 | Required properties: |
| 4 | - compatible : should be "marvell,orion-spi". | 4 | - compatible : should be "marvell,orion-spi" or "marvell,armada-370-spi". |
| 5 | - reg : offset and length of the register set for the device | 5 | - reg : offset and length of the register set for the device |
| 6 | - cell-index : Which of multiple SPI controllers is this. | 6 | - cell-index : Which of multiple SPI controllers is this. |
| 7 | Optional properties: | 7 | Optional properties: |
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 | ||
| 48 | enum orion_spi_type { | ||
| 49 | ORION_SPI, | ||
| 50 | ARMADA_SPI, | ||
| 51 | }; | ||
| 52 | |||
| 53 | struct 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 | |||
| 46 | struct orion_spi { | 60 | struct 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 | ||
| 52 | static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) | 67 | static 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 | ||
| 396 | static 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 | |||
| 403 | static 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 | |||
| 410 | static 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 | }; | ||
| 415 | MODULE_DEVICE_TABLE(of, orion_spi_of_match_table); | ||
| 416 | |||
| 345 | static int orion_spi_probe(struct platform_device *pdev) | 417 | static 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 | ||
| 472 | static const struct of_device_id orion_spi_of_match_table[] = { | ||
| 473 | { .compatible = "marvell,orion-spi", }, | ||
| 474 | {} | ||
| 475 | }; | ||
| 476 | MODULE_DEVICE_TABLE(of, orion_spi_of_match_table); | ||
| 477 | |||
| 478 | static struct platform_driver orion_spi_driver = { | 550 | static struct platform_driver orion_spi_driver = { |
| 479 | .driver = { | 551 | .driver = { |
| 480 | .name = DRIVER_NAME, | 552 | .name = DRIVER_NAME, |
