diff options
Diffstat (limited to 'drivers/spi/spi-pxa2xx.c')
-rw-r--r-- | drivers/spi/spi-pxa2xx.c | 192 |
1 files changed, 121 insertions, 71 deletions
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 6f72ad01e041..e3223ac75a7c 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -20,6 +20,7 @@ #include <linux/errno.h> #include <linux/err.h> #include <linux/interrupt.h> +#include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/spi/pxa2xx_spi.h> #include <linux/spi/spi.h> @@ -30,10 +31,6 @@ #include <linux/pm_runtime.h> #include <linux/acpi.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/delay.h> - #include "spi-pxa2xx.h" MODULE_AUTHOR("Stephen Street"); @@ -67,54 +64,6 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define LPSS_TX_LOTHRESH_DFLT 160 #define LPSS_TX_HITHRESH_DFLT 224 -struct quark_spi_rate { - u32 bitrate; - u32 dds_clk_rate; - u32 clk_div; -}; - -/* - * 'rate', 'dds', 'clk_div' lookup table, which is defined in - * the Quark SPI datasheet. - */ -static const struct quark_spi_rate quark_spi_rate_table[] = { -/* bitrate, dds_clk_rate, clk_div */ - {50000000, 0x800000, 0}, - {40000000, 0x666666, 0}, - {25000000, 0x400000, 0}, - {20000000, 0x666666, 1}, - {16667000, 0x800000, 2}, - {13333000, 0x666666, 2}, - {12500000, 0x200000, 0}, - {10000000, 0x800000, 4}, - {8000000, 0x666666, 4}, - {6250000, 0x400000, 3}, - {5000000, 0x400000, 4}, - {4000000, 0x666666, 9}, - {3125000, 0x80000, 0}, - {2500000, 0x400000, 9}, - {2000000, 0x666666, 19}, - {1563000, 0x40000, 0}, - {1250000, 0x200000, 9}, - {1000000, 0x400000, 24}, - {800000, 0x666666, 49}, - {781250, 0x20000, 0}, - {625000, 0x200000, 19}, - {500000, 0x400000, 49}, - {400000, 0x666666, 99}, - {390625, 0x10000, 0}, - {250000, 0x400000, 99}, - {200000, 0x666666, 199}, - {195313, 0x8000, 0}, - {125000, 0x100000, 49}, - {100000, 0x200000, 124}, - {50000, 0x100000, 124}, - {25000, 0x80000, 124}, - {10016, 0x20000, 77}, - {5040, 0x20000, 154}, - {1002, 0x8000, 194}, -}; - /* Offset from drv_data->lpss_base */ #define GENERAL_REG 0x08 #define GENERAL_REG_RXTO_HOLDOFF_DISABLE BIT(24) @@ -701,25 +650,124 @@ static irqreturn_t ssp_int(int irq, void *dev_id) } /* - * The Quark SPI data sheet gives a table, and for the given 'rate', - * the 'dds' and 'clk_div' can be found in the table. + * The Quark SPI has an additional 24 bit register (DDS_CLK_RATE) to multiply + * input frequency by fractions of 2^24. It also has a divider by 5. + * + * There are formulas to get baud rate value for given input frequency and + * divider parameters, such as DDS_CLK_RATE and SCR: + * + * Fsys = 200MHz + * + * Fssp = Fsys * DDS_CLK_RATE / 2^24 (1) + * Baud rate = Fsclk = Fssp / (2 * (SCR + 1)) (2) + * + * DDS_CLK_RATE either 2^n or 2^n / 5. + * SCR is in range 0 .. 255 + * + * Divisor = 5^i * 2^j * 2 * k + * i = [0, 1] i = 1 iff j = 0 or j > 3 + * j = [0, 23] j = 0 iff i = 1 + * k = [1, 256] + * Special case: j = 0, i = 1: Divisor = 2 / 5 + * + * Accordingly to the specification the recommended values for DDS_CLK_RATE + * are: + * Case 1: 2^n, n = [0, 23] + * Case 2: 2^24 * 2 / 5 (0x666666) + * Case 3: less than or equal to 2^24 / 5 / 16 (0x33333) + * + * In all cases the lowest possible value is better. + * + * The function calculates parameters for all cases and chooses the one closest + * to the asked baud rate. */ -static u32 quark_x1000_set_clk_regvals(u32 rate, u32 *dds, u32 *clk_div) +static unsigned int quark_x1000_get_clk_div(int rate, u32 *dds) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(quark_spi_rate_table); i++) { - if (rate >= quark_spi_rate_table[i].bitrate) { - *dds = quark_spi_rate_table[i].dds_clk_rate; - *clk_div = quark_spi_rate_table[i].clk_div; - return quark_spi_rate_table[i].bitrate; + unsigned long xtal = 200000000; + unsigned long fref = xtal / 2; /* mandatory division by 2, + see (2) */ + /* case 3 */ + unsigned long fref1 = fref / 2; /* case 1 */ + unsigned long fref2 = fref * 2 / 5; /* case 2 */ + unsigned long scale; + unsigned long q, q1, q2; + long r, r1, r2; + u32 mul; + + /* Case 1 */ + + /* Set initial value for DDS_CLK_RATE */ + mul = (1 << 24) >> 1; + + /* Calculate initial quot */ + q1 = DIV_ROUND_CLOSEST(fref1, rate); + + /* Scale q1 if it's too big */ + if (q1 > 256) { + /* Scale q1 to range [1, 512] */ + scale = fls_long(q1 - 1); + if (scale > 9) { + q1 >>= scale - 9; + mul >>= scale - 9; } + + /* Round the result if we have a remainder */ + q1 += q1 & 1; } - *dds = quark_spi_rate_table[i-1].dds_clk_rate; - *clk_div = quark_spi_rate_table[i-1].clk_div; + /* Decrease DDS_CLK_RATE as much as we can without loss in precision */ + scale = __ffs(q1); + q1 >>= scale; + mul >>= scale; + + /* Get the remainder */ + r1 = abs(fref1 / (1 << (24 - fls_long(mul))) / q1 - rate); + + /* Case 2 */ + + q2 = DIV_ROUND_CLOSEST(fref2, rate); + r2 = abs(fref2 / q2 - rate); - return quark_spi_rate_table[i-1].bitrate; + /* + * Choose the best between two: less remainder we have the better. We + * can't go case 2 if q2 is greater than 256 since SCR register can + * hold only values 0 .. 255. + */ + if (r2 >= r1 || q2 > 256) { + /* case 1 is better */ + r = r1; + q = q1; + } else { + /* case 2 is better */ + r = r2; + q = q2; + mul = (1 << 24) * 2 / 5; + } + + /* Check case 3 only If the divisor is big enough */ + if (fref / rate >= 80) { + u64 fssp; + u32 m; + + /* Calculate initial quot */ + q1 = DIV_ROUND_CLOSEST(fref, rate); + m = (1 << 24) / q1; + + /* Get the remainder */ + fssp = (u64)fref * m; + do_div(fssp, 1 << 24); + r1 = abs(fssp - rate); + + /* Choose this one if it suits better */ + if (r1 < r) { + /* case 3 is better */ + q = 1; + mul = m; + } + } + + *dds = mul; + return q - 1; } static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) @@ -730,23 +778,25 @@ static unsigned int ssp_get_clk_div(struct driver_data *drv_data, int rate) rate = min_t(int, ssp_clk, rate); if (ssp->type == PXA25x_SSP || ssp->type == CE4100_SSP) - return ((ssp_clk / (2 * rate) - 1) & 0xff) << 8; + return (ssp_clk / (2 * rate) - 1) & 0xff; else - return ((ssp_clk / rate - 1) & 0xfff) << 8; + return (ssp_clk / rate - 1) & 0xfff; } static unsigned int pxa2xx_ssp_get_clk_div(struct driver_data *drv_data, struct chip_data *chip, int rate) { - u32 clk_div; + unsigned int clk_div; switch (drv_data->ssp_type) { case QUARK_X1000_SSP: - quark_x1000_set_clk_regvals(rate, &chip->dds_rate, &clk_div); - return clk_div << 8; + clk_div = quark_x1000_get_clk_div(rate, &chip->dds_rate); + break; default: - return ssp_get_clk_div(drv_data, rate); + clk_div = ssp_get_clk_div(drv_data, rate); + break; } + return clk_div << 8; } static void pump_transfers(unsigned long data) |