diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2006-02-22 14:50:57 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2006-02-22 14:50:57 +0000 |
commit | 2a2d346b08002fd4c83229960728e7a5dad3144f (patch) | |
tree | 0b5d57157028ec430c5cac64a51ace66e21af836 | |
parent | 37b15d3833a8663458a541a2ad1e20fc66b233cf (diff) | |
download | rockbox-2a2d346b08002fd4c83229960728e7a5dad3144f.tar.gz rockbox-2a2d346b08002fd4c83229960728e7a5dad3144f.zip |
Generic bitbang I2C driver
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8782 a1c6a512-1295-4272-9138-f99709370657
-rwxr-xr-x | firmware/drivers/generic_i2c.c | 204 | ||||
-rwxr-xr-x | firmware/export/generic_i2c.h | 53 |
2 files changed, 257 insertions, 0 deletions
diff --git a/firmware/drivers/generic_i2c.c b/firmware/drivers/generic_i2c.c new file mode 100755 index 0000000000..8475f00ee6 --- /dev/null +++ b/firmware/drivers/generic_i2c.c | |||
@@ -0,0 +1,204 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Linus Nielsen Feltzing | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "config.h" | ||
20 | #include "cpu.h" | ||
21 | #include <stdbool.h> | ||
22 | #include <stdlib.h> | ||
23 | #include "debug.h" | ||
24 | #include "logf.h" | ||
25 | #include "generic_i2c.h" | ||
26 | |||
27 | int i2c_num_ifs = 0; | ||
28 | struct i2c_interface *i2c_if[5]; | ||
29 | |||
30 | static void i2c_start(struct i2c_interface *iface) | ||
31 | { | ||
32 | iface->sda_hi(); | ||
33 | iface->scl_hi(); | ||
34 | iface->sda_lo(); | ||
35 | iface->delay_hd_sta(); | ||
36 | iface->scl_lo(); | ||
37 | } | ||
38 | |||
39 | static void i2c_stop(struct i2c_interface *iface) | ||
40 | { | ||
41 | iface->sda_lo(); | ||
42 | iface->scl_hi(); | ||
43 | iface->delay_su_sto(); | ||
44 | iface->sda_hi(); | ||
45 | } | ||
46 | |||
47 | static void i2c_ack(struct i2c_interface *iface, bool ack) | ||
48 | { | ||
49 | iface->scl_lo(); | ||
50 | if ( ack ) | ||
51 | iface->sda_lo(); | ||
52 | else | ||
53 | iface->sda_hi(); | ||
54 | |||
55 | iface->delay_su_dat(); | ||
56 | iface->scl_hi(); | ||
57 | iface->delay_thigh(); | ||
58 | iface->scl_lo(); | ||
59 | } | ||
60 | |||
61 | static int i2c_getack(struct i2c_interface *iface) | ||
62 | { | ||
63 | int ret = 1; | ||
64 | |||
65 | iface->sda_input(); | ||
66 | iface->delay_su_dat(); | ||
67 | iface->scl_hi(); | ||
68 | |||
69 | if (iface->sda()) | ||
70 | ret = 0; /* ack failed */ | ||
71 | |||
72 | iface->delay_thigh(); | ||
73 | iface->scl_lo(); | ||
74 | iface->sda_hi(); | ||
75 | iface->sda_output(); | ||
76 | iface->delay_hd_dat(); | ||
77 | return ret; | ||
78 | } | ||
79 | |||
80 | static unsigned char i2c_inb(struct i2c_interface *iface, bool ack) | ||
81 | { | ||
82 | int i; | ||
83 | unsigned char byte = 0; | ||
84 | |||
85 | /* clock in each bit, MSB first */ | ||
86 | for ( i=0x80; i; i>>=1 ) { | ||
87 | iface->sda_input(); | ||
88 | iface->scl_hi(); | ||
89 | iface->delay_thigh(); | ||
90 | if (iface->sda()) | ||
91 | byte |= i; | ||
92 | iface->scl_lo(); | ||
93 | iface->delay_hd_dat(); | ||
94 | iface->sda_output(); | ||
95 | } | ||
96 | |||
97 | i2c_ack(iface, ack); | ||
98 | |||
99 | return byte; | ||
100 | } | ||
101 | |||
102 | static void i2c_outb(struct i2c_interface *iface, unsigned char byte) | ||
103 | { | ||
104 | int i; | ||
105 | |||
106 | /* clock out each bit, MSB first */ | ||
107 | for (i=0x80; i; i>>=1) { | ||
108 | if (i & byte) | ||
109 | iface->sda_hi(); | ||
110 | else | ||
111 | iface->sda_lo(); | ||
112 | iface->delay_su_dat(); | ||
113 | iface->scl_hi(); | ||
114 | iface->delay_thigh(); | ||
115 | iface->scl_lo(); | ||
116 | iface->delay_hd_dat(); | ||
117 | } | ||
118 | |||
119 | iface->sda_hi(); | ||
120 | } | ||
121 | |||
122 | static struct i2c_interface *find_if(int address) | ||
123 | { | ||
124 | int i; | ||
125 | |||
126 | for(i = 0;i < i2c_num_ifs;i++) { | ||
127 | if(i2c_if[i]->address == address) | ||
128 | return i2c_if[i]; | ||
129 | } | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | int i2c_write_data(int bus_address, int address, | ||
134 | const unsigned char* buf, int count) | ||
135 | { | ||
136 | int i; | ||
137 | int ret = 0; | ||
138 | struct i2c_interface *iface = find_if(bus_address); | ||
139 | if(!iface) | ||
140 | return -1; | ||
141 | |||
142 | i2c_start(iface); | ||
143 | i2c_outb(iface, iface->address & 0xfe); | ||
144 | if (i2c_getack(iface)) { | ||
145 | i2c_outb(iface, address); | ||
146 | if (i2c_getack(iface)) { | ||
147 | for(i = 0;i < count;i++) { | ||
148 | i2c_outb(iface, buf[i]); | ||
149 | if (!i2c_getack(iface)) { | ||
150 | ret = -3; | ||
151 | break; | ||
152 | } | ||
153 | } | ||
154 | } else { | ||
155 | ret = -2; | ||
156 | } | ||
157 | } else { | ||
158 | logf("i2c_write_data() - no ack\n"); | ||
159 | ret = -1; | ||
160 | } | ||
161 | |||
162 | i2c_stop(iface); | ||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | int i2c_read_data(int bus_address, int address, | ||
167 | unsigned char* buf, int count) | ||
168 | { | ||
169 | int i; | ||
170 | int ret = 0; | ||
171 | struct i2c_interface *iface = find_if(bus_address); | ||
172 | if(!iface) | ||
173 | return -1; | ||
174 | |||
175 | i2c_start(iface); | ||
176 | i2c_outb(iface, iface->address & 0xfe); | ||
177 | if (i2c_getack(iface)) { | ||
178 | i2c_outb(iface, address); | ||
179 | if (i2c_getack(iface)) { | ||
180 | i2c_start(iface); | ||
181 | i2c_outb(iface, iface->address | 1); | ||
182 | if (i2c_getack(iface)) { | ||
183 | for(i = 0;i < count-1;i++) | ||
184 | buf[i] = i2c_inb(iface, true); | ||
185 | |||
186 | buf[i] = i2c_inb(iface, false); | ||
187 | } else { | ||
188 | ret = -3; | ||
189 | } | ||
190 | } else { | ||
191 | ret = -2; | ||
192 | } | ||
193 | } else { | ||
194 | ret = -1; | ||
195 | } | ||
196 | |||
197 | i2c_stop(iface); | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | void i2c_add_node(struct i2c_interface *iface) | ||
202 | { | ||
203 | i2c_if[i2c_num_ifs++] = iface; | ||
204 | } | ||
diff --git a/firmware/export/generic_i2c.h b/firmware/export/generic_i2c.h new file mode 100755 index 0000000000..5570d940fb --- /dev/null +++ b/firmware/export/generic_i2c.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Linus Nielsen Feltzing | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #ifndef _GEN_I2C_ | ||
20 | #define _GEN_I2C_ | ||
21 | |||
22 | struct i2c_interface | ||
23 | { | ||
24 | unsigned char address; /* Address of the chip that this interface | ||
25 | describes */ | ||
26 | |||
27 | void (*scl_hi)(void); /* Drive SCL high, might sleep on clk stretch */ | ||
28 | void (*scl_lo)(void); /* Drive SCL low */ | ||
29 | void (*sda_hi)(void); /* Drive SDA high */ | ||
30 | void (*sda_lo)(void); /* Drive SDA low */ | ||
31 | void (*sda_input)(void); /* Set SDA as input */ | ||
32 | void (*sda_output)(void); /* Set SDA as output */ | ||
33 | void (*scl_input)(void); /* Set SCL as input */ | ||
34 | void (*scl_output)(void); /* Set SCL as output */ | ||
35 | int (*scl)(void); /* Read SCL, returns 0 or non-zero */ | ||
36 | int (*sda)(void); /* Read SDA, returns 0 or non-zero */ | ||
37 | |||
38 | /* These are the delays specified in the I2C specification, the | ||
39 | time pairs to the right are the minimum 100kHz and 400kHz specs */ | ||
40 | void (*delay_hd_sta)(void); /* START SDA hold (tHD:SDA) 4.0us/0.6us */ | ||
41 | void (*delay_hd_dat)(void); /* SDA hold (tHD:DAT) 5.0us/0us */ | ||
42 | void (*delay_su_dat)(void); /* SDA setup (tSU:DAT) 250ns/100ns */ | ||
43 | void (*delay_su_sto)(void); /* STOP setup (tSU:STO) 4.0us/0.6us */ | ||
44 | void (*delay_su_sta)(void); /* Rep. START setup (tSU:STA) 4.7us/0.6us */ | ||
45 | void (*delay_thigh)(void); /* SCL high period (tHIGH) 4.0us/0.6us */ | ||
46 | }; | ||
47 | |||
48 | extern void i2c_add_node(struct i2c_interface *iface); | ||
49 | extern int i2c_write_data(int bus_address, int address, | ||
50 | const unsigned char* buf, int count); | ||
51 | extern int i2c_read_data(int bus_address, int address, | ||
52 | unsigned char* buf, int count); | ||
53 | #endif | ||