1 | /* $NetBSD: nouveau_subdev_i2c_bit.c,v 1.1.1.1 2014/08/06 12:36:30 riastradh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright 2012 Red Hat Inc. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
22 | * OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * Authors: Ben Skeggs |
25 | */ |
26 | |
27 | #include <sys/cdefs.h> |
28 | __KERNEL_RCSID(0, "$NetBSD: nouveau_subdev_i2c_bit.c,v 1.1.1.1 2014/08/06 12:36:30 riastradh Exp $" ); |
29 | |
30 | #include "subdev/i2c.h" |
31 | |
32 | #ifdef CONFIG_NOUVEAU_I2C_INTERNAL |
33 | #define T_TIMEOUT 2200000 |
34 | #define T_RISEFALL 1000 |
35 | #define T_HOLD 5000 |
36 | |
37 | static inline void |
38 | i2c_drive_scl(struct nouveau_i2c_port *port, int state) |
39 | { |
40 | port->func->drive_scl(port, state); |
41 | } |
42 | |
43 | static inline void |
44 | i2c_drive_sda(struct nouveau_i2c_port *port, int state) |
45 | { |
46 | port->func->drive_sda(port, state); |
47 | } |
48 | |
49 | static inline int |
50 | i2c_sense_scl(struct nouveau_i2c_port *port) |
51 | { |
52 | return port->func->sense_scl(port); |
53 | } |
54 | |
55 | static inline int |
56 | i2c_sense_sda(struct nouveau_i2c_port *port) |
57 | { |
58 | return port->func->sense_sda(port); |
59 | } |
60 | |
61 | static void |
62 | i2c_delay(struct nouveau_i2c_port *port, u32 nsec) |
63 | { |
64 | udelay((nsec + 500) / 1000); |
65 | } |
66 | |
67 | static bool |
68 | i2c_raise_scl(struct nouveau_i2c_port *port) |
69 | { |
70 | u32 timeout = T_TIMEOUT / T_RISEFALL; |
71 | |
72 | i2c_drive_scl(port, 1); |
73 | do { |
74 | i2c_delay(port, T_RISEFALL); |
75 | } while (!i2c_sense_scl(port) && --timeout); |
76 | |
77 | return timeout != 0; |
78 | } |
79 | |
80 | static int |
81 | i2c_start(struct nouveau_i2c_port *port) |
82 | { |
83 | int ret = 0; |
84 | |
85 | if (!i2c_sense_scl(port) || |
86 | !i2c_sense_sda(port)) { |
87 | i2c_drive_scl(port, 0); |
88 | i2c_drive_sda(port, 1); |
89 | if (!i2c_raise_scl(port)) |
90 | ret = -EBUSY; |
91 | } |
92 | |
93 | i2c_drive_sda(port, 0); |
94 | i2c_delay(port, T_HOLD); |
95 | i2c_drive_scl(port, 0); |
96 | i2c_delay(port, T_HOLD); |
97 | return ret; |
98 | } |
99 | |
100 | static void |
101 | i2c_stop(struct nouveau_i2c_port *port) |
102 | { |
103 | i2c_drive_scl(port, 0); |
104 | i2c_drive_sda(port, 0); |
105 | i2c_delay(port, T_RISEFALL); |
106 | |
107 | i2c_drive_scl(port, 1); |
108 | i2c_delay(port, T_HOLD); |
109 | i2c_drive_sda(port, 1); |
110 | i2c_delay(port, T_HOLD); |
111 | } |
112 | |
113 | static int |
114 | i2c_bitw(struct nouveau_i2c_port *port, int sda) |
115 | { |
116 | i2c_drive_sda(port, sda); |
117 | i2c_delay(port, T_RISEFALL); |
118 | |
119 | if (!i2c_raise_scl(port)) |
120 | return -ETIMEDOUT; |
121 | i2c_delay(port, T_HOLD); |
122 | |
123 | i2c_drive_scl(port, 0); |
124 | i2c_delay(port, T_HOLD); |
125 | return 0; |
126 | } |
127 | |
128 | static int |
129 | i2c_bitr(struct nouveau_i2c_port *port) |
130 | { |
131 | int sda; |
132 | |
133 | i2c_drive_sda(port, 1); |
134 | i2c_delay(port, T_RISEFALL); |
135 | |
136 | if (!i2c_raise_scl(port)) |
137 | return -ETIMEDOUT; |
138 | i2c_delay(port, T_HOLD); |
139 | |
140 | sda = i2c_sense_sda(port); |
141 | |
142 | i2c_drive_scl(port, 0); |
143 | i2c_delay(port, T_HOLD); |
144 | return sda; |
145 | } |
146 | |
147 | static int |
148 | i2c_get_byte(struct nouveau_i2c_port *port, u8 *byte, bool last) |
149 | { |
150 | int i, bit; |
151 | |
152 | *byte = 0; |
153 | for (i = 7; i >= 0; i--) { |
154 | bit = i2c_bitr(port); |
155 | if (bit < 0) |
156 | return bit; |
157 | *byte |= bit << i; |
158 | } |
159 | |
160 | return i2c_bitw(port, last ? 1 : 0); |
161 | } |
162 | |
163 | static int |
164 | i2c_put_byte(struct nouveau_i2c_port *port, u8 byte) |
165 | { |
166 | int i, ret; |
167 | for (i = 7; i >= 0; i--) { |
168 | ret = i2c_bitw(port, !!(byte & (1 << i))); |
169 | if (ret < 0) |
170 | return ret; |
171 | } |
172 | |
173 | ret = i2c_bitr(port); |
174 | if (ret == 1) /* nack */ |
175 | ret = -EIO; |
176 | return ret; |
177 | } |
178 | |
179 | static int |
180 | i2c_addr(struct nouveau_i2c_port *port, struct i2c_msg *msg) |
181 | { |
182 | u32 addr = msg->addr << 1; |
183 | if (msg->flags & I2C_M_RD) |
184 | addr |= 1; |
185 | return i2c_put_byte(port, addr); |
186 | } |
187 | |
188 | static int |
189 | i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) |
190 | { |
191 | struct nouveau_i2c_port *port = adap->algo_data; |
192 | struct i2c_msg *msg = msgs; |
193 | int ret = 0, mcnt = num; |
194 | |
195 | if (port->func->acquire) |
196 | port->func->acquire(port); |
197 | |
198 | while (!ret && mcnt--) { |
199 | u8 remaining = msg->len; |
200 | u8 *ptr = msg->buf; |
201 | |
202 | ret = i2c_start(port); |
203 | if (ret == 0) |
204 | ret = i2c_addr(port, msg); |
205 | |
206 | if (msg->flags & I2C_M_RD) { |
207 | while (!ret && remaining--) |
208 | ret = i2c_get_byte(port, ptr++, !remaining); |
209 | } else { |
210 | while (!ret && remaining--) |
211 | ret = i2c_put_byte(port, *ptr++); |
212 | } |
213 | |
214 | msg++; |
215 | } |
216 | |
217 | i2c_stop(port); |
218 | return (ret < 0) ? ret : num; |
219 | } |
220 | #else |
221 | static int |
222 | i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) |
223 | { |
224 | return -ENODEV; |
225 | } |
226 | #endif |
227 | |
228 | static u32 |
229 | i2c_bit_func(struct i2c_adapter *adap) |
230 | { |
231 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
232 | } |
233 | |
234 | const struct i2c_algorithm nouveau_i2c_bit_algo = { |
235 | .master_xfer = i2c_bit_xfer, |
236 | .functionality = i2c_bit_func |
237 | }; |
238 | |