diff options
author | Michal Simek | 2013-04-22 08:21:33 -0500 |
---|---|---|
committer | Michal Simek | 2013-04-30 04:39:28 -0500 |
commit | 8934f7846501070a5b01c1fab5db27559e9d70d1 (patch) | |
tree | 56012200ad94320370944669f7ab2427902366f3 | |
parent | 293eb33fcb95e2b2bae71edf97b0ca3bdd6ba98d (diff) | |
download | u-boot-8934f7846501070a5b01c1fab5db27559e9d70d1.tar.gz u-boot-8934f7846501070a5b01c1fab5db27559e9d70d1.tar.xz u-boot-8934f7846501070a5b01c1fab5db27559e9d70d1.zip |
i2c: zynq: Add support for Xilinx Zynq
Support Xilinx Zynq i2c controller.
Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Acked-by: Heiko Schocher <hs@denx.de>
Reviewed-by: Tom Rini <trini@ti.com>
-rw-r--r-- | arch/arm/include/asm/arch-zynq/hardware.h | 2 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/zynq_i2c.c | 306 | ||||
-rw-r--r-- | include/configs/zynq.h | 11 |
4 files changed, 320 insertions, 0 deletions
diff --git a/arch/arm/include/asm/arch-zynq/hardware.h b/arch/arm/include/asm/arch-zynq/hardware.h index 8eb4e1a7a9..6af892a789 100644 --- a/arch/arm/include/asm/arch-zynq/hardware.h +++ b/arch/arm/include/asm/arch-zynq/hardware.h | |||
@@ -31,6 +31,8 @@ | |||
31 | #define ZYNQ_GEM_BASEADDR1 0xE000C000 | 31 | #define ZYNQ_GEM_BASEADDR1 0xE000C000 |
32 | #define ZYNQ_SDHCI_BASEADDR0 0xE0100000 | 32 | #define ZYNQ_SDHCI_BASEADDR0 0xE0100000 |
33 | #define ZYNQ_SDHCI_BASEADDR1 0xE0101000 | 33 | #define ZYNQ_SDHCI_BASEADDR1 0xE0101000 |
34 | #define ZYNQ_I2C_BASEADDR0 0xE0004000 | ||
35 | #define ZYNQ_I2C_BASEADDR1 0xE0005000 | ||
34 | 36 | ||
35 | /* Reflect slcr offsets */ | 37 | /* Reflect slcr offsets */ |
36 | struct slcr_regs { | 38 | struct slcr_regs { |
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 5dbdbe3672..72e85a349a 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile | |||
@@ -46,6 +46,7 @@ COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o | |||
46 | COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o | 46 | COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o |
47 | COBJS-$(CONFIG_SH_I2C) += sh_i2c.o | 47 | COBJS-$(CONFIG_SH_I2C) += sh_i2c.o |
48 | COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o | 48 | COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o |
49 | COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o | ||
49 | 50 | ||
50 | COBJS := $(COBJS-y) | 51 | COBJS := $(COBJS-y) |
51 | SRCS := $(COBJS:.o=.c) | 52 | SRCS := $(COBJS:.o=.c) |
diff --git a/drivers/i2c/zynq_i2c.c b/drivers/i2c/zynq_i2c.c new file mode 100644 index 0000000000..ec49660cf7 --- /dev/null +++ b/drivers/i2c/zynq_i2c.c | |||
@@ -0,0 +1,306 @@ | |||
1 | /* | ||
2 | * Driver for the Zynq-7000 PS I2C controller | ||
3 | * IP from Cadence (ID T-CS-PE-0007-100, Version R1p10f2) | ||
4 | * | ||
5 | * Author: Joe Hershberger <joe.hershberger@ni.com> | ||
6 | * Copyright (c) 2012 Joe Hershberger. | ||
7 | * | ||
8 | * Copyright (c) 2012-2013 Xilinx, Michal Simek | ||
9 | * | ||
10 | * See file CREDITS for list of people who contributed to this | ||
11 | * project. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License as | ||
15 | * published by the Free Software Foundation; either version 2 of | ||
16 | * the License, or (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, write to the Free Software | ||
25 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | ||
26 | * MA 02110-1301 USA | ||
27 | */ | ||
28 | |||
29 | #include <common.h> | ||
30 | #include <asm/io.h> | ||
31 | #include <i2c.h> | ||
32 | #include <asm/errno.h> | ||
33 | #include <asm/arch/hardware.h> | ||
34 | |||
35 | /* i2c register set */ | ||
36 | struct zynq_i2c_registers { | ||
37 | u32 control; | ||
38 | u32 status; | ||
39 | u32 address; | ||
40 | u32 data; | ||
41 | u32 interrupt_status; | ||
42 | u32 transfer_size; | ||
43 | u32 slave_mon_pause; | ||
44 | u32 time_out; | ||
45 | u32 interrupt_mask; | ||
46 | u32 interrupt_enable; | ||
47 | u32 interrupt_disable; | ||
48 | }; | ||
49 | |||
50 | /* Control register fields */ | ||
51 | #define ZYNQ_I2C_CONTROL_RW 0x00000001 | ||
52 | #define ZYNQ_I2C_CONTROL_MS 0x00000002 | ||
53 | #define ZYNQ_I2C_CONTROL_NEA 0x00000004 | ||
54 | #define ZYNQ_I2C_CONTROL_ACKEN 0x00000008 | ||
55 | #define ZYNQ_I2C_CONTROL_HOLD 0x00000010 | ||
56 | #define ZYNQ_I2C_CONTROL_SLVMON 0x00000020 | ||
57 | #define ZYNQ_I2C_CONTROL_CLR_FIFO 0x00000040 | ||
58 | #define ZYNQ_I2C_CONTROL_DIV_B_SHIFT 8 | ||
59 | #define ZYNQ_I2C_CONTROL_DIV_B_MASK 0x00003F00 | ||
60 | #define ZYNQ_I2C_CONTROL_DIV_A_SHIFT 14 | ||
61 | #define ZYNQ_I2C_CONTROL_DIV_A_MASK 0x0000C000 | ||
62 | |||
63 | /* Status register values */ | ||
64 | #define ZYNQ_I2C_STATUS_RXDV 0x00000020 | ||
65 | #define ZYNQ_I2C_STATUS_TXDV 0x00000040 | ||
66 | #define ZYNQ_I2C_STATUS_RXOVF 0x00000080 | ||
67 | #define ZYNQ_I2C_STATUS_BA 0x00000100 | ||
68 | |||
69 | /* Interrupt register fields */ | ||
70 | #define ZYNQ_I2C_INTERRUPT_COMP 0x00000001 | ||
71 | #define ZYNQ_I2C_INTERRUPT_DATA 0x00000002 | ||
72 | #define ZYNQ_I2C_INTERRUPT_NACK 0x00000004 | ||
73 | #define ZYNQ_I2C_INTERRUPT_TO 0x00000008 | ||
74 | #define ZYNQ_I2C_INTERRUPT_SLVRDY 0x00000010 | ||
75 | #define ZYNQ_I2C_INTERRUPT_RXOVF 0x00000020 | ||
76 | #define ZYNQ_I2C_INTERRUPT_TXOVF 0x00000040 | ||
77 | #define ZYNQ_I2C_INTERRUPT_RXUNF 0x00000080 | ||
78 | #define ZYNQ_I2C_INTERRUPT_ARBLOST 0x00000200 | ||
79 | |||
80 | #define ZYNQ_I2C_FIFO_DEPTH 16 | ||
81 | #define ZYNQ_I2C_TRANSFERT_SIZE_MAX 255 /* Controller transfer limit */ | ||
82 | |||
83 | #if defined(CONFIG_ZYNQ_I2C0) | ||
84 | # define ZYNQ_I2C_BASE ZYNQ_I2C_BASEADDR0 | ||
85 | #else | ||
86 | # define ZYNQ_I2C_BASE ZYNQ_I2C_BASEADDR1 | ||
87 | #endif | ||
88 | |||
89 | static struct zynq_i2c_registers *zynq_i2c = | ||
90 | (struct zynq_i2c_registers *)ZYNQ_I2C_BASE; | ||
91 | |||
92 | /* I2C init called by cmd_i2c when doing 'i2c reset'. */ | ||
93 | void i2c_init(int requested_speed, int slaveadd) | ||
94 | { | ||
95 | /* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */ | ||
96 | writel((16 << ZYNQ_I2C_CONTROL_DIV_B_SHIFT) | | ||
97 | (2 << ZYNQ_I2C_CONTROL_DIV_A_SHIFT), &zynq_i2c->control); | ||
98 | |||
99 | /* Enable master mode, ack, and 7-bit addressing */ | ||
100 | setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_MS | | ||
101 | ZYNQ_I2C_CONTROL_ACKEN | ZYNQ_I2C_CONTROL_NEA); | ||
102 | } | ||
103 | |||
104 | #ifdef DEBUG | ||
105 | static void zynq_i2c_debug_status(void) | ||
106 | { | ||
107 | int int_status; | ||
108 | int status; | ||
109 | int_status = readl(&zynq_i2c->interrupt_status); | ||
110 | |||
111 | status = readl(&zynq_i2c->status); | ||
112 | if (int_status || status) { | ||
113 | debug("Status: "); | ||
114 | if (int_status & ZYNQ_I2C_INTERRUPT_COMP) | ||
115 | debug("COMP "); | ||
116 | if (int_status & ZYNQ_I2C_INTERRUPT_DATA) | ||
117 | debug("DATA "); | ||
118 | if (int_status & ZYNQ_I2C_INTERRUPT_NACK) | ||
119 | debug("NACK "); | ||
120 | if (int_status & ZYNQ_I2C_INTERRUPT_TO) | ||
121 | debug("TO "); | ||
122 | if (int_status & ZYNQ_I2C_INTERRUPT_SLVRDY) | ||
123 | debug("SLVRDY "); | ||
124 | if (int_status & ZYNQ_I2C_INTERRUPT_RXOVF) | ||
125 | debug("RXOVF "); | ||
126 | if (int_status & ZYNQ_I2C_INTERRUPT_TXOVF) | ||
127 | debug("TXOVF "); | ||
128 | if (int_status & ZYNQ_I2C_INTERRUPT_RXUNF) | ||
129 | debug("RXUNF "); | ||
130 | if (int_status & ZYNQ_I2C_INTERRUPT_ARBLOST) | ||
131 | debug("ARBLOST "); | ||
132 | if (status & ZYNQ_I2C_STATUS_RXDV) | ||
133 | debug("RXDV "); | ||
134 | if (status & ZYNQ_I2C_STATUS_TXDV) | ||
135 | debug("TXDV "); | ||
136 | if (status & ZYNQ_I2C_STATUS_RXOVF) | ||
137 | debug("RXOVF "); | ||
138 | if (status & ZYNQ_I2C_STATUS_BA) | ||
139 | debug("BA "); | ||
140 | debug("TS%d ", readl(&zynq_i2c->transfer_size)); | ||
141 | debug("\n"); | ||
142 | } | ||
143 | } | ||
144 | #endif | ||
145 | |||
146 | /* Wait for an interrupt */ | ||
147 | static u32 zynq_i2c_wait(u32 mask) | ||
148 | { | ||
149 | int timeout, int_status; | ||
150 | |||
151 | for (timeout = 0; timeout < 100; timeout++) { | ||
152 | udelay(100); | ||
153 | int_status = readl(&zynq_i2c->interrupt_status); | ||
154 | if (int_status & mask) | ||
155 | break; | ||
156 | } | ||
157 | #ifdef DEBUG | ||
158 | zynq_i2c_debug_status(); | ||
159 | #endif | ||
160 | /* Clear interrupt status flags */ | ||
161 | writel(int_status & mask, &zynq_i2c->interrupt_status); | ||
162 | |||
163 | return int_status & mask; | ||
164 | } | ||
165 | |||
166 | /* | ||
167 | * I2C probe called by cmd_i2c when doing 'i2c probe'. | ||
168 | * Begin read, nak data byte, end. | ||
169 | */ | ||
170 | int i2c_probe(u8 dev) | ||
171 | { | ||
172 | /* Attempt to read a byte */ | ||
173 | setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | | ||
174 | ZYNQ_I2C_CONTROL_RW); | ||
175 | clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); | ||
176 | writel(0xFF, &zynq_i2c->interrupt_status); | ||
177 | writel(dev, &zynq_i2c->address); | ||
178 | writel(1, &zynq_i2c->transfer_size); | ||
179 | |||
180 | return (zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP | | ||
181 | ZYNQ_I2C_INTERRUPT_NACK) & | ||
182 | ZYNQ_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c | ||
187 | * Begin write, send address byte(s), begin read, receive data bytes, end. | ||
188 | */ | ||
189 | int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) | ||
190 | { | ||
191 | u32 status; | ||
192 | u32 i = 0; | ||
193 | u8 *cur_data = data; | ||
194 | |||
195 | /* Check the hardware can handle the requested bytes */ | ||
196 | if ((length < 0) || (length > ZYNQ_I2C_TRANSFERT_SIZE_MAX)) | ||
197 | return -EINVAL; | ||
198 | |||
199 | /* Write the register address */ | ||
200 | setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | | ||
201 | ZYNQ_I2C_CONTROL_HOLD); | ||
202 | /* | ||
203 | * Temporarily disable restart (by clearing hold) | ||
204 | * It doesn't seem to work. | ||
205 | */ | ||
206 | clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW | | ||
207 | ZYNQ_I2C_CONTROL_HOLD); | ||
208 | writel(0xFF, &zynq_i2c->interrupt_status); | ||
209 | while (alen--) | ||
210 | writel(addr >> (8*alen), &zynq_i2c->data); | ||
211 | writel(dev, &zynq_i2c->address); | ||
212 | |||
213 | /* Wait for the address to be sent */ | ||
214 | if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) { | ||
215 | /* Release the bus */ | ||
216 | clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); | ||
217 | return -ETIMEDOUT; | ||
218 | } | ||
219 | debug("Device acked address\n"); | ||
220 | |||
221 | setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | | ||
222 | ZYNQ_I2C_CONTROL_RW); | ||
223 | /* Start reading data */ | ||
224 | writel(dev, &zynq_i2c->address); | ||
225 | writel(length, &zynq_i2c->transfer_size); | ||
226 | |||
227 | /* Wait for data */ | ||
228 | do { | ||
229 | status = zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP | | ||
230 | ZYNQ_I2C_INTERRUPT_DATA); | ||
231 | if (!status) { | ||
232 | /* Release the bus */ | ||
233 | clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); | ||
234 | return -ETIMEDOUT; | ||
235 | } | ||
236 | debug("Read %d bytes\n", | ||
237 | length - readl(&zynq_i2c->transfer_size)); | ||
238 | for (; i < length - readl(&zynq_i2c->transfer_size); i++) | ||
239 | *(cur_data++) = readl(&zynq_i2c->data); | ||
240 | } while (readl(&zynq_i2c->transfer_size) != 0); | ||
241 | /* All done... release the bus */ | ||
242 | clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); | ||
243 | |||
244 | #ifdef DEBUG | ||
245 | zynq_i2c_debug_status(); | ||
246 | #endif | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c | ||
252 | * Begin write, send address byte(s), send data bytes, end. | ||
253 | */ | ||
254 | int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) | ||
255 | { | ||
256 | u8 *cur_data = data; | ||
257 | |||
258 | /* Write the register address */ | ||
259 | setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | | ||
260 | ZYNQ_I2C_CONTROL_HOLD); | ||
261 | clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW); | ||
262 | writel(0xFF, &zynq_i2c->interrupt_status); | ||
263 | while (alen--) | ||
264 | writel(addr >> (8*alen), &zynq_i2c->data); | ||
265 | /* Start the tranfer */ | ||
266 | writel(dev, &zynq_i2c->address); | ||
267 | if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) { | ||
268 | /* Release the bus */ | ||
269 | clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); | ||
270 | return -ETIMEDOUT; | ||
271 | } | ||
272 | |||
273 | debug("Device acked address\n"); | ||
274 | while (length--) { | ||
275 | writel(*(cur_data++), &zynq_i2c->data); | ||
276 | if (readl(&zynq_i2c->transfer_size) == ZYNQ_I2C_FIFO_DEPTH) { | ||
277 | if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) { | ||
278 | /* Release the bus */ | ||
279 | clrbits_le32(&zynq_i2c->control, | ||
280 | ZYNQ_I2C_CONTROL_HOLD); | ||
281 | return -ETIMEDOUT; | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | /* All done... release the bus */ | ||
287 | clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); | ||
288 | /* Wait for the address and data to be sent */ | ||
289 | if (!zynq_i2c_wait(ZYNQ_I2C_INTERRUPT_COMP)) | ||
290 | return -ETIMEDOUT; | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | int i2c_set_bus_num(unsigned int bus) | ||
295 | { | ||
296 | /* Only support bus 0 */ | ||
297 | if (bus > 0) | ||
298 | return -1; | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | unsigned int i2c_get_bus_num(void) | ||
303 | { | ||
304 | /* Only support bus 0 */ | ||
305 | return 0; | ||
306 | } | ||
diff --git a/include/configs/zynq.h b/include/configs/zynq.h index 2ed88a72c4..f1f182edfb 100644 --- a/include/configs/zynq.h +++ b/include/configs/zynq.h | |||
@@ -72,6 +72,17 @@ | |||
72 | # define CONFIG_DOS_PARTITION | 72 | # define CONFIG_DOS_PARTITION |
73 | #endif | 73 | #endif |
74 | 74 | ||
75 | #define CONFIG_ZYNQ_I2C0 | ||
76 | |||
77 | /* I2C */ | ||
78 | #if defined(CONFIG_ZYNQ_I2C0) || defined(CONFIG_ZYNQ_I2C1) | ||
79 | # define CONFIG_CMD_I2C | ||
80 | # define CONFIG_ZYNQ_I2C | ||
81 | # define CONFIG_HARD_I2C | ||
82 | # define CONFIG_SYS_I2C_SPEED 100000 | ||
83 | # define CONFIG_SYS_I2C_SLAVE 1 | ||
84 | #endif | ||
85 | |||
75 | #if defined(CONFIG_ZYNQ_DCC) | 86 | #if defined(CONFIG_ZYNQ_DCC) |
76 | # define CONFIG_ARM_DCC | 87 | # define CONFIG_ARM_DCC |
77 | # define CONFIG_CPU_V6 /* Required by CONFIG_ARM_DCC */ | 88 | # define CONFIG_CPU_V6 /* Required by CONFIG_ARM_DCC */ |