diff options
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/twl4030-core.c | 300 |
1 files changed, 84 insertions, 216 deletions
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index ef9a971e3ead..f5486cce86f4 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c | |||
@@ -352,258 +352,126 @@ EXPORT_SYMBOL(twl4030_i2c_read_u8); | |||
352 | 352 | ||
353 | /*----------------------------------------------------------------------*/ | 353 | /*----------------------------------------------------------------------*/ |
354 | 354 | ||
355 | /* | 355 | static struct device *add_child(unsigned chip, const char *name, |
356 | * NOTE: We know the first 8 IRQs after pdata->base_irq are | 356 | void *pdata, unsigned pdata_len, |
357 | * for the PIH, and the next are for the PWR_INT SIH, since | 357 | bool can_wakeup, int irq0, int irq1) |
358 | * that's how twl_init_irq() sets things up. | ||
359 | */ | ||
360 | |||
361 | static int add_children(struct twl4030_platform_data *pdata) | ||
362 | { | 358 | { |
363 | struct platform_device *pdev = NULL; | 359 | struct platform_device *pdev; |
364 | struct twl4030_client *twl = NULL; | 360 | struct twl4030_client *twl = &twl4030_modules[chip]; |
365 | int status = 0; | 361 | int status; |
362 | |||
363 | pdev = platform_device_alloc(name, -1); | ||
364 | if (!pdev) { | ||
365 | dev_dbg(&twl->client->dev, "can't alloc dev\n"); | ||
366 | status = -ENOMEM; | ||
367 | goto err; | ||
368 | } | ||
366 | 369 | ||
367 | if (twl_has_bci() && pdata->bci) { | 370 | device_init_wakeup(&pdev->dev, can_wakeup); |
368 | twl = &twl4030_modules[3]; | 371 | pdev->dev.parent = &twl->client->dev; |
369 | 372 | ||
370 | pdev = platform_device_alloc("twl4030_bci", -1); | 373 | if (pdata) { |
371 | if (!pdev) { | 374 | status = platform_device_add_data(pdev, pdata, pdata_len); |
372 | pr_debug("%s: can't alloc bci dev\n", DRIVER_NAME); | 375 | if (status < 0) { |
373 | status = -ENOMEM; | 376 | dev_dbg(&pdev->dev, "can't add platform_data\n"); |
374 | goto err; | 377 | goto err; |
375 | } | 378 | } |
379 | } | ||
376 | 380 | ||
377 | if (status == 0) { | 381 | if (irq0) { |
378 | pdev->dev.parent = &twl->client->dev; | 382 | struct resource r[2] = { |
379 | status = platform_device_add_data(pdev, pdata->bci, | 383 | { .start = irq0, .flags = IORESOURCE_IRQ, }, |
380 | sizeof(*pdata->bci)); | 384 | { .start = irq1, .flags = IORESOURCE_IRQ, }, |
381 | if (status < 0) { | 385 | }; |
382 | dev_dbg(&twl->client->dev, | ||
383 | "can't add bci data, %d\n", | ||
384 | status); | ||
385 | goto err; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | if (status == 0) { | ||
390 | struct resource r = { | ||
391 | .start = pdata->irq_base + 8 + 1, | ||
392 | .flags = IORESOURCE_IRQ, | ||
393 | }; | ||
394 | |||
395 | status = platform_device_add_resources(pdev, &r, 1); | ||
396 | } | ||
397 | |||
398 | if (status == 0) | ||
399 | status = platform_device_add(pdev); | ||
400 | 386 | ||
387 | status = platform_device_add_resources(pdev, r, irq1 ? 2 : 1); | ||
401 | if (status < 0) { | 388 | if (status < 0) { |
402 | platform_device_put(pdev); | 389 | dev_dbg(&pdev->dev, "can't add irqs\n"); |
403 | dev_dbg(&twl->client->dev, | ||
404 | "can't create bci dev, %d\n", | ||
405 | status); | ||
406 | goto err; | 390 | goto err; |
407 | } | 391 | } |
408 | } | 392 | } |
409 | 393 | ||
410 | if (twl_has_gpio() && pdata->gpio) { | 394 | status = platform_device_add(pdev); |
411 | twl = &twl4030_modules[1]; | ||
412 | 395 | ||
413 | pdev = platform_device_alloc("twl4030_gpio", -1); | 396 | err: |
414 | if (!pdev) { | 397 | if (status < 0) { |
415 | pr_debug("%s: can't alloc gpio dev\n", DRIVER_NAME); | 398 | platform_device_put(pdev); |
416 | status = -ENOMEM; | 399 | dev_err(&twl->client->dev, "can't add %s dev\n", name); |
417 | goto err; | 400 | return ERR_PTR(status); |
418 | } | 401 | } |
419 | 402 | return &pdev->dev; | |
420 | /* more driver model init */ | 403 | } |
421 | if (status == 0) { | ||
422 | pdev->dev.parent = &twl->client->dev; | ||
423 | /* device_init_wakeup(&pdev->dev, 1); */ | ||
424 | |||
425 | status = platform_device_add_data(pdev, pdata->gpio, | ||
426 | sizeof(*pdata->gpio)); | ||
427 | if (status < 0) { | ||
428 | dev_dbg(&twl->client->dev, | ||
429 | "can't add gpio data, %d\n", | ||
430 | status); | ||
431 | goto err; | ||
432 | } | ||
433 | } | ||
434 | 404 | ||
435 | /* GPIO module IRQ */ | 405 | /* |
436 | if (status == 0) { | 406 | * NOTE: We know the first 8 IRQs after pdata->base_irq are |
437 | struct resource r = { | 407 | * for the PIH, and the next are for the PWR_INT SIH, since |
438 | .start = pdata->irq_base + 0, | 408 | * that's how twl_init_irq() sets things up. |
439 | .flags = IORESOURCE_IRQ, | 409 | */ |
440 | }; | ||
441 | 410 | ||
442 | status = platform_device_add_resources(pdev, &r, 1); | 411 | static int add_children(struct twl4030_platform_data *pdata) |
443 | } | 412 | { |
413 | struct device *child; | ||
444 | 414 | ||
445 | if (status == 0) | 415 | if (twl_has_bci() && pdata->bci) { |
446 | status = platform_device_add(pdev); | 416 | child = add_child(3, "twl4030_bci", |
417 | pdata->bci, sizeof(*pdata->bci), | ||
418 | false, | ||
419 | /* irq0 = CHG_PRES, irq1 = BCI */ | ||
420 | pdata->irq_base + 8 + 1, pdata->irq_base + 2); | ||
421 | if (IS_ERR(child)) | ||
422 | return PTR_ERR(child); | ||
423 | } | ||
447 | 424 | ||
448 | if (status < 0) { | 425 | if (twl_has_gpio() && pdata->gpio) { |
449 | platform_device_put(pdev); | 426 | child = add_child(1, "twl4030_gpio", |
450 | dev_dbg(&twl->client->dev, | 427 | pdata->gpio, sizeof(*pdata->gpio), |
451 | "can't create gpio dev, %d\n", | 428 | false, pdata->irq_base + 0, 0); |
452 | status); | 429 | if (IS_ERR(child)) |
453 | goto err; | 430 | return PTR_ERR(child); |
454 | } | ||
455 | } | 431 | } |
456 | 432 | ||
457 | if (twl_has_keypad() && pdata->keypad) { | 433 | if (twl_has_keypad() && pdata->keypad) { |
458 | pdev = platform_device_alloc("twl4030_keypad", -1); | 434 | child = add_child(2, "twl4030_keypad", |
459 | if (pdev) { | 435 | pdata->keypad, sizeof(*pdata->keypad), |
460 | twl = &twl4030_modules[2]; | 436 | true, pdata->irq_base + 1, 0); |
461 | pdev->dev.parent = &twl->client->dev; | 437 | if (IS_ERR(child)) |
462 | device_init_wakeup(&pdev->dev, 1); | 438 | return PTR_ERR(child); |
463 | status = platform_device_add_data(pdev, pdata->keypad, | ||
464 | sizeof(*pdata->keypad)); | ||
465 | if (status < 0) { | ||
466 | dev_dbg(&twl->client->dev, | ||
467 | "can't add keypad data, %d\n", | ||
468 | status); | ||
469 | platform_device_put(pdev); | ||
470 | goto err; | ||
471 | } | ||
472 | status = platform_device_add(pdev); | ||
473 | if (status < 0) { | ||
474 | platform_device_put(pdev); | ||
475 | dev_dbg(&twl->client->dev, | ||
476 | "can't create keypad dev, %d\n", | ||
477 | status); | ||
478 | goto err; | ||
479 | } | ||
480 | } else { | ||
481 | pr_debug("%s: can't alloc keypad dev\n", DRIVER_NAME); | ||
482 | status = -ENOMEM; | ||
483 | goto err; | ||
484 | } | ||
485 | } | 439 | } |
486 | 440 | ||
487 | if (twl_has_madc() && pdata->madc) { | 441 | if (twl_has_madc() && pdata->madc) { |
488 | pdev = platform_device_alloc("twl4030_madc", -1); | 442 | child = add_child(2, "twl4030_madc", |
489 | if (pdev) { | 443 | pdata->madc, sizeof(*pdata->madc), |
490 | twl = &twl4030_modules[2]; | 444 | true, pdata->irq_base + 3, 0); |
491 | pdev->dev.parent = &twl->client->dev; | 445 | if (IS_ERR(child)) |
492 | device_init_wakeup(&pdev->dev, 1); | 446 | return PTR_ERR(child); |
493 | status = platform_device_add_data(pdev, pdata->madc, | ||
494 | sizeof(*pdata->madc)); | ||
495 | if (status < 0) { | ||
496 | platform_device_put(pdev); | ||
497 | dev_dbg(&twl->client->dev, | ||
498 | "can't add madc data, %d\n", | ||
499 | status); | ||
500 | goto err; | ||
501 | } | ||
502 | status = platform_device_add(pdev); | ||
503 | if (status < 0) { | ||
504 | platform_device_put(pdev); | ||
505 | dev_dbg(&twl->client->dev, | ||
506 | "can't create madc dev, %d\n", | ||
507 | status); | ||
508 | goto err; | ||
509 | } | ||
510 | } else { | ||
511 | pr_debug("%s: can't alloc madc dev\n", DRIVER_NAME); | ||
512 | status = -ENOMEM; | ||
513 | goto err; | ||
514 | } | ||
515 | } | 447 | } |
516 | 448 | ||
517 | if (twl_has_rtc()) { | 449 | if (twl_has_rtc()) { |
518 | twl = &twl4030_modules[3]; | ||
519 | |||
520 | pdev = platform_device_alloc("twl4030_rtc", -1); | ||
521 | if (!pdev) { | ||
522 | pr_debug("%s: can't alloc rtc dev\n", DRIVER_NAME); | ||
523 | status = -ENOMEM; | ||
524 | } else { | ||
525 | pdev->dev.parent = &twl->client->dev; | ||
526 | device_init_wakeup(&pdev->dev, 1); | ||
527 | } | ||
528 | |||
529 | /* | 450 | /* |
530 | * REVISIT platform_data here currently might use of | 451 | * REVISIT platform_data here currently might expose the |
531 | * "msecure" line ... but for now we just expect board | 452 | * "msecure" line ... but for now we just expect board |
532 | * setup to tell the chip "we are secure" at all times. | 453 | * setup to tell the chip "it's always ok to SET_TIME". |
533 | * Eventually, Linux might become more aware of such | 454 | * Eventually, Linux might become more aware of such |
534 | * HW security concerns, and "least privilege". | 455 | * HW security concerns, and "least privilege". |
535 | */ | 456 | */ |
536 | 457 | child = add_child(3, "twl4030_rtc", | |
537 | /* RTC module IRQ */ | 458 | NULL, 0, |
538 | if (status == 0) { | 459 | true, pdata->irq_base + 8 + 3, 0); |
539 | struct resource r = { | 460 | if (IS_ERR(child)) |
540 | .start = pdata->irq_base + 8 + 3, | 461 | return PTR_ERR(child); |
541 | .flags = IORESOURCE_IRQ, | ||
542 | }; | ||
543 | |||
544 | status = platform_device_add_resources(pdev, &r, 1); | ||
545 | } | ||
546 | |||
547 | if (status == 0) | ||
548 | status = platform_device_add(pdev); | ||
549 | |||
550 | if (status < 0) { | ||
551 | platform_device_put(pdev); | ||
552 | dev_dbg(&twl->client->dev, | ||
553 | "can't create rtc dev, %d\n", | ||
554 | status); | ||
555 | goto err; | ||
556 | } | ||
557 | } | 462 | } |
558 | 463 | ||
559 | if (twl_has_usb() && pdata->usb) { | 464 | if (twl_has_usb() && pdata->usb) { |
560 | twl = &twl4030_modules[0]; | 465 | child = add_child(0, "twl4030_usb", |
561 | 466 | pdata->usb, sizeof(*pdata->usb), | |
562 | pdev = platform_device_alloc("twl4030_usb", -1); | 467 | true, |
563 | if (!pdev) { | 468 | /* irq0 = USB_PRES, irq1 = USB */ |
564 | pr_debug("%s: can't alloc usb dev\n", DRIVER_NAME); | 469 | pdata->irq_base + 8 + 2, pdata->irq_base + 4); |
565 | status = -ENOMEM; | 470 | if (IS_ERR(child)) |
566 | goto err; | 471 | return PTR_ERR(child); |
567 | } | ||
568 | |||
569 | if (status == 0) { | ||
570 | pdev->dev.parent = &twl->client->dev; | ||
571 | device_init_wakeup(&pdev->dev, 1); | ||
572 | status = platform_device_add_data(pdev, pdata->usb, | ||
573 | sizeof(*pdata->usb)); | ||
574 | if (status < 0) { | ||
575 | platform_device_put(pdev); | ||
576 | dev_dbg(&twl->client->dev, | ||
577 | "can't add usb data, %d\n", | ||
578 | status); | ||
579 | goto err; | ||
580 | } | ||
581 | } | ||
582 | |||
583 | if (status == 0) { | ||
584 | struct resource r = { | ||
585 | .start = pdata->irq_base + 8 + 2, | ||
586 | .flags = IORESOURCE_IRQ, | ||
587 | }; | ||
588 | |||
589 | status = platform_device_add_resources(pdev, &r, 1); | ||
590 | } | ||
591 | |||
592 | if (status == 0) | ||
593 | status = platform_device_add(pdev); | ||
594 | |||
595 | if (status < 0) { | ||
596 | platform_device_put(pdev); | ||
597 | dev_dbg(&twl->client->dev, | ||
598 | "can't create usb dev, %d\n", | ||
599 | status); | ||
600 | } | ||
601 | } | 472 | } |
602 | 473 | ||
603 | err: | 474 | return 0; |
604 | if (status) | ||
605 | pr_err("failed to add twl4030's children (status %d)\n", status); | ||
606 | return status; | ||
607 | } | 475 | } |
608 | 476 | ||
609 | /*----------------------------------------------------------------------*/ | 477 | /*----------------------------------------------------------------------*/ |