OSDN Git Service

0736bb171e98c9884b66efbd52f984633b34508c
[uzume/uzume_bfin.git] / uzume_prototype / kernel / uzume / i2c_subsystem.c
1 /**
2  * \file i2c_subsystem.c
3  * \brief i2cペリフェラルをマスターモードで使うためのサブシステム
4  * \details
5  * CMSISを使い、i2cペリフェラルをマスターモードで使用する
6  */
7 #include "i2c_subsystem.h"
8 #include "kernel_id.h"
9 #include <s_services.h>
10 #include <t_services.h>
11
12 #ifdef _COMMON_BF592
13 #include <cdefBF592-A.h>
14 #elif defined(_COMMON_BF506)
15 #include <cdefBF506F.h>
16 #elif defined(_COMMON_BF518)
17 #include <cdefBF518.h>
18 #elif defined(_COMMON_BF537)
19 #include <cdefBF537.h>
20 #else
21 #error "This processor is not supported"
22 #endif
23
24 #include <ccblkfn.h>
25
26
27 //#define I2C_DEBUG
28
29 #ifdef I2C_DEBUG
30 #define I2C_SYSLOG(level,msg) syslog( level, msg )
31 #else
32 #define I2C_SYSLOG(level,msg)
33 #endif
34
35
36 /**
37  * \brief チップ上のI2Cペリフェラルの数
38  */
39 #define I2CNUM 1
40
41 /**
42  * \brief I2C ペリフェラルのタイムアウト時間(mSec)
43  */
44 #define I2C_TIMEOUT 100
45
46 /**
47  * \brief TWIペリフェラルの管理用ステート型。内部ステートマシンの状態値。
48  */
49 enum I2C_STATE {
50     I2C_XMT_ENTRY,
51     I2C_XMTRCV_ENTRY,
52     I2C_XMT_WAIT,
53     I2C_XMT_INT,
54     I2C_XMT_NEXT_BYTE,
55     I2C_RCV_ENTRY,
56     I2C_RCV_WAIT,
57     I2C_RCV_INT,
58     I2C_RCV_NEXT_BYTE,
59     I2C_EXIT
60 };
61
62
63 /**
64  * \brief I2C管理用構造体
65  */
66 struct I2C_MASTER_CONTROL_TYPE {
67     ID signal;                /**< データの送受信が全部終わったときに割り込みハンドラからタスクに知らせるためのセマフォ */
68     ID blocking;            /**< I2Cペリフェラルへの排他アクセスのためのセマフォ */
69     unsigned short intr_state;         /**< TWIペリフェラルの割り込みステータスのコピー **/
70     volatile unsigned short *   clkdiv;      /**< TWI_CLCKDIVレジスタのアドレス **/
71     volatile unsigned short *    control;     /**< TWI_CONTROLレジスタのアドレス **/
72     volatile unsigned short *    master_ctl;  /**< TWI_MASTER_CTLレジスタのアドレス **/
73     volatile unsigned short *    master_stat; /**< TWI_MASTER_STATレジスタのアドレス **/
74     volatile unsigned short *    master_addr; /**< TWI_MASTER_ADDRレジスタのアドレス **/
75     volatile unsigned short *    int_stat;    /**< TWI_MASTER_STATレジスタのアドレス **/
76     volatile unsigned short *    int_mask;    /**< TWI_INT_MASKレジスタのアドレス **/
77     volatile unsigned short *    fifo_ctl;    /**< TWI_FIFO_CTLレジスタのアドレス **/
78     volatile unsigned short *    fifo_stat;   /**< TWI_FIFO_STATレジスタのアドレス **/
79     volatile unsigned short *    xmt_data8;   /**< TWI_XMT_DATA8レジスタのアドレス **/
80     volatile unsigned short *    rcv_data8;   /**< TWI_RCV_DATA8レジスタのアドレス **/
81     volatile unsigned short *    slave_ctl;   /**< TWI_MASTER_CTLレジスタのアドレス **/
82
83 };
84
85 /**
86  * \brief i2cペリフェラルを管理するための構造体群。
87  */
88 static struct I2C_MASTER_CONTROL_TYPE i2c_control[I2CNUM];
89
90 static char * i2c_strerror( ER ercd )
91 {
92     ercd &= 0xFFFF0000;
93     switch (ercd)
94     {
95     case I2C_ERR_WRONGPARAM :
96         return("I2C_ERR_WRONGPARAM");
97     case I2C_ERR_TOOLONGBUFFER :
98         return("I2C_ERR_TOOLONGBUFFER");
99     case I2C_ERR_TIMEOUT :
100         return ("I2C_ERR_TIMEOUT");
101     }
102     return ("I2C_ERR_UNKNOWN");
103 }
104
105
106 void
107 i2c_perror(UINT prio, const char *file, int line, const char *expr, ER ercd)
108 {
109     syslog_5(prio, "%s reported by `%s' in line %d of `%s'. Interrupt Status = %04x",
110         i2c_strerror(ercd), expr, line, file, ercd & 0xFFFF);
111 }
112
113
114 int i2c_master_write( int peripheral, int slave, unsigned char write_data[], int write_count )
115 {
116         // read のための引数を0にしておくと、writeのみがおこなわれる
117     return i2c_master_write_read( peripheral, slave, write_data, write_count, 0, 0);
118 }
119
120 int i2c_master_read( int peripheral, int slave, unsigned char read_data[], int read_count )
121 {
122         // write のための引数を0にしておくと、readのみがおこなわれる
123     return i2c_master_write_read( peripheral, slave, 0, 0, read_data, read_count);
124 }
125
126
127 int i2c_master_write_read( int peripheral, int slave, unsigned char write_data[], int write_count, unsigned char read_data[], int read_count)
128 {
129     struct I2C_MASTER_CONTROL_TYPE *twi;
130     BOOL read, write, rstart, quit;
131     unsigned char *wptr, *rptr;
132     enum I2C_STATE state;      /**< I2Cペリフェラルハンドラの内部状態 */
133     PRI old_priority;         // タスクの優先順位を保存しておくための変数
134
135         // 可読性向上のため、TWI管理構造体のポインタを設定する。
136     twi = &i2c_control[peripheral];
137
138         // 送受信ポインタ.とかくこの世は生きにくい
139     wptr = write_data;
140     rptr = read_data;
141
142         // この代入は必要ないが、警告がうるさいので
143     rstart= FALSE;
144
145         // ループ終了条件の成立フラグ
146     quit = FALSE;
147
148         // 読み書きの有無の確認
149         // バッファがNULLだったり長さが0のパラメタは転送に使わない
150     write = ( write_data != NULL ) && ( write_count != 0 );
151     read =  ( read_data != NULL )  && ( read_count != 0 );
152
153         // パラメタの組み合わせによって初期状態が変わる
154     if ( read && write )
155         state = I2C_XMTRCV_ENTRY;
156     else if ( write && ! read )
157         state = I2C_XMT_ENTRY;
158     else if ( ! write && read )
159         state = I2C_RCV_ENTRY;
160     else
161         return I2C_ERR_WRONGPARAM;
162
163     if ( write_count > 254 || read_count > 254 )
164         return I2C_ERR_TOOLONGBUFFER;
165
166
167         // peripheral 引数で指定されたi2cペリフェラルを排他的に使うためのPV処理。
168         // これでスレッドセーフにできる
169     wai_sem(twi->blocking);
170
171         // タスクの優先順位を一旦引き上げる。これは、TWIのレスポンスを保証するためである
172     get_pri( TSK_SELF, &old_priority );
173     chg_pri( TSK_SELF, TMAX_TPRI );
174
175         // SPIスレーブデバイスのアドレスを設定(7bit)
176     *twi->master_addr = slave;
177
178         // FIFO フラッシュ
179     *twi->fifo_ctl = XMTFLUSH | RCVFLUSH;
180     ssync();
181
182         // FIFO 設定
183     *twi->fifo_ctl = XMTINTLEN;                        // 割り込みスレシホールドは送受とも1バイト、FLUSHはクリアする
184
185         // TWIステートマシン
186     do {
187         switch (state)
188         {
189         case I2C_XMT_ENTRY :
190             I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_ENTRY" );
191             rstart = FALSE;
192             *twi->master_ctl = ( write_count<<6 ) | MEN;            // no rstart, transmit, no fast mode;
193             state = I2C_XMT_NEXT_BYTE;
194             break;
195         case I2C_XMTRCV_ENTRY :
196             I2C_SYSLOG(LOG_NOTICE, "I2C_XMTRCV_ENTRY" );
197             rstart = TRUE;
198             *twi->master_ctl = ( write_count<<6 ) | RSTART | MEN;    // rstart, transmit, no fast mode;
199             state = I2C_XMT_NEXT_BYTE;
200             break;
201         case I2C_RCV_ENTRY :
202             I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_ENTRY" );
203             rstart = FALSE;
204             *twi->master_ctl = ( read_count<<6 ) | MDIR | MEN;      // no rstart, receive, no fast mode;
205             state = I2C_RCV_WAIT;
206             break;
207         case I2C_XMT_WAIT :
208             I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_WAIT" );
209                 // 割り込みハンドラが送信割り込みを通知するまで待つ
210             if ( E_OK != twai_sem(twi->signal, I2C_TIMEOUT))
211             {
212                 twi->intr_state = I2C_ERR_TIMEOUT | *twi->int_stat;     // エラーなら割り込みステータスを併記
213                 state = I2C_EXIT;
214             }
215             else
216                 state = I2C_XMT_INT;
217             break;
218         case I2C_XMT_INT :
219             I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_INT" );
220             if ( twi->intr_state & MERR )                        // エラーならすぐ終了
221                 state = I2C_EXIT;
222             else if ( ( twi->intr_state & MCOMP ) && rstart)    // MCOMP かつ RSTARTなら受信へ
223                 state = I2C_RCV_ENTRY;
224             else if ( twi->intr_state & MCOMP ){                // RSTARTがないMCOMPなら終了
225                 twi->intr_state &= ~MCOMP;
226                 state = I2C_EXIT;
227             }
228             else                                                // それ以外は送信割り込み
229                 state = I2C_XMT_NEXT_BYTE;
230             break;
231         case I2C_XMT_NEXT_BYTE :
232             I2C_SYSLOG(LOG_NOTICE, "I2C_XMT_NEXT_BYTE" );
233             *twi->xmt_data8 = *(wptr++);        // 1バイト送信
234             state = I2C_XMT_WAIT;          // 次の送信待
235             break;
236         case I2C_RCV_WAIT :
237             I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_WAIT" );
238                 // 割り込みハンドラが受信割り込みを通知するまで待つ
239             if ( E_OK != twai_sem(twi->signal, I2C_TIMEOUT))
240             {
241                 twi->intr_state = I2C_ERR_TIMEOUT | *twi->int_stat;     // エラーなら割り込みステータスを併記
242                 state = I2C_EXIT;
243             }
244             else
245                 state = I2C_RCV_INT;
246             break;
247         case I2C_RCV_INT :
248             I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_INT" );
249             if ( twi->intr_state & MERR)            // エラーならすぐ終了
250                 state = I2C_EXIT;
251             else if ( twi->intr_state & MCOMP ){   // 終了ならエラークリア
252                 twi->intr_state &= ~MCOMP;
253                 state = I2C_EXIT;
254             }
255             else                                   // それ以外は受信割り込み
256                 state = I2C_RCV_NEXT_BYTE;
257             break;
258         case I2C_RCV_NEXT_BYTE :
259             I2C_SYSLOG(LOG_NOTICE, "I2C_RCV_NEXT_BYTE" );
260             *(rptr++) = *twi->rcv_data8;        // 1バイト受信
261             state = I2C_RCV_WAIT;        // 次の受信待
262             break;
263         case I2C_EXIT :
264             I2C_SYSLOG(LOG_NOTICE, "I2C_EXIT" );
265             * twi->master_ctl = 0;            // マスターをディセーブルして終了
266             quit = TRUE;
267             break;
268         }
269     } while ( !quit );
270
271     // タスクの優先順位を元に戻す
272     chg_pri( TSK_SELF, old_priority);
273
274     // 排他区間の終了
275     sig_sem(twi->blocking);
276
277         // 割り込みステータスを返す。正常終了なら0
278     return twi->intr_state;
279 }
280
281 /**
282  * \brief i2c 割り込みサービスルーチン本体
283  * \param peripheral I2Cペリフェラル番号。0から始まる。
284  * \details
285  * 割り込みサービスルーチンから呼び出す。この関数が割り込みハンドラの実体である。
286  * チップに複数のTWIペリフェラルがある場合には、引数に当該TWIペリフェラル番号を与えて呼び出す。
287  *
288  * 関数内部では、タスクに割り込みが入ったことを知らせ、ペリフェラルの割り込みステータスを
289  * 変数に退避してから戻る。
290  */
291 static void i2c_master_handler( int peripheral )
292 {
293     struct I2C_MASTER_CONTROL_TYPE *twi;
294
295         // 可読性向上のため、TWI管理構造体のポインタを設定する。
296     twi = &i2c_control[peripheral];
297
298         // TWIペリフェラルの割り込みステータスを取得し、コピーを保存する。
299         // コピーを保存するのは、このあと割り込みクリアで消えるからである。
300     twi->intr_state = *twi->int_stat;
301
302         // 割り込みクリア。すべての割り込み要素をクリアしてしまう。ステータスはコピーしているのでタスクで処理する。
303     *twi->int_stat = 0xFF;
304
305         // 割り込みクリアが確定するまで待つ。
306     ssync();
307
308     /* 通知はセマフォを使う。タスクは i2c_master_write_read()の中で待っている。 */
309     isig_sem(twi->signal);
310 }
311
312
313 /**
314  * \details
315  * TOPPERS/JSPでは、DEF_INHから割り込みハンドラに引数を渡せない。そのため、この関数内で
316  * i2c_master_handler() を引数0決め打ちで呼ぶ。
317  */
318 void i2c0_master_handler(void)
319 {
320     i2c_master_handler( 0 );
321 }
322
323
324 /**
325  * \brief i2c イニシャライザ
326  * \param exinf ペリフェラル番号。TWI0ならば、0を渡す。
327  * \details
328  * I2C用のコントロールブロックのI2C相当部分を初期化する。
329  */
330 void i2c_master_initialize(VP_INT exinf)
331 {
332     struct I2C_MASTER_CONTROL_TYPE *twi;
333     unsigned int peripheral = (unsigned int)exinf;
334
335         // 可読性向上のため、TWI管理構造体のポインタを設定する。
336     twi = &i2c_control[peripheral];
337
338     twi->blocking = SEM_I2C0_BLOCK;    // ペリフェラルブロック用セマフォ
339     twi->signal = SEM_I2C0_SIGNAL;    // 割り込み・タスク通信用セマフォ
340         // レジスタアドレスを設定
341     twi->clkdiv         =    pTWI_CLKDIV    ;
342     twi->control        =    pTWI_CONTROL    ;
343     twi->master_ctl     =    pTWI_MASTER_CTL    ;
344     twi->master_stat    =    pTWI_MASTER_STAT    ;
345     twi->master_addr    =    pTWI_MASTER_ADDR    ;
346     twi->int_stat       =    pTWI_INT_STAT    ;
347     twi->int_mask       =    pTWI_INT_MASK    ;
348     twi->fifo_ctl       =    pTWI_FIFO_CTL    ;
349     twi->fifo_stat      =    pTWI_FIFO_STAT    ;
350     twi->xmt_data8      =    pTWI_XMT_DATA8    ;
351     twi->rcv_data8      =    pTWI_RCV_DATA8    ;
352     twi->slave_ctl      =    pTWI_SLAVE_CTL    ;
353
354
355     *twi->control = 0;        // TWI をディセーブル
356     *twi->master_ctl = 0;    // マスター機能をディセーブル
357     *twi->slave_ctl = 0;    // スレーブ機能をディセーブル
358     *twi->master_addr = 0;
359     *twi->fifo_ctl = 0;
360     *twi->int_mask = 0;
361     *twi->int_stat = 0x1F;  // すべての割り込みステータスをクリア
362     *twi->master_stat =0x1F; // すべてのステータスをクリア
363
364     *twi->control = TWI_ENA | (SYSCLOCK/10000000);     // HWRによると、プリスケール値はSYSCLKを10MHzで割ったものでなければならない。
365     *twi->clkdiv = 30<<8 | 70;                         // TWI内部クロック10MHzに対して、100kHzのI2Cクロックは100分の1である
366
367         // 割り込みイネーブル設定。送受割り込み、完了割り込み及びエラー割り込み。
368     *twi->int_mask =     MCOMP | MERR | XMTSERV | RCVSERV;
369
370         // システム割り込みをイネーブルにする。
371     ena_int(INTNO_TWI);
372 }
373
374