diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-12-21 03:09:12 -0500 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-12-21 04:01:41 -0500 |
commit | f553b79c03f0dbd52f6f03abe8233a2bef8cbd0d (patch) | |
tree | 113aab2ad223825d67f79d0e19c6cb96522db098 /drivers/gpu/drm/nouveau/nouveau_i2c.c | |
parent | 9e3b6b99075a01ce47379eee74365aece14242f3 (diff) |
drm/nouveau/i2c: handle bit-banging ourselves
i2c-algo-bit doesn't actually work very well on one card I have access to
(NVS 300), random single-bit errors occur most of the time - what we're
doing now is closer to what xf86i2c.c does.
The original plan was to figure out why i2c-algo-bit fails on the NVS 300,
and fix it. However, while investigating I discovered i2c-algo-bit calls
cond_resched(), which makes it a bad idea for us to be using as we execute
VBIOS scripts from a tasklet, and there may very well be i2c transfers as
a result.
So, since I already wrote this code in userspace to track down the NVS 300
bug, and it's not really much code - lets use it.
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_i2c.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_i2c.c | 199 |
1 files changed, 178 insertions, 21 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index 116ef3193fc7..820ae7f52044 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c | |||
@@ -29,6 +29,10 @@ | |||
29 | #include "nouveau_i2c.h" | 29 | #include "nouveau_i2c.h" |
30 | #include "nouveau_hw.h" | 30 | #include "nouveau_hw.h" |
31 | 31 | ||
32 | #define T_TIMEOUT 2200000 | ||
33 | #define T_RISEFALL 1000 | ||
34 | #define T_HOLD 5000 | ||
35 | |||
32 | static void | 36 | static void |
33 | i2c_drive_scl(void *data, int state) | 37 | i2c_drive_scl(void *data, int state) |
34 | { | 38 | { |
@@ -109,6 +113,175 @@ i2c_sense_sda(void *data) | |||
109 | return 0; | 113 | return 0; |
110 | } | 114 | } |
111 | 115 | ||
116 | static void | ||
117 | i2c_delay(struct nouveau_i2c_chan *port, u32 nsec) | ||
118 | { | ||
119 | udelay((nsec + 500) / 1000); | ||
120 | } | ||
121 | |||
122 | static bool | ||
123 | i2c_raise_scl(struct nouveau_i2c_chan *port) | ||
124 | { | ||
125 | u32 timeout = T_TIMEOUT / T_RISEFALL; | ||
126 | |||
127 | i2c_drive_scl(port, 1); | ||
128 | do { | ||
129 | i2c_delay(port, T_RISEFALL); | ||
130 | } while (!i2c_sense_scl(port) && --timeout); | ||
131 | |||
132 | return timeout != 0; | ||
133 | } | ||
134 | |||
135 | static int | ||
136 | i2c_start(struct nouveau_i2c_chan *port) | ||
137 | { | ||
138 | int ret = 0; | ||
139 | |||
140 | port->state = i2c_sense_scl(port); | ||
141 | port->state |= i2c_sense_sda(port) << 1; | ||
142 | if (port->state != 3) { | ||
143 | i2c_drive_scl(port, 0); | ||
144 | i2c_drive_sda(port, 1); | ||
145 | if (!i2c_raise_scl(port)) | ||
146 | ret = -EBUSY; | ||
147 | } | ||
148 | |||
149 | i2c_drive_sda(port, 0); | ||
150 | i2c_delay(port, T_HOLD); | ||
151 | i2c_drive_scl(port, 0); | ||
152 | i2c_delay(port, T_HOLD); | ||
153 | return ret; | ||
154 | } | ||
155 | |||
156 | static void | ||
157 | i2c_stop(struct nouveau_i2c_chan *port) | ||
158 | { | ||
159 | i2c_drive_scl(port, 0); | ||
160 | i2c_drive_sda(port, 0); | ||
161 | i2c_delay(port, T_RISEFALL); | ||
162 | |||
163 | i2c_drive_scl(port, 1); | ||
164 | i2c_delay(port, T_HOLD); | ||
165 | i2c_drive_sda(port, 1); | ||
166 | i2c_delay(port, T_HOLD); | ||
167 | } | ||
168 | |||
169 | static int | ||
170 | i2c_bitw(struct nouveau_i2c_chan *port, int sda) | ||
171 | { | ||
172 | i2c_drive_sda(port, sda); | ||
173 | i2c_delay(port, T_RISEFALL); | ||
174 | |||
175 | if (!i2c_raise_scl(port)) | ||
176 | return -ETIMEDOUT; | ||
177 | i2c_delay(port, T_HOLD); | ||
178 | |||
179 | i2c_drive_scl(port, 0); | ||
180 | i2c_delay(port, T_HOLD); | ||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static int | ||
185 | i2c_bitr(struct nouveau_i2c_chan *port) | ||
186 | { | ||
187 | int sda; | ||
188 | |||
189 | i2c_drive_sda(port, 1); | ||
190 | i2c_delay(port, T_RISEFALL); | ||
191 | |||
192 | if (!i2c_raise_scl(port)) | ||
193 | return -ETIMEDOUT; | ||
194 | i2c_delay(port, T_HOLD); | ||
195 | |||
196 | sda = i2c_sense_sda(port); | ||
197 | |||
198 | i2c_drive_scl(port, 0); | ||
199 | i2c_delay(port, T_HOLD); | ||
200 | return sda; | ||
201 | } | ||
202 | |||
203 | static int | ||
204 | i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last) | ||
205 | { | ||
206 | int i, bit; | ||
207 | |||
208 | *byte = 0; | ||
209 | for (i = 7; i >= 0; i--) { | ||
210 | bit = i2c_bitr(port); | ||
211 | if (bit < 0) | ||
212 | return bit; | ||
213 | *byte |= bit << i; | ||
214 | } | ||
215 | |||
216 | return i2c_bitw(port, last ? 1 : 0); | ||
217 | } | ||
218 | |||
219 | static int | ||
220 | i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte) | ||
221 | { | ||
222 | int i, ret; | ||
223 | for (i = 7; i >= 0; i--) { | ||
224 | ret = i2c_bitw(port, !!(byte & (1 << i))); | ||
225 | if (ret < 0) | ||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | ret = i2c_bitr(port); | ||
230 | if (ret == 1) /* nack */ | ||
231 | ret = -EIO; | ||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | static int | ||
236 | i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg) | ||
237 | { | ||
238 | u32 addr = msg->addr << 1; | ||
239 | if (msg->flags & I2C_M_RD) | ||
240 | addr |= 1; | ||
241 | return i2c_put_byte(port, addr); | ||
242 | } | ||
243 | |||
244 | static int | ||
245 | i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) | ||
246 | { | ||
247 | struct nouveau_i2c_chan *port = (struct nouveau_i2c_chan *)adap; | ||
248 | struct i2c_msg *msg = msgs; | ||
249 | int ret = 0, mcnt = num; | ||
250 | |||
251 | while (!ret && mcnt--) { | ||
252 | u8 remaining = msg->len; | ||
253 | u8 *ptr = msg->buf; | ||
254 | |||
255 | ret = i2c_start(port); | ||
256 | if (ret == 0) | ||
257 | ret = i2c_addr(port, msg); | ||
258 | |||
259 | if (msg->flags & I2C_M_RD) { | ||
260 | while (!ret && remaining--) | ||
261 | ret = i2c_get_byte(port, ptr++, !remaining); | ||
262 | } else { | ||
263 | while (!ret && remaining--) | ||
264 | ret = i2c_put_byte(port, *ptr++); | ||
265 | } | ||
266 | |||
267 | msg++; | ||
268 | } | ||
269 | |||
270 | i2c_stop(port); | ||
271 | return (ret < 0) ? ret : num; | ||
272 | } | ||
273 | |||
274 | static u32 | ||
275 | i2c_bit_func(struct i2c_adapter *adap) | ||
276 | { | ||
277 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; | ||
278 | } | ||
279 | |||
280 | const struct i2c_algorithm i2c_bit_algo = { | ||
281 | .master_xfer = i2c_bit_xfer, | ||
282 | .functionality = i2c_bit_func | ||
283 | }; | ||
284 | |||
112 | static const uint32_t nv50_i2c_port[] = { | 285 | static const uint32_t nv50_i2c_port[] = { |
113 | 0x00e138, 0x00e150, 0x00e168, 0x00e180, | 286 | 0x00e138, 0x00e150, 0x00e168, 0x00e180, |
114 | 0x00e254, 0x00e274, 0x00e764, 0x00e780, | 287 | 0x00e254, 0x00e274, 0x00e764, 0x00e780, |
@@ -211,10 +384,12 @@ nouveau_i2c_init(struct drm_device *dev) | |||
211 | case 0: /* NV04:NV50 */ | 384 | case 0: /* NV04:NV50 */ |
212 | port->drive = entry[0]; | 385 | port->drive = entry[0]; |
213 | port->sense = entry[1]; | 386 | port->sense = entry[1]; |
387 | port->adapter.algo = &i2c_bit_algo; | ||
214 | break; | 388 | break; |
215 | case 4: /* NV4E */ | 389 | case 4: /* NV4E */ |
216 | port->drive = 0x600800 + entry[1]; | 390 | port->drive = 0x600800 + entry[1]; |
217 | port->sense = port->drive; | 391 | port->sense = port->drive; |
392 | port->adapter.algo = &i2c_bit_algo; | ||
218 | break; | 393 | break; |
219 | case 5: /* NV50- */ | 394 | case 5: /* NV50- */ |
220 | port->drive = entry[0] & 0x0f; | 395 | port->drive = entry[0] & 0x0f; |
@@ -227,6 +402,7 @@ nouveau_i2c_init(struct drm_device *dev) | |||
227 | port->drive = 0x00d014 + (port->drive * 0x20); | 402 | port->drive = 0x00d014 + (port->drive * 0x20); |
228 | port->sense = port->drive; | 403 | port->sense = port->drive; |
229 | } | 404 | } |
405 | port->adapter.algo = &i2c_bit_algo; | ||
230 | break; | 406 | break; |
231 | case 6: /* NV50- DP AUX */ | 407 | case 6: /* NV50- DP AUX */ |
232 | port->drive = entry[0]; | 408 | port->drive = entry[0]; |
@@ -237,7 +413,7 @@ nouveau_i2c_init(struct drm_device *dev) | |||
237 | break; | 413 | break; |
238 | } | 414 | } |
239 | 415 | ||
240 | if (!port->adapter.algo && !port->drive) { | 416 | if (!port->adapter.algo) { |
241 | NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", | 417 | NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", |
242 | i, port->type, port->drive, port->sense); | 418 | i, port->type, port->drive, port->sense); |
243 | kfree(port); | 419 | kfree(port); |
@@ -253,26 +429,7 @@ nouveau_i2c_init(struct drm_device *dev) | |||
253 | port->dcb = ROM32(entry[0]); | 429 | port->dcb = ROM32(entry[0]); |
254 | i2c_set_adapdata(&port->adapter, i2c); | 430 | i2c_set_adapdata(&port->adapter, i2c); |
255 | 431 | ||
256 | if (port->adapter.algo != &nouveau_dp_i2c_algo) { | 432 | ret = i2c_add_adapter(&port->adapter); |
257 | port->adapter.algo_data = &port->bit; | ||
258 | port->bit.udelay = 40; | ||
259 | port->bit.timeout = usecs_to_jiffies(5000); | ||
260 | port->bit.data = port; | ||
261 | port->bit.setsda = i2c_drive_sda; | ||
262 | port->bit.setscl = i2c_drive_scl; | ||
263 | port->bit.getsda = i2c_sense_sda; | ||
264 | port->bit.getscl = i2c_sense_scl; | ||
265 | |||
266 | i2c_drive_scl(port, 0); | ||
267 | i2c_drive_sda(port, 1); | ||
268 | i2c_drive_scl(port, 1); | ||
269 | |||
270 | ret = i2c_bit_add_bus(&port->adapter); | ||
271 | } else { | ||
272 | port->adapter.algo = &nouveau_dp_i2c_algo; | ||
273 | ret = i2c_add_adapter(&port->adapter); | ||
274 | } | ||
275 | |||
276 | if (ret) { | 433 | if (ret) { |
277 | NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); | 434 | NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); |
278 | kfree(port); | 435 | kfree(port); |