Commit 981219eebe3cc29f155a37951788c18786260514
Committed by
Albert ARIBAUD
1 parent
606f704760
Exists in
v2017.01-smarct4x
and in
37 other branches
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
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 | +} |