blob: 42ca25dbd9bbed07f4b087ad853347e0a631c3c7 [file] [log] [blame]
Angel Pons230e4f9d2020-04-05 15:47:14 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Lee Leahya5258cb2016-05-30 14:06:25 -07002
Lee Leahy14d09262016-07-21 09:17:10 -07003#include <assert.h>
Elyes HAOUASf97c1c92019-12-03 18:22:06 +01004#include <commonlib/helpers.h>
Lee Leahya5258cb2016-05-30 14:06:25 -07005#include <console/console.h>
6#include <delay.h>
7#include <device/device.h>
Nico Huber0f2dd1e2017-08-01 14:02:40 +02008#include <device/i2c_simple.h>
Lee Leahya5258cb2016-05-30 14:06:25 -07009#include <device/pci.h>
Lee Leahya5258cb2016-05-30 14:06:25 -070010#include <soc/i2c.h>
11#include <soc/ramstage.h>
12#include <soc/reg_access.h>
Lee Leahy16568c72017-01-03 16:00:15 -080013#include <timer.h>
Lee Leahya5258cb2016-05-30 14:06:25 -070014
15static void i2c_disable(I2C_REGS *regs)
16{
17 uint32_t status;
18 uint32_t timeout;
19
20 /* Disable I2C controller */
21 regs->ic_enable = 0;
22
23 /* Wait for the enable bit to clear */
24 timeout = 1 * 1000 * 1000;
25 status = regs->ic_enable_status;
26 while (status & IC_ENABLE_CONTROLLER) {
27 udelay(1);
28 if (--timeout == 0)
Keith Short15588b02019-05-09 11:40:34 -060029 die_with_post_code(POST_HW_INIT_FAILURE,
30 "ERROR - I2C failed to disable!\n");
Lee Leahya5258cb2016-05-30 14:06:25 -070031 status = regs->ic_enable_status;
32 }
33
34 /* Clear any pending interrupts */
35 status = regs->ic_clr_intr;
36}
37
Lee Leahy16568c72017-01-03 16:00:15 -080038static int platform_i2c_write(uint32_t restart, uint8_t *tx_buffer, int length,
39 uint32_t stop, uint8_t *rx_buffer, struct stopwatch *timeout)
Lee Leahya5258cb2016-05-30 14:06:25 -070040{
Lee Leahy16568c72017-01-03 16:00:15 -080041 int bytes_transferred;
42 uint32_t cmd;
43 I2C_REGS *regs;
44 uint32_t status;
45
46 ASSERT(tx_buffer != NULL);
47 ASSERT(timeout != NULL);
48 regs = get_i2c_address();
49
50 /* Fill the FIFO with the write operation */
51 bytes_transferred = 0;
52 do {
53 status = regs->ic_raw_intr_stat;
54
55 /* Check for errors */
56 if (status & (IC_INTR_RX_OVER | IC_INTR_RX_UNDER
57 | IC_INTR_TX_ABRT | IC_INTR_TX_OVER)) {
58 i2c_disable(regs);
Julius Wernercd49cce2019-03-05 16:53:33 -080059 if (CONFIG(I2C_DEBUG))
Lee Leahy5764cbb2017-06-29 17:29:07 -070060 printk(BIOS_ERR,
61 "0x%08x: ic_raw_intr_stat, I2C write error!\n",
62 status);
Lee Leahy16568c72017-01-03 16:00:15 -080063 return -1;
64 }
65
66 /* Check for timeout */
Lee Leahy5764cbb2017-06-29 17:29:07 -070067 if (stopwatch_expired(timeout)) {
Julius Wernercd49cce2019-03-05 16:53:33 -080068 if (CONFIG(I2C_DEBUG))
Lee Leahy5764cbb2017-06-29 17:29:07 -070069 printk(BIOS_ERR,
70 "0x%08x: ic_raw_intr_stat, I2C write timeout!\n",
71 status);
Lee Leahy16568c72017-01-03 16:00:15 -080072 return -1;
Lee Leahy5764cbb2017-06-29 17:29:07 -070073 }
Lee Leahy16568c72017-01-03 16:00:15 -080074
75 /* Receive any available data */
76 status = regs->ic_status;
77 if (rx_buffer != NULL) {
78 while (status & IC_STATUS_RFNE) {
79 *rx_buffer++ = (uint8_t)regs->ic_data_cmd;
80 bytes_transferred++;
81 status = regs->ic_status;
82 }
83 }
84
85 /* Determine if space is available in the FIFO */
86 if (status & IC_STATUS_TFNF) {
87 /* End of the transaction? */
88 cmd = IC_DATA_CMD_WRITE | *tx_buffer++ | restart;
89 if (length == 1)
90 cmd |= stop;
91 restart = 0;
92
93 /* Place a data byte into the FIFO */
94 regs->ic_data_cmd = cmd;
95 length--;
96 bytes_transferred++;
97 } else
98 udelay(1);
99 } while (length > 0);
100 return bytes_transferred;
101}
102
103static int platform_i2c_read(uint32_t restart, uint8_t *rx_buffer, int length,
104 uint32_t stop, struct stopwatch *timeout)
105{
106 int bytes_transferred;
107 uint32_t cmd;
108 int fifo_bytes;
Lee Leahy16568c72017-01-03 16:00:15 -0800109 I2C_REGS *regs;
110 uint32_t status;
111
112 ASSERT(rx_buffer != NULL);
113 ASSERT(timeout != NULL);
114 regs = get_i2c_address();
115
116 /* Empty the FIFO */
117 status = regs->ic_status;
118 while (status & IC_STATUS_RFNE) {
Elyes HAOUAS36479202019-05-29 13:47:43 +0200119 (void)regs->ic_data_cmd;
Lee Leahy16568c72017-01-03 16:00:15 -0800120 status = regs->ic_status;
121 }
122
123 /* Fill the FIFO with read commands */
Elyes HAOUASf97c1c92019-12-03 18:22:06 +0100124 fifo_bytes = MIN(length, 16);
Lee Leahy16568c72017-01-03 16:00:15 -0800125 bytes_transferred = 0;
126 while (length > 0) {
127 status = regs->ic_raw_intr_stat;
128
129 /* Check for errors */
130 if (status & (IC_INTR_RX_OVER | IC_INTR_RX_UNDER
131 | IC_INTR_TX_ABRT | IC_INTR_TX_OVER)) {
132 i2c_disable(regs);
Julius Wernercd49cce2019-03-05 16:53:33 -0800133 if (CONFIG(I2C_DEBUG))
Lee Leahy5764cbb2017-06-29 17:29:07 -0700134 printk(BIOS_ERR,
135 "0x%08x: ic_raw_intr_stat, I2C read error!\n",
136 status);
Lee Leahy16568c72017-01-03 16:00:15 -0800137 return -1;
138 }
139
140 /* Check for timeout */
Lee Leahy5764cbb2017-06-29 17:29:07 -0700141 if (stopwatch_expired(timeout)) {
Julius Wernercd49cce2019-03-05 16:53:33 -0800142 if (CONFIG(I2C_DEBUG))
Lee Leahy5764cbb2017-06-29 17:29:07 -0700143 printk(BIOS_ERR,
144 "0x%08x: ic_raw_intr_stat, I2C read timeout!\n",
145 status);
Lee Leahy16568c72017-01-03 16:00:15 -0800146 return -1;
Lee Leahy5764cbb2017-06-29 17:29:07 -0700147 }
Lee Leahy16568c72017-01-03 16:00:15 -0800148
149 /* Receive any available data */
150 status = regs->ic_status;
151 if (status & IC_STATUS_RFNE) {
152 /* Save the next data byte, removed from the RX FIFO */
153 *rx_buffer++ = (uint8_t)regs->ic_data_cmd;
154 bytes_transferred++;
155 }
156
157 if ((status & IC_STATUS_TFNF)
158 || ((status & IC_STATUS_RFNE) && (fifo_bytes > 0))) {
159 /* End of the transaction? */
160 cmd = IC_DATA_CMD_READ | restart;
161 if (length == 1)
162 cmd |= stop;
163 restart = 0;
164
165 /* Place a read command into the TX FIFO */
166 regs->ic_data_cmd = cmd;
167 if (fifo_bytes > 0)
168 fifo_bytes--;
169 length--;
170 } else
171 udelay(1);
172 }
173 return bytes_transferred;
174}
175
Nico Huber029dfff2017-07-12 17:59:16 +0200176int platform_i2c_transfer(unsigned int bus, struct i2c_msg *segment,
177 int seg_count)
Lee Leahy16568c72017-01-03 16:00:15 -0800178{
Lee Leahya5258cb2016-05-30 14:06:25 -0700179 int bytes_transferred;
180 uint8_t chip;
181 uint32_t cmd;
Lee Leahy16568c72017-01-03 16:00:15 -0800182 int data_bytes;
Lee Leahy5764cbb2017-06-29 17:29:07 -0700183 int index;
Lee Leahya5258cb2016-05-30 14:06:25 -0700184 int length;
Lee Leahya5258cb2016-05-30 14:06:25 -0700185 I2C_REGS *regs;
Lee Leahy16568c72017-01-03 16:00:15 -0800186 uint32_t restart;
187 uint8_t *rx_buffer;
Lee Leahya5258cb2016-05-30 14:06:25 -0700188 uint32_t status;
Lee Leahy16568c72017-01-03 16:00:15 -0800189 uint32_t stop;
190 struct stopwatch timeout;
191 int total_bytes;
192 uint8_t *tx_buffer;
193 int tx_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700194
Julius Wernercd49cce2019-03-05 16:53:33 -0800195 if (CONFIG(I2C_DEBUG)) {
Lee Leahy5764cbb2017-06-29 17:29:07 -0700196 for (index = 0; index < seg_count;) {
197 if (index == 0)
198 printk(BIOS_ERR, "I2C Start\n");
199 printk(BIOS_ERR,
Julius Werner540a9802019-12-09 13:03:29 -0800200 "I2C segment[%d]: %s 0x%02x %s %p, 0x%08x bytes\n",
Lee Leahy5764cbb2017-06-29 17:29:07 -0700201 index,
Nico Huber029dfff2017-07-12 17:59:16 +0200202 (segment[index].flags & I2C_M_RD) ? "Read from" : "Write to",
203 segment[index].slave,
204 (segment[index].flags & I2C_M_RD) ? "to " : "from",
Lee Leahy5764cbb2017-06-29 17:29:07 -0700205 segment[index].buf,
206 segment[index].len);
207 printk(BIOS_ERR, "I2C %s\n",
208 (++index >= seg_count) ? "Stop" : "Restart");
209 }
210 }
211
Lee Leahya5258cb2016-05-30 14:06:25 -0700212 regs = get_i2c_address();
213
214 /* Disable the I2C controller to get access to the registers */
215 i2c_disable(regs);
216
217 /* Set the slave address */
Lee Leahy16568c72017-01-03 16:00:15 -0800218 ASSERT(seg_count > 0);
219 ASSERT(segment != NULL);
Lee Leahya5258cb2016-05-30 14:06:25 -0700220
221 /* Clear the start and stop detection */
222 status = regs->ic_clr_start_det;
223 status = regs->ic_clr_stop_det;
224
225 /* Set addressing mode to 7-bit and fast mode */
226 cmd = regs->ic_con;
227 cmd &= ~(IC_CON_10B | IC_CON_SPEED);
Lee Leahy16568c72017-01-03 16:00:15 -0800228 cmd |= IC_CON_RESTART_EN | IC_CON_7B | IC_CON_SPEED_400_KHz
Lee Leahya5258cb2016-05-30 14:06:25 -0700229 | IC_CON_MASTER_MODE;
230 regs->ic_con = cmd;
231
232 /* Set the target chip address */
Nico Huber029dfff2017-07-12 17:59:16 +0200233 chip = segment->slave;
Lee Leahya5258cb2016-05-30 14:06:25 -0700234 regs->ic_tar = chip;
235
236 /* Enable the I2C controller */
237 regs->ic_enable = IC_ENABLE_CONTROLLER;
238
239 /* Clear the interrupts */
240 status = regs->ic_clr_rx_under;
241 status = regs->ic_clr_rx_over;
242 status = regs->ic_clr_tx_over;
243 status = regs->ic_clr_tx_abrt;
244
Lee Leahy16568c72017-01-03 16:00:15 -0800245 /* Start the timeout */
Jes Klinke19baa9d2022-02-22 16:00:09 -0800246 stopwatch_init_usecs_expire(&timeout, CONFIG_I2C_TRANSFER_TIMEOUT_US);
Lee Leahy16568c72017-01-03 16:00:15 -0800247
Lee Leahya5258cb2016-05-30 14:06:25 -0700248 /* Process each of the segments */
Lee Leahy16568c72017-01-03 16:00:15 -0800249 total_bytes = 0;
250 tx_bytes = 0;
Lee Leahya5258cb2016-05-30 14:06:25 -0700251 bytes_transferred = 0;
Lee Leahy16568c72017-01-03 16:00:15 -0800252 rx_buffer = NULL;
253 restart = 0;
Lee Leahy5764cbb2017-06-29 17:29:07 -0700254 index = 0;
255 while (index++ < seg_count) {
Lee Leahy16568c72017-01-03 16:00:15 -0800256 length = segment->len;
257 total_bytes += length;
258 ASSERT(segment->buf != NULL);
259 ASSERT(length >= 1);
Nico Huber029dfff2017-07-12 17:59:16 +0200260 ASSERT(segment->slave == chip);
Lee Leahya5258cb2016-05-30 14:06:25 -0700261
Lee Leahy16568c72017-01-03 16:00:15 -0800262 /* Determine if this is the last segment of the transaction */
Lee Leahy5764cbb2017-06-29 17:29:07 -0700263 stop = (index == seg_count) ? IC_DATA_CMD_STOP : 0;
Lee Leahy16568c72017-01-03 16:00:15 -0800264
265 /* Fill the FIFO with the necessary command bytes */
Nico Huber029dfff2017-07-12 17:59:16 +0200266 if (segment->flags & I2C_M_RD) {
Lee Leahya5258cb2016-05-30 14:06:25 -0700267 /* Place read commands into the FIFO */
Lee Leahy16568c72017-01-03 16:00:15 -0800268 rx_buffer = segment->buf;
269 data_bytes = platform_i2c_read(restart, rx_buffer,
270 length, stop, &timeout);
Lee Leahya5258cb2016-05-30 14:06:25 -0700271
Lee Leahy16568c72017-01-03 16:00:15 -0800272 /* Return any detected error */
Lee Leahy5764cbb2017-06-29 17:29:07 -0700273 if (data_bytes < 0) {
Julius Wernercd49cce2019-03-05 16:53:33 -0800274 if (CONFIG(I2C_DEBUG))
Lee Leahy5764cbb2017-06-29 17:29:07 -0700275 printk(BIOS_ERR,
276 "I2C segment[%d] failed\n",
277 index);
Lee Leahy16568c72017-01-03 16:00:15 -0800278 return data_bytes;
Lee Leahy5764cbb2017-06-29 17:29:07 -0700279 }
Lee Leahy16568c72017-01-03 16:00:15 -0800280 bytes_transferred += data_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700281 } else {
282 /* Write the data into the FIFO */
Lee Leahy16568c72017-01-03 16:00:15 -0800283 tx_buffer = segment->buf;
284 tx_bytes += length;
285 data_bytes = platform_i2c_write(restart, tx_buffer,
286 length, stop, rx_buffer, &timeout);
Lee Leahya5258cb2016-05-30 14:06:25 -0700287
Lee Leahy16568c72017-01-03 16:00:15 -0800288 /* Return any detected error */
Lee Leahy5764cbb2017-06-29 17:29:07 -0700289 if (data_bytes < 0) {
Julius Wernercd49cce2019-03-05 16:53:33 -0800290 if (CONFIG(I2C_DEBUG))
Lee Leahy5764cbb2017-06-29 17:29:07 -0700291 printk(BIOS_ERR,
292 "I2C segment[%d] failed\n",
293 index);
Lee Leahy16568c72017-01-03 16:00:15 -0800294 return data_bytes;
Lee Leahy5764cbb2017-06-29 17:29:07 -0700295 }
Lee Leahy16568c72017-01-03 16:00:15 -0800296 bytes_transferred += data_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700297 }
Lee Leahy16568c72017-01-03 16:00:15 -0800298 segment++;
299 restart = IC_DATA_CMD_RESTART;
Lee Leahya5258cb2016-05-30 14:06:25 -0700300 }
301
302 /* Wait for the end of the transaction */
Lee Leahy16568c72017-01-03 16:00:15 -0800303 if (rx_buffer != NULL)
304 rx_buffer += bytes_transferred - tx_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700305 do {
Lee Leahy16568c72017-01-03 16:00:15 -0800306 /* Receive any available data */
307 status = regs->ic_status;
308 if ((rx_buffer != NULL) && (status & IC_STATUS_RFNE)) {
309 *rx_buffer++ = (uint8_t)regs->ic_data_cmd;
310 bytes_transferred++;
311 } else {
312 status = regs->ic_raw_intr_stat;
313 if ((total_bytes == bytes_transferred)
314 && (status & IC_INTR_STOP_DET))
315 break;
316
317 /* Check for errors */
318 if (status & (IC_INTR_RX_OVER | IC_INTR_RX_UNDER
319 | IC_INTR_TX_ABRT | IC_INTR_TX_OVER)) {
320 i2c_disable(regs);
Julius Wernercd49cce2019-03-05 16:53:33 -0800321 if (CONFIG(I2C_DEBUG)) {
Lee Leahy5764cbb2017-06-29 17:29:07 -0700322 printk(BIOS_ERR,
323 "0x%08x: ic_raw_intr_stat, I2C read error!\n",
324 status);
325 printk(BIOS_ERR,
326 "I2C segment[%d] failed\n",
327 seg_count - 1);
328 }
Lee Leahy16568c72017-01-03 16:00:15 -0800329 return -1;
330 }
331
332 /* Check for timeout */
Lee Leahy5764cbb2017-06-29 17:29:07 -0700333 if (stopwatch_expired(&timeout)) {
Julius Wernercd49cce2019-03-05 16:53:33 -0800334 if (CONFIG(I2C_DEBUG)) {
Lee Leahy5764cbb2017-06-29 17:29:07 -0700335 printk(BIOS_ERR,
336 "0x%08x: ic_raw_intr_stat, I2C read timeout!\n",
337 status);
338 printk(BIOS_ERR,
339 "I2C segment[%d] failed\n",
340 seg_count - 1);
341 }
Lee Leahy16568c72017-01-03 16:00:15 -0800342 return -1;
Lee Leahy5764cbb2017-06-29 17:29:07 -0700343 }
Lee Leahy16568c72017-01-03 16:00:15 -0800344
345 /* Delay for a while */
346 udelay(1);
Lee Leahya5258cb2016-05-30 14:06:25 -0700347 }
Elyes HAOUAS4a83f1c2016-08-25 21:07:59 +0200348 } while (1);
Lee Leahy16568c72017-01-03 16:00:15 -0800349 i2c_disable(regs);
350 regs->ic_tar = 0;
Lee Leahya5258cb2016-05-30 14:06:25 -0700351
Lee Leahy16568c72017-01-03 16:00:15 -0800352 /* Return the number of bytes transferred */
Julius Wernercd49cce2019-03-05 16:53:33 -0800353 if (CONFIG(I2C_DEBUG))
Lee Leahy5764cbb2017-06-29 17:29:07 -0700354 printk(BIOS_ERR, "0x%08x: bytes transferred\n",
355 bytes_transferred);
Lee Leahya5258cb2016-05-30 14:06:25 -0700356 return bytes_transferred;
357}