aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c/busses/i2c-powermac.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-06-17 22:00:50 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-07-11 04:22:46 -0400
commit3a3dd0186f619b74e61e6f29dddcaf59af7d3cac (patch)
treef8a42e6cc66c4df6f48857f92e5f977fed98e35c /drivers/i2c/busses/i2c-powermac.c
parente1612de9e4cdf375c3cf1c72434ab8abdcb3927e (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.c157
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
230static 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
258static 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
274static 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
300static 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
230static void __devinit i2c_powermac_register_devices(struct i2c_adapter *adap, 339static 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
297static int __devinit i2c_powermac_probe(struct platform_device *dev) 406static int __devinit i2c_powermac_probe(struct platform_device *dev)