From 981219eebe3cc29f155a37951788c18786260514 Mon Sep 17 00:00:00 2001 From: "Albert ARIBAUD \\(3ADEV\\)" Date: Tue, 31 Mar 2015 11:40:47 +0200 Subject: [PATCH] lpc32xx: add LPC32xx SSP support (SPI mode) Reviewed-by: Jagannadha Sutradharudu Teki Signed-off-by: Albert ARIBAUD (3ADEV) --- arch/arm/cpu/arm926ejs/lpc32xx/devices.c | 14 ++ arch/arm/include/asm/arch-lpc32xx/clk.h | 3 + arch/arm/include/asm/arch-lpc32xx/sys_proto.h | 1 + drivers/spi/Makefile | 1 + drivers/spi/lpc32xx_ssp.c | 144 ++++++++++++++++++ 5 files changed, 163 insertions(+) create mode 100644 drivers/spi/lpc32xx_ssp.c diff --git a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c index a407098eda..5a453e303d 100644 --- a/arch/arm/cpu/arm926ejs/lpc32xx/devices.c +++ b/arch/arm/cpu/arm926ejs/lpc32xx/devices.c @@ -8,11 +8,13 @@ #include #include #include +#include #include #include static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE; static struct uart_ctrl_regs *ctrl = (struct uart_ctrl_regs *)UART_CTRL_BASE; +static struct mux_regs *mux = (struct mux_regs *)MUX_BASE; void lpc32xx_uart_init(unsigned int uart_id) { @@ -66,3 +68,15 @@ void lpc32xx_i2c_init(unsigned int devnum) U_BOOT_DEVICE(lpc32xx_gpios) = { .name = "gpio_lpc32xx" }; + +/* Mux for SCK0, MISO0, MOSI0. We do not use SSEL0. */ + +#define P_MUX_SET_SSP0 0x1600 + +void lpc32xx_ssp_init(void) +{ + /* Enable SSP0 interface */ + writel(CLK_SSP0_ENABLE_CLOCK, &clk->ssp_ctrl); + /* Mux SSP0 pins */ + writel(P_MUX_SET_SSP0, &mux->p_mux_set); +} diff --git a/arch/arm/include/asm/arch-lpc32xx/clk.h b/arch/arm/include/asm/arch-lpc32xx/clk.h index 781ac07103..2cb5703877 100644 --- a/arch/arm/include/asm/arch-lpc32xx/clk.h +++ b/arch/arm/include/asm/arch-lpc32xx/clk.h @@ -155,6 +155,9 @@ struct clk_pm_regs { #define CLK_NAND_MLC (1 << 1) #define CLK_NAND_MLC_INT (1 << 5) +/* SSP Clock Control Register bits */ +#define CLK_SSP0_ENABLE_CLOCK (1 << 0) + unsigned int get_sys_clk_rate(void); unsigned int get_hclk_pll_rate(void); unsigned int get_hclk_clk_div(void); diff --git a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h index a4a05d1ea4..86d5ee9b05 100644 --- a/arch/arm/include/asm/arch-lpc32xx/sys_proto.h +++ b/arch/arm/include/asm/arch-lpc32xx/sys_proto.h @@ -11,5 +11,6 @@ void lpc32xx_uart_init(unsigned int uart_id); void lpc32xx_mac_init(void); void lpc32xx_mlc_nand_init(void); void lpc32xx_i2c_init(unsigned int devnum); +void lpc32xx_ssp_init(void); #endif /* _LPC32XX_SYS_PROTO_H */ diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index edbd520141..ce6f1cc74e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o obj-$(CONFIG_ICH_SPI) += ich.o obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o +obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o obj-$(CONFIG_MXC_SPI) += mxc_spi.o diff --git a/drivers/spi/lpc32xx_ssp.c b/drivers/spi/lpc32xx_ssp.c new file mode 100644 index 0000000000..c5b766c0dd --- /dev/null +++ b/drivers/spi/lpc32xx_ssp.c @@ -0,0 +1,144 @@ +/* + * LPC32xx SSP interface (SPI mode) + * + * (C) Copyright 2014 DENX Software Engineering GmbH + * Written-by: Albert ARIBAUD + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* SSP chip registers */ +struct ssp_regs { + u32 cr0; + u32 cr1; + u32 data; + u32 sr; + u32 cpsr; + u32 imsc; + u32 ris; + u32 mis; + u32 icr; + u32 dmacr; +}; + +/* CR1 register defines */ +#define SSP_CR1_SSP_ENABLE 0x0002 + +/* SR register defines */ +#define SSP_SR_TNF 0x0002 +/* SSP status RX FIFO not empty bit */ +#define SSP_SR_RNE 0x0004 + +/* lpc32xx spi slave */ +struct lpc32xx_spi_slave { + struct spi_slave slave; + struct ssp_regs *regs; +}; + +static inline struct lpc32xx_spi_slave *to_lpc32xx_spi_slave( + struct spi_slave *slave) +{ + return container_of(slave, struct lpc32xx_spi_slave, slave); +} + +/* spi_init is called during boot when CONFIG_CMD_SPI is defined */ +void spi_init(void) +{ + /* + * nothing to do: clocking was enabled in lpc32xx_ssp_enable() + * and configuration will be done in spi_setup_slave() + */ +} + +/* the following is called in sequence by do_spi_xfer() */ + +struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode) +{ + struct lpc32xx_spi_slave *lslave; + + /* we only set up SSP0 for now, so ignore bus */ + + if (mode & SPI_3WIRE) { + error("3-wire mode not supported"); + return NULL; + } + + if (mode & SPI_SLAVE) { + error("slave mode not supported\n"); + return NULL; + } + + if (mode & SPI_PREAMBLE) { + error("preamble byte skipping not supported\n"); + return NULL; + } + + lslave = spi_alloc_slave(struct lpc32xx_spi_slave, bus, cs); + if (!lslave) { + printf("SPI_error: Fail to allocate lpc32xx_spi_slave\n"); + return NULL; + } + + lslave->regs = (struct ssp_regs *)SSP0_BASE; + + /* + * 8 bit frame, SPI fmt, 500kbps -> clock divider is 26. + * Set SCR to 0 and CPSDVSR to 26. + */ + + writel(0x7, &lslave->regs->cr0); /* 8-bit chunks, SPI, 1 clk/bit */ + writel(26, &lslave->regs->cpsr); /* SSP clock = HCLK/26 = 500kbps */ + writel(0, &lslave->regs->imsc); /* do not raise any interrupts */ + writel(0, &lslave->regs->icr); /* clear any pending interrupt */ + writel(0, &lslave->regs->dmacr); /* do not do DMAs */ + writel(SSP_CR1_SSP_ENABLE, &lslave->regs->cr1); /* enable SSP0 */ + return &lslave->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave); + + debug("(lpc32xx) spi_free_slave: 0x%08x\n", (u32)lslave); + free(lslave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + /* only one bus and slave so far, always available */ + return 0; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave); + int bytelen = bitlen >> 3; + int idx_out = 0; + int idx_in = 0; + int start_time; + + start_time = get_timer(0); + while ((idx_out < bytelen) || (idx_in < bytelen)) { + int status = readl(&lslave->regs->sr); + if ((idx_out < bytelen) && (status & SSP_SR_TNF)) + writel(((u8 *)dout)[idx_out++], &lslave->regs->data); + if ((idx_in < bytelen) && (status & status & SSP_SR_RNE)) + ((u8 *)din)[idx_in++] = readl(&lslave->regs->data); + if (get_timer(start_time) >= CONFIG_LPC32XX_SSP_TIMEOUT) + return -1; + } + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* do nothing */ +} -- 2.25.1