Testing for the idle i2c bus was done backwards, we have
[osmocom-bb.git] / src / target / firmware / calypso / i2c.c
1 /* Driver for I2C Master Controller inside TI Calypso */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  *
5  * All Rights Reserved
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  */
22
23 #include <stdint.h>
24 #include <stdio.h>
25
26 #include <debug.h>
27 #include <memory.h>
28 #include <i2c.h>
29
30 #define BASE_ADDR_I2C   0xfffe2800
31 #define I2C_REG(x)      (BASE_ADDR_I2C+(x))
32
33 enum i2c_reg {
34         DEVICE_REG      = 0,
35         ADDRESS_REG,
36         DATA_WR_REG,    
37         DATA_RD_REG,
38         CMD_REG,
39         CONF_FIFO_REG,
40         CONF_CLK_REG,
41         CONF_CLK_FUNC_REF,
42         STATUS_FIFO_REG,
43         STATUS_ACTIVITY_REG,
44 };
45
46 #define I2C_CMD_SOFT_RESET      (1 << 0)
47 #define I2C_CMD_EN_CLK          (1 << 1)
48 #define I2C_CMD_START           (1 << 2)
49 #define I2C_CMD_RW_READ         (1 << 3)
50 #define I2C_CMD_COMP_READ       (1 << 4)
51 #define I2C_CMD_IRQ_ENABLE      (1 << 5)
52
53 #define I2C_STATUS_ERROR_DATA   (1 << 0)
54 #define I2C_STATUS_ERROR_DEV    (1 << 1)
55 #define I2C_STATUS_IDLE         (1 << 2) // 1: not idle, 0: idle
56 #define I2C_STATUS_INTERRUPT    (1 << 3)
57
58 int i2c_write(uint8_t chip, uint32_t addr, int alen, const uint8_t *buffer, int len)
59 {
60         uint8_t cmd;
61
62         /* Calypso I2C controller doesn't support fancy addressing */
63         if (alen > 1)
64                 return -1;
65
66         /* FIXME: implement writes longer than fifo size */
67         if (len > 16)
68                 return -1;
69
70         printd("i2c_write(chip=0x%02u, addr=0x%02u): ", chip, addr)
71
72         writeb(chip & 0x3f, I2C_REG(DEVICE_REG));
73         writeb(addr & 0xff, I2C_REG(ADDRESS_REG));
74         
75         /* we have to tell the controler how many bits we'll put into the fifo ?!? */
76         writeb(len-1, I2C_REG(CONF_FIFO_REG));
77
78         /* fill the FIFO */
79         while (len--) {
80                 uint8_t byte = *buffer++;
81                 writeb(byte, I2C_REG(DATA_WR_REG));
82                 printd("%02X ", byte);
83         }
84         dputchar('\n');
85
86         /* start the transfer */
87         cmd = readb(I2C_REG(CMD_REG));
88         cmd |= I2C_CMD_START;
89         writeb(cmd, I2C_REG(CMD_REG));
90
91         /* wait until transfer completes */
92         while (1) {
93                 uint8_t reg = readb(I2C_REG(STATUS_ACTIVITY_REG));
94                 printd("I2C Status: 0x%02x\n", rerg & 0xf);
95                 if (!(reg & I2C_STATUS_IDLE)) // 0: idle 1: not idle
96                         break;
97         }
98         dputs("I2C transfer completed\n");
99
100         return 0;
101 }
102
103 void i2c_init(int speed, int slaveadd)
104 {
105         /* scl_out = clk_func_ref / 3,
106            clk_func_ref = master_clock_freq / (divisor_2 + 1)
107            master_clock_freq = ext_clock_freq / divisor_1 */
108         /* clk_func_ref = scl_out * 3,
109            divisor_2 = (master_clock_freq / clk_func_ref) - 1
110            divisor_1 = ext_clock_freq / master_clock_freq */
111         /* for a target freq of 200kHz:
112                 ext_clock_freq = 13MHz
113                 clk_func_ref = 3 * 300kHZ = 600kHz
114                 divisor_1 = 1 => master_clock_freq = ext_clock_freq = 13MHz
115                 divisor_2 = 21 => clk_func_ref = 13MHz / (21+2) = 590.91 kHz
116                 scl_out = clk_func_ref / 3 = 509.91 kHz / 3 = 196.97kHz */
117         writeb(I2C_CMD_SOFT_RESET, I2C_REG(CMD_REG));
118
119         writeb(0x00, I2C_REG(CONF_CLK_REG));
120         writeb(21, I2C_REG(CONF_CLK_FUNC_REF));
121
122         writeb(I2C_CMD_EN_CLK, I2C_REG(CMD_REG));
123 }