Commit 981219eebe3cc29f155a37951788c18786260514

Authored by Albert ARIBAUD \(3ADEV\)
Committed by Albert ARIBAUD
1 parent 606f704760

lpc32xx: add LPC32xx SSP support (SPI mode)

Reviewed-by: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
Signed-off-by: Albert ARIBAUD (3ADEV) <albert.aribaud@3adev.fr>

Showing 5 changed files with 163 additions and 0 deletions Side-by-side Diff

arch/arm/cpu/arm926ejs/lpc32xx/devices.c
... ... @@ -8,11 +8,13 @@
8 8 #include <asm/arch/cpu.h>
9 9 #include <asm/arch/clk.h>
10 10 #include <asm/arch/uart.h>
  11 +#include <asm/arch/mux.h>
11 12 #include <asm/io.h>
12 13 #include <dm.h>
13 14  
14 15 static struct clk_pm_regs *clk = (struct clk_pm_regs *)CLK_PM_BASE;
15 16 static struct uart_ctrl_regs *ctrl = (struct uart_ctrl_regs *)UART_CTRL_BASE;
  17 +static struct mux_regs *mux = (struct mux_regs *)MUX_BASE;
16 18  
17 19 void lpc32xx_uart_init(unsigned int uart_id)
18 20 {
... ... @@ -66,4 +68,16 @@
66 68 U_BOOT_DEVICE(lpc32xx_gpios) = {
67 69 .name = "gpio_lpc32xx"
68 70 };
  71 +
  72 +/* Mux for SCK0, MISO0, MOSI0. We do not use SSEL0. */
  73 +
  74 +#define P_MUX_SET_SSP0 0x1600
  75 +
  76 +void lpc32xx_ssp_init(void)
  77 +{
  78 + /* Enable SSP0 interface */
  79 + writel(CLK_SSP0_ENABLE_CLOCK, &clk->ssp_ctrl);
  80 + /* Mux SSP0 pins */
  81 + writel(P_MUX_SET_SSP0, &mux->p_mux_set);
  82 +}
arch/arm/include/asm/arch-lpc32xx/clk.h
... ... @@ -155,6 +155,9 @@
155 155 #define CLK_NAND_MLC (1 << 1)
156 156 #define CLK_NAND_MLC_INT (1 << 5)
157 157  
  158 +/* SSP Clock Control Register bits */
  159 +#define CLK_SSP0_ENABLE_CLOCK (1 << 0)
  160 +
158 161 unsigned int get_sys_clk_rate(void);
159 162 unsigned int get_hclk_pll_rate(void);
160 163 unsigned int get_hclk_clk_div(void);
arch/arm/include/asm/arch-lpc32xx/sys_proto.h
... ... @@ -11,6 +11,7 @@
11 11 void lpc32xx_mac_init(void);
12 12 void lpc32xx_mlc_nand_init(void);
13 13 void lpc32xx_i2c_init(unsigned int devnum);
  14 +void lpc32xx_ssp_init(void);
14 15  
15 16 #endif /* _LPC32XX_SYS_PROTO_H */
drivers/spi/Makefile
... ... @@ -32,6 +32,7 @@
32 32 obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o
33 33 obj-$(CONFIG_ICH_SPI) += ich.o
34 34 obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
  35 +obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o
35 36 obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
36 37 obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
37 38 obj-$(CONFIG_MXC_SPI) += mxc_spi.o
drivers/spi/lpc32xx_ssp.c
  1 +/*
  2 + * LPC32xx SSP interface (SPI mode)
  3 + *
  4 + * (C) Copyright 2014 DENX Software Engineering GmbH
  5 + * Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
  6 + *
  7 + * SPDX-License-Identifier: GPL-2.0+
  8 + */
  9 +
  10 +#include <common.h>
  11 +#include <linux/compat.h>
  12 +#include <asm/io.h>
  13 +#include <malloc.h>
  14 +#include <spi.h>
  15 +#include <asm/arch/clk.h>
  16 +
  17 +/* SSP chip registers */
  18 +struct ssp_regs {
  19 + u32 cr0;
  20 + u32 cr1;
  21 + u32 data;
  22 + u32 sr;
  23 + u32 cpsr;
  24 + u32 imsc;
  25 + u32 ris;
  26 + u32 mis;
  27 + u32 icr;
  28 + u32 dmacr;
  29 +};
  30 +
  31 +/* CR1 register defines */
  32 +#define SSP_CR1_SSP_ENABLE 0x0002
  33 +
  34 +/* SR register defines */
  35 +#define SSP_SR_TNF 0x0002
  36 +/* SSP status RX FIFO not empty bit */
  37 +#define SSP_SR_RNE 0x0004
  38 +
  39 +/* lpc32xx spi slave */
  40 +struct lpc32xx_spi_slave {
  41 + struct spi_slave slave;
  42 + struct ssp_regs *regs;
  43 +};
  44 +
  45 +static inline struct lpc32xx_spi_slave *to_lpc32xx_spi_slave(
  46 + struct spi_slave *slave)
  47 +{
  48 + return container_of(slave, struct lpc32xx_spi_slave, slave);
  49 +}
  50 +
  51 +/* spi_init is called during boot when CONFIG_CMD_SPI is defined */
  52 +void spi_init(void)
  53 +{
  54 + /*
  55 + * nothing to do: clocking was enabled in lpc32xx_ssp_enable()
  56 + * and configuration will be done in spi_setup_slave()
  57 + */
  58 +}
  59 +
  60 +/* the following is called in sequence by do_spi_xfer() */
  61 +
  62 +struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
  63 +{
  64 + struct lpc32xx_spi_slave *lslave;
  65 +
  66 + /* we only set up SSP0 for now, so ignore bus */
  67 +
  68 + if (mode & SPI_3WIRE) {
  69 + error("3-wire mode not supported");
  70 + return NULL;
  71 + }
  72 +
  73 + if (mode & SPI_SLAVE) {
  74 + error("slave mode not supported\n");
  75 + return NULL;
  76 + }
  77 +
  78 + if (mode & SPI_PREAMBLE) {
  79 + error("preamble byte skipping not supported\n");
  80 + return NULL;
  81 + }
  82 +
  83 + lslave = spi_alloc_slave(struct lpc32xx_spi_slave, bus, cs);
  84 + if (!lslave) {
  85 + printf("SPI_error: Fail to allocate lpc32xx_spi_slave\n");
  86 + return NULL;
  87 + }
  88 +
  89 + lslave->regs = (struct ssp_regs *)SSP0_BASE;
  90 +
  91 + /*
  92 + * 8 bit frame, SPI fmt, 500kbps -> clock divider is 26.
  93 + * Set SCR to 0 and CPSDVSR to 26.
  94 + */
  95 +
  96 + writel(0x7, &lslave->regs->cr0); /* 8-bit chunks, SPI, 1 clk/bit */
  97 + writel(26, &lslave->regs->cpsr); /* SSP clock = HCLK/26 = 500kbps */
  98 + writel(0, &lslave->regs->imsc); /* do not raise any interrupts */
  99 + writel(0, &lslave->regs->icr); /* clear any pending interrupt */
  100 + writel(0, &lslave->regs->dmacr); /* do not do DMAs */
  101 + writel(SSP_CR1_SSP_ENABLE, &lslave->regs->cr1); /* enable SSP0 */
  102 + return &lslave->slave;
  103 +}
  104 +
  105 +void spi_free_slave(struct spi_slave *slave)
  106 +{
  107 + struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
  108 +
  109 + debug("(lpc32xx) spi_free_slave: 0x%08x\n", (u32)lslave);
  110 + free(lslave);
  111 +}
  112 +
  113 +int spi_claim_bus(struct spi_slave *slave)
  114 +{
  115 + /* only one bus and slave so far, always available */
  116 + return 0;
  117 +}
  118 +
  119 +int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
  120 + const void *dout, void *din, unsigned long flags)
  121 +{
  122 + struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
  123 + int bytelen = bitlen >> 3;
  124 + int idx_out = 0;
  125 + int idx_in = 0;
  126 + int start_time;
  127 +
  128 + start_time = get_timer(0);
  129 + while ((idx_out < bytelen) || (idx_in < bytelen)) {
  130 + int status = readl(&lslave->regs->sr);
  131 + if ((idx_out < bytelen) && (status & SSP_SR_TNF))
  132 + writel(((u8 *)dout)[idx_out++], &lslave->regs->data);
  133 + if ((idx_in < bytelen) && (status & status & SSP_SR_RNE))
  134 + ((u8 *)din)[idx_in++] = readl(&lslave->regs->data);
  135 + if (get_timer(start_time) >= CONFIG_LPC32XX_SSP_TIMEOUT)
  136 + return -1;
  137 + }
  138 + return 0;
  139 +}
  140 +
  141 +void spi_release_bus(struct spi_slave *slave)
  142 +{
  143 + /* do nothing */
  144 +}