blob: 98e4458defb6da64b97ef5a64bdcd8697c387258 [file] [log] [blame]
huang lin441a5782014-07-30 20:34:40 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2014 Rockchip Inc.
5 *
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.
huang lin441a5782014-07-30 20:34:40 -070014 */
15
huang lin441a5782014-07-30 20:34:40 -070016#include <arch/io.h>
huang lin441a5782014-07-30 20:34:40 -070017#include <assert.h>
huang lin441a5782014-07-30 20:34:40 -070018#include <cbfs.h>
Julius Werner7a453eb2014-10-20 13:14:55 -070019#include <console/console.h>
20#include <delay.h>
huang lin441a5782014-07-30 20:34:40 -070021#include <device/i2c.h>
Julius Werner7a453eb2014-10-20 13:14:55 -070022#include <soc/addressmap.h>
23#include <soc/grf.h>
24#include <soc/soc.h>
25#include <soc/i2c.h>
26#include <soc/clock.h>
27#include <stdlib.h>
28#include <string.h>
huang linbbcffd92014-09-27 12:02:27 +080029
huang lin441a5782014-07-30 20:34:40 -070030#define RETRY_COUNT 3
31/* 100000us = 100ms */
32#define I2C_TIMEOUT_US 100000
Daisuke Nojiri51de5a82014-09-18 12:35:09 -070033#define I2C_BUS_MAX 6
huang lin441a5782014-07-30 20:34:40 -070034#define I2C_NOACK 2
35#define I2C_TIMEOUT 3
36
37#define i2c_info(x...) do {if (0) printk(BIOS_DEBUG, x); } while (0)
38
39struct rk3288_i2c_regs {
40 u32 i2c_con;
41 u32 i2c_clkdiv;
42 u32 i2c_mrxaddr;
43 u32 i2c_mrxraddr;
44 u32 i2c_mtxcnt;
45 u32 i2c_mrxcnt;
46 u32 i2c_ien;
47 u32 i2c_ipd;
48 u32 i2c_fcnt;
49 u32 reserved0[(0x100 - 0x24) / 4];
50 u32 txdata[8];
51 u32 reserved1[(0x200 - 0x120) / 4];
52 u32 rxdata[8];
53};
54
55struct rk3288_i2c_regs *i2c_bus[] = {
56 (struct rk3288_i2c_regs *)0xff650000,
57 (struct rk3288_i2c_regs *)0xff140000,
58 (struct rk3288_i2c_regs *)0xff660000,
59 (struct rk3288_i2c_regs *)0xff150000,
60 (struct rk3288_i2c_regs *)0xff160000,
61 (struct rk3288_i2c_regs *)0xff170000,
62};
63
64/* Con register bits. */
65#define I2C_ACT2NAK (1<<6)
66#define I2C_NAK (1<<5)
67#define I2C_STOP (1<<4)
68#define I2C_START (1<<3)
69#define I2C_MODE_TX (0<<1)
70#define I2C_MODE_TRX (1<<1)
71#define I2C_MODE_RX (2<<1)
72#define I2C_EN (1<<0)
73
74#define I2C_8BIT (1<<24)
75#define I2C_16BIT (3<<24)
76#define I2C_24BIT (7<<24)
77
78/* Mtxcnt register bits. */
79#define I2C_CNT(cnt) ((cnt) & 0x3F)
80
81#define I2C_NAKRCVI (1<<6)
82#define I2C_STOPI (1<<5)
83#define I2C_STARTI (1<<4)
84#define I2C_MBRFI (1<<3)
85#define I2C_MBTFI (1<<2)
86#define I2C_BRFI (1<<1)
87#define I2C_BTFI (1<<0)
88#define I2C_CLEANI 0x7F
89
90static int i2c_send_start(struct rk3288_i2c_regs *reg_addr)
91{
92 int res = 0;
93 int timeout = I2C_TIMEOUT_US;
94
95 i2c_info("I2c Start::Send Start bit\n");
Julius Werner2f37bd62015-02-19 14:51:15 -080096 write32(&reg_addr->i2c_ipd, I2C_CLEANI);
97 write32(&reg_addr->i2c_con, I2C_EN | I2C_START);
huang lin441a5782014-07-30 20:34:40 -070098 while (timeout--) {
Julius Werner2f37bd62015-02-19 14:51:15 -080099 if (read32(&reg_addr->i2c_ipd) & I2C_STARTI)
huang lin441a5782014-07-30 20:34:40 -0700100 break;
101 udelay(1);
102 }
103
104 if (timeout <= 0) {
105 printk(BIOS_ERR, "I2C Start::Send Start Bit Timeout\n");
106 res = I2C_TIMEOUT;
107 }
108
109 return res;
110}
111
112static int i2c_send_stop(struct rk3288_i2c_regs *reg_addr)
113{
114 int res = 0;
115 int timeout = I2C_TIMEOUT_US;
116
117 i2c_info("I2c Stop::Send Stop bit\n");
Julius Werner2f37bd62015-02-19 14:51:15 -0800118 write32(&reg_addr->i2c_ipd, I2C_CLEANI);
119 write32(&reg_addr->i2c_con, I2C_EN | I2C_STOP);
huang lin441a5782014-07-30 20:34:40 -0700120 while (timeout--) {
Julius Werner2f37bd62015-02-19 14:51:15 -0800121 if (read32(&reg_addr->i2c_ipd) & I2C_STOPI)
huang lin441a5782014-07-30 20:34:40 -0700122 break;
123 udelay(1);
124 }
Julius Werner2f37bd62015-02-19 14:51:15 -0800125 write32(&reg_addr->i2c_con, 0);
huang lin441a5782014-07-30 20:34:40 -0700126 if (timeout <= 0) {
127 printk(BIOS_ERR, "I2C Stop::Send Stop Bit Timeout\n");
128 res = I2C_TIMEOUT;
129 }
130
131 return res;
132}
133
134static int i2c_read(struct rk3288_i2c_regs *reg_addr, struct i2c_seg segment)
135{
136 int res = 0;
137 uint8_t *data = segment.buf;
138 int timeout = I2C_TIMEOUT_US;
139 unsigned int bytes_remaining = segment.len;
140 unsigned int bytes_transfered = 0;
141 unsigned int words_transfered = 0;
142 unsigned int rxdata = 0;
143 unsigned int con = 0;
144 unsigned int i, j;
145
Julius Werner2f37bd62015-02-19 14:51:15 -0800146 write32(&reg_addr->i2c_mrxaddr, I2C_8BIT | segment.chip << 1 | 1);
147 write32(&reg_addr->i2c_mrxraddr, 0);
Daisuke Nojiri51de5a82014-09-18 12:35:09 -0700148 con = I2C_MODE_TRX | I2C_EN | I2C_ACT2NAK;
huang lin441a5782014-07-30 20:34:40 -0700149 while (bytes_remaining) {
150 bytes_transfered = MIN(bytes_remaining, 32);
151 bytes_remaining -= bytes_transfered;
152 if (!bytes_remaining)
153 con |= I2C_EN | I2C_NAK;
154 words_transfered = ALIGN_UP(bytes_transfered, 4) / 4;
155
Julius Werner2f37bd62015-02-19 14:51:15 -0800156 write32(&reg_addr->i2c_ipd, I2C_CLEANI);
157 write32(&reg_addr->i2c_con, con);
158 write32(&reg_addr->i2c_mrxcnt, bytes_transfered);
huang lin441a5782014-07-30 20:34:40 -0700159
160 timeout = I2C_TIMEOUT_US;
161 while (timeout--) {
Julius Werner2f37bd62015-02-19 14:51:15 -0800162 if (read32(&reg_addr->i2c_ipd) & I2C_NAKRCVI) {
163 write32(&reg_addr->i2c_mrxcnt, 0);
164 write32(&reg_addr->i2c_con, 0);
huang lin441a5782014-07-30 20:34:40 -0700165 return I2C_NOACK;
166 }
Julius Werner2f37bd62015-02-19 14:51:15 -0800167 if (read32(&reg_addr->i2c_ipd) & I2C_MBRFI)
huang lin441a5782014-07-30 20:34:40 -0700168 break;
169 udelay(1);
170 }
171 if (timeout <= 0) {
172 printk(BIOS_ERR, "I2C Read::Recv Data Timeout\n");
Julius Werner2f37bd62015-02-19 14:51:15 -0800173 write32(&reg_addr->i2c_mrxcnt, 0);
174 write32(&reg_addr->i2c_con, 0);
Daisuke Nojiri51de5a82014-09-18 12:35:09 -0700175 return I2C_TIMEOUT;
huang lin441a5782014-07-30 20:34:40 -0700176 }
177
178 for (i = 0; i < words_transfered; i++) {
Julius Werner2f37bd62015-02-19 14:51:15 -0800179 rxdata = read32(&reg_addr->rxdata[i]);
huang lin441a5782014-07-30 20:34:40 -0700180 i2c_info("I2c Read::RXDATA[%d] = 0x%x\n", i, rxdata);
181 for (j = 0; j < 4; j++) {
182 if ((i * 4 + j) == bytes_transfered)
183 break;
184 *data++ = (rxdata >> (j * 8)) & 0xff;
185 }
186 }
Daisuke Nojiri51de5a82014-09-18 12:35:09 -0700187 con = I2C_MODE_RX | I2C_EN | I2C_ACT2NAK;
huang lin441a5782014-07-30 20:34:40 -0700188 }
189 return res;
190}
191
192static int i2c_write(struct rk3288_i2c_regs *reg_addr, struct i2c_seg segment)
193{
194 int res = 0;
195 uint8_t *data = segment.buf;
196 int timeout = I2C_TIMEOUT_US;
197 int bytes_remaining = segment.len + 1;
198 int bytes_transfered = 0;
199 int words_transfered = 0;
200 unsigned int i;
201 unsigned int j = 1;
202 u32 txdata = 0;
203
204 txdata |= (segment.chip << 1);
205 while (bytes_remaining) {
206 bytes_transfered = MIN(bytes_remaining, 32);
207 words_transfered = ALIGN_UP(bytes_transfered, 4) / 4;
208 for (i = 0; i < words_transfered; i++) {
209 do {
210 if ((i * 4 + j) == bytes_transfered)
211 break;
212 txdata |= (*data++) << (j * 8);
213 } while (++j < 4);
Julius Werner2f37bd62015-02-19 14:51:15 -0800214 write32(&reg_addr->txdata[i], txdata);
huang lin441a5782014-07-30 20:34:40 -0700215 j = 0;
216 i2c_info("I2c Write::TXDATA[%d] = 0x%x\n", i, txdata);
217 txdata = 0;
218 }
219
Julius Werner2f37bd62015-02-19 14:51:15 -0800220 write32(&reg_addr->i2c_ipd, I2C_CLEANI);
221 write32(&reg_addr->i2c_con,
222 I2C_EN | I2C_MODE_TX | I2C_ACT2NAK);
223 write32(&reg_addr->i2c_mtxcnt, bytes_transfered);
huang lin441a5782014-07-30 20:34:40 -0700224
225 timeout = I2C_TIMEOUT_US;
226 while (timeout--) {
Julius Werner2f37bd62015-02-19 14:51:15 -0800227 if (read32(&reg_addr->i2c_ipd) & I2C_NAKRCVI) {
228 write32(&reg_addr->i2c_mtxcnt, 0);
229 write32(&reg_addr->i2c_con, 0);
huang lin441a5782014-07-30 20:34:40 -0700230 return I2C_NOACK;
231 }
Julius Werner2f37bd62015-02-19 14:51:15 -0800232 if (read32(&reg_addr->i2c_ipd) & I2C_MBTFI)
huang lin441a5782014-07-30 20:34:40 -0700233 break;
234 udelay(1);
235 }
236
237 if (timeout <= 0) {
238 printk(BIOS_ERR, "I2C Write::Send Data Timeout\n");
Julius Werner2f37bd62015-02-19 14:51:15 -0800239 write32(&reg_addr->i2c_mtxcnt, 0);
240 write32(&reg_addr->i2c_con, 0);
Daisuke Nojiri51de5a82014-09-18 12:35:09 -0700241 return I2C_TIMEOUT;
huang lin441a5782014-07-30 20:34:40 -0700242 }
243
244 bytes_remaining -= bytes_transfered;
245 }
246 return res;
247}
248
249static int i2c_do_xfer(void *reg_addr, struct i2c_seg segment)
250{
251 int res = 0;
252
253 if (i2c_send_start(reg_addr))
254 return I2C_TIMEOUT;
255 if (segment.read)
256 res = i2c_read(reg_addr, segment);
257 else
258 res = i2c_write(reg_addr, segment);
259 return i2c_send_stop(reg_addr) || res;
260}
261
262int platform_i2c_transfer(unsigned bus, struct i2c_seg *segments, int seg_count)
263{
264 int i;
265 int res = 0;
266 struct rk3288_i2c_regs *regs = i2c_bus[bus];
Daisuke Nojiri51de5a82014-09-18 12:35:09 -0700267 struct i2c_seg *seg = segments;
huang lin441a5782014-07-30 20:34:40 -0700268
269 for (i = 0; i < seg_count; i++, seg++) {
270 res = i2c_do_xfer(regs, *seg);
271 if (res)
272 break;
273 }
274 return res;
275}
huang linbbcffd92014-09-27 12:02:27 +0800276
277void i2c_init(unsigned int bus, unsigned int hz)
278{
279 unsigned int clk_div;
280 unsigned int divl;
281 unsigned int divh;
huang lin8affee52014-10-10 23:26:21 -0700282 unsigned int i2c_src_clk = 0;
huang linbbcffd92014-09-27 12:02:27 +0800283 struct rk3288_i2c_regs *regs = i2c_bus[bus];
284
285 /*i2c0,i2c2 src clk from pd_bus_pclk
286 other i2c src clk from peri_pclk
287 */
288 switch (bus) {
289 case 0:
290 case 2:
291 i2c_src_clk = PD_BUS_PCLK_HZ;
292 break;
293 case 1:
294 case 3:
295 case 4:
296 case 5:
297 i2c_src_clk = PERI_PCLK_HZ;
298 break;
299 default:
300 break;
301 }
302
303 /*SCL Divisor = 8*(CLKDIVL + 1 + CLKDIVH + 1)
304 SCL = PCLK/ SCLK Divisor
305 */
huang lin8affee52014-10-10 23:26:21 -0700306 clk_div = div_round_up(i2c_src_clk, hz * 8);
307 divh = clk_div * 3 / 7 - 1;
308 divl = clk_div - divh - 2;
huang linbbcffd92014-09-27 12:02:27 +0800309 assert((divh < 65536) && (divl < 65536));
Julius Werner2f37bd62015-02-19 14:51:15 -0800310 write32(&regs->i2c_clkdiv, (divh << 16) | (divl << 0));
huang linbbcffd92014-09-27 12:02:27 +0800311}