diff options
author | Lee Jones <lee.jones@linaro.org> | 2012-05-31 10:16:36 -0400 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2013-03-06 23:35:36 -0500 |
commit | 93ff722e88530b9719cbf53be4f3197722461394 (patch) | |
tree | 7803b080fdf52c91b50aa2719ee82f55a6165bc1 | |
parent | 330b7ebfa59d70ea5b814a04a28b8c7d8e462a81 (diff) |
ab8500-fg: Add power cut feature for ab8505 and ab8540
Add support for a power cut feature which allows user to
configure when ab8505 and ab8540 based platforms should shut
down system due to low battery.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
-rw-r--r-- | drivers/mfd/ab8500-core.c | 36 | ||||
-rw-r--r-- | drivers/power/ab8500_bmdata.c | 5 | ||||
-rw-r--r-- | drivers/power/ab8500_fg.c | 474 | ||||
-rw-r--r-- | include/linux/mfd/abx500.h | 10 | ||||
-rw-r--r-- | include/linux/mfd/abx500/ab8500-bm.h | 18 |
5 files changed, 543 insertions, 0 deletions
diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index c7ff55753a8f..f276352cc9ef 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c | |||
@@ -113,6 +113,7 @@ | |||
113 | #define AB8500_SWITCH_OFF_STATUS 0x00 | 113 | #define AB8500_SWITCH_OFF_STATUS 0x00 |
114 | 114 | ||
115 | #define AB8500_TURN_ON_STATUS 0x00 | 115 | #define AB8500_TURN_ON_STATUS 0x00 |
116 | #define AB8505_TURN_ON_STATUS_2 0x04 | ||
116 | 117 | ||
117 | #define AB8500_CH_USBCH_STAT1_REG 0x02 | 118 | #define AB8500_CH_USBCH_STAT1_REG 0x02 |
118 | #define VBUS_DET_DBNC100 0x02 | 119 | #define VBUS_DET_DBNC100 0x02 |
@@ -1401,6 +1402,21 @@ static ssize_t show_turn_on_status(struct device *dev, | |||
1401 | return sprintf(buf, "%#x\n", value); | 1402 | return sprintf(buf, "%#x\n", value); |
1402 | } | 1403 | } |
1403 | 1404 | ||
1405 | static ssize_t show_turn_on_status_2(struct device *dev, | ||
1406 | struct device_attribute *attr, char *buf) | ||
1407 | { | ||
1408 | int ret; | ||
1409 | u8 value; | ||
1410 | struct ab8500 *ab8500; | ||
1411 | |||
1412 | ab8500 = dev_get_drvdata(dev); | ||
1413 | ret = get_register_interruptible(ab8500, AB8500_SYS_CTRL1_BLOCK, | ||
1414 | AB8505_TURN_ON_STATUS_2, &value); | ||
1415 | if (ret < 0) | ||
1416 | return ret; | ||
1417 | return sprintf(buf, "%#x\n", (value & 0x1)); | ||
1418 | } | ||
1419 | |||
1404 | static ssize_t show_ab9540_dbbrstn(struct device *dev, | 1420 | static ssize_t show_ab9540_dbbrstn(struct device *dev, |
1405 | struct device_attribute *attr, char *buf) | 1421 | struct device_attribute *attr, char *buf) |
1406 | { | 1422 | { |
@@ -1457,6 +1473,7 @@ exit: | |||
1457 | static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL); | 1473 | static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL); |
1458 | static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL); | 1474 | static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL); |
1459 | static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL); | 1475 | static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL); |
1476 | static DEVICE_ATTR(turn_on_status_2, S_IRUGO, show_turn_on_status_2, NULL); | ||
1460 | static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR, | 1477 | static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR, |
1461 | show_ab9540_dbbrstn, store_ab9540_dbbrstn); | 1478 | show_ab9540_dbbrstn, store_ab9540_dbbrstn); |
1462 | 1479 | ||
@@ -1467,6 +1484,11 @@ static struct attribute *ab8500_sysfs_entries[] = { | |||
1467 | NULL, | 1484 | NULL, |
1468 | }; | 1485 | }; |
1469 | 1486 | ||
1487 | static struct attribute *ab8505_sysfs_entries[] = { | ||
1488 | &dev_attr_turn_on_status_2.attr, | ||
1489 | NULL, | ||
1490 | }; | ||
1491 | |||
1470 | static struct attribute *ab9540_sysfs_entries[] = { | 1492 | static struct attribute *ab9540_sysfs_entries[] = { |
1471 | &dev_attr_chip_id.attr, | 1493 | &dev_attr_chip_id.attr, |
1472 | &dev_attr_switch_off_status.attr, | 1494 | &dev_attr_switch_off_status.attr, |
@@ -1479,6 +1501,10 @@ static struct attribute_group ab8500_attr_group = { | |||
1479 | .attrs = ab8500_sysfs_entries, | 1501 | .attrs = ab8500_sysfs_entries, |
1480 | }; | 1502 | }; |
1481 | 1503 | ||
1504 | static struct attribute_group ab8505_attr_group = { | ||
1505 | .attrs = ab8505_sysfs_entries, | ||
1506 | }; | ||
1507 | |||
1482 | static struct attribute_group ab9540_attr_group = { | 1508 | static struct attribute_group ab9540_attr_group = { |
1483 | .attrs = ab9540_sysfs_entries, | 1509 | .attrs = ab9540_sysfs_entries, |
1484 | }; | 1510 | }; |
@@ -1719,6 +1745,12 @@ static int ab8500_probe(struct platform_device *pdev) | |||
1719 | else | 1745 | else |
1720 | ret = sysfs_create_group(&ab8500->dev->kobj, | 1746 | ret = sysfs_create_group(&ab8500->dev->kobj, |
1721 | &ab8500_attr_group); | 1747 | &ab8500_attr_group); |
1748 | |||
1749 | if ((is_ab8505(ab8500) || is_ab9540(ab8500)) && | ||
1750 | ab8500->chip_id >= AB8500_CUT2P0) | ||
1751 | ret = sysfs_create_group(&ab8500->dev->kobj, | ||
1752 | &ab8505_attr_group); | ||
1753 | |||
1722 | if (ret) | 1754 | if (ret) |
1723 | dev_err(ab8500->dev, "error creating sysfs entries\n"); | 1755 | dev_err(ab8500->dev, "error creating sysfs entries\n"); |
1724 | 1756 | ||
@@ -1735,6 +1767,10 @@ static int ab8500_remove(struct platform_device *pdev) | |||
1735 | else | 1767 | else |
1736 | sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); | 1768 | sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); |
1737 | 1769 | ||
1770 | if ((is_ab8505(ab8500) || is_ab9540(ab8500)) && | ||
1771 | ab8500->chip_id >= AB8500_CUT2P0) | ||
1772 | sysfs_remove_group(&ab8500->dev->kobj, &ab8505_attr_group); | ||
1773 | |||
1738 | mfd_remove_devices(ab8500->dev); | 1774 | mfd_remove_devices(ab8500->dev); |
1739 | 1775 | ||
1740 | return 0; | 1776 | return 0; |
diff --git a/drivers/power/ab8500_bmdata.c b/drivers/power/ab8500_bmdata.c index 7a96c0650fbb..e8759763fbe0 100644 --- a/drivers/power/ab8500_bmdata.c +++ b/drivers/power/ab8500_bmdata.c | |||
@@ -407,6 +407,11 @@ static const struct abx500_fg_parameters fg = { | |||
407 | .battok_raising_th_sel1 = 2860, | 407 | .battok_raising_th_sel1 = 2860, |
408 | .maint_thres = 95, | 408 | .maint_thres = 95, |
409 | .user_cap_limit = 15, | 409 | .user_cap_limit = 15, |
410 | .pcut_enable = 1, | ||
411 | .pcut_max_time = 127, | ||
412 | .pcut_flag_time = 112, | ||
413 | .pcut_max_restart = 15, | ||
414 | .pcut_debounce_time = 2, | ||
410 | }; | 415 | }; |
411 | 416 | ||
412 | static const struct abx500_maxim_parameters maxi_params = { | 417 | static const struct abx500_maxim_parameters maxi_params = { |
diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 25dae4c4b0ef..92f342bcf188 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c | |||
@@ -2344,6 +2344,50 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) | |||
2344 | dev_err(di->dev, "BattOk init write failed.\n"); | 2344 | dev_err(di->dev, "BattOk init write failed.\n"); |
2345 | goto out; | 2345 | goto out; |
2346 | } | 2346 | } |
2347 | |||
2348 | if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && | ||
2349 | abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) | ||
2350 | || is_ab8540(di->parent)) { | ||
2351 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2352 | AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time); | ||
2353 | |||
2354 | if (ret) { | ||
2355 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_MAX_TIME_REG\n", __func__); | ||
2356 | goto out; | ||
2357 | }; | ||
2358 | |||
2359 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2360 | AB8505_RTC_PCUT_FLAG_TIME_REG, di->bm->fg_params->pcut_flag_time); | ||
2361 | |||
2362 | if (ret) { | ||
2363 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_FLAG_TIME_REG\n", __func__); | ||
2364 | goto out; | ||
2365 | }; | ||
2366 | |||
2367 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2368 | AB8505_RTC_PCUT_RESTART_REG, di->bm->fg_params->pcut_max_restart); | ||
2369 | |||
2370 | if (ret) { | ||
2371 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_RESTART_REG\n", __func__); | ||
2372 | goto out; | ||
2373 | }; | ||
2374 | |||
2375 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2376 | AB8505_RTC_PCUT_DEBOUNCE_REG, di->bm->fg_params->pcut_debounce_time); | ||
2377 | |||
2378 | if (ret) { | ||
2379 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_DEBOUNCE_REG\n", __func__); | ||
2380 | goto out; | ||
2381 | }; | ||
2382 | |||
2383 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2384 | AB8505_RTC_PCUT_CTL_STATUS_REG, di->bm->fg_params->pcut_enable); | ||
2385 | |||
2386 | if (ret) { | ||
2387 | dev_err(di->dev, "%s write failed AB8505_RTC_PCUT_CTL_STATUS_REG\n", __func__); | ||
2388 | goto out; | ||
2389 | }; | ||
2390 | } | ||
2347 | out: | 2391 | out: |
2348 | return ret; | 2392 | return ret; |
2349 | } | 2393 | } |
@@ -2546,6 +2590,428 @@ static int ab8500_fg_sysfs_init(struct ab8500_fg *di) | |||
2546 | 2590 | ||
2547 | return ret; | 2591 | return ret; |
2548 | } | 2592 | } |
2593 | |||
2594 | static ssize_t ab8505_powercut_flagtime_read(struct device *dev, | ||
2595 | struct device_attribute *attr, | ||
2596 | char *buf) | ||
2597 | { | ||
2598 | int ret; | ||
2599 | u8 reg_value; | ||
2600 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2601 | struct ab8500_fg *di; | ||
2602 | |||
2603 | di = to_ab8500_fg_device_info(psy); | ||
2604 | |||
2605 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2606 | AB8505_RTC_PCUT_FLAG_TIME_REG, ®_value); | ||
2607 | |||
2608 | if (ret < 0) { | ||
2609 | dev_err(dev, "Failed to read AB8505_RTC_PCUT_FLAG_TIME_REG\n"); | ||
2610 | goto fail; | ||
2611 | } | ||
2612 | |||
2613 | return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); | ||
2614 | |||
2615 | fail: | ||
2616 | return ret; | ||
2617 | } | ||
2618 | |||
2619 | static ssize_t ab8505_powercut_flagtime_write(struct device *dev, | ||
2620 | struct device_attribute *attr, | ||
2621 | const char *buf, size_t count) | ||
2622 | { | ||
2623 | int ret; | ||
2624 | long unsigned reg_value; | ||
2625 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2626 | struct ab8500_fg *di; | ||
2627 | |||
2628 | di = to_ab8500_fg_device_info(psy); | ||
2629 | |||
2630 | reg_value = simple_strtoul(buf, NULL, 10); | ||
2631 | |||
2632 | if (reg_value > 0x7F) { | ||
2633 | dev_err(dev, "Incorrect parameter, echo 0 (1.98s) - 127 (15.625ms) for flagtime\n"); | ||
2634 | goto fail; | ||
2635 | } | ||
2636 | |||
2637 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2638 | AB8505_RTC_PCUT_FLAG_TIME_REG, (u8)reg_value); | ||
2639 | |||
2640 | if (ret < 0) | ||
2641 | dev_err(dev, "Failed to set AB8505_RTC_PCUT_FLAG_TIME_REG\n"); | ||
2642 | |||
2643 | fail: | ||
2644 | return count; | ||
2645 | } | ||
2646 | |||
2647 | static ssize_t ab8505_powercut_maxtime_read(struct device *dev, | ||
2648 | struct device_attribute *attr, | ||
2649 | char *buf) | ||
2650 | { | ||
2651 | int ret; | ||
2652 | u8 reg_value; | ||
2653 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2654 | struct ab8500_fg *di; | ||
2655 | |||
2656 | di = to_ab8500_fg_device_info(psy); | ||
2657 | |||
2658 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2659 | AB8505_RTC_PCUT_MAX_TIME_REG, ®_value); | ||
2660 | |||
2661 | if (ret < 0) { | ||
2662 | dev_err(dev, "Failed to read AB8505_RTC_PCUT_MAX_TIME_REG\n"); | ||
2663 | goto fail; | ||
2664 | } | ||
2665 | |||
2666 | return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); | ||
2667 | |||
2668 | fail: | ||
2669 | return ret; | ||
2670 | |||
2671 | } | ||
2672 | |||
2673 | static ssize_t ab8505_powercut_maxtime_write(struct device *dev, | ||
2674 | struct device_attribute *attr, | ||
2675 | const char *buf, size_t count) | ||
2676 | { | ||
2677 | int ret; | ||
2678 | int reg_value; | ||
2679 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2680 | struct ab8500_fg *di; | ||
2681 | |||
2682 | di = to_ab8500_fg_device_info(psy); | ||
2683 | |||
2684 | reg_value = simple_strtoul(buf, NULL, 10); | ||
2685 | if (reg_value > 0x7F) { | ||
2686 | dev_err(dev, "Incorrect parameter, echo 0 (0.0s) - 127 (1.98s) for maxtime\n"); | ||
2687 | goto fail; | ||
2688 | } | ||
2689 | |||
2690 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2691 | AB8505_RTC_PCUT_MAX_TIME_REG, (u8)reg_value); | ||
2692 | |||
2693 | if (ret < 0) | ||
2694 | dev_err(dev, "Failed to set AB8505_RTC_PCUT_MAX_TIME_REG\n"); | ||
2695 | |||
2696 | fail: | ||
2697 | return count; | ||
2698 | } | ||
2699 | |||
2700 | static ssize_t ab8505_powercut_restart_read(struct device *dev, | ||
2701 | struct device_attribute *attr, | ||
2702 | char *buf) | ||
2703 | { | ||
2704 | int ret; | ||
2705 | u8 reg_value; | ||
2706 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2707 | struct ab8500_fg *di; | ||
2708 | |||
2709 | di = to_ab8500_fg_device_info(psy); | ||
2710 | |||
2711 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2712 | AB8505_RTC_PCUT_RESTART_REG, ®_value); | ||
2713 | |||
2714 | if (ret < 0) { | ||
2715 | dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n"); | ||
2716 | goto fail; | ||
2717 | } | ||
2718 | |||
2719 | return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF)); | ||
2720 | |||
2721 | fail: | ||
2722 | return ret; | ||
2723 | } | ||
2724 | |||
2725 | static ssize_t ab8505_powercut_restart_write(struct device *dev, | ||
2726 | struct device_attribute *attr, | ||
2727 | const char *buf, size_t count) | ||
2728 | { | ||
2729 | int ret; | ||
2730 | int reg_value; | ||
2731 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2732 | struct ab8500_fg *di; | ||
2733 | |||
2734 | di = to_ab8500_fg_device_info(psy); | ||
2735 | |||
2736 | reg_value = simple_strtoul(buf, NULL, 10); | ||
2737 | if (reg_value > 0xF) { | ||
2738 | dev_err(dev, "Incorrect parameter, echo 0 - 15 for number of restart\n"); | ||
2739 | goto fail; | ||
2740 | } | ||
2741 | |||
2742 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2743 | AB8505_RTC_PCUT_RESTART_REG, (u8)reg_value); | ||
2744 | |||
2745 | if (ret < 0) | ||
2746 | dev_err(dev, "Failed to set AB8505_RTC_PCUT_RESTART_REG\n"); | ||
2747 | |||
2748 | fail: | ||
2749 | return count; | ||
2750 | |||
2751 | } | ||
2752 | |||
2753 | static ssize_t ab8505_powercut_timer_read(struct device *dev, | ||
2754 | struct device_attribute *attr, | ||
2755 | char *buf) | ||
2756 | { | ||
2757 | int ret; | ||
2758 | u8 reg_value; | ||
2759 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2760 | struct ab8500_fg *di; | ||
2761 | |||
2762 | di = to_ab8500_fg_device_info(psy); | ||
2763 | |||
2764 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2765 | AB8505_RTC_PCUT_TIME_REG, ®_value); | ||
2766 | |||
2767 | if (ret < 0) { | ||
2768 | dev_err(dev, "Failed to read AB8505_RTC_PCUT_TIME_REG\n"); | ||
2769 | goto fail; | ||
2770 | } | ||
2771 | |||
2772 | return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7F)); | ||
2773 | |||
2774 | fail: | ||
2775 | return ret; | ||
2776 | } | ||
2777 | |||
2778 | static ssize_t ab8505_powercut_restart_counter_read(struct device *dev, | ||
2779 | struct device_attribute *attr, | ||
2780 | char *buf) | ||
2781 | { | ||
2782 | int ret; | ||
2783 | u8 reg_value; | ||
2784 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2785 | struct ab8500_fg *di; | ||
2786 | |||
2787 | di = to_ab8500_fg_device_info(psy); | ||
2788 | |||
2789 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2790 | AB8505_RTC_PCUT_RESTART_REG, ®_value); | ||
2791 | |||
2792 | if (ret < 0) { | ||
2793 | dev_err(dev, "Failed to read AB8505_RTC_PCUT_RESTART_REG\n"); | ||
2794 | goto fail; | ||
2795 | } | ||
2796 | |||
2797 | return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0xF0) >> 4); | ||
2798 | |||
2799 | fail: | ||
2800 | return ret; | ||
2801 | } | ||
2802 | |||
2803 | static ssize_t ab8505_powercut_read(struct device *dev, | ||
2804 | struct device_attribute *attr, | ||
2805 | char *buf) | ||
2806 | { | ||
2807 | int ret; | ||
2808 | u8 reg_value; | ||
2809 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2810 | struct ab8500_fg *di; | ||
2811 | |||
2812 | di = to_ab8500_fg_device_info(psy); | ||
2813 | |||
2814 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2815 | AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); | ||
2816 | |||
2817 | if (ret < 0) | ||
2818 | goto fail; | ||
2819 | |||
2820 | return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x1)); | ||
2821 | |||
2822 | fail: | ||
2823 | return ret; | ||
2824 | } | ||
2825 | |||
2826 | static ssize_t ab8505_powercut_write(struct device *dev, | ||
2827 | struct device_attribute *attr, | ||
2828 | const char *buf, size_t count) | ||
2829 | { | ||
2830 | int ret; | ||
2831 | int reg_value; | ||
2832 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2833 | struct ab8500_fg *di; | ||
2834 | |||
2835 | di = to_ab8500_fg_device_info(psy); | ||
2836 | |||
2837 | reg_value = simple_strtoul(buf, NULL, 10); | ||
2838 | if (reg_value > 0x1) { | ||
2839 | dev_err(dev, "Incorrect parameter, echo 0/1 to disable/enable Pcut feature\n"); | ||
2840 | goto fail; | ||
2841 | } | ||
2842 | |||
2843 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2844 | AB8505_RTC_PCUT_CTL_STATUS_REG, (u8)reg_value); | ||
2845 | |||
2846 | if (ret < 0) | ||
2847 | dev_err(dev, "Failed to set AB8505_RTC_PCUT_CTL_STATUS_REG\n"); | ||
2848 | |||
2849 | fail: | ||
2850 | return count; | ||
2851 | } | ||
2852 | |||
2853 | static ssize_t ab8505_powercut_flag_read(struct device *dev, | ||
2854 | struct device_attribute *attr, | ||
2855 | char *buf) | ||
2856 | { | ||
2857 | |||
2858 | int ret; | ||
2859 | u8 reg_value; | ||
2860 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2861 | struct ab8500_fg *di; | ||
2862 | |||
2863 | di = to_ab8500_fg_device_info(psy); | ||
2864 | |||
2865 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2866 | AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); | ||
2867 | |||
2868 | if (ret < 0) { | ||
2869 | dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n"); | ||
2870 | goto fail; | ||
2871 | } | ||
2872 | |||
2873 | return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x10) >> 4)); | ||
2874 | |||
2875 | fail: | ||
2876 | return ret; | ||
2877 | } | ||
2878 | |||
2879 | static ssize_t ab8505_powercut_debounce_read(struct device *dev, | ||
2880 | struct device_attribute *attr, | ||
2881 | char *buf) | ||
2882 | { | ||
2883 | int ret; | ||
2884 | u8 reg_value; | ||
2885 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2886 | struct ab8500_fg *di; | ||
2887 | |||
2888 | di = to_ab8500_fg_device_info(psy); | ||
2889 | |||
2890 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2891 | AB8505_RTC_PCUT_DEBOUNCE_REG, ®_value); | ||
2892 | |||
2893 | if (ret < 0) { | ||
2894 | dev_err(dev, "Failed to read AB8505_RTC_PCUT_DEBOUNCE_REG\n"); | ||
2895 | goto fail; | ||
2896 | } | ||
2897 | |||
2898 | return scnprintf(buf, PAGE_SIZE, "%d\n", (reg_value & 0x7)); | ||
2899 | |||
2900 | fail: | ||
2901 | return ret; | ||
2902 | } | ||
2903 | |||
2904 | static ssize_t ab8505_powercut_debounce_write(struct device *dev, | ||
2905 | struct device_attribute *attr, | ||
2906 | const char *buf, size_t count) | ||
2907 | { | ||
2908 | int ret; | ||
2909 | int reg_value; | ||
2910 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2911 | struct ab8500_fg *di; | ||
2912 | |||
2913 | di = to_ab8500_fg_device_info(psy); | ||
2914 | |||
2915 | reg_value = simple_strtoul(buf, NULL, 10); | ||
2916 | if (reg_value > 0x7) { | ||
2917 | dev_err(dev, "Incorrect parameter, echo 0 to 7 for debounce setting\n"); | ||
2918 | goto fail; | ||
2919 | } | ||
2920 | |||
2921 | ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, | ||
2922 | AB8505_RTC_PCUT_DEBOUNCE_REG, (u8)reg_value); | ||
2923 | |||
2924 | if (ret < 0) | ||
2925 | dev_err(dev, "Failed to set AB8505_RTC_PCUT_DEBOUNCE_REG\n"); | ||
2926 | |||
2927 | fail: | ||
2928 | return count; | ||
2929 | } | ||
2930 | |||
2931 | static ssize_t ab8505_powercut_enable_status_read(struct device *dev, | ||
2932 | struct device_attribute *attr, | ||
2933 | char *buf) | ||
2934 | { | ||
2935 | int ret; | ||
2936 | u8 reg_value; | ||
2937 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2938 | struct ab8500_fg *di; | ||
2939 | |||
2940 | di = to_ab8500_fg_device_info(psy); | ||
2941 | |||
2942 | ret = abx500_get_register_interruptible(di->dev, AB8500_RTC, | ||
2943 | AB8505_RTC_PCUT_CTL_STATUS_REG, ®_value); | ||
2944 | |||
2945 | if (ret < 0) { | ||
2946 | dev_err(dev, "Failed to read AB8505_RTC_PCUT_CTL_STATUS_REG\n"); | ||
2947 | goto fail; | ||
2948 | } | ||
2949 | |||
2950 | return scnprintf(buf, PAGE_SIZE, "%d\n", ((reg_value & 0x20) >> 5)); | ||
2951 | |||
2952 | fail: | ||
2953 | return ret; | ||
2954 | } | ||
2955 | |||
2956 | static struct device_attribute ab8505_fg_sysfs_psy_attrs[] = { | ||
2957 | __ATTR(powercut_flagtime, (S_IRUGO | S_IWUSR | S_IWGRP), | ||
2958 | ab8505_powercut_flagtime_read, ab8505_powercut_flagtime_write), | ||
2959 | __ATTR(powercut_maxtime, (S_IRUGO | S_IWUSR | S_IWGRP), | ||
2960 | ab8505_powercut_maxtime_read, ab8505_powercut_maxtime_write), | ||
2961 | __ATTR(powercut_restart_max, (S_IRUGO | S_IWUSR | S_IWGRP), | ||
2962 | ab8505_powercut_restart_read, ab8505_powercut_restart_write), | ||
2963 | __ATTR(powercut_timer, S_IRUGO, ab8505_powercut_timer_read, NULL), | ||
2964 | __ATTR(powercut_restart_counter, S_IRUGO, | ||
2965 | ab8505_powercut_restart_counter_read, NULL), | ||
2966 | __ATTR(powercut_enable, (S_IRUGO | S_IWUSR | S_IWGRP), | ||
2967 | ab8505_powercut_read, ab8505_powercut_write), | ||
2968 | __ATTR(powercut_flag, S_IRUGO, ab8505_powercut_flag_read, NULL), | ||
2969 | __ATTR(powercut_debounce_time, (S_IRUGO | S_IWUSR | S_IWGRP), | ||
2970 | ab8505_powercut_debounce_read, ab8505_powercut_debounce_write), | ||
2971 | __ATTR(powercut_enable_status, S_IRUGO, | ||
2972 | ab8505_powercut_enable_status_read, NULL), | ||
2973 | }; | ||
2974 | |||
2975 | static int ab8500_fg_sysfs_psy_create_attrs(struct device *dev) | ||
2976 | { | ||
2977 | unsigned int i, j; | ||
2978 | struct power_supply *psy = dev_get_drvdata(dev); | ||
2979 | struct ab8500_fg *di; | ||
2980 | |||
2981 | di = to_ab8500_fg_device_info(psy); | ||
2982 | |||
2983 | if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && | ||
2984 | abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) | ||
2985 | || is_ab8540(di->parent)) { | ||
2986 | for (j = 0; j < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); j++) | ||
2987 | if (device_create_file(dev, &ab8505_fg_sysfs_psy_attrs[j])) | ||
2988 | goto sysfs_psy_create_attrs_failed_ab8505; | ||
2989 | } | ||
2990 | return 0; | ||
2991 | sysfs_psy_create_attrs_failed_ab8505: | ||
2992 | dev_err(dev, "Failed creating sysfs psy attrs for ab8505.\n"); | ||
2993 | while (j--) | ||
2994 | device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]); | ||
2995 | |||
2996 | return -EIO; | ||
2997 | } | ||
2998 | |||
2999 | static void ab8500_fg_sysfs_psy_remove_attrs(struct device *dev) | ||
3000 | { | ||
3001 | unsigned int i; | ||
3002 | struct power_supply *psy = dev_get_drvdata(dev); | ||
3003 | struct ab8500_fg *di; | ||
3004 | |||
3005 | di = to_ab8500_fg_device_info(psy); | ||
3006 | |||
3007 | if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && | ||
3008 | abx500_get_chip_id(dev->parent) >= AB8500_CUT2P0) | ||
3009 | || is_ab8540(di->parent)) { | ||
3010 | for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) | ||
3011 | (void)device_remove_file(dev, &ab8505_fg_sysfs_psy_attrs[i]); | ||
3012 | } | ||
3013 | } | ||
3014 | |||
2549 | /* Exposure to the sysfs interface <<END>> */ | 3015 | /* Exposure to the sysfs interface <<END>> */ |
2550 | 3016 | ||
2551 | #if defined(CONFIG_PM) | 3017 | #if defined(CONFIG_PM) |
@@ -2607,6 +3073,7 @@ static int ab8500_fg_remove(struct platform_device *pdev) | |||
2607 | ab8500_fg_sysfs_exit(di); | 3073 | ab8500_fg_sysfs_exit(di); |
2608 | 3074 | ||
2609 | flush_scheduled_work(); | 3075 | flush_scheduled_work(); |
3076 | ab8500_fg_sysfs_psy_remove_attrs(di->fg_psy.dev); | ||
2610 | power_supply_unregister(&di->fg_psy); | 3077 | power_supply_unregister(&di->fg_psy); |
2611 | platform_set_drvdata(pdev, NULL); | 3078 | platform_set_drvdata(pdev, NULL); |
2612 | return ret; | 3079 | return ret; |
@@ -2772,6 +3239,13 @@ static int ab8500_fg_probe(struct platform_device *pdev) | |||
2772 | goto free_irq; | 3239 | goto free_irq; |
2773 | } | 3240 | } |
2774 | 3241 | ||
3242 | ret = ab8500_fg_sysfs_psy_create_attrs(di->fg_psy.dev); | ||
3243 | if (ret) { | ||
3244 | dev_err(di->dev, "failed to create FG psy\n"); | ||
3245 | ab8500_fg_sysfs_exit(di); | ||
3246 | goto free_irq; | ||
3247 | } | ||
3248 | |||
2775 | /* Calibrate the fg first time */ | 3249 | /* Calibrate the fg first time */ |
2776 | di->flags.calibrate = true; | 3250 | di->flags.calibrate = true; |
2777 | di->calib_state = AB8500_FG_CALIB_INIT; | 3251 | di->calib_state = AB8500_FG_CALIB_INIT; |
diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 9ead60bc66b7..188aedc322c2 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h | |||
@@ -89,6 +89,11 @@ struct abx500_fg; | |||
89 | * points. | 89 | * points. |
90 | * @maint_thres This is the threshold where we stop reporting | 90 | * @maint_thres This is the threshold where we stop reporting |
91 | * battery full while in maintenance, in per cent | 91 | * battery full while in maintenance, in per cent |
92 | * @pcut_enable: Enable power cut feature in ab8505 | ||
93 | * @pcut_max_time: Max time threshold | ||
94 | * @pcut_flag_time: Flagtime threshold | ||
95 | * @pcut_max_restart: Max number of restarts | ||
96 | * @pcut_debounce_time: Sets battery debounce time | ||
92 | */ | 97 | */ |
93 | struct abx500_fg_parameters { | 98 | struct abx500_fg_parameters { |
94 | int recovery_sleep_timer; | 99 | int recovery_sleep_timer; |
@@ -106,6 +111,11 @@ struct abx500_fg_parameters { | |||
106 | int battok_raising_th_sel1; | 111 | int battok_raising_th_sel1; |
107 | int user_cap_limit; | 112 | int user_cap_limit; |
108 | int maint_thres; | 113 | int maint_thres; |
114 | bool pcut_enable; | ||
115 | u8 pcut_max_time; | ||
116 | u8 pcut_flag_time; | ||
117 | u8 pcut_max_restart; | ||
118 | u8 pcut_debounce_time; | ||
109 | }; | 119 | }; |
110 | 120 | ||
111 | /** | 121 | /** |
diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h index 8d35bfe164c8..0efbe0efee7f 100644 --- a/include/linux/mfd/abx500/ab8500-bm.h +++ b/include/linux/mfd/abx500/ab8500-bm.h | |||
@@ -235,6 +235,14 @@ | |||
235 | /* Battery type */ | 235 | /* Battery type */ |
236 | #define BATTERY_UNKNOWN 00 | 236 | #define BATTERY_UNKNOWN 00 |
237 | 237 | ||
238 | /* Registers for pcut feature in ab8505 and ab9540 */ | ||
239 | #define AB8505_RTC_PCUT_CTL_STATUS_REG 0x12 | ||
240 | #define AB8505_RTC_PCUT_TIME_REG 0x13 | ||
241 | #define AB8505_RTC_PCUT_MAX_TIME_REG 0x14 | ||
242 | #define AB8505_RTC_PCUT_FLAG_TIME_REG 0x15 | ||
243 | #define AB8505_RTC_PCUT_RESTART_REG 0x16 | ||
244 | #define AB8505_RTC_PCUT_DEBOUNCE_REG 0x17 | ||
245 | |||
238 | /** | 246 | /** |
239 | * struct res_to_temp - defines one point in a temp to res curve. To | 247 | * struct res_to_temp - defines one point in a temp to res curve. To |
240 | * be used in battery packs that combines the identification resistor with a | 248 | * be used in battery packs that combines the identification resistor with a |
@@ -283,6 +291,11 @@ struct ab8500_fg; | |||
283 | * points. | 291 | * points. |
284 | * @maint_thres This is the threshold where we stop reporting | 292 | * @maint_thres This is the threshold where we stop reporting |
285 | * battery full while in maintenance, in per cent | 293 | * battery full while in maintenance, in per cent |
294 | * @pcut_enable: Enable power cut feature in ab8505 | ||
295 | * @pcut_max_time: Max time threshold | ||
296 | * @pcut_flag_time: Flagtime threshold | ||
297 | * @pcut_max_restart: Max number of restarts | ||
298 | * @pcut_debunce_time: Sets battery debounce time | ||
286 | */ | 299 | */ |
287 | struct ab8500_fg_parameters { | 300 | struct ab8500_fg_parameters { |
288 | int recovery_sleep_timer; | 301 | int recovery_sleep_timer; |
@@ -299,6 +312,11 @@ struct ab8500_fg_parameters { | |||
299 | int battok_raising_th_sel1; | 312 | int battok_raising_th_sel1; |
300 | int user_cap_limit; | 313 | int user_cap_limit; |
301 | int maint_thres; | 314 | int maint_thres; |
315 | bool pcut_enable; | ||
316 | u8 pcut_max_time; | ||
317 | u8 pcut_flag_time; | ||
318 | u8 pcut_max_restart; | ||
319 | u8 pcut_debunce_time; | ||
302 | }; | 320 | }; |
303 | 321 | ||
304 | /** | 322 | /** |