forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									7d1311b93e
								
							
						
					
					
						commit
						df59fa7f4b
					
				
					 2 changed files with 95 additions and 23 deletions
				
			
		| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
Marvell Orion SPI device
 | 
					Marvell Orion SPI device
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Required properties:
 | 
					Required properties:
 | 
				
			||||||
- compatible : should be "marvell,orion-spi".
 | 
					- compatible : should be "marvell,orion-spi" or "marvell,armada-370-spi".
 | 
				
			||||||
- reg : offset and length of the register set for the device
 | 
					- reg : offset and length of the register set for the device
 | 
				
			||||||
- cell-index : Which of multiple SPI controllers is this.
 | 
					- cell-index : Which of multiple SPI controllers is this.
 | 
				
			||||||
Optional properties:
 | 
					Optional properties:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@
 | 
				
			||||||
#include <linux/module.h>
 | 
					#include <linux/module.h>
 | 
				
			||||||
#include <linux/pm_runtime.h>
 | 
					#include <linux/pm_runtime.h>
 | 
				
			||||||
#include <linux/of.h>
 | 
					#include <linux/of.h>
 | 
				
			||||||
 | 
					#include <linux/of_device.h>
 | 
				
			||||||
#include <linux/clk.h>
 | 
					#include <linux/clk.h>
 | 
				
			||||||
#include <linux/sizes.h>
 | 
					#include <linux/sizes.h>
 | 
				
			||||||
#include <asm/unaligned.h>
 | 
					#include <asm/unaligned.h>
 | 
				
			||||||
| 
						 | 
					@ -40,13 +41,27 @@
 | 
				
			||||||
#define ORION_SPI_MODE_CPHA		(1 << 12)
 | 
					#define ORION_SPI_MODE_CPHA		(1 << 12)
 | 
				
			||||||
#define ORION_SPI_IF_8_16_BIT_MODE	(1 << 5)
 | 
					#define ORION_SPI_IF_8_16_BIT_MODE	(1 << 5)
 | 
				
			||||||
#define ORION_SPI_CLK_PRESCALE_MASK	0x1F
 | 
					#define ORION_SPI_CLK_PRESCALE_MASK	0x1F
 | 
				
			||||||
 | 
					#define ARMADA_SPI_CLK_PRESCALE_MASK	0xDF
 | 
				
			||||||
