blob: 3988bbe836840f8795c30d28f48e106c59051208 [file] [log] [blame]
Lee Leahya5258cb2016-05-30 14:06:25 -07001/*
2 * This file is part of the coreboot project.
3 *
Lee Leahy16568c72017-01-03 16:00:15 -08004 * Copyright (C) 2016-2017 Intel Corporation.
Lee Leahya5258cb2016-05-30 14:06:25 -07005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
Lee Leahy14d09262016-07-21 09:17:10 -070016#include <assert.h>
Lee Leahya5258cb2016-05-30 14:06:25 -070017#include <console/console.h>
18#include <delay.h>
19#include <device/device.h>
20#include <device/i2c.h>
21#include <device/pci.h>
22#include <device/pci_ids.h>
23#include <soc/i2c.h>
24#include <soc/ramstage.h>
25#include <soc/reg_access.h>
Lee Leahy16568c72017-01-03 16:00:15 -080026#include <timer.h>
Lee Leahya5258cb2016-05-30 14:06:25 -070027
28static void i2c_disable(I2C_REGS *regs)
29{
30 uint32_t status;
31 uint32_t timeout;
32
33 /* Disable I2C controller */
34 regs->ic_enable = 0;
35
36 /* Wait for the enable bit to clear */
37 timeout = 1 * 1000 * 1000;
38 status = regs->ic_enable_status;
39 while (status & IC_ENABLE_CONTROLLER) {
40 udelay(1);
41 if (--timeout == 0)
42 die("ERROR - I2C failed to disable!\n");
43 status = regs->ic_enable_status;
44 }
45
46 /* Clear any pending interrupts */
47 status = regs->ic_clr_intr;
48}
49
Lee Leahy16568c72017-01-03 16:00:15 -080050static int platform_i2c_write(uint32_t restart, uint8_t *tx_buffer, int length,
51 uint32_t stop, uint8_t *rx_buffer, struct stopwatch *timeout)
Lee Leahya5258cb2016-05-30 14:06:25 -070052{
Lee Leahy16568c72017-01-03 16:00:15 -080053 int bytes_transferred;
54 uint32_t cmd;
55 I2C_REGS *regs;
56 uint32_t status;
57
58 ASSERT(tx_buffer != NULL);
59 ASSERT(timeout != NULL);
60 regs = get_i2c_address();
61
62 /* Fill the FIFO with the write operation */
63 bytes_transferred = 0;
64 do {
65 status = regs->ic_raw_intr_stat;
66
67 /* Check for errors */
68 if (status & (IC_INTR_RX_OVER | IC_INTR_RX_UNDER
69 | IC_INTR_TX_ABRT | IC_INTR_TX_OVER)) {
70 i2c_disable(regs);
71 return -1;
72 }
73
74 /* Check for timeout */
75 if (stopwatch_expired(timeout))
76 return -1;
77
78 /* Receive any available data */
79 status = regs->ic_status;
80 if (rx_buffer != NULL) {
81 while (status & IC_STATUS_RFNE) {
82 *rx_buffer++ = (uint8_t)regs->ic_data_cmd;
83 bytes_transferred++;
84 status = regs->ic_status;
85 }
86 }
87
88 /* Determine if space is available in the FIFO */
89 if (status & IC_STATUS_TFNF) {
90 /* End of the transaction? */
91 cmd = IC_DATA_CMD_WRITE | *tx_buffer++ | restart;
92 if (length == 1)
93 cmd |= stop;
94 restart = 0;
95
96 /* Place a data byte into the FIFO */
97 regs->ic_data_cmd = cmd;
98 length--;
99 bytes_transferred++;
100 } else
101 udelay(1);
102 } while (length > 0);
103 return bytes_transferred;
104}
105
106static int platform_i2c_read(uint32_t restart, uint8_t *rx_buffer, int length,
107 uint32_t stop, struct stopwatch *timeout)
108{
109 int bytes_transferred;
110 uint32_t cmd;
111 int fifo_bytes;
112 uint8_t junk;
113 I2C_REGS *regs;
114 uint32_t status;
115
116 ASSERT(rx_buffer != NULL);
117 ASSERT(timeout != NULL);
118 regs = get_i2c_address();
119
120 /* Empty the FIFO */
121 status = regs->ic_status;
122 while (status & IC_STATUS_RFNE) {
123 junk = (uint8_t)regs->ic_data_cmd;
124 status = regs->ic_status;
125 }
126
127 /* Fill the FIFO with read commands */
128 fifo_bytes = min(length, 16);
129 bytes_transferred = 0;
130 while (length > 0) {
131 status = regs->ic_raw_intr_stat;
132
133 /* Check for errors */
134 if (status & (IC_INTR_RX_OVER | IC_INTR_RX_UNDER
135 | IC_INTR_TX_ABRT | IC_INTR_TX_OVER)) {
136 i2c_disable(regs);
137 return -1;
138 }
139
140 /* Check for timeout */
141 if (stopwatch_expired(timeout))
142 return -1;
143
144 /* Receive any available data */
145 status = regs->ic_status;
146 if (status & IC_STATUS_RFNE) {
147 /* Save the next data byte, removed from the RX FIFO */
148 *rx_buffer++ = (uint8_t)regs->ic_data_cmd;
149 bytes_transferred++;
150 }
151
152 if ((status & IC_STATUS_TFNF)
153 || ((status & IC_STATUS_RFNE) && (fifo_bytes > 0))) {
154 /* End of the transaction? */
155 cmd = IC_DATA_CMD_READ | restart;
156 if (length == 1)
157 cmd |= stop;
158 restart = 0;
159
160 /* Place a read command into the TX FIFO */
161 regs->ic_data_cmd = cmd;
162 if (fifo_bytes > 0)
163 fifo_bytes--;
164 length--;
165 } else
166 udelay(1);
167 }
168 return bytes_transferred;
169}
170
171int platform_i2c_transfer(unsigned int bus, struct i2c_seg *segment,
172 int seg_count)
173{
Lee Leahya5258cb2016-05-30 14:06:25 -0700174 int bytes_transferred;
175 uint8_t chip;
176 uint32_t cmd;
Lee Leahy16568c72017-01-03 16:00:15 -0800177 int data_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700178 int length;
Lee Leahya5258cb2016-05-30 14:06:25 -0700179 I2C_REGS *regs;
Lee Leahy16568c72017-01-03 16:00:15 -0800180 uint32_t restart;
181 uint8_t *rx_buffer;
Lee Leahya5258cb2016-05-30 14:06:25 -0700182 uint32_t status;
Lee Leahy16568c72017-01-03 16:00:15 -0800183 uint32_t stop;
184 struct stopwatch timeout;
185 int total_bytes;
186 uint8_t *tx_buffer;
187 int tx_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700188
189 regs = get_i2c_address();
190
191 /* Disable the I2C controller to get access to the registers */
192 i2c_disable(regs);
193
194 /* Set the slave address */
Lee Leahy16568c72017-01-03 16:00:15 -0800195 ASSERT(seg_count > 0);
196 ASSERT(segment != NULL);
Lee Leahya5258cb2016-05-30 14:06:25 -0700197
198 /* Clear the start and stop detection */
199 status = regs->ic_clr_start_det;
200 status = regs->ic_clr_stop_det;
201
202 /* Set addressing mode to 7-bit and fast mode */
203 cmd = regs->ic_con;
204 cmd &= ~(IC_CON_10B | IC_CON_SPEED);
Lee Leahy16568c72017-01-03 16:00:15 -0800205 cmd |= IC_CON_RESTART_EN | IC_CON_7B | IC_CON_SPEED_400_KHz
Lee Leahya5258cb2016-05-30 14:06:25 -0700206 | IC_CON_MASTER_MODE;
207 regs->ic_con = cmd;
208
209 /* Set the target chip address */
Lee Leahy16568c72017-01-03 16:00:15 -0800210 chip = segment->chip;
Lee Leahya5258cb2016-05-30 14:06:25 -0700211 regs->ic_tar = chip;
212
213 /* Enable the I2C controller */
214 regs->ic_enable = IC_ENABLE_CONTROLLER;
215
216 /* Clear the interrupts */
217 status = regs->ic_clr_rx_under;
218 status = regs->ic_clr_rx_over;
219 status = regs->ic_clr_tx_over;
220 status = regs->ic_clr_tx_abrt;
221
Lee Leahy16568c72017-01-03 16:00:15 -0800222 /* Start the timeout */
223 stopwatch_init_msecs_expire(&timeout, 1000);
224
Lee Leahya5258cb2016-05-30 14:06:25 -0700225 /* Process each of the segments */
Lee Leahy16568c72017-01-03 16:00:15 -0800226 total_bytes = 0;
227 tx_bytes = 0;
Lee Leahya5258cb2016-05-30 14:06:25 -0700228 bytes_transferred = 0;
Lee Leahy16568c72017-01-03 16:00:15 -0800229 rx_buffer = NULL;
230 restart = 0;
231 while (seg_count-- > 0) {
232 length = segment->len;
233 total_bytes += length;
234 ASSERT(segment->buf != NULL);
235 ASSERT(length >= 1);
236 ASSERT(segment->chip == chip);
Lee Leahya5258cb2016-05-30 14:06:25 -0700237
Lee Leahy16568c72017-01-03 16:00:15 -0800238 /* Determine if this is the last segment of the transaction */
239 stop = (seg_count == 0) ? IC_DATA_CMD_STOP : 0;
240
241 /* Fill the FIFO with the necessary command bytes */
242 if (segment->read) {
Lee Leahya5258cb2016-05-30 14:06:25 -0700243 /* Place read commands into the FIFO */
Lee Leahy16568c72017-01-03 16:00:15 -0800244 rx_buffer = segment->buf;
245 data_bytes = platform_i2c_read(restart, rx_buffer,
246 length, stop, &timeout);
Lee Leahya5258cb2016-05-30 14:06:25 -0700247
Lee Leahy16568c72017-01-03 16:00:15 -0800248 /* Return any detected error */
249 if (data_bytes < 0)
250 return data_bytes;
251 bytes_transferred += data_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700252 } else {
253 /* Write the data into the FIFO */
Lee Leahy16568c72017-01-03 16:00:15 -0800254 tx_buffer = segment->buf;
255 tx_bytes += length;
256 data_bytes = platform_i2c_write(restart, tx_buffer,
257 length, stop, rx_buffer, &timeout);
Lee Leahya5258cb2016-05-30 14:06:25 -0700258
Lee Leahy16568c72017-01-03 16:00:15 -0800259 /* Return any detected error */
260 if (data_bytes < 0)
261 return data_bytes;
262 bytes_transferred += data_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700263 }
Lee Leahy16568c72017-01-03 16:00:15 -0800264 segment++;
265 restart = IC_DATA_CMD_RESTART;
Lee Leahya5258cb2016-05-30 14:06:25 -0700266 }
267
268 /* Wait for the end of the transaction */
Lee Leahy16568c72017-01-03 16:00:15 -0800269 if (rx_buffer != NULL)
270 rx_buffer += bytes_transferred - tx_bytes;
Lee Leahya5258cb2016-05-30 14:06:25 -0700271 do {
Lee Leahy16568c72017-01-03 16:00:15 -0800272 /* Receive any available data */
273 status = regs->ic_status;
274 if ((rx_buffer != NULL) && (status & IC_STATUS_RFNE)) {
275 *rx_buffer++ = (uint8_t)regs->ic_data_cmd;
276 bytes_transferred++;
277 } else {
278 status = regs->ic_raw_intr_stat;
279 if ((total_bytes == bytes_transferred)
280 && (status & IC_INTR_STOP_DET))
281 break;
282
283 /* Check for errors */
284 if (status & (IC_INTR_RX_OVER | IC_INTR_RX_UNDER
285 | IC_INTR_TX_ABRT | IC_INTR_TX_OVER)) {
286 i2c_disable(regs);
287 return -1;
288 }
289
290 /* Check for timeout */
291 if (stopwatch_expired(&timeout))
292 return -1;
293
294 /* Delay for a while */
295 udelay(1);
Lee Leahya5258cb2016-05-30 14:06:25 -0700296 }
Elyes HAOUAS4a83f1c2016-08-25 21:07:59 +0200297 } while (1);
Lee Leahy16568c72017-01-03 16:00:15 -0800298 i2c_disable(regs);
299 regs->ic_tar = 0;
Lee Leahya5258cb2016-05-30 14:06:25 -0700300
Lee Leahy16568c72017-01-03 16:00:15 -0800301 /* Return the number of bytes transferred */
Lee Leahya5258cb2016-05-30 14:06:25 -0700302 return bytes_transferred;
303}