summaryrefslogtreecommitdiff
path: root/firmware/target/arm/rk27xx/i2c-rk27xx.c
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2011-05-30 21:10:37 +0000
committerMarcin Bukat <marcin.bukat@gmail.com>2011-05-30 21:10:37 +0000
commit976a1699da373f01dabc9353b34aef261ebf740f (patch)
tree5f1649ceb51d603471e6b1cf5dcb5192626897d6 /firmware/target/arm/rk27xx/i2c-rk27xx.c
parent8a5a2b82fd2d35e3eb7afa8f0dc875e3874988bb (diff)
downloadrockbox-976a1699da373f01dabc9353b34aef261ebf740f.tar.gz
rockbox-976a1699da373f01dabc9353b34aef261ebf740f.zip
Rockchip rk27xx port initial commit. This is still work in progress.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29935 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/rk27xx/i2c-rk27xx.c')
-rw-r--r--firmware/target/arm/rk27xx/i2c-rk27xx.c242
1 files changed, 242 insertions, 0 deletions
diff --git a/firmware/target/arm/rk27xx/i2c-rk27xx.c b/firmware/target/arm/rk27xx/i2c-rk27xx.c
new file mode 100644
index 0000000000..34a6f49a32
--- /dev/null
+++ b/firmware/target/arm/rk27xx/i2c-rk27xx.c
@@ -0,0 +1,242 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2011 by Marcin Bukat
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "config.h"
23#include "system.h"
24#include "kernel.h"
25#include "i2c-rk27xx.h"
26
27/* NOT TESTED YET */
28
29/* Driver for the rockchip rk27xx built-in I2C controller in master mode
30
31 Both the i2c_read and i2c_write function take the following arguments:
32 * slave, the address of the i2c slave device to read from / write to
33 * address, optional sub-address in the i2c slave (unused if -1)
34 * len, number of bytes to be transfered
35 * data, pointer to data to be transfered
36 A return value other than 0 indicates an error.
37*/
38
39static struct mutex i2c_mtx;
40
41static bool i2c_write_byte(uint8_t data, bool start)
42{
43 long timeout = current_tick + HZ / 50;
44
45 /* START */
46 I2C_CONR |= (1<<3) | (1<<2); /* master port enable, transmit bit */
47 I2C_MTXR = data;
48
49 if (start)
50 I2C_LCMR = (1<<2) | (1<<0); /* resume op, start bit */
51 else
52 I2C_LCMR = (1<<2); /* resume op */
53
54 I2C_CONR &= ~(1<<4); /* ACK enable */
55
56 /* wait for ACK from slave */
57 while ( !(I2C_ISR & (1<<0)) || (I2C_LSR & (1<<0)) )
58 if (TIME_AFTER(current_tick, timeout))
59 return false;
60
61 /* clear status bit */
62 I2C_ISR &= ~(1<<0);
63
64 return true;
65}
66
67static bool i2c_read_byte(unsigned char *data)
68{
69 long timeout = current_tick + HZ / 50;
70
71 I2C_LCMR = (1<<2); /* resume op */
72
73 while (I2C_ISR & (1<<1))
74 if (TIME_AFTER(current_tick, timeout))
75 return false;
76
77 *data = I2C_MRXR;
78
79 /* clear status bit */
80 I2C_ISR &= ~(1<<1);
81
82 return true;
83}
84
85static bool i2c_stop(void)
86{
87 long timeout = current_tick + HZ / 50;
88
89 I2C_CONR &= ~(1<<4);
90 I2C_LCMR |= (1<<2) | (1<<1); /* resume op, stop */
91
92 while (I2C_LCMR & (1<<1))
93 if (TIME_AFTER(current_tick, timeout))
94 return false;
95
96 return true;
97}
98
99/* route i2c bus to internal codec or external bus
100 * internal codec has 0x27 i2c slave address so
101 * access to this address is routed to internal bus.
102 * All other addresses are routed to external pads
103 */
104static void i2c_iomux(unsigned char slave)
105{
106 unsigned long muxa = SCU_IOMUXA_CON & ~(0x1f<<14);
107
108 if (slave == (0x27<<1))
109 {
110 /* internal codec */
111 SCU_IOMUXA_CON = muxa | (1<<16) | (1<<14);
112 }
113 else
114 {
115 /* external I2C bus */
116 SCU_IOMUXA_CON = muxa | (1<<18);
117 }
118}
119
120void i2c_init(void)
121{
122 mutex_init(&i2c_mtx);
123
124 SCU_CLKCFG &= ~(1<< 20);
125
126 I2C_OPR |= (1<<7); /* reset state machine */
127 sleep(HZ/100);
128 I2C_OPR &= ~((1<<7) | (1<<6)); /* clear ENABLE bit, deasert reset */
129
130 /* set I2C divider to stay within allowed SCL freq limit
131 * APBfreq = 50Mhz
132 * SCLfreq = (APBfreq/5*(I2CCDVR[5:3] + 1) * 2^((I2CCDVR[2:0] + 1))
133 */
134 I2C_OPR = (I2C_OPR & ~(0x3F)) | (6<<3) | 1<<0;
135
136 I2C_IER = 0x00;
137
138 I2C_OPR |= (1<<6); /* enable i2c core */
139}
140
141int i2c_write(unsigned char slave, int address, int len,
142 const unsigned char *data)
143{
144 mutex_lock(&i2c_mtx);
145
146 i2c_iomux(slave);
147
148 /* START */
149 if (! i2c_write_byte(slave & ~1, true))
150 {
151 mutex_unlock(&i2c_mtx);
152 return 1;
153 }
154
155 if (address >= 0)
156 {
157 if (! i2c_write_byte(address, false))
158 {
159 mutex_unlock(&i2c_mtx);
160 return 2;
161 }
162 }
163
164 /* write data */
165 while (len--)
166 {
167 if (! i2c_write_byte(*data++, false))
168 {
169 mutex_unlock(&i2c_mtx);
170 return 4;
171 }
172 }
173
174 /* STOP */
175 if (! i2c_stop())
176 {
177 mutex_unlock(&i2c_mtx);
178 return 5;
179 }
180
181 mutex_unlock(&i2c_mtx);
182 return 0;
183}
184
185int i2c_read(unsigned char slave, int address, int len, unsigned char *data)
186{
187 mutex_lock(&i2c_mtx);
188
189 i2c_iomux(slave);
190
191 if (address >= 0)
192 {
193 /* START */
194 if (! i2c_write_byte(slave & ~1, true))
195 {
196 mutex_unlock(&i2c_mtx);
197 return 1;
198 }
199
200 /* write address */
201 if (! i2c_write_byte(address, false))
202 {
203 mutex_unlock(&i2c_mtx);
204 return 2;
205 }
206 }
207
208 /* (repeated) START */
209 if (! i2c_write_byte(slave | 1, true))
210 {
211 mutex_unlock(&i2c_mtx);
212 return 3;
213 }
214
215 I2C_CONR &= ~(1<<3); /* clear transmit bit (switch to receive mode) */
216
217 while (len)
218 {
219 if (! i2c_read_byte(data++))
220 {
221 mutex_unlock(&i2c_mtx);
222 return 4;
223 }
224
225 if (len == 1)
226 I2C_CONR |= (1<<4); /* NACK */
227 else
228 I2C_CONR &= ~(1<<4); /* ACK */
229
230 len--;
231 }
232
233 /* STOP */
234 if (! i2c_stop())
235 {
236 mutex_unlock(&i2c_mtx);
237 return 5;
238 }
239
240 mutex_unlock(&i2c_mtx);
241 return 0;
242}