spi/imx: add support for imx51's eCSPI and CSPI
i.MX51 comes with two eCSPI interfaces (that are quite different from what was known before---the tried and tested Freescale way) and a CSPI interface that is identical to the devices found on i.MX25 and i.MX35. This patch is a merge of two very similar patches (by Jason Wang and Sascha Hauer resp.) plus a (now hopefully correct) reimplementation of the clock calculation. Acked-by: Jason Wang <jason77.wang@gmail.com> Acked-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
committed by
Sascha Hauer
parent
3b2aa89eb3
commit
0b599603d8
@@ -153,7 +153,10 @@ config SPI_IMX_VER_0_4
|
|||||||
def_bool y if ARCH_MX31
|
def_bool y if ARCH_MX31
|
||||||
|
|
||||||
config SPI_IMX_VER_0_7
|
config SPI_IMX_VER_0_7
|
||||||
def_bool y if ARCH_MX25 || ARCH_MX35
|
def_bool y if ARCH_MX25 || ARCH_MX35 || ARCH_MX51
|
||||||
|
|
||||||
|
config SPI_IMX_VER_2_3
|
||||||
|
def_bool y if ARCH_MX51
|
||||||
|
|
||||||
config SPI_IMX
|
config SPI_IMX
|
||||||
tristate "Freescale i.MX SPI controllers"
|
tristate "Freescale i.MX SPI controllers"
|
||||||
|
@@ -65,6 +65,7 @@ enum spi_imx_devtype {
|
|||||||
SPI_IMX_VER_0_4,
|
SPI_IMX_VER_0_4,
|
||||||
SPI_IMX_VER_0_5,
|
SPI_IMX_VER_0_5,
|
||||||
SPI_IMX_VER_0_7,
|
SPI_IMX_VER_0_7,
|
||||||
|
SPI_IMX_VER_2_3,
|
||||||
SPI_IMX_VER_AUTODETECT,
|
SPI_IMX_VER_AUTODETECT,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -155,7 +156,7 @@ static unsigned int spi_imx_clkdiv_1(unsigned int fin,
|
|||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MX1, MX31, MX35 */
|
/* MX1, MX31, MX35, MX51 CSPI */
|
||||||
static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
||||||
unsigned int fspi)
|
unsigned int fspi)
|
||||||
{
|
{
|
||||||
@@ -170,6 +171,128 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,
|
|||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SPI_IMX2_3_CTRL 0x08
|
||||||
|
#define SPI_IMX2_3_CTRL_ENABLE (1 << 0)
|
||||||
|
#define SPI_IMX2_3_CTRL_XCH (1 << 2)
|
||||||
|
#define SPI_IMX2_3_CTRL_MODE(cs) (1 << ((cs) + 4))
|
||||||
|
#define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8
|
||||||
|
#define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12
|
||||||
|
#define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18)
|
||||||
|
#define SPI_IMX2_3_CTRL_BL_OFFSET 20
|
||||||
|
|
||||||
|
#define SPI_IMX2_3_CONFIG 0x0c
|
||||||
|
#define SPI_IMX2_3_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0))
|
||||||
|
#define SPI_IMX2_3_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4))
|
||||||
|
#define SPI_IMX2_3_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8))
|
||||||
|
#define SPI_IMX2_3_CONFIG_SSBPOL(cs) (1 << ((cs) + 12))
|
||||||
|
|
||||||
|
#define SPI_IMX2_3_INT 0x10
|
||||||
|
#define SPI_IMX2_3_INT_TEEN (1 << 0)
|
||||||
|
#define SPI_IMX2_3_INT_RREN (1 << 3)
|
||||||
|
|
||||||
|
#define SPI_IMX2_3_STAT 0x18
|
||||||
|
#define SPI_IMX2_3_STAT_RR (1 << 3)
|
||||||
|
|
||||||
|
/* MX51 eCSPI */
|
||||||
|
static unsigned int spi_imx2_3_clkdiv(unsigned int fin, unsigned int fspi)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* there are two 4-bit dividers, the pre-divider divides by
|
||||||
|
* $pre, the post-divider by 2^$post
|
||||||
|
*/
|
||||||
|
unsigned int pre, post;
|
||||||
|
|
||||||
|
if (unlikely(fspi > fin))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
post = fls(fin) - fls(fspi);
|
||||||
|
if (fin > fspi << post)
|
||||||
|
post++;
|
||||||
|
|
||||||
|
/* now we have: (fin <= fspi << post) with post being minimal */
|
||||||
|
|
||||||
|
post = max(4U, post) - 4;
|
||||||
|
if (unlikely(post > 0xf)) {
|
||||||
|
pr_err("%s: cannot set clock freq: %u (base freq: %u)\n",
|
||||||
|
__func__, fspi, fin);
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre = DIV_ROUND_UP(fin, fspi << post) - 1;
|
||||||
|
|
||||||
|
pr_debug("%s: fin: %u, fspi: %u, post: %u, pre: %u\n",
|
||||||
|
__func__, fin, fspi, post, pre);
|
||||||
|
return (pre << SPI_IMX2_3_CTRL_PREDIV_OFFSET) |
|
||||||
|
(post << SPI_IMX2_3_CTRL_POSTDIV_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __maybe_unused spi_imx2_3_intctrl(struct spi_imx_data *spi_imx, int enable)
|
||||||
|
{
|
||||||
|
unsigned val = 0;
|
||||||
|
|
||||||
|
if (enable & MXC_INT_TE)
|
||||||
|
val |= SPI_IMX2_3_INT_TEEN;
|
||||||
|
|
||||||
|
if (enable & MXC_INT_RR)
|
||||||
|
val |= SPI_IMX2_3_INT_RREN;
|
||||||
|
|
||||||
|
writel(val, spi_imx->base + SPI_IMX2_3_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __maybe_unused spi_imx2_3_trigger(struct spi_imx_data *spi_imx)
|
||||||
|
{
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = readl(spi_imx->base + SPI_IMX2_3_CTRL);
|
||||||
|
reg |= SPI_IMX2_3_CTRL_XCH;
|
||||||
|
writel(reg, spi_imx->base + SPI_IMX2_3_CTRL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx,
|
||||||
|
struct spi_imx_config *config)
|
||||||
|
{
|
||||||
|
u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0;
|
||||||
|
|
||||||
|
/* set master mode */
|
||||||
|
ctrl |= SPI_IMX2_3_CTRL_MODE(config->cs);
|
||||||
|
|
||||||
|
/* set clock speed */
|
||||||
|
ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz);
|
||||||
|
|
||||||
|
/* set chip select to use */
|
||||||
|
ctrl |= SPI_IMX2_3_CTRL_CS(config->cs);
|
||||||
|
|
||||||
|
ctrl |= (config->bpw - 1) << SPI_IMX2_3_CTRL_BL_OFFSET;
|
||||||
|
|
||||||
|
cfg |= SPI_IMX2_3_CONFIG_SBBCTRL(config->cs);
|
||||||
|
|
||||||
|
if (config->mode & SPI_CPHA)
|
||||||
|
cfg |= SPI_IMX2_3_CONFIG_SCLKPHA(config->cs);
|
||||||
|
|
||||||
|
if (config->mode & SPI_CPOL)
|
||||||
|
cfg |= SPI_IMX2_3_CONFIG_SCLKPOL(config->cs);
|
||||||
|
|
||||||
|
if (config->mode & SPI_CS_HIGH)
|
||||||
|
cfg |= SPI_IMX2_3_CONFIG_SSBPOL(config->cs);
|
||||||
|
|
||||||
|
writel(ctrl, spi_imx->base + SPI_IMX2_3_CTRL);
|
||||||
|
writel(cfg, spi_imx->base + SPI_IMX2_3_CONFIG);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused spi_imx2_3_rx_available(struct spi_imx_data *spi_imx)
|
||||||
|
{
|
||||||
|
return readl(spi_imx->base + SPI_IMX2_3_STAT) & SPI_IMX2_3_STAT_RR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __maybe_unused spi_imx2_3_reset(struct spi_imx_data *spi_imx)
|
||||||
|
{
|
||||||
|
/* drain receive buffer */
|
||||||
|
while (spi_imx2_3_rx_available(spi_imx))
|
||||||
|
readl(spi_imx->base + MXC_CSPIRXDATA);
|
||||||
|
}
|
||||||
|
|
||||||
#define MX31_INTREG_TEEN (1 << 0)
|
#define MX31_INTREG_TEEN (1 << 0)
|
||||||
#define MX31_INTREG_RREN (1 << 3)
|
#define MX31_INTREG_RREN (1 << 3)
|
||||||
|
|
||||||
@@ -447,6 +570,15 @@ static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = {
|
|||||||
.reset = spi_imx0_4_reset,
|
.reset = spi_imx0_4_reset,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_SPI_IMX_VER_2_3
|
||||||
|
[SPI_IMX_VER_2_3] = {
|
||||||
|
.intctrl = spi_imx2_3_intctrl,
|
||||||
|
.config = spi_imx2_3_config,
|
||||||
|
.trigger = spi_imx2_3_trigger,
|
||||||
|
.rx_available = spi_imx2_3_rx_available,
|
||||||
|
.reset = spi_imx2_3_reset,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static void spi_imx_chipselect(struct spi_device *spi, int is_active)
|
static void spi_imx_chipselect(struct spi_device *spi, int is_active)
|
||||||
@@ -602,6 +734,12 @@ static struct platform_device_id spi_imx_devtype[] = {
|
|||||||
}, {
|
}, {
|
||||||
.name = "imx35-cspi",
|
.name = "imx35-cspi",
|
||||||
.driver_data = SPI_IMX_VER_0_7,
|
.driver_data = SPI_IMX_VER_0_7,
|
||||||
|
}, {
|
||||||
|
.name = "imx51-cspi",
|
||||||
|
.driver_data = SPI_IMX_VER_0_7,
|
||||||
|
}, {
|
||||||
|
.name = "imx51-ecspi",
|
||||||
|
.driver_data = SPI_IMX_VER_2_3,
|
||||||
}, {
|
}, {
|
||||||
/* sentinel */
|
/* sentinel */
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user