#define ORION_SPI_MODE_MASK		(ORION_SPI_MODE_CPOL | \
 | 
					#define ORION_SPI_MODE_MASK		(ORION_SPI_MODE_CPOL | \
 | 
				
			||||||
					 ORION_SPI_MODE_CPHA)
 | 
										 ORION_SPI_MODE_CPHA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum orion_spi_type {
 | 
				
			||||||
 | 
						ORION_SPI,
 | 
				
			||||||
 | 
						ARMADA_SPI,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct orion_spi_dev {
 | 
				
			||||||
 | 
						enum orion_spi_type	typ;
 | 
				
			||||||
 | 
						unsigned int		min_divisor;
 | 
				
			||||||
 | 
						unsigned int		max_divisor;
 | 
				
			||||||
 | 
						u32			prescale_mask;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct orion_spi {
 | 
					struct orion_spi {
 | 
				
			||||||
	struct spi_master	*master;
 | 
						struct spi_master	*master;
 | 
				
			||||||
	void __iomem		*base;
 | 
						void __iomem		*base;
 | 
				
			||||||
	struct clk              *clk;
 | 
						struct clk              *clk;
 | 
				
			||||||
 | 
						const struct orion_spi_dev *devdata;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
 | 
					static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
 | 
				
			||||||
| 
						 | 
					@ -83,11 +98,46 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
 | 
				
			||||||
	u32 prescale;
 | 
						u32 prescale;
 | 
				
			||||||
	u32 reg;
 | 
						u32 reg;
 | 
				
			||||||
	struct orion_spi *orion_spi;
 | 
						struct orion_spi *orion_spi;
 | 
				
			||||||
 | 
						const struct orion_spi_dev *devdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	orion_spi = spi_master_get_devdata(spi->master);
 | 
						orion_spi = spi_master_get_devdata(spi->master);
 | 
				
			||||||
 | 
						devdata = orion_spi->devdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tclk_hz = clk_get_rate(orion_spi->clk);
 | 
						tclk_hz = clk_get_rate(orion_spi->clk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (devdata->typ == ARMADA_SPI) {
 | 
				
			||||||
 | 
							unsigned int clk, spr, sppr, sppr2, err;
 | 
				
			||||||
 | 
							unsigned int best_spr, best_sppr, best_err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							best_err = speed;
 | 
				
			||||||
 | 
							best_spr = 0;
 | 
				
			||||||
 | 
							best_sppr = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Iterate over the valid range looking for best fit */
 | 
				
			||||||
 | 
							for (sppr = 0; sppr < 8; sppr++) {
 | 
				
			||||||
 | 
								sppr2 = 0x1 << sppr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								spr = tclk_hz / sppr2;
 | 
				
			||||||
 | 
								spr = DIV_ROUND_UP(spr, speed);
 | 
				
			||||||
 | 
								if ((spr == 0) || (spr > 15))
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								clk = tclk_hz / (spr * sppr2);
 | 
				
			||||||
 | 
								err = speed - clk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (err < best_err) {
 | 
				
			||||||
 | 
									best_spr = spr;
 | 
				
			||||||
 | 
									best_sppr = sppr;
 | 
				
			||||||
 | 
									best_err = err;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if ((best_sppr == 0) && (best_spr == 0))
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							prescale = ((best_sppr & 0x6) << 5) |
 | 
				
			||||||
 | 
								((best_sppr & 0x1) << 4) | best_spr;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * the supported rates are: 4,6,8...30
 | 
							 * the supported rates are: 4,6,8...30
 | 
				
			||||||
		 * round up as we look for equal or less speed
 | 
							 * round up as we look for equal or less speed
 | 
				
			||||||
| 
						 | 
					@ -104,9 +154,10 @@ static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Convert the rate to SPI clock divisor value.	*/
 | 
							/* Convert the rate to SPI clock divisor value.	*/
 | 
				
			||||||
		prescale = 0x10 + rate/2;
 | 
							prescale = 0x10 + rate/2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
 | 
						reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
 | 
				
			||||||
	reg = ((reg & ~ORION_SPI_CLK_PRESCALE_MASK) | prescale);
 | 
						reg = ((reg & ~devdata->prescale_mask) | prescale);
 | 
				
			||||||
	writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
 | 
						writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
| 
						 | 
					@ -342,8 +393,31 @@ static int orion_spi_reset(struct orion_spi *orion_spi)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct orion_spi_dev orion_spi_dev_data = {
 | 
				
			||||||
 | 
						.typ = ORION_SPI,
 | 
				
			||||||
 | 
						.min_divisor = 4,
 | 
				
			||||||
 | 
						.max_divisor = 30,
 | 
				
			||||||
 | 
						.prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct orion_spi_dev armada_spi_dev_data = {
 | 
				
			||||||
 | 
						.typ = ARMADA_SPI,
 | 
				
			||||||
 | 
						.min_divisor = 1,
 | 
				
			||||||
 | 
						.max_divisor = 1920,
 | 
				
			||||||
 | 
						.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct of_device_id orion_spi_of_match_table[] = {
 | 
				
			||||||
 | 
						{ .compatible = "marvell,orion-spi", .data = &orion_spi_dev_data, },
 | 
				
			||||||
 | 
						{ .compatible = "marvell,armada-370-spi", .data = &armada_spi_dev_data, },
 | 
				
			||||||
 | 
						{}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int orion_spi_probe(struct platform_device *pdev)
 | 
					static int orion_spi_probe(struct platform_device *pdev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						const struct of_device_id *of_id;
 | 
				
			||||||
 | 
						const struct orion_spi_dev *devdata;
 | 
				
			||||||
	struct spi_master *master;
 | 
						struct spi_master *master;
 | 
				
			||||||
	struct orion_spi *spi;
 | 
						struct orion_spi *spi;
 | 
				
			||||||
	struct resource *r;
 | 
						struct resource *r;
 | 
				
			||||||
| 
						 | 
					@ -378,6 +452,10 @@ static int orion_spi_probe(struct platform_device *pdev)
 | 
				
			||||||
	spi = spi_master_get_devdata(master);
 | 
						spi = spi_master_get_devdata(master);
 | 
				
			||||||
	spi->master = master;
 | 
						spi->master = master;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
 | 
				
			||||||
 | 
						devdata = of_id->data;
 | 
				
			||||||
 | 
						spi->devdata = devdata;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spi->clk = devm_clk_get(&pdev->dev, NULL);
 | 
						spi->clk = devm_clk_get(&pdev->dev, NULL);
 | 
				
			||||||
	if (IS_ERR(spi->clk)) {
 | 
						if (IS_ERR(spi->clk)) {
 | 
				
			||||||
		status = PTR_ERR(spi->clk);
 | 
							status = PTR_ERR(spi->clk);
 | 
				
			||||||
| 
						 | 
					@ -389,8 +467,8 @@ static int orion_spi_probe(struct platform_device *pdev)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tclk_hz = clk_get_rate(spi->clk);
 | 
						tclk_hz = clk_get_rate(spi->clk);
 | 
				
			||||||
	master->max_speed_hz = DIV_ROUND_UP(tclk_hz, 4);
 | 
						master->max_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
 | 
				
			||||||
	master->min_speed_hz = DIV_ROUND_UP(tclk_hz, 30);
 | 
						master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
						r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
				
			||||||
	spi->base = devm_ioremap_resource(&pdev->dev, r);
 | 
						spi->base = devm_ioremap_resource(&pdev->dev, r);
 | 
				
			||||||
| 
						 | 
					@ -469,12 +547,6 @@ static const struct dev_pm_ops orion_spi_pm_ops = {
 | 
				
			||||||
			   NULL)
 | 
								   NULL)
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct of_device_id orion_spi_of_match_table[] = {
 | 
					 | 
				
			||||||
	{ .compatible = "marvell,orion-spi", },
 | 
					 | 
				
			||||||
	{}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static struct platform_driver orion_spi_driver = {
 | 
					static struct platform_driver orion_spi_driver = {
 | 
				
			||||||
	.driver = {
 | 
						.driver = {
 | 
				
			||||||
		.name	= DRIVER_NAME,
 | 
							.name	= DRIVER_NAME,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue