1*5c47fe09SDavid du Colombier /*
2*5c47fe09SDavid du Colombier * bcm2835 i2c controller
3*5c47fe09SDavid du Colombier *
4*5c47fe09SDavid du Colombier * Only i2c1 is supported.
5*5c47fe09SDavid du Colombier * i2c2 is reserved for HDMI.
6*5c47fe09SDavid du Colombier * i2c0 SDA0/SCL0 pins are not routed to P1 connector (except for early Rev 0 boards)
7*5c47fe09SDavid du Colombier *
8*5c47fe09SDavid du Colombier * maybe hardware problems lurking, see: https://github.com/raspberrypi/linux/issues/254
9*5c47fe09SDavid du Colombier */
10*5c47fe09SDavid du Colombier
11*5c47fe09SDavid du Colombier #include "u.h"
12*5c47fe09SDavid du Colombier #include "../port/lib.h"
13*5c47fe09SDavid du Colombier #include "../port/error.h"
14*5c47fe09SDavid du Colombier #include "mem.h"
15*5c47fe09SDavid du Colombier #include "dat.h"
16*5c47fe09SDavid du Colombier #include "fns.h"
17*5c47fe09SDavid du Colombier #include "io.h"
18*5c47fe09SDavid du Colombier
19*5c47fe09SDavid du Colombier #define I2CREGS (VIRTIO+0x804000)
20*5c47fe09SDavid du Colombier #define SDA0Pin 2
21*5c47fe09SDavid du Colombier #define SCL0Pin 3
22*5c47fe09SDavid du Colombier #define Alt0 0x4
23*5c47fe09SDavid du Colombier
24*5c47fe09SDavid du Colombier typedef struct I2c I2c;
25*5c47fe09SDavid du Colombier typedef struct Bsc Bsc;
26*5c47fe09SDavid du Colombier
27*5c47fe09SDavid du Colombier /*
28*5c47fe09SDavid du Colombier * Registers for Broadcom Serial Controller (i2c compatible)
29*5c47fe09SDavid du Colombier */
30*5c47fe09SDavid du Colombier struct Bsc {
31*5c47fe09SDavid du Colombier u32int ctrl;
32*5c47fe09SDavid du Colombier u32int stat;
33*5c47fe09SDavid du Colombier u32int dlen;
34*5c47fe09SDavid du Colombier u32int addr;
35*5c47fe09SDavid du Colombier u32int fifo;
36*5c47fe09SDavid du Colombier u32int clkdiv; /* default 1500 => 100 KHz assuming 150Mhz input clock */
37*5c47fe09SDavid du Colombier u32int delay; /* default (48<<16)|48 falling:rising edge */
38*5c47fe09SDavid du Colombier u32int clktimeout; /* default 64 */
39*5c47fe09SDavid du Colombier };
40*5c47fe09SDavid du Colombier
41*5c47fe09SDavid du Colombier /*
42*5c47fe09SDavid du Colombier * Per-controller info
43*5c47fe09SDavid du Colombier */
44*5c47fe09SDavid du Colombier struct I2c {
45*5c47fe09SDavid du Colombier QLock lock;
46*5c47fe09SDavid du Colombier Lock reglock;
47*5c47fe09SDavid du Colombier Rendez r;
48*5c47fe09SDavid du Colombier Bsc *regs;
49*5c47fe09SDavid du Colombier };
50*5c47fe09SDavid du Colombier
51*5c47fe09SDavid du Colombier static I2c i2c;
52*5c47fe09SDavid du Colombier
53*5c47fe09SDavid du Colombier enum {
54*5c47fe09SDavid du Colombier /* ctrl */
55*5c47fe09SDavid du Colombier I2cen = 1<<15, /* I2c enable */
56*5c47fe09SDavid du Colombier Intr = 1<<10, /* interrupt on reception */
57*5c47fe09SDavid du Colombier Intt = 1<<9, /* interrupt on transmission */
58*5c47fe09SDavid du Colombier Intd = 1<<8, /* interrupt on done */
59*5c47fe09SDavid du Colombier Start = 1<<7, /* aka ST, start a transfer */
60*5c47fe09SDavid du Colombier Clear = 1<<4, /* clear fifo */
61*5c47fe09SDavid du Colombier Read = 1<<0, /* read transfer */
62*5c47fe09SDavid du Colombier Write = 0<<0, /* write transfer */
63*5c47fe09SDavid du Colombier
64*5c47fe09SDavid du Colombier /* stat */
65*5c47fe09SDavid du Colombier Clkt = 1<<9, /* clock stretch timeout */
66*5c47fe09SDavid du Colombier Err = 1<<8, /* NAK */
67*5c47fe09SDavid du Colombier Rxf = 1<<7, /* RX fifo full */
68*5c47fe09SDavid du Colombier Txe = 1<<6, /* TX fifo full */
69*5c47fe09SDavid du Colombier Rxd = 1<<5, /* RX fifo has data */
70*5c47fe09SDavid du Colombier Txd = 1<<4, /* TX fifo has space */
71*5c47fe09SDavid du Colombier Rxr = 1<<3, /* RX fiio needs reading */
72*5c47fe09SDavid du Colombier Txw = 1<<2, /* TX fifo needs writing */
73*5c47fe09SDavid du Colombier Done = 1<<1, /* transfer done */
74*5c47fe09SDavid du Colombier Ta = 1<<0, /* Transfer active */
75*5c47fe09SDavid du Colombier
76*5c47fe09SDavid du Colombier /* maximum I2C I/O (can change) */
77*5c47fe09SDavid du Colombier MaxIO = 128,
78*5c47fe09SDavid du Colombier MaxSA = 2, /* longest subaddress */
79*5c47fe09SDavid du Colombier Bufsize = (MaxIO+MaxSA+1+4)&~3, /* extra space for subaddress/clock bytes and alignment */
80*5c47fe09SDavid du Colombier
81*5c47fe09SDavid du Colombier Chatty = 0,
82*5c47fe09SDavid du Colombier };
83*5c47fe09SDavid du Colombier
84*5c47fe09SDavid du Colombier static void
i2cinterrupt(Ureg *,void *)85*5c47fe09SDavid du Colombier i2cinterrupt(Ureg*, void*)
86*5c47fe09SDavid du Colombier {
87*5c47fe09SDavid du Colombier Bsc *r;
88*5c47fe09SDavid du Colombier int st;
89*5c47fe09SDavid du Colombier
90*5c47fe09SDavid du Colombier r = i2c.regs;
91*5c47fe09SDavid du Colombier st = 0;
92*5c47fe09SDavid du Colombier if((r->ctrl & Intr) && (r->stat & Rxd))
93*5c47fe09SDavid du Colombier st |= Intr;
94*5c47fe09SDavid du Colombier if((r->ctrl & Intt) && (r->stat & Txd))
95*5c47fe09SDavid du Colombier st |= Intt;
96*5c47fe09SDavid du Colombier if(r->stat & Done)
97*5c47fe09SDavid du Colombier st |= Intd;
98*5c47fe09SDavid du Colombier if(st){
99*5c47fe09SDavid du Colombier r->ctrl &= ~st;
100*5c47fe09SDavid du Colombier wakeup(&i2c.r);
101*5c47fe09SDavid du Colombier }
102*5c47fe09SDavid du Colombier }
103*5c47fe09SDavid du Colombier
104*5c47fe09SDavid du Colombier static int
i2cready(void * st)105*5c47fe09SDavid du Colombier i2cready(void *st)
106*5c47fe09SDavid du Colombier {
107*5c47fe09SDavid du Colombier return (i2c.regs->stat & (uintptr)st);
108*5c47fe09SDavid du Colombier }
109*5c47fe09SDavid du Colombier
110*5c47fe09SDavid du Colombier static void
i2cinit(void)111*5c47fe09SDavid du Colombier i2cinit(void)
112*5c47fe09SDavid du Colombier {
113*5c47fe09SDavid du Colombier i2c.regs = (Bsc*)I2CREGS;
114*5c47fe09SDavid du Colombier i2c.regs->clkdiv = 2500;
115*5c47fe09SDavid du Colombier
116*5c47fe09SDavid du Colombier gpiosel(SDA0Pin, Alt0);
117*5c47fe09SDavid du Colombier gpiosel(SCL0Pin, Alt0);
118*5c47fe09SDavid du Colombier gpiopullup(SDA0Pin);
119*5c47fe09SDavid du Colombier gpiopullup(SCL0Pin);
120*5c47fe09SDavid du Colombier
121*5c47fe09SDavid du Colombier intrenable(IRQi2c, i2cinterrupt, 0, 0, "i2c");
122*5c47fe09SDavid du Colombier }
123*5c47fe09SDavid du Colombier
124*5c47fe09SDavid du Colombier /*
125*5c47fe09SDavid du Colombier * To do subaddressing avoiding a STOP condition between the address and payload.
126*5c47fe09SDavid du Colombier * - write the subaddress,
127*5c47fe09SDavid du Colombier * - poll until the transfer starts,
128*5c47fe09SDavid du Colombier * - overwrite the registers for the payload transfer, before the subaddress
129*5c47fe09SDavid du Colombier * transaction has completed.
130*5c47fe09SDavid du Colombier *
131*5c47fe09SDavid du Colombier * FIXME: neither 10bit adressing nor 100Khz transfers implemented yet.
132*5c47fe09SDavid du Colombier */
133*5c47fe09SDavid du Colombier static void
i2cio(int rw,int tenbit,uint addr,void * buf,int len,int salen,uint subaddr)134*5c47fe09SDavid du Colombier i2cio(int rw, int tenbit, uint addr, void *buf, int len, int salen, uint subaddr)
135*5c47fe09SDavid du Colombier {
136*5c47fe09SDavid du Colombier Bsc *r;
137*5c47fe09SDavid du Colombier uchar *p;
138*5c47fe09SDavid du Colombier int st;
139*5c47fe09SDavid du Colombier
140*5c47fe09SDavid du Colombier if(tenbit)
141*5c47fe09SDavid du Colombier error("10bit addressing not supported");
142*5c47fe09SDavid du Colombier if(salen == 0 && subaddr) /* default subaddress len == 1byte */
143*5c47fe09SDavid du Colombier salen = 1;
144*5c47fe09SDavid du Colombier
145*5c47fe09SDavid du Colombier qlock(&i2c.lock);
146*5c47fe09SDavid du Colombier r = i2c.regs;
147*5c47fe09SDavid du Colombier r->ctrl = I2cen | Clear;
148*5c47fe09SDavid du Colombier r->addr = addr;
149*5c47fe09SDavid du Colombier r->stat = Clkt|Err|Done;
150*5c47fe09SDavid du Colombier
151*5c47fe09SDavid du Colombier if(salen){
152*5c47fe09SDavid du Colombier r->dlen = salen;
153*5c47fe09SDavid du Colombier r->ctrl = I2cen | Start | Write;
154*5c47fe09SDavid du Colombier while((r->stat & Ta) == 0) {
155*5c47fe09SDavid du Colombier if(r->stat & (Err|Clkt)) {
156*5c47fe09SDavid du Colombier qunlock(&i2c.lock);
157*5c47fe09SDavid du Colombier error(Eio);
158*5c47fe09SDavid du Colombier }
159*5c47fe09SDavid du Colombier }
160*5c47fe09SDavid du Colombier r->dlen = len;
161*5c47fe09SDavid du Colombier r->ctrl = I2cen | Start | Intd | rw;
162*5c47fe09SDavid du Colombier for(; salen > 0; salen--)
163*5c47fe09SDavid du Colombier r->fifo = subaddr >> ((salen-1)*8);
164*5c47fe09SDavid du Colombier /*
165*5c47fe09SDavid du Colombier * Adapted from Linux code...uses undocumented
166*5c47fe09SDavid du Colombier * status information.
167*5c47fe09SDavid du Colombier */
168*5c47fe09SDavid du Colombier if(rw == Read) {
169*5c47fe09SDavid du Colombier do {
170*5c47fe09SDavid du Colombier if(r->stat & (Err|Clkt)) {
171*5c47fe09SDavid du Colombier qunlock(&i2c.lock);
172*5c47fe09SDavid du Colombier error(Eio);
173*5c47fe09SDavid du Colombier }
174*5c47fe09SDavid du Colombier st = r->stat >> 28;
175*5c47fe09SDavid du Colombier } while(st != 0 && st != 4 && st != 5);
176*5c47fe09SDavid du Colombier }
177*5c47fe09SDavid du Colombier }
178*5c47fe09SDavid du Colombier else {
179*5c47fe09SDavid du Colombier r->dlen = len;
180*5c47fe09SDavid du Colombier r->ctrl = I2cen | Start | Intd | rw;
181*5c47fe09SDavid du Colombier }
182*5c47fe09SDavid du Colombier
183*5c47fe09SDavid du Colombier p = buf;
184*5c47fe09SDavid du Colombier st = rw == Read? Rxd : Txd;
185*5c47fe09SDavid du Colombier while(len > 0){
186*5c47fe09SDavid du Colombier while((r->stat & (st|Done)) == 0){
187*5c47fe09SDavid du Colombier r->ctrl |= rw == Read? Intr : Intt;
188*5c47fe09SDavid du Colombier sleep(&i2c.r, i2cready, (void*)(st|Done));
189*5c47fe09SDavid du Colombier }
190*5c47fe09SDavid du Colombier if(r->stat & (Err|Clkt)){
191*5c47fe09SDavid du Colombier qunlock(&i2c.lock);
192*5c47fe09SDavid du Colombier error(Eio);
193*5c47fe09SDavid du Colombier }
194*5c47fe09SDavid du Colombier if(rw == Read){
195*5c47fe09SDavid du Colombier do{
196*5c47fe09SDavid du Colombier *p++ = r->fifo;
197*5c47fe09SDavid du Colombier len--;
198*5c47fe09SDavid du Colombier }while ((r->stat & Rxd) && len > 0);
199*5c47fe09SDavid du Colombier }else{
200*5c47fe09SDavid du Colombier do{
201*5c47fe09SDavid du Colombier r->fifo = *p++;
202*5c47fe09SDavid du Colombier len--;
203*5c47fe09SDavid du Colombier }while((r->stat & Txd) && len > 0);
204*5c47fe09SDavid du Colombier }
205*5c47fe09SDavid du Colombier }
206*5c47fe09SDavid du Colombier while((r->stat & Done) == 0)
207*5c47fe09SDavid du Colombier sleep(&i2c.r, i2cready, (void*)Done);
208*5c47fe09SDavid du Colombier if(r->stat & (Err|Clkt)){
209*5c47fe09SDavid du Colombier qunlock(&i2c.lock);
210*5c47fe09SDavid du Colombier error(Eio);
211*5c47fe09SDavid du Colombier }
212*5c47fe09SDavid du Colombier r->ctrl = 0;
213*5c47fe09SDavid du Colombier qunlock(&i2c.lock);
214*5c47fe09SDavid du Colombier }
215*5c47fe09SDavid du Colombier
216*5c47fe09SDavid du Colombier
217*5c47fe09SDavid du Colombier void
i2csetup(int)218*5c47fe09SDavid du Colombier i2csetup(int)
219*5c47fe09SDavid du Colombier {
220*5c47fe09SDavid du Colombier //print("i2csetup\n");
221*5c47fe09SDavid du Colombier i2cinit();
222*5c47fe09SDavid du Colombier }
223*5c47fe09SDavid du Colombier
224*5c47fe09SDavid du Colombier long
i2csend(I2Cdev * d,void * buf,long len,ulong offset)225*5c47fe09SDavid du Colombier i2csend(I2Cdev *d, void *buf, long len, ulong offset)
226*5c47fe09SDavid du Colombier {
227*5c47fe09SDavid du Colombier i2cio(Write, d->tenbit, d->addr, buf, len, d->salen, offset);
228*5c47fe09SDavid du Colombier return len;
229*5c47fe09SDavid du Colombier }
230*5c47fe09SDavid du Colombier
231*5c47fe09SDavid du Colombier long
i2crecv(I2Cdev * d,void * buf,long len,ulong offset)232*5c47fe09SDavid du Colombier i2crecv(I2Cdev *d, void *buf, long len, ulong offset)
233*5c47fe09SDavid du Colombier {
234*5c47fe09SDavid du Colombier i2cio(Read, d->tenbit, d->addr, buf, len, d->salen, offset);
235*5c47fe09SDavid du Colombier return len;
236*5c47fe09SDavid du Colombier }
237