From patchwork Wed Mar 18 09:04:48 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Albert ARIBAUD (3ADEV)" X-Patchwork-Id: 451275 X-Patchwork-Delegate: albert.aribaud@free.fr Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 1EDEF1400EA for ; Wed, 18 Mar 2015 20:07:14 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 7A2A24B660; Wed, 18 Mar 2015 10:06:59 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id nz9gdV_hTKH8; Wed, 18 Mar 2015 10:06:59 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id D33F84B624; Wed, 18 Mar 2015 10:06:44 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 426934B62C for ; Wed, 18 Mar 2015 10:06:37 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id tIBx06AmZHm9 for ; Wed, 18 Mar 2015 10:06:37 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from smtp2-g21.free.fr (smtp2-g21.free.fr [212.27.42.2]) by theia.denx.de (Postfix) with ESMTPS id 289B64B627 for ; Wed, 18 Mar 2015 10:06:37 +0100 (CET) Received: from localhost.localdomain (unknown [82.235.144.2]) (Authenticated sender: aribaud.smtp) by smtp2-g21.free.fr (Postfix) with ESMTPSA id 77A064B018C; Wed, 18 Mar 2015 10:05:19 +0100 (CET) From: "Albert ARIBAUD (3ADEV)" To: u-boot@lists.denx.de Date: Wed, 18 Mar 2015 10:04:48 +0100 Message-Id: <1426669491-13878-6-git-send-email-albert.aribaud@3adev.fr> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1426669491-13878-5-git-send-email-albert.aribaud@3adev.fr> References: <1426669491-13878-1-git-send-email-albert.aribaud@3adev.fr> <1426669491-13878-2-git-send-email-albert.aribaud@3adev.fr> <1426669491-13878-3-git-send-email-albert.aribaud@3adev.fr> <1426669491-13878-4-git-send-email-albert.aribaud@3adev.fr> <1426669491-13878-5-git-send-email-albert.aribaud@3adev.fr> Cc: "Albert ARIBAUD \(3ADEV\)" Subject: [U-Boot] [PATCH v6 5/8] lpc32xx: add LPC32xx SSP support (SPI mode) X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Signed-off-by: Albert ARIBAUD (3ADEV) Reviewed-by: Jagannadha Sutradharudu Teki --- Changes in v6: None Changes in v5: None Changes in v4: None Changes in v3: - move regs and functions in private struct Changes in v2: - added MUX setting for SSP0 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 a407098..5a453e3 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 781ac07..2cb5703 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 a4a05d1..86d5ee9 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 edbd520..ce6f1cc 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 0000000..0fc44a0 --- /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 + +/* zynq 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 */ +}