]> rtime.felk.cvut.cz Git - lincan.git/blob - embedded/libs4c/i2c/i2c_c552.c
Update of system-less architecture and board support code to actual uLAN.sf.net version.
[lincan.git] / embedded / libs4c / i2c / i2c_c552.c
1 /*******************************************************************
2   Components for embedded applications builded for
3   laboratory and medical instruments firmware  
4  
5   i2c_mx1.c - I2C communication automata for M9328 MX1 microcontroller
6  
7   Copyright holders and project originators
8     (C) 2001-2008 by Pavel Pisa pisa@cmp.felk.cvut.cz
9     (C) 2002-2008 by PiKRON Ltd. http://www.pikron.com
10     (C) 2007-2008 by Petr Smolik
11
12  The COLAMI components can be used and copied under next licenses
13    - MPL - Mozilla Public License
14    - GPL - GNU Public License
15    - LGPL - Lesser GNU Public License
16    - and other licenses added by project originators
17  Code can be modified and re-distributed under any combination
18  of the above listed licenses. If contributor does not agree with
19  some of the licenses, he can delete appropriate line.
20  Warning, if you delete all lines, you are not allowed to
21  distribute code or build project.
22  *******************************************************************/
23
24
25 #include <system_def.h>
26 #include <cpu_def.h>
27 #include <hal_machperiph.h> /* for PCLK on LPC17xx */
28 #include "i2c_drv_config.h"
29 #include "i2c_drv.h"
30
31 int c552_poll(i2c_drv_t *drv);
32 IRQ_HANDLER_FNC(c552_irq_handler);
33 static int c552_ctrl_fnc(struct i2c_drv *drv, int ctrl, void *p);
34 int c552_stroke(i2c_drv_t *drv);
35
36 // I2C Registers
37
38 #ifdef __LPC17xx_H__
39
40 #define C552_CONSET(port)  (((I2C_TypeDef *)(port))->I2CONSET)  /* Control Set Register */
41 #define C552_STAT(port)    (((I2C_TypeDef *)(port))->I2STAT)    /* Status Register */
42 #define C552_DAT(port)     (((I2C_TypeDef *)(port))->I2DAT)     /* Data Register */
43 #define C552_ADR(port)     (((I2C_TypeDef *)(port))->I2ADR0)    /* Slave Address Register */
44 #define C552_SCLH(port)    (((I2C_TypeDef *)(port))->I2SCLH)    /* SCL Duty Cycle Register (high half word) */
45 #define C552_SCLL(port)    (((I2C_TypeDef *)(port))->I2SCLL)    /* SCL Duty Cycle Register (low half word) */
46 #define C552_CONCLR(port)  (((I2C_TypeDef *)(port))->I2CONCLR)  /* Control Clear Register */
47 #define C552_MMCTRL(port)  (((I2C_TypeDef *)(port))->MMCTRL)    /* Monitor Mode Control */
48
49 #else /*__LPC17xx_H__*/
50
51 #define C552_CONSET(port)  (((i2cRegs_t *)(port))->conset)     /* Control Set Register */
52 #define C552_STAT(port)    (((i2cRegs_t *)(port))->stat)       /* Status Register */
53 #define C552_DAT(port)     (((i2cRegs_t *)(port))->dat)        /* Data Register */
54 #define C552_ADR(port)     (((i2cRegs_t *)(port))->adr)        /* Slave Address Register */
55 #define C552_SCLH(port)    (((i2cRegs_t *)(port))->sclh)       /* SCL Duty Cycle Register (high half word) */
56 #define C552_SCLL(port)    (((i2cRegs_t *)(port))->scll)       /* SCL Duty Cycle Register (low half word) */
57 #define C552_CONCLR(port)  (((i2cRegs_t *)(port))->conclr)     /* Control Clear Register */
58
59 #endif /*__LPC17xx_H__*/
60
61 #define C552CON_AA      (1 << 2)
62 #define C552CON_SI      (1 << 3)
63 #define C552CON_STO     (1 << 4)
64 #define C552CON_STA     (1 << 5)
65 #define C552CON_EN      (1 << 6)
66
67 #define C552CON_AAC     (1 << 2)
68 #define C552CON_SIC     (1 << 3)
69 #define C552CON_STAC    (1 << 5)
70 #define C552CON_ENC     (1 << 6)
71
72 /***************************************************************************/
73 int c552_init_start(struct i2c_drv *drv, int port, int irq, int bitrate, int sladr)
74 {
75   unsigned long clock_base;
76
77   clock_base=PCLK/2;
78
79   C552_ADR(port)=sladr;
80   C552_SCLH(port)=clock_base/bitrate; //minimal value
81   C552_SCLL(port)=clock_base/bitrate;
82   drv->irq=irq;
83   drv->port=port;
84   drv->sfnc_act=NULL;
85   drv->ctrl_fnc=c552_ctrl_fnc;
86   drv->poll_fnc=c552_poll;
87   drv->stroke_fnc=c552_stroke;
88   drv->flags=I2C_DRV_ON;    /* todo - use atomic operation */
89   C552_CONCLR(port)=0x6C;   /* clearing all flags */
90   C552_CONSET(port)=C552CON_EN;
91   request_irq(irq, c552_irq_handler, 0, "i2c", drv);
92   return 0;
93 }
94
95 static int c552_sfnc_ms_end(struct i2c_drv *drv);
96 static inline i2c_msg_head_t *c552_sfnc_sl_prep(struct i2c_drv *drv, int cmd, int rxtx);
97
98 /***************************************************************************/
99 static int c552_sfnc_ms_end(struct i2c_drv *drv)
100 {
101   i2c_msg_head_t *msg=drv->msg_act;
102
103   if(msg) {
104     if(msg->flags&I2C_MSG_REPEAT){
105       drv->master_queue=msg->next;
106     }else{
107       i2c_drv_queue_msg(msg->flags&I2C_MSG_NOPROC?NULL:&drv->proc_queue,msg);
108     }
109     msg->flags|=I2C_MSG_FINISHED;
110     if((msg->flags&I2C_MSG_CB_END) && (msg->callback))
111       msg->callback(drv,I2C_MSG_CB_END,msg);
112   }
113
114   if(drv->master_queue) {
115     /* there is some more work for master*/
116     /* We need to request start of the next transfer somewhere */
117     C552_CONSET(drv->port)=C552CON_STA;
118   } else {
119     drv->flags&=~I2C_DRV_MS_INPR;
120   }
121
122   drv->msg_act = NULL;
123   return 0;
124 }
125
126 static inline
127 i2c_msg_head_t *c552_sfnc_sl_prep(struct i2c_drv *drv, int cmd, int rxtx)
128 {
129   i2c_msg_head_t *msg=drv->slave_queue;
130   if(!msg) do {
131     if((msg->flags&rxtx) && !((cmd^msg->sl_cmd)&msg->sl_msk)){
132       drv->slave_queue=msg;
133       if((msg->flags&I2C_MSG_CB_START) && (msg->callback))
134         msg->callback(drv,I2C_MSG_CB_START|rxtx,msg);
135       return msg;
136     }
137   } while((msg=msg->next)!=drv->slave_queue);
138   return NULL;
139 }
140
141 /***************************************************************************/
142 static int c552_ctrl_fnc(struct i2c_drv *drv, int ctrl, void *p)
143 {
144   unsigned long saveif;
145   switch(ctrl){
146     case I2C_CTRL_MS_RQ:
147       if(!(drv->flags&I2C_DRV_ON))
148         return -1;
149       if(!drv->master_queue)
150         return 0;
151       save_and_cli(saveif);
152       if(!(drv->flags&I2C_DRV_MS_INPR)) {
153         drv->flags|=I2C_DRV_MS_INPR;
154         drv->flags&=~I2C_DRV_NA;
155         C552_CONSET(drv->port)=C552CON_STA;
156       }
157       restore_flags(saveif);
158       return 0;
159     default:
160       return -1;
161   }
162   return 0;
163 }
164
165 /***************************************************************************/
166 int c552_poll(i2c_drv_t *drv)
167 {
168   i2c_msg_head_t *msg;
169
170   if((msg=drv->proc_queue)!=NULL){
171     i2c_drv_queue_msg(NULL,msg);
172     if((msg->flags&I2C_MSG_CB_PROC) && (msg->callback))
173       msg->callback(drv,I2C_MSG_CB_PROC,msg);
174   }  
175   return 0;
176 }
177
178 int c552_stroke(i2c_drv_t *drv)
179 {
180   int port;
181   volatile int d;
182   port=drv->port;
183
184   C552_CONSET(port)=C552CON_STO;
185   C552_CONSET(port)=C552CON_STA;
186   for(d=0;d<10;d++);
187   C552_CONSET(port)=C552CON_STA;
188   return 0;
189 }
190
191
192 int i2c_irq_seq_num=0;
193
194 /***************************************************************************/
195 IRQ_HANDLER_FNC(c552_irq_handler)
196 {
197   i2c_drv_t *drv;
198   i2c_msg_head_t *msg;
199   int port;
200   int stat;
201
202   drv=(i2c_drv_t*)irq_handler_get_context();
203   if(drv->magic!=I2C_DRV_MAGIC)
204   {
205     #ifdef FOR_LINUX_KERNEL
206      panic("i2c_irq_handler : BAD drv magic !!!");
207     #elif defined(_WIN32)
208      I2C_PRINTF("i2c_irq_handler : BAD drv magic !!!\n");
209      return FALSE;
210     #elif defined(__DJGPP__)||defined(CONFIG_OC_I2C_DRV_SYSLESS)
211      I2C_PRINTF("i2c_irq_handler : BAD drv magic !!!\n");
212      return;
213     #else
214      error("i2c_irq_handler : BAD drv magic !!!");
215     #endif
216   }
217   drv->flags&=~I2C_DRV_NA;
218
219   port=drv->port;  
220   msg=drv->msg_act;
221
222   stat=C552_STAT(port);
223
224   switch(stat) {
225     case 0x00: 
226       /* Bus Error has occured */ 
227       drv->msg_act=NULL;
228       C552_CONSET(port)=C552CON_STO;
229       if(drv->master_queue) {
230         /* there is some work for master*/
231         C552_CONSET(port)=C552CON_STA;
232       }
233       break;
234     case 0x08: /* MS_STA */
235       /* the initial start condition has been sent */
236       if(!drv->master_queue) {
237         C552_CONCLR(port)=C552CON_STAC;
238         C552_CONSET(port)=C552CON_STO;
239         drv->msg_act=NULL;
240         break;
241       }
242       C552_CONCLR(port)=C552CON_STAC;
243       C552_CONSET(port)=C552CON_AA;
244
245       msg=drv->master_queue;
246       drv->msg_act=msg;
247       msg->tx_len=msg->rx_len=0;
248
249       if((msg->flags&I2C_MSG_CB_START) && (msg->callback))
250         msg->callback(drv,I2C_MSG_CB_START,msg);
251
252       if (msg->flags&I2C_MSG_MS_TX) {
253         /* proceed Tx request first */
254         C552_DAT(port) = msg->addr&~1;
255         break;
256       }
257       /* if there is no request for transmit, continue by Rx immediately */
258     case 0x10: /* MS_REPS */
259       /* the repeated start has been successfully sent, continue by Rx */
260       C552_CONCLR(port)=C552CON_STAC;
261       C552_CONSET(port)=C552CON_AA;
262       C552_DAT(port) = msg->addr|1;
263       if (!msg || !(msg->flags&I2C_MSG_MS_RX)) {
264         /* there are no data to be received */
265         C552_CONSET(port)=C552CON_STO;
266         c552_sfnc_ms_end(drv);
267       } else {
268         msg->rx_len=0;
269       }
270       break;
271     case 0x18:
272       /* sent SLA W received ACK */
273     case 0x28:
274       /* sent DATA received ACK */
275       if (msg->tx_len<msg->tx_rq) {
276         C552_DAT(port) = msg->tx_buf[msg->tx_len];
277         msg->tx_len++;
278         break;
279       }
280       /* all data has been sent */
281       if (!(msg->flags&I2C_MSG_MS_RX)) {
282         C552_CONSET(port)=C552CON_STO;
283         c552_sfnc_ms_end(drv);
284       } else {
285         C552_CONSET(port)=C552CON_STA;
286       }
287       break;
288     case 0x30:
289       /* sent DATA received NACK */
290     case 0x48:
291       /* sent SLA R received ACK */
292     case 0x20:
293       /* vyslano SLA W prijato NACK */
294       C552_CONSET(port)=C552CON_STO;
295       msg->flags|=I2C_MSG_FAIL;
296       c552_sfnc_ms_end(drv);
297       break;
298     case 0x38:
299       /* arbitration lost during Tx */
300       C552_CONSET(port)=C552CON_STA;
301       break;
302     case 0x40:
303       /* sent SLA R received ACK */
304       if (msg->rx_rq==1)
305         C552_CONCLR(port)=C552CON_AAC;
306       break;
307     case 0x50:
308       /* received DATA sent ACK */   
309       msg->rx_buf[msg->rx_len]= C552_DAT(port);
310       msg->rx_len++;
311       if (msg->rx_len+1>=msg->rx_rq)
312         C552_CONCLR(port)=C552CON_AAC;
313       break;
314     case 0x58:
315       /* received DATA sent NACK */   
316       msg->rx_buf[msg->rx_len]= C552_DAT(port);
317       msg->rx_len++;
318       C552_CONSET(port)=C552CON_STO;
319       c552_sfnc_ms_end(drv);
320       break;
321
322     /*** slave mode ***/
323
324     case 0x68:
325       /* received own SLA W sent ACK after arbitration lost */
326
327     case 0x78:
328       /* received Generall CALL sent ACK after arbitration lost */
329       C552_CONSET(port)=C552CON_STA;
330
331     case 0x60:
332       /* received own SLA W sent ACK */
333
334     case 0x70:
335       /* received Generall CALL sent ACK */
336       if(!drv->slave_queue) {
337         C552_CONCLR(port)=C552CON_AAC;
338         break;
339       }
340       C552_CONSET(port)=C552CON_AA;
341       drv->flags|=I2C_DRV_SL_CEXP|I2C_DRV_SL_INRX;
342       break;
343
344     case 0x80:
345       /* SLA W : received DATA sent ACK */
346
347     case 0x90:
348       /* GCall : received DATA sent ACK */
349
350       if(drv->flags&I2C_DRV_SL_CEXP){
351         drv->flags&=~I2C_DRV_SL_CEXP;
352         drv->sl_last_cmd=C552_DAT(port);
353         msg=c552_sfnc_sl_prep(drv, drv->sl_last_cmd, I2C_MSG_SL_RX);
354         drv->msg_act=msg;
355       }
356       if(!msg || (msg->rx_len>=msg->rx_rq)){
357         C552_CONCLR(port)=C552CON_AAC;
358         break;
359       }
360       msg->rx_buf[msg->rx_len]= C552_DAT(port);
361       msg->rx_len++;
362       break;
363
364     case 0x88:
365       /* SLA W : received DATA sent NACK */
366       /* may it be, the handling should fall into A0 state */
367
368     case 0x98:
369       /* GCall : received DATA sent NACK */
370       /* may it be, the handling should fall into A0 state */
371
372       C552_CONSET(port)=C552CON_AA;
373       break;
374
375     case 0xA0:
376       /* Slave : Repeated START or STOP */
377       if(msg && (msg->flags&I2C_MSG_CB_END) && (msg->callback)) {
378         int cbcode;
379         if(drv->flags&I2C_DRV_SL_INRX)
380            cbcode=I2C_MSG_CB_END|I2C_MSG_SL_RX;
381         else
382            cbcode=I2C_MSG_CB_END|I2C_MSG_SL_TX;
383         msg->callback(drv,cbcode,msg);
384       }
385       C552_CONSET(port)=C552CON_AA;
386       break;
387
388     case 0xB0:
389       /* received own SLA R sent ACK  after arbitration lost */
390       C552_CONSET(port)=C552CON_STA;
391
392     case 0xA8:
393       /* received own SLA R sent ACK */
394       drv->flags&=~I2C_DRV_SL_INRX;
395       msg=c552_sfnc_sl_prep(drv, drv->sl_last_cmd, I2C_MSG_SL_RX);
396       drv->msg_act=msg;
397       if(!msg) {
398         C552_CONCLR(port)=C552CON_AAC;
399         break;
400       }
401       C552_CONSET(port)=C552CON_AA;
402
403     case 0xB8:
404       /* SLA R : sent DATA received ACK */
405       if(!msg || (msg->tx_len>=msg->tx_rq)){
406         C552_DAT(port) = 0xff;
407         break;
408       }
409       C552_DAT(port) = msg->tx_buf[msg->tx_len];
410       msg->tx_len++;
411       break;
412
413     case 0xC0:
414       /* SLA R : sent DATA received NACK */
415       /* the A0 state is not enerred most probably */
416
417     case 0xC8:
418       /* SLA R : last data sent, DATA (AA=0) received ACK */
419       /* the A0 state is not enerred most probably */
420       C552_CONSET(port)=C552CON_AA;
421
422       if(msg && (msg->flags&I2C_MSG_CB_END) && (msg->callback)) {
423         msg->callback(drv,I2C_MSG_CB_END|I2C_MSG_SL_TX,msg);
424       }
425       break;
426
427     default: break;
428   }
429
430   /* vymaz SI bit */
431   C552_CONCLR(port)=C552CON_SIC;
432 }