Imported Upstream version 3.2.2
[debian/gnuradio] / usrp2 / firmware / lib / i2c.c
1 /* -*- c -*- */
2 /*
3  * Copyright 2007 Free Software Foundation, Inc.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
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.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "i2c.h"
20 #include "memory_map.h"
21 #include "stdint.h"
22
23 #define MAX_WB_DIV 4    // maximum wishbone divisor (from 100 MHz MASTER_CLK)
24
25 // prescaler divisor values for 100 kHz I2C [uses 5 * SCLK internally]
26
27 #define PRESCALER(wb_div) (((MASTER_CLK_RATE/(wb_div)) / (5 * 100000)) - 1)
28
29 static uint16_t prescaler_values[MAX_WB_DIV+1] = {
30   0xffff,       // 0: can't happen
31   PRESCALER(1), // 1: 100 MHz
32   PRESCALER(2), // 2:  50 MHz
33   PRESCALER(3), // 3:  33.333 MHz
34   PRESCALER(4), // 4:  25 MHz
35 };
36
37 void
38 i2c_init(void)
39 {
40   i2c_regs->ctrl = 0;           // disable core
41   
42   // setup prescaler depending on wishbone divisor
43   int wb_div = hwconfig_wishbone_divisor();
44   if (wb_div > MAX_WB_DIV)
45     wb_div = MAX_WB_DIV;
46
47   i2c_regs->prescaler_lo = prescaler_values[wb_div] & 0xff;
48   i2c_regs->prescaler_hi = (prescaler_values[wb_div] >> 8) & 0xff;
49
50   i2c_regs->ctrl = I2C_CTRL_EN; // enable core
51
52   // FIXME interrupt driven?
53 }
54
55 static inline void
56 wait_for_xfer(void)
57 {
58   while (i2c_regs->cmd_status & I2C_ST_TIP)     // wait for xfer to complete
59     ;
60 }
61
62 static inline bool
63 wait_chk_ack(void)
64 {
65   wait_for_xfer();
66
67   if ((i2c_regs->cmd_status & I2C_ST_RXACK) != 0){      // target NAK'd
68     return false;
69   }
70   return true;
71 }
72
73 bool 
74 i2c_read (unsigned char i2c_addr, unsigned char *buf, unsigned int len)
75 {
76   if (len == 0)                 // reading zero bytes always works
77     return true;
78
79   while (i2c_regs->cmd_status & I2C_ST_BUSY)
80     ;
81
82   i2c_regs->data = (i2c_addr << 1) | 1;  // 7 bit address and read bit (1)
83   // generate START and write addr
84   i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START;
85   if (!wait_chk_ack())
86     goto fail;
87
88   for (; len > 0; buf++, len--){
89     i2c_regs->cmd_status = I2C_CMD_RD | (len == 1 ? (I2C_CMD_NACK | I2C_CMD_STOP) : 0);
90     wait_for_xfer();
91     *buf = i2c_regs->data;
92   }
93   return true;
94
95  fail:
96   i2c_regs->cmd_status = I2C_CMD_STOP;  // generate STOP
97   return false;
98 }
99
100
101 bool 
102 i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len)
103 {
104   while (i2c_regs->cmd_status & I2C_ST_BUSY)
105     ;
106
107   i2c_regs->data = (i2c_addr << 1) | 0;  // 7 bit address and write bit (0)
108
109   // generate START and write addr (and maybe STOP)
110   i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START | (len == 0 ? I2C_CMD_STOP : 0);  
111   if (!wait_chk_ack())
112     goto fail;
113
114   for (; len > 0; buf++, len--){
115     i2c_regs->data = *buf;
116     i2c_regs->cmd_status = I2C_CMD_WR | (len == 1 ? I2C_CMD_STOP : 0);
117     if (!wait_chk_ack())
118       goto fail;
119   }
120   return true;
121
122  fail:
123   i2c_regs->cmd_status = I2C_CMD_STOP;  // generate STOP
124   return false;
125 }
126
127