.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Driver for the TI bq24190 battery charger. |
---|
3 | 4 | * |
---|
4 | 5 | * Author: Mark A. Greer <mgreer@animalcreek.com> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify |
---|
7 | | - * it under the terms of the GNU General Public License version 2 as |
---|
8 | | - * published by the Free Software Foundation. |
---|
9 | 6 | */ |
---|
10 | 7 | |
---|
11 | 8 | #include <linux/module.h> |
---|
.. | .. |
---|
21 | 18 | #include <linux/workqueue.h> |
---|
22 | 19 | #include <linux/gpio.h> |
---|
23 | 20 | #include <linux/i2c.h> |
---|
| 21 | +#include <linux/extcon-provider.h> |
---|
24 | 22 | |
---|
25 | 23 | #define BQ24190_MANUFACTURER "Texas Instruments" |
---|
26 | 24 | |
---|
.. | .. |
---|
43 | 41 | #define BQ24190_REG_POC_CHG_CONFIG_DISABLE 0x0 |
---|
44 | 42 | #define BQ24190_REG_POC_CHG_CONFIG_CHARGE 0x1 |
---|
45 | 43 | #define BQ24190_REG_POC_CHG_CONFIG_OTG 0x2 |
---|
| 44 | +#define BQ24190_REG_POC_CHG_CONFIG_OTG_ALT 0x3 |
---|
46 | 45 | #define BQ24190_REG_POC_SYS_MIN_MASK (BIT(3) | BIT(2) | BIT(1)) |
---|
47 | 46 | #define BQ24190_REG_POC_SYS_MIN_SHIFT 1 |
---|
48 | 47 | #define BQ24190_REG_POC_SYS_MIN_MIN 3000 |
---|
.. | .. |
---|
142 | 141 | #define BQ24190_REG_VPRS_PN_MASK (BIT(5) | BIT(4) | BIT(3)) |
---|
143 | 142 | #define BQ24190_REG_VPRS_PN_SHIFT 3 |
---|
144 | 143 | #define BQ24190_REG_VPRS_PN_24190 0x4 |
---|
145 | | -#define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193 */ |
---|
| 144 | +#define BQ24190_REG_VPRS_PN_24192 0x5 /* Also 24193, 24196 */ |
---|
146 | 145 | #define BQ24190_REG_VPRS_PN_24192I 0x3 |
---|
147 | 146 | #define BQ24190_REG_VPRS_TS_PROFILE_MASK BIT(2) |
---|
148 | 147 | #define BQ24190_REG_VPRS_TS_PROFILE_SHIFT 2 |
---|
.. | .. |
---|
159 | 158 | struct bq24190_dev_info { |
---|
160 | 159 | struct i2c_client *client; |
---|
161 | 160 | struct device *dev; |
---|
| 161 | + struct extcon_dev *edev; |
---|
162 | 162 | struct power_supply *charger; |
---|
163 | 163 | struct power_supply *battery; |
---|
164 | 164 | struct delayed_work input_current_limit_work; |
---|
.. | .. |
---|
172 | 172 | u8 f_reg; |
---|
173 | 173 | u8 ss_reg; |
---|
174 | 174 | u8 watchdog; |
---|
| 175 | +}; |
---|
| 176 | + |
---|
| 177 | +static const unsigned int bq24190_usb_extcon_cable[] = { |
---|
| 178 | + EXTCON_USB, |
---|
| 179 | + EXTCON_NONE, |
---|
175 | 180 | }; |
---|
176 | 181 | |
---|
177 | 182 | /* |
---|
.. | .. |
---|
402 | 407 | static struct attribute * |
---|
403 | 408 | bq24190_sysfs_attrs[ARRAY_SIZE(bq24190_sysfs_field_tbl) + 1]; |
---|
404 | 409 | |
---|
405 | | -static const struct attribute_group bq24190_sysfs_attr_group = { |
---|
406 | | - .attrs = bq24190_sysfs_attrs, |
---|
407 | | -}; |
---|
| 410 | +ATTRIBUTE_GROUPS(bq24190_sysfs); |
---|
408 | 411 | |
---|
409 | 412 | static void bq24190_sysfs_init_attrs(void) |
---|
410 | 413 | { |
---|
.. | .. |
---|
481 | 484 | return ret; |
---|
482 | 485 | |
---|
483 | 486 | ret = pm_runtime_get_sync(bdi->dev); |
---|
484 | | - if (ret < 0) |
---|
| 487 | + if (ret < 0) { |
---|
| 488 | + pm_runtime_put_noidle(bdi->dev); |
---|
485 | 489 | return ret; |
---|
| 490 | + } |
---|
486 | 491 | |
---|
487 | 492 | ret = bq24190_write_mask(bdi, info->reg, info->mask, info->shift, v); |
---|
488 | 493 | if (ret) |
---|
.. | .. |
---|
493 | 498 | |
---|
494 | 499 | return count; |
---|
495 | 500 | } |
---|
496 | | - |
---|
497 | | -static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) |
---|
498 | | -{ |
---|
499 | | - bq24190_sysfs_init_attrs(); |
---|
500 | | - |
---|
501 | | - return sysfs_create_group(&bdi->charger->dev.kobj, |
---|
502 | | - &bq24190_sysfs_attr_group); |
---|
503 | | -} |
---|
504 | | - |
---|
505 | | -static void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) |
---|
506 | | -{ |
---|
507 | | - sysfs_remove_group(&bdi->charger->dev.kobj, &bq24190_sysfs_attr_group); |
---|
508 | | -} |
---|
509 | | -#else |
---|
510 | | -static int bq24190_sysfs_create_group(struct bq24190_dev_info *bdi) |
---|
511 | | -{ |
---|
512 | | - return 0; |
---|
513 | | -} |
---|
514 | | - |
---|
515 | | -static inline void bq24190_sysfs_remove_group(struct bq24190_dev_info *bdi) {} |
---|
516 | 501 | #endif |
---|
517 | 502 | |
---|
518 | 503 | #ifdef CONFIG_REGULATOR |
---|
.. | .. |
---|
568 | 553 | pm_runtime_mark_last_busy(bdi->dev); |
---|
569 | 554 | pm_runtime_put_autosuspend(bdi->dev); |
---|
570 | 555 | |
---|
571 | | - return ret ? ret : val == BQ24190_REG_POC_CHG_CONFIG_OTG; |
---|
| 556 | + if (ret) |
---|
| 557 | + return ret; |
---|
| 558 | + |
---|
| 559 | + return (val == BQ24190_REG_POC_CHG_CONFIG_OTG || |
---|
| 560 | + val == BQ24190_REG_POC_CHG_CONFIG_OTG_ALT); |
---|
572 | 561 | } |
---|
573 | 562 | |
---|
574 | 563 | static const struct regulator_ops bq24190_vbus_ops = { |
---|
.. | .. |
---|
579 | 568 | |
---|
580 | 569 | static const struct regulator_desc bq24190_vbus_desc = { |
---|
581 | 570 | .name = "usb_otg_vbus", |
---|
| 571 | + .of_match = "usb-otg-vbus", |
---|
582 | 572 | .type = REGULATOR_VOLTAGE, |
---|
583 | 573 | .owner = THIS_MODULE, |
---|
584 | 574 | .ops = &bq24190_vbus_ops, |
---|
.. | .. |
---|
692 | 682 | * { .type = "bq24190", .addr = 0x6b, .properties = pe, .irq = irq }; |
---|
693 | 683 | * struct i2c_adapter ad = { ... }; |
---|
694 | 684 | * i2c_add_adapter(&ad); |
---|
695 | | - * i2c_new_device(&ad, &bi); |
---|
| 685 | + * i2c_new_client_device(&ad, &bi); |
---|
696 | 686 | */ |
---|
697 | 687 | if (device_property_read_bool(bdi->dev, "disable-reset")) |
---|
698 | 688 | return 0; |
---|
.. | .. |
---|
1537 | 1527 | .property_is_writeable = bq24190_battery_property_is_writeable, |
---|
1538 | 1528 | }; |
---|
1539 | 1529 | |
---|
| 1530 | +static int bq24190_configure_usb_otg(struct bq24190_dev_info *bdi, u8 ss_reg) |
---|
| 1531 | +{ |
---|
| 1532 | + bool otg_enabled; |
---|
| 1533 | + int ret; |
---|
| 1534 | + |
---|
| 1535 | + otg_enabled = !!(ss_reg & BQ24190_REG_SS_VBUS_STAT_MASK); |
---|
| 1536 | + ret = extcon_set_state_sync(bdi->edev, EXTCON_USB, otg_enabled); |
---|
| 1537 | + if (ret < 0) |
---|
| 1538 | + dev_err(bdi->dev, "Can't set extcon state to %d: %d\n", |
---|
| 1539 | + otg_enabled, ret); |
---|
| 1540 | + |
---|
| 1541 | + return ret; |
---|
| 1542 | +} |
---|
| 1543 | + |
---|
1540 | 1544 | static void bq24190_check_status(struct bq24190_dev_info *bdi) |
---|
1541 | 1545 | { |
---|
1542 | 1546 | const u8 battery_mask_ss = BQ24190_REG_SS_CHRG_STAT_MASK; |
---|
.. | .. |
---|
1606 | 1610 | bdi->ss_reg = ss_reg; |
---|
1607 | 1611 | } |
---|
1608 | 1612 | |
---|
1609 | | - if (alert_charger || alert_battery) |
---|
| 1613 | + if (alert_charger || alert_battery) { |
---|
1610 | 1614 | power_supply_changed(bdi->charger); |
---|
| 1615 | + bq24190_configure_usb_otg(bdi, ss_reg); |
---|
| 1616 | + } |
---|
1611 | 1617 | if (alert_battery && bdi->battery) |
---|
1612 | 1618 | power_supply_changed(bdi->battery); |
---|
1613 | 1619 | |
---|
.. | .. |
---|
1647 | 1653 | if (ret < 0) |
---|
1648 | 1654 | return ret; |
---|
1649 | 1655 | |
---|
1650 | | - if (v != BQ24190_REG_VPRS_PN_24190 && |
---|
1651 | | - v != BQ24190_REG_VPRS_PN_24192I) { |
---|
| 1656 | + switch (v) { |
---|
| 1657 | + case BQ24190_REG_VPRS_PN_24190: |
---|
| 1658 | + case BQ24190_REG_VPRS_PN_24192: |
---|
| 1659 | + case BQ24190_REG_VPRS_PN_24192I: |
---|
| 1660 | + break; |
---|
| 1661 | + default: |
---|
1652 | 1662 | dev_err(bdi->dev, "Error unknown model: 0x%02x\n", v); |
---|
1653 | 1663 | return -ENODEV; |
---|
1654 | 1664 | } |
---|
.. | .. |
---|
1704 | 1714 | static int bq24190_probe(struct i2c_client *client, |
---|
1705 | 1715 | const struct i2c_device_id *id) |
---|
1706 | 1716 | { |
---|
1707 | | - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
---|
| 1717 | + struct i2c_adapter *adapter = client->adapter; |
---|
1708 | 1718 | struct device *dev = &client->dev; |
---|
1709 | 1719 | struct power_supply_config charger_cfg = {}, battery_cfg = {}; |
---|
1710 | 1720 | struct bq24190_dev_info *bdi; |
---|
.. | .. |
---|
1737 | 1747 | return -EINVAL; |
---|
1738 | 1748 | } |
---|
1739 | 1749 | |
---|
| 1750 | + bdi->edev = devm_extcon_dev_allocate(dev, bq24190_usb_extcon_cable); |
---|
| 1751 | + if (IS_ERR(bdi->edev)) |
---|
| 1752 | + return PTR_ERR(bdi->edev); |
---|
| 1753 | + |
---|
| 1754 | + ret = devm_extcon_dev_register(dev, bdi->edev); |
---|
| 1755 | + if (ret < 0) |
---|
| 1756 | + return ret; |
---|
| 1757 | + |
---|
1740 | 1758 | pm_runtime_enable(dev); |
---|
1741 | 1759 | pm_runtime_use_autosuspend(dev); |
---|
1742 | 1760 | pm_runtime_set_autosuspend_delay(dev, 600); |
---|
.. | .. |
---|
1745 | 1763 | dev_err(dev, "pm_runtime_get failed: %i\n", ret); |
---|
1746 | 1764 | goto out_pmrt; |
---|
1747 | 1765 | } |
---|
| 1766 | + |
---|
| 1767 | +#ifdef CONFIG_SYSFS |
---|
| 1768 | + bq24190_sysfs_init_attrs(); |
---|
| 1769 | + charger_cfg.attr_grp = bq24190_sysfs_groups; |
---|
| 1770 | +#endif |
---|
1748 | 1771 | |
---|
1749 | 1772 | charger_cfg.drv_data = bdi; |
---|
1750 | 1773 | charger_cfg.of_node = dev->of_node; |
---|
.. | .. |
---|
1783 | 1806 | goto out_charger; |
---|
1784 | 1807 | } |
---|
1785 | 1808 | |
---|
1786 | | - ret = bq24190_sysfs_create_group(bdi); |
---|
1787 | | - if (ret < 0) { |
---|
1788 | | - dev_err(dev, "Can't create sysfs entries\n"); |
---|
| 1809 | + ret = bq24190_configure_usb_otg(bdi, bdi->ss_reg); |
---|
| 1810 | + if (ret < 0) |
---|
1789 | 1811 | goto out_charger; |
---|
1790 | | - } |
---|
1791 | 1812 | |
---|
1792 | 1813 | bdi->initialized = true; |
---|
1793 | 1814 | |
---|
.. | .. |
---|
1797 | 1818 | "bq24190-charger", bdi); |
---|
1798 | 1819 | if (ret < 0) { |
---|
1799 | 1820 | dev_err(dev, "Can't set up irq handler\n"); |
---|
1800 | | - goto out_sysfs; |
---|
| 1821 | + goto out_charger; |
---|
1801 | 1822 | } |
---|
1802 | 1823 | |
---|
1803 | 1824 | ret = bq24190_register_vbus_regulator(bdi); |
---|
1804 | 1825 | if (ret < 0) |
---|
1805 | | - goto out_sysfs; |
---|
| 1826 | + goto out_charger; |
---|
1806 | 1827 | |
---|
1807 | 1828 | enable_irq_wake(client->irq); |
---|
1808 | 1829 | |
---|
.. | .. |
---|
1810 | 1831 | pm_runtime_put_autosuspend(dev); |
---|
1811 | 1832 | |
---|
1812 | 1833 | return 0; |
---|
1813 | | - |
---|
1814 | | -out_sysfs: |
---|
1815 | | - bq24190_sysfs_remove_group(bdi); |
---|
1816 | 1834 | |
---|
1817 | 1835 | out_charger: |
---|
1818 | 1836 | if (!IS_ERR_OR_NULL(bdi->battery)) |
---|
.. | .. |
---|
1838 | 1856 | } |
---|
1839 | 1857 | |
---|
1840 | 1858 | bq24190_register_reset(bdi); |
---|
1841 | | - bq24190_sysfs_remove_group(bdi); |
---|
1842 | 1859 | if (bdi->battery) |
---|
1843 | 1860 | power_supply_unregister(bdi->battery); |
---|
1844 | 1861 | power_supply_unregister(bdi->charger); |
---|
.. | .. |
---|
1941 | 1958 | |
---|
1942 | 1959 | static const struct i2c_device_id bq24190_i2c_ids[] = { |
---|
1943 | 1960 | { "bq24190" }, |
---|
| 1961 | + { "bq24192" }, |
---|
1944 | 1962 | { "bq24192i" }, |
---|
| 1963 | + { "bq24196" }, |
---|
1945 | 1964 | { }, |
---|
1946 | 1965 | }; |
---|
1947 | 1966 | MODULE_DEVICE_TABLE(i2c, bq24190_i2c_ids); |
---|
.. | .. |
---|
1949 | 1968 | #ifdef CONFIG_OF |
---|
1950 | 1969 | static const struct of_device_id bq24190_of_match[] = { |
---|
1951 | 1970 | { .compatible = "ti,bq24190", }, |
---|
| 1971 | + { .compatible = "ti,bq24192", }, |
---|
1952 | 1972 | { .compatible = "ti,bq24192i", }, |
---|
| 1973 | + { .compatible = "ti,bq24196", }, |
---|
1953 | 1974 | { }, |
---|
1954 | 1975 | }; |
---|
1955 | 1976 | MODULE_DEVICE_TABLE(of, bq24190_of_match); |
---|