diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-06-17 22:00:50 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-07-11 04:22:46 -0400 |
commit | 3a3dd0186f619b74e61e6f29dddcaf59af7d3cac (patch) | |
tree | f8a42e6cc66c4df6f48857f92e5f977fed98e35c /drivers/i2c/busses/i2c-powermac.c | |
parent | e1612de9e4cdf375c3cf1c72434ab8abdcb3927e (diff) |
i2c/powermac: Improve detection of devices from device-tree
This patch adds a number of workarounds for broken Apple device-trees
mostly around sound chips. It handles creating the missing audio codec
devices and works around various issues with missing addresses or
missing compatible properties.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers/i2c/busses/i2c-powermac.c')
-rw-r--r-- | drivers/i2c/busses/i2c-powermac.c | 157 |
1 files changed, 133 insertions, 24 deletions
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c index 31c47e18d83c..5285f8565de4 100644 --- a/drivers/i2c/busses/i2c-powermac.c +++ b/drivers/i2c/busses/i2c-powermac.c | |||
@@ -227,28 +227,138 @@ static int __devexit i2c_powermac_remove(struct platform_device *dev) | |||
227 | return 0; | 227 | return 0; |
228 | } | 228 | } |
229 | 229 | ||
230 | static u32 __devinit i2c_powermac_get_addr(struct i2c_adapter *adap, | ||
231 | struct pmac_i2c_bus *bus, | ||
232 | struct device_node *node) | ||
233 | { | ||
234 | const __be32 *prop; | ||
235 | int len; | ||
236 | |||
237 | /* First check for valid "reg" */ | ||
238 | prop = of_get_property(node, "reg", &len); | ||
239 | if (prop && (len >= sizeof(int))) | ||
240 | return (be32_to_cpup(prop) & 0xff) >> 1; | ||
241 | |||
242 | /* Then check old-style "i2c-address" */ | ||
243 | prop = of_get_property(node, "i2c-address", &len); | ||
244 | if (prop && (len >= sizeof(int))) | ||
245 | return (be32_to_cpup(prop) & 0xff) >> 1; | ||
246 | |||
247 | /* Now handle some devices with missing "reg" properties */ | ||
248 | if (!strcmp(node->name, "cereal")) | ||
249 | return 0x60; | ||
250 | else if (!strcmp(node->name, "deq")) | ||
251 | return 0x34; | ||
252 | |||
253 | dev_warn(&adap->dev, "No i2c address for %s\n", node->full_name); | ||
254 | |||
255 | return 0xffffffff; | ||
256 | } | ||
257 | |||
258 | static void __devinit i2c_powermac_create_one(struct i2c_adapter *adap, | ||
259 | const char *type, | ||
260 | u32 addr) | ||
261 | { | ||
262 | struct i2c_board_info info = {}; | ||
263 | struct i2c_client *newdev; | ||
264 | |||
265 | strncpy(info.type, type, sizeof(info.type)); | ||
266 | info.addr = addr; | ||
267 | newdev = i2c_new_device(adap, &info); | ||
268 | if (!newdev) | ||
269 | dev_err(&adap->dev, | ||
270 | "i2c-powermac: Failure to register missing %s\n", | ||
271 | type); | ||
272 | } | ||
273 | |||
274 | static void __devinit i2c_powermac_add_missing(struct i2c_adapter *adap, | ||
275 | struct pmac_i2c_bus *bus, | ||
276 | bool found_onyx) | ||
277 | { | ||
278 | struct device_node *busnode = pmac_i2c_get_bus_node(bus); | ||
279 | int rc; | ||
280 | |||
281 | /* Check for the onyx audio codec */ | ||
282 | #define ONYX_REG_CONTROL 67 | ||
283 | if (of_device_is_compatible(busnode, "k2-i2c") && !found_onyx) { | ||
284 | union i2c_smbus_data data; | ||
285 | |||
286 | rc = i2c_smbus_xfer(adap, 0x46, 0, I2C_SMBUS_READ, | ||
287 | ONYX_REG_CONTROL, I2C_SMBUS_BYTE_DATA, | ||
288 | &data); | ||
289 | if (rc >= 0) | ||
290 | i2c_powermac_create_one(adap, "MAC,pcm3052", 0x46); | ||
291 | |||
292 | rc = i2c_smbus_xfer(adap, 0x47, 0, I2C_SMBUS_READ, | ||
293 | ONYX_REG_CONTROL, I2C_SMBUS_BYTE_DATA, | ||
294 | &data); | ||
295 | if (rc >= 0) | ||
296 | i2c_powermac_create_one(adap, "MAC,pcm3052", 0x47); | ||
297 | } | ||
298 | } | ||
299 | |||
300 | static bool __devinit i2c_powermac_get_type(struct i2c_adapter *adap, | ||
301 | struct device_node *node, | ||
302 | u32 addr, char *type, int type_size) | ||
303 | { | ||
304 | char tmp[16]; | ||
305 | |||
306 | /* Note: we to _NOT_ want the standard | ||
307 | * i2c drivers to match with any of our powermac stuff | ||
308 | * unless they have been specifically modified to handle | ||
309 | * it on a case by case basis. For example, for thermal | ||
310 | * control, things like lm75 etc... shall match with their | ||
311 | * corresponding windfarm drivers, _NOT_ the generic ones, | ||
312 | * so we force a prefix of AAPL, onto the modalias to | ||
313 | * make that happen | ||
314 | */ | ||
315 | |||
316 | /* First try proper modalias */ | ||
317 | if (of_modalias_node(node, tmp, sizeof(tmp)) >= 0) { | ||
318 | snprintf(type, type_size, "MAC,%s", tmp); | ||
319 | return true; | ||
320 | } | ||
321 | |||
322 | /* Now look for known workarounds */ | ||
323 | if (!strcmp(node->name, "deq")) { | ||
324 | /* Apple uses address 0x34 for TAS3001 and 0x35 for TAS3004 */ | ||
325 | if (addr == 0x34) { | ||
326 | snprintf(type, type_size, "MAC,tas3001"); | ||
327 | return true; | ||
328 | } else if (addr == 0x35) { | ||
329 | snprintf(type, type_size, "MAC,tas3004"); | ||
330 | return true; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | dev_err(&adap->dev, "i2c-powermac: modalias failure" | ||
335 | " on %s\n", node->full_name); | ||
336 | return false; | ||
337 | } | ||
338 | |||
230 | static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap, | 339 | static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap, |
231 | struct pmac_i2c_bus *bus) | 340 | struct pmac_i2c_bus *bus) |
232 | { | 341 | { |
233 | struct i2c_client *newdev; | 342 | struct i2c_client *newdev; |
234 | struct device_node *node; | 343 | struct device_node *node; |
344 | bool found_onyx = 0; | ||
345 | |||
346 | /* | ||
347 | * In some cases we end up with the via-pmu node itself, in this | ||
348 | * case we skip this function completely as the device-tree will | ||
349 | * not contain anything useful. | ||
350 | */ | ||
351 | if (!strcmp(adap->dev.of_node->name, "via-pmu")) | ||
352 | return; | ||
235 | 353 | ||
236 | for_each_child_of_node(adap->dev.of_node, node) { | 354 | for_each_child_of_node(adap->dev.of_node, node) { |
237 | struct i2c_board_info info = {}; | 355 | struct i2c_board_info info = {}; |
238 | struct dev_archdata dev_ad = {}; | ||
239 | const __be32 *reg; | ||
240 | char tmp[16]; | ||
241 | u32 addr; | 356 | u32 addr; |
242 | int len; | ||
243 | 357 | ||
244 | /* Get address & channel */ | 358 | /* Get address & channel */ |
245 | reg = of_get_property(node, "reg", &len); | 359 | addr = i2c_powermac_get_addr(adap, bus, node); |
246 | if (!reg || (len < sizeof(int))) { | 360 | if (addr == 0xffffffff) |
247 | dev_err(&adap->dev, "i2c-powermac: invalid reg on %s\n", | ||
248 | node->full_name); | ||
249 | continue; | 361 | continue; |
250 | } | ||
251 | addr = be32_to_cpup(reg); | ||
252 | 362 | ||
253 | /* Multibus setup, check channel */ | 363 | /* Multibus setup, check channel */ |
254 | if (!pmac_i2c_match_adapter(node, adap)) | 364 | if (!pmac_i2c_match_adapter(node, adap)) |
@@ -257,27 +367,23 @@ static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap, | |||
257 | dev_dbg(&adap->dev, "i2c-powermac: register %s\n", | 367 | dev_dbg(&adap->dev, "i2c-powermac: register %s\n", |
258 | node->full_name); | 368 | node->full_name); |
259 | 369 | ||
260 | /* Make up a modalias. Note: we to _NOT_ want the standard | 370 | /* |
261 | * i2c drivers to match with any of our powermac stuff | 371 | * Keep track of some device existence to handle |
262 | * unless they have been specifically modified to handle | 372 | * workarounds later. |
263 | * it on a case by case basis. For example, for thermal | ||
264 | * control, things like lm75 etc... shall match with their | ||
265 | * corresponding windfarm drivers, _NOT_ the generic ones, | ||
266 | * so we force a prefix of AAPL, onto the modalias to | ||
267 | * make that happen | ||
268 | */ | 373 | */ |
269 | if (of_modalias_node(node, tmp, sizeof(tmp)) < 0) { | 374 | if (of_device_is_compatible(node, "pcm3052")) |
270 | dev_err(&adap->dev, "i2c-powermac: modalias failure" | 375 | found_onyx = true; |
271 | " on %s\n", node->full_name); | 376 | |
377 | /* Make up a modalias */ | ||
378 | if (!i2c_powermac_get_type(adap, node, addr, | ||
379 | info.type, sizeof(info.type))) { | ||
272 | continue; | 380 | continue; |
273 | } | 381 | } |
274 | snprintf(info.type, sizeof(info.type), "MAC,%s", tmp); | ||
275 | 382 | ||
276 | /* Fill out the rest of the info structure */ | 383 | /* Fill out the rest of the info structure */ |
277 | info.addr = (addr & 0xff) >> 1; | 384 | info.addr = addr; |
278 | info.irq = irq_of_parse_and_map(node, 0); | 385 | info.irq = irq_of_parse_and_map(node, 0); |
279 | info.of_node = of_node_get(node); | 386 | info.of_node = of_node_get(node); |
280 | info.archdata = &dev_ad; | ||
281 | 387 | ||
282 | newdev = i2c_new_device(adap, &info); | 388 | newdev = i2c_new_device(adap, &info); |
283 | if (!newdev) { | 389 | if (!newdev) { |
@@ -292,6 +398,9 @@ static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap, | |||
292 | continue; | 398 | continue; |
293 | } | 399 | } |
294 | } | 400 | } |
401 | |||
402 | /* Additional workarounds */ | ||
403 | i2c_powermac_add_missing(adap, bus, found_onyx); | ||
295 | } | 404 | } |
296 | 405 | ||
297 | static int __devinit i2c_powermac_probe(struct platform_device *dev) | 406 | static int __devinit i2c_powermac_probe(struct platform_device *dev) |