.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * TI BQ257000 charger driver |
---|
| 4 | + |
---|
| 5 | + * Copyright (c) 2021 Rockchip Electronics Co. Ltd. |
---|
3 | 6 | * |
---|
4 | | - * Copyright (C) 2016 Rockchip Corporation |
---|
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 as published by |
---|
8 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
9 | | - * (at your option) any later version. |
---|
10 | | - * |
---|
11 | | - * This program is distributed in the hope that it will be useful, |
---|
12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | | - * MERCHANTABILITY or FITNESS FR A PARTICULAR PURPOSE. See the |
---|
14 | | - * GNU General Public License for more details. |
---|
15 | | - * |
---|
| 7 | + * Author: shengfeixu <xsf@rock-chips.com> |
---|
16 | 8 | */ |
---|
17 | 9 | |
---|
18 | 10 | #include <linux/power/bq25700-charge.h> |
---|
.. | .. |
---|
22 | 14 | #include <linux/mfd/core.h> |
---|
23 | 15 | #include <linux/module.h> |
---|
24 | 16 | #include <linux/regmap.h> |
---|
| 17 | +#include <linux/regulator/driver.h> |
---|
25 | 18 | #include <linux/of_device.h> |
---|
26 | 19 | #include <linux/delay.h> |
---|
27 | 20 | #include <linux/usb/phy.h> |
---|
.. | .. |
---|
54 | 47 | #define MAX_CHARGEVOLTAGE 16800000 |
---|
55 | 48 | #define MAX_CHARGECURRETNT 8128000 |
---|
56 | 49 | #define MAX_OTGVOLTAGE 20800000 |
---|
| 50 | +#define MIN_OTGVOLTAGE 4280000 |
---|
57 | 51 | #define MAX_OTGCURRENT 6350000 |
---|
58 | 52 | |
---|
59 | 53 | enum bq25700_fields { |
---|
.. | .. |
---|
168 | 162 | bool charger_health_valid; |
---|
169 | 163 | bool battery_health_valid; |
---|
170 | 164 | bool battery_status_valid; |
---|
171 | | - |
---|
| 165 | + int automode; |
---|
| 166 | + struct notifier_block nb; |
---|
| 167 | + struct bq2570x_platform_data plat_data; |
---|
| 168 | + struct device_node *notify_node; |
---|
172 | 169 | struct workqueue_struct *usb_charger_wq; |
---|
173 | 170 | struct workqueue_struct *dc_charger_wq; |
---|
174 | 171 | struct workqueue_struct *finish_sig_wq; |
---|
175 | 172 | struct delayed_work usb_work; |
---|
176 | | - struct delayed_work pd_work; |
---|
177 | 173 | struct delayed_work host_work; |
---|
178 | 174 | struct delayed_work discnt_work; |
---|
179 | 175 | struct delayed_work usb_work1; |
---|
180 | | - struct delayed_work pd_work1; |
---|
181 | 176 | struct delayed_work host_work1; |
---|
182 | 177 | struct delayed_work discnt_work1; |
---|
183 | 178 | struct delayed_work irq_work; |
---|
184 | 179 | struct notifier_block cable_cg_nb; |
---|
185 | | - struct notifier_block cable_pd_nb; |
---|
186 | 180 | struct notifier_block cable_host_nb; |
---|
187 | | - struct notifier_block cable_discnt_nb; |
---|
188 | 181 | struct notifier_block cable_cg_nb1; |
---|
189 | | - struct notifier_block cable_pd_nb1; |
---|
190 | 182 | struct notifier_block cable_host_nb1; |
---|
191 | | - struct notifier_block cable_discnt_nb1; |
---|
192 | 183 | struct extcon_dev *cable_edev; |
---|
193 | 184 | struct extcon_dev *cable_edev_1; |
---|
194 | 185 | int typec0_status; |
---|
.. | .. |
---|
199 | 190 | struct gpio_desc *typec1_discharge_io; |
---|
200 | 191 | struct gpio_desc *otg_mode_en_io; |
---|
201 | 192 | |
---|
| 193 | + struct regulator_dev *otg_vbus_reg; |
---|
202 | 194 | struct regmap *regmap; |
---|
203 | 195 | struct regmap_field *rmap_fields[F_MAX_FIELDS]; |
---|
204 | 196 | int chip_id; |
---|
.. | .. |
---|
515 | 507 | u32 size; |
---|
516 | 508 | }; |
---|
517 | 509 | |
---|
518 | | -static const union { |
---|
| 510 | +static const struct bq25700_range sc8886_otg_range = { |
---|
| 511 | + .min = 1280000, |
---|
| 512 | + .max = 20800000, |
---|
| 513 | + .step = 128000, |
---|
| 514 | +}; |
---|
| 515 | + |
---|
| 516 | +static union { |
---|
519 | 517 | struct bq25700_range rt; |
---|
520 | 518 | struct bq25700_lookup lt; |
---|
521 | 519 | } bq25700_tables[] = { |
---|
.. | .. |
---|
816 | 814 | return 0; |
---|
817 | 815 | } |
---|
818 | 816 | |
---|
819 | | -ssize_t bq25700_charge_info_show(struct device *dev, |
---|
| 817 | +static ssize_t bq25700_charge_info_show(struct device *dev, |
---|
820 | 818 | struct device_attribute *attr, char *buf) |
---|
821 | 819 | { |
---|
822 | 820 | struct bq25700_device *charger = dev_get_drvdata(dev); |
---|
.. | .. |
---|
944 | 942 | dev_err(charger->dev, "ti,input-current is error\n"); |
---|
945 | 943 | return -ENODEV; |
---|
946 | 944 | } |
---|
947 | | - if ((props[i].tbl_id == TBL_OTGVOL) && |
---|
948 | | - (property > MAX_OTGVOLTAGE)) { |
---|
949 | | - dev_err(charger->dev, "ti,ti,otg-voltage is error\n"); |
---|
950 | | - return -ENODEV; |
---|
| 945 | + if (props[i].tbl_id == TBL_OTGVOL) { |
---|
| 946 | + if (of_device_is_compatible(charger->dev->of_node, |
---|
| 947 | + "southchip,sc8886")) { |
---|
| 948 | + bq25700_tables[TBL_OTGVOL].rt = sc8886_otg_range; |
---|
| 949 | + |
---|
| 950 | + if (property < MIN_OTGVOLTAGE) { |
---|
| 951 | + dev_err(charger->dev, |
---|
| 952 | + "ti,otg-voltage is error"); |
---|
| 953 | + return -ENODEV; |
---|
| 954 | + } |
---|
| 955 | + } |
---|
| 956 | + |
---|
| 957 | + if (property > MAX_OTGVOLTAGE) { |
---|
| 958 | + dev_err(charger->dev, "ti,otg-voltage is error\n"); |
---|
| 959 | + return -ENODEV; |
---|
| 960 | + }; |
---|
951 | 961 | } |
---|
952 | | - if ((props[i].tbl_id == TBL_OTGVOL) && |
---|
| 962 | + |
---|
| 963 | + if ((props[i].tbl_id == TBL_OTGCUR) && |
---|
953 | 964 | (property > MAX_OTGCURRENT)) { |
---|
954 | 965 | dev_err(charger->dev, "ti,otg-current is error\n"); |
---|
955 | 966 | return -ENODEV; |
---|
.. | .. |
---|
1231 | 1242 | |
---|
1232 | 1243 | psy_cfg.supplied_to = bq25700_charger_supplied_to; |
---|
1233 | 1244 | psy_cfg.num_supplicants = ARRAY_SIZE(bq25700_charger_supplied_to); |
---|
| 1245 | + psy_cfg.of_node = charger->dev->of_node; |
---|
1234 | 1246 | |
---|
1235 | 1247 | charger->supply_charger = |
---|
1236 | 1248 | power_supply_register(charger->dev, |
---|
.. | .. |
---|
1238 | 1250 | &psy_cfg); |
---|
1239 | 1251 | |
---|
1240 | 1252 | return PTR_ERR_OR_ZERO(charger->supply_charger); |
---|
| 1253 | +} |
---|
| 1254 | + |
---|
| 1255 | +static void bq25700_discnt(struct bq25700_device *charger, enum tpyec_port_t port); |
---|
| 1256 | + |
---|
| 1257 | +static int bq2570x_pd_notifier_call(struct notifier_block *nb, |
---|
| 1258 | + unsigned long val, void *v) |
---|
| 1259 | +{ |
---|
| 1260 | + struct bq25700_device *bq = |
---|
| 1261 | + container_of(nb, struct bq25700_device, nb); |
---|
| 1262 | + struct power_supply *psy = v; |
---|
| 1263 | + union power_supply_propval prop; |
---|
| 1264 | + struct bq25700_state state; |
---|
| 1265 | + int ret; |
---|
| 1266 | + int vol_idx, cur_idx, chr_idx; |
---|
| 1267 | + |
---|
| 1268 | + if (val != PSY_EVENT_PROP_CHANGED) |
---|
| 1269 | + return NOTIFY_OK; |
---|
| 1270 | + |
---|
| 1271 | + /* Ignore event if it was not send by notify_node/notify_device */ |
---|
| 1272 | + if (bq->notify_node) { |
---|
| 1273 | + if (!psy->dev.parent || |
---|
| 1274 | + psy->dev.parent->of_node != bq->notify_node) |
---|
| 1275 | + return NOTIFY_OK; |
---|
| 1276 | + } else if (bq->plat_data.notify_device) { |
---|
| 1277 | + if (strcmp(psy->desc->name, bq->plat_data.notify_device) != 0) |
---|
| 1278 | + return NOTIFY_OK; |
---|
| 1279 | + } |
---|
| 1280 | + |
---|
| 1281 | + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &prop); |
---|
| 1282 | + if (ret != 0) |
---|
| 1283 | + return NOTIFY_OK; |
---|
| 1284 | + /* online=0: USB out */ |
---|
| 1285 | + if (prop.intval == 0) { |
---|
| 1286 | + queue_delayed_work(bq->usb_charger_wq, &bq->discnt_work, |
---|
| 1287 | + msecs_to_jiffies(10)); |
---|
| 1288 | + return NOTIFY_OK; |
---|
| 1289 | + } |
---|
| 1290 | + |
---|
| 1291 | + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, &prop); |
---|
| 1292 | + if (ret != 0) |
---|
| 1293 | + return NOTIFY_OK; |
---|
| 1294 | + if (prop.intval > 0) { |
---|
| 1295 | + cur_idx = bq25700_find_idx(prop.intval, TBL_INPUTCUR); |
---|
| 1296 | + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, |
---|
| 1297 | + &prop); |
---|
| 1298 | + if (ret != 0) |
---|
| 1299 | + return NOTIFY_OK; |
---|
| 1300 | + vol_idx = bq25700_find_idx((prop.intval - 1280000 - 3200000), TBL_INPUTVOL); |
---|
| 1301 | + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, |
---|
| 1302 | + &prop); |
---|
| 1303 | + if (ret != 0) |
---|
| 1304 | + return NOTIFY_OK; |
---|
| 1305 | + chr_idx = bq25700_find_idx(prop.intval, TBL_ICHG); |
---|
| 1306 | + |
---|
| 1307 | + bq25700_field_write(bq, INPUT_CURRENT, cur_idx); |
---|
| 1308 | + bq25700_field_write(bq, INPUT_VOLTAGE, vol_idx); |
---|
| 1309 | + bq25700_field_write(bq, CHARGE_CURRENT, chr_idx); |
---|
| 1310 | + dev_info(bq->dev, "INPUT_CURRENT:%d, INPUT_VOLTAGE:%d, CHARGE_CURRENT:%d\n", |
---|
| 1311 | + cur_idx, vol_idx, chr_idx); |
---|
| 1312 | + |
---|
| 1313 | + bq25700_get_chip_state(bq, &state); |
---|
| 1314 | + bq->state = state; |
---|
| 1315 | + power_supply_changed(bq->supply_charger); |
---|
| 1316 | + } |
---|
| 1317 | + return NOTIFY_OK; |
---|
1241 | 1318 | } |
---|
1242 | 1319 | |
---|
1243 | 1320 | static irqreturn_t bq25700_irq_handler_thread(int irq, void *private) |
---|
.. | .. |
---|
1304 | 1381 | msleep(20); |
---|
1305 | 1382 | if (!IS_ERR_OR_NULL(charger->typec1_discharge_io)) |
---|
1306 | 1383 | gpiod_direction_output(charger->typec1_discharge_io, 0); |
---|
1307 | | -} |
---|
1308 | | - |
---|
1309 | | -static void bq25700_pd_connect(struct bq25700_device *charger, |
---|
1310 | | - struct extcon_dev *edev, |
---|
1311 | | - enum tpyec_port_t port) |
---|
1312 | | -{ |
---|
1313 | | - union extcon_property_value prop_val; |
---|
1314 | | - struct bq25700_state state; |
---|
1315 | | - int ret; |
---|
1316 | | - int vol, cur; |
---|
1317 | | - int vol_idx, cur_idx; |
---|
1318 | | - int i; |
---|
1319 | | - |
---|
1320 | | - if (charger->typec0_status == USB_STATUS_PD || |
---|
1321 | | - charger->typec1_status == USB_STATUS_PD) |
---|
1322 | | - return; |
---|
1323 | | - |
---|
1324 | | - if (extcon_get_state(edev, EXTCON_CHG_USB_FAST) > 0) { |
---|
1325 | | - ret = extcon_get_property(edev, EXTCON_CHG_USB_FAST, |
---|
1326 | | - EXTCON_PROP_USB_TYPEC_POLARITY, |
---|
1327 | | - &prop_val); |
---|
1328 | | - DBG("usb pd charge...\n"); |
---|
1329 | | - vol = prop_val.intval & 0xffff; |
---|
1330 | | - cur = prop_val.intval >> 15; |
---|
1331 | | - if (ret == 0) { |
---|
1332 | | - if (port == USB_TYPEC_0) { |
---|
1333 | | - charger->typec0_status = USB_STATUS_PD; |
---|
1334 | | - bq25700_enable_typec0(charger); |
---|
1335 | | - } else { |
---|
1336 | | - charger->typec1_status = USB_STATUS_PD; |
---|
1337 | | - bq25700_enable_typec1(charger); |
---|
1338 | | - } |
---|
1339 | | - |
---|
1340 | | - i = 0; |
---|
1341 | | - while (!bq25700_field_read(charger, AC_STAT) && i < 5) { |
---|
1342 | | - msleep(1000); |
---|
1343 | | - i++; |
---|
1344 | | - } |
---|
1345 | | - vol_idx = bq25700_find_idx((vol - 1280) * 1000, |
---|
1346 | | - TBL_INPUTVOL); |
---|
1347 | | - cur_idx = bq25700_find_idx(cur * 1000, TBL_INPUTCUR); |
---|
1348 | | - bq25700_field_write(charger, INPUT_VOLTAGE, vol_idx); |
---|
1349 | | - bq25700_field_write(charger, INPUT_CURRENT, cur_idx); |
---|
1350 | | - bq25700_field_write(charger, CHARGE_CURRENT, |
---|
1351 | | - charger->init_data.ichg); |
---|
1352 | | - } |
---|
1353 | | - |
---|
1354 | | - bq25700_get_chip_state(charger, &state); |
---|
1355 | | - charger->state = state; |
---|
1356 | | - power_supply_changed(charger->supply_charger); |
---|
1357 | | - } |
---|
1358 | | -} |
---|
1359 | | - |
---|
1360 | | -static void bq25700_pd_evt_worker(struct work_struct *work) |
---|
1361 | | -{ |
---|
1362 | | - struct bq25700_device *charger = container_of(work, |
---|
1363 | | - struct bq25700_device, pd_work.work); |
---|
1364 | | - struct extcon_dev *edev = charger->cable_edev; |
---|
1365 | | - |
---|
1366 | | - bq25700_pd_connect(charger, edev, USB_TYPEC_0); |
---|
1367 | | -} |
---|
1368 | | - |
---|
1369 | | -static void bq25700_pd_evt_worker1(struct work_struct *work) |
---|
1370 | | -{ |
---|
1371 | | - struct bq25700_device *charger = container_of(work, |
---|
1372 | | - struct bq25700_device, pd_work1.work); |
---|
1373 | | - struct extcon_dev *edev = charger->cable_edev_1; |
---|
1374 | | - |
---|
1375 | | - bq25700_pd_connect(charger, edev, USB_TYPEC_1); |
---|
1376 | | -} |
---|
1377 | | - |
---|
1378 | | -static int bq25700_pd_evt_notifier(struct notifier_block *nb, |
---|
1379 | | - unsigned long event, |
---|
1380 | | - void *ptr) |
---|
1381 | | -{ |
---|
1382 | | - struct bq25700_device *charger = |
---|
1383 | | - container_of(nb, struct bq25700_device, cable_pd_nb); |
---|
1384 | | - |
---|
1385 | | - queue_delayed_work(charger->usb_charger_wq, &charger->pd_work, |
---|
1386 | | - msecs_to_jiffies(10)); |
---|
1387 | | - |
---|
1388 | | - return NOTIFY_DONE; |
---|
1389 | | -} |
---|
1390 | | - |
---|
1391 | | -static int bq25700_pd_evt_notifier1(struct notifier_block *nb, |
---|
1392 | | - unsigned long event, |
---|
1393 | | - void *ptr) |
---|
1394 | | -{ |
---|
1395 | | - struct bq25700_device *charger = |
---|
1396 | | - container_of(nb, struct bq25700_device, cable_pd_nb1); |
---|
1397 | | - |
---|
1398 | | - queue_delayed_work(charger->usb_charger_wq, &charger->pd_work1, |
---|
1399 | | - msecs_to_jiffies(10)); |
---|
1400 | | - |
---|
1401 | | - return NOTIFY_DONE; |
---|
1402 | 1384 | } |
---|
1403 | 1385 | |
---|
1404 | 1386 | static void bq25700_charger_evt_handel(struct bq25700_device *charger, |
---|
.. | .. |
---|
1481 | 1463 | struct bq25700_device *charger = container_of(work, |
---|
1482 | 1464 | struct bq25700_device, usb_work.work); |
---|
1483 | 1465 | struct extcon_dev *edev = charger->cable_edev; |
---|
| 1466 | + |
---|
1484 | 1467 | if (charger->usb_bc == 0) |
---|
1485 | 1468 | bq25700_charger_evt_handel(charger, edev, USB_TYPEC_0); |
---|
1486 | 1469 | else |
---|
.. | .. |
---|
1522 | 1505 | return NOTIFY_DONE; |
---|
1523 | 1506 | } |
---|
1524 | 1507 | |
---|
| 1508 | +static void bq25700_set_otg_vbus(struct bq25700_device *charger, bool enable) |
---|
| 1509 | +{ |
---|
| 1510 | + DBG("OTG %s\n", enable ? "enable" : "disable"); |
---|
| 1511 | + |
---|
| 1512 | + if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) |
---|
| 1513 | + gpiod_direction_output(charger->otg_mode_en_io, enable); |
---|
| 1514 | + bq25700_field_write(charger, EN_OTG, enable); |
---|
| 1515 | +} |
---|
| 1516 | + |
---|
1525 | 1517 | static void bq25700_host_evt_worker(struct work_struct *work) |
---|
1526 | 1518 | { |
---|
1527 | 1519 | struct bq25700_device *charger = |
---|
1528 | 1520 | container_of(work, struct bq25700_device, host_work.work); |
---|
1529 | 1521 | struct extcon_dev *edev = charger->cable_edev; |
---|
1530 | 1522 | |
---|
1531 | | - /* Determine charger type */ |
---|
1532 | | - if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) > 0) { |
---|
1533 | | - if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) |
---|
1534 | | - gpiod_direction_output(charger->otg_mode_en_io, 1); |
---|
1535 | | - bq25700_field_write(charger, EN_OTG, 1); |
---|
1536 | | - DBG("OTG enable\n"); |
---|
1537 | | - } else if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) == 0) { |
---|
1538 | | - if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) |
---|
1539 | | - gpiod_direction_output(charger->otg_mode_en_io, 0); |
---|
1540 | | - bq25700_field_write(charger, EN_OTG, 0); |
---|
1541 | | - DBG("OTG disable\n"); |
---|
1542 | | - } |
---|
| 1523 | + if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) > 0) |
---|
| 1524 | + bq25700_set_otg_vbus(charger, true); |
---|
| 1525 | + else if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) == 0) |
---|
| 1526 | + bq25700_set_otg_vbus(charger, false); |
---|
1543 | 1527 | } |
---|
1544 | 1528 | |
---|
1545 | 1529 | static void bq25700_host_evt_worker1(struct work_struct *work) |
---|
.. | .. |
---|
1548 | 1532 | container_of(work, struct bq25700_device, host_work1.work); |
---|
1549 | 1533 | struct extcon_dev *edev = charger->cable_edev_1; |
---|
1550 | 1534 | |
---|
1551 | | - /* Determine charger type */ |
---|
1552 | | - if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) > 0) { |
---|
1553 | | - if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) |
---|
1554 | | - gpiod_direction_output(charger->otg_mode_en_io, 1); |
---|
1555 | | - bq25700_field_write(charger, EN_OTG, 1); |
---|
1556 | | - DBG("OTG enable\n"); |
---|
1557 | | - } else if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) == 0) { |
---|
1558 | | - if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) |
---|
1559 | | - gpiod_direction_output(charger->otg_mode_en_io, 0); |
---|
1560 | | - bq25700_field_write(charger, EN_OTG, 0); |
---|
1561 | | - DBG("OTG disable\n"); |
---|
1562 | | - } |
---|
| 1535 | + if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) > 0) |
---|
| 1536 | + bq25700_set_otg_vbus(charger, true); |
---|
| 1537 | + else if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) == 0) |
---|
| 1538 | + bq25700_set_otg_vbus(charger, false); |
---|
1563 | 1539 | } |
---|
1564 | 1540 | |
---|
1565 | 1541 | static int bq25700_host_evt_notifier(struct notifier_block *nb, |
---|
.. | .. |
---|
1619 | 1595 | discnt_work.work); |
---|
1620 | 1596 | |
---|
1621 | 1597 | bq25700_discnt(charger, USB_TYPEC_0); |
---|
1622 | | -} |
---|
1623 | | - |
---|
1624 | | -static void bq25700_discnt_evt_worker1(struct work_struct *work) |
---|
1625 | | -{ |
---|
1626 | | - struct bq25700_device *charger = container_of(work, |
---|
1627 | | - struct bq25700_device, |
---|
1628 | | - discnt_work1.work); |
---|
1629 | | - bq25700_discnt(charger, USB_TYPEC_1); |
---|
1630 | | -} |
---|
1631 | | - |
---|
1632 | | -static int bq25700_discnt_evt_notfier(struct notifier_block *nb, |
---|
1633 | | - unsigned long event, |
---|
1634 | | - void *ptr) |
---|
1635 | | -{ |
---|
1636 | | - struct bq25700_device *charger = |
---|
1637 | | - container_of(nb, struct bq25700_device, cable_discnt_nb); |
---|
1638 | | - |
---|
1639 | | - queue_delayed_work(charger->usb_charger_wq, |
---|
1640 | | - &charger->discnt_work, |
---|
1641 | | - msecs_to_jiffies(0)); |
---|
1642 | | - |
---|
1643 | | - return NOTIFY_DONE; |
---|
1644 | | -} |
---|
1645 | | - |
---|
1646 | | -static int bq25700_discnt_evt_notfier1(struct notifier_block *nb, |
---|
1647 | | - unsigned long event, |
---|
1648 | | - void *ptr) |
---|
1649 | | -{ |
---|
1650 | | - struct bq25700_device *charger = |
---|
1651 | | - container_of(nb, struct bq25700_device, cable_discnt_nb1); |
---|
1652 | | - |
---|
1653 | | - queue_delayed_work(charger->usb_charger_wq, |
---|
1654 | | - &charger->discnt_work1, |
---|
1655 | | - msecs_to_jiffies(0)); |
---|
1656 | | - |
---|
1657 | | - return NOTIFY_DONE; |
---|
1658 | 1598 | } |
---|
1659 | 1599 | |
---|
1660 | 1600 | static int bq25700_register_cg_extcon(struct bq25700_device *charger, |
---|
.. | .. |
---|
1731 | 1671 | return 0; |
---|
1732 | 1672 | } |
---|
1733 | 1673 | |
---|
1734 | | -static int bq25700_register_discnt_nb(struct bq25700_device *charger) |
---|
1735 | | -{ |
---|
1736 | | - int ret; |
---|
1737 | | - |
---|
1738 | | - /* Register discnt usb */ |
---|
1739 | | - if (charger->cable_edev) { |
---|
1740 | | - INIT_DELAYED_WORK(&charger->discnt_work, |
---|
1741 | | - bq25700_discnt_evt_worker); |
---|
1742 | | - charger->cable_discnt_nb.notifier_call = |
---|
1743 | | - bq25700_discnt_evt_notfier; |
---|
1744 | | - ret = extcon_register_notifier(charger->cable_edev, |
---|
1745 | | - EXTCON_USB, |
---|
1746 | | - &charger->cable_discnt_nb); |
---|
1747 | | - if (ret < 0) { |
---|
1748 | | - dev_err(charger->dev, |
---|
1749 | | - "failed to register notifier for HOST\n"); |
---|
1750 | | - return ret; |
---|
1751 | | - } |
---|
1752 | | - } |
---|
1753 | | - |
---|
1754 | | - if (charger->cable_edev_1) { |
---|
1755 | | - INIT_DELAYED_WORK(&charger->discnt_work1, |
---|
1756 | | - bq25700_discnt_evt_worker1); |
---|
1757 | | - charger->cable_discnt_nb1.notifier_call = |
---|
1758 | | - bq25700_discnt_evt_notfier1; |
---|
1759 | | - ret = extcon_register_notifier(charger->cable_edev_1, |
---|
1760 | | - EXTCON_USB, |
---|
1761 | | - &charger->cable_discnt_nb1); |
---|
1762 | | - if (ret < 0) { |
---|
1763 | | - dev_err(charger->dev, |
---|
1764 | | - "failed to register notifier for HOST\n"); |
---|
1765 | | - return ret; |
---|
1766 | | - } |
---|
1767 | | - } |
---|
1768 | | - |
---|
1769 | | - return 0; |
---|
1770 | | -} |
---|
1771 | | - |
---|
1772 | 1674 | static int bq25700_register_pd_nb(struct bq25700_device *charger) |
---|
1773 | 1675 | { |
---|
1774 | | - if (charger->cable_edev) { |
---|
1775 | | - INIT_DELAYED_WORK(&charger->pd_work, bq25700_pd_evt_worker); |
---|
1776 | | - charger->cable_pd_nb.notifier_call = bq25700_pd_evt_notifier; |
---|
1777 | | - extcon_register_notifier(charger->cable_edev, |
---|
1778 | | - EXTCON_CHG_USB_FAST, |
---|
1779 | | - &charger->cable_pd_nb); |
---|
| 1676 | + struct power_supply *notify_psy = NULL; |
---|
| 1677 | + int vol_idx, cur_idx; |
---|
| 1678 | + int ret; |
---|
| 1679 | + union power_supply_propval prop; |
---|
| 1680 | + |
---|
| 1681 | + if (charger->notify_node || charger->plat_data.notify_device) { |
---|
| 1682 | + INIT_DELAYED_WORK(&charger->discnt_work, |
---|
| 1683 | + bq25700_discnt_evt_worker); |
---|
| 1684 | + charger->nb.notifier_call = bq2570x_pd_notifier_call; |
---|
| 1685 | + ret = power_supply_reg_notifier(&charger->nb); |
---|
| 1686 | + if (ret) { |
---|
| 1687 | + dev_err(charger->dev, "failed to reg notifier: %d\n", ret); |
---|
| 1688 | + return ret; |
---|
| 1689 | + } |
---|
| 1690 | + charger->automode = 1; |
---|
| 1691 | + dev_info(charger->dev, "automode supported, waiting for events\n"); |
---|
| 1692 | + } else { |
---|
| 1693 | + charger->automode = -1; |
---|
| 1694 | + dev_info(charger->dev, "automode not supported\n"); |
---|
1780 | 1695 | } |
---|
1781 | 1696 | |
---|
1782 | | - if (charger->cable_edev_1) { |
---|
1783 | | - INIT_DELAYED_WORK(&charger->pd_work1, bq25700_pd_evt_worker1); |
---|
1784 | | - charger->cable_pd_nb1.notifier_call = bq25700_pd_evt_notifier1; |
---|
1785 | | - extcon_register_notifier(charger->cable_edev_1, |
---|
1786 | | - EXTCON_CHG_USB_FAST, |
---|
1787 | | - &charger->cable_pd_nb1); |
---|
| 1697 | + if (charger->nb.notifier_call) { |
---|
| 1698 | + if (charger->dev->of_node) { |
---|
| 1699 | + notify_psy = power_supply_get_by_phandle(charger->dev->of_node, |
---|
| 1700 | + "ti,usb-charger-detection"); |
---|
| 1701 | + if (IS_ERR_OR_NULL(notify_psy)) { |
---|
| 1702 | + dev_info(charger->dev, "bq25700 notify_psy is error\n"); |
---|
| 1703 | + notify_psy = NULL; |
---|
| 1704 | + } |
---|
| 1705 | + } else if (charger->plat_data.notify_device) { |
---|
| 1706 | + notify_psy = power_supply_get_by_name( |
---|
| 1707 | + charger->plat_data.notify_device); |
---|
| 1708 | + } |
---|
| 1709 | + } |
---|
| 1710 | + |
---|
| 1711 | + if (notify_psy) { |
---|
| 1712 | + ret = power_supply_get_property(notify_psy, |
---|
| 1713 | + POWER_SUPPLY_PROP_CURRENT_MAX, &prop); |
---|
| 1714 | + if (ret != 0) |
---|
| 1715 | + return ret; |
---|
| 1716 | + ret = power_supply_get_property(notify_psy, |
---|
| 1717 | + POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop); |
---|
| 1718 | + if (ret != 0) |
---|
| 1719 | + return ret; |
---|
| 1720 | + |
---|
| 1721 | + cur_idx = bq25700_find_idx(prop.intval, TBL_INPUTCUR); |
---|
| 1722 | + vol_idx = bq25700_find_idx((prop.intval - 1280000 - 3200000), TBL_INPUTVOL); |
---|
| 1723 | + bq25700_field_write(charger, INPUT_CURRENT, cur_idx); |
---|
| 1724 | + bq25700_field_write(charger, INPUT_VOLTAGE, vol_idx); |
---|
| 1725 | + bq25700_field_write(charger, CHARGE_CURRENT, |
---|
| 1726 | + charger->init_data.ichg); |
---|
| 1727 | + dev_info(charger->dev, "INPUT_CURRENT:%d, INPUT_VOLTAGE:%d, CHARGE_CURRENT:%d\n", |
---|
| 1728 | + cur_idx, vol_idx, charger->init_data.ichg); |
---|
1788 | 1729 | } |
---|
1789 | 1730 | |
---|
1790 | 1731 | return 0; |
---|
.. | .. |
---|
1800 | 1741 | charger->cable_host_nb.notifier_call = |
---|
1801 | 1742 | bq25700_host_evt_notifier; |
---|
1802 | 1743 | ret = extcon_register_notifier(charger->cable_edev, |
---|
1803 | | - EXTCON_USB_HOST, |
---|
| 1744 | + EXTCON_USB_VBUS_EN, |
---|
1804 | 1745 | &charger->cable_host_nb); |
---|
1805 | 1746 | if (ret < 0) { |
---|
1806 | 1747 | dev_err(charger->dev, |
---|
.. | .. |
---|
1815 | 1756 | charger->cable_host_nb1.notifier_call = |
---|
1816 | 1757 | bq25700_host_evt_notifier1; |
---|
1817 | 1758 | ret = extcon_register_notifier(charger->cable_edev_1, |
---|
1818 | | - EXTCON_USB_HOST, |
---|
| 1759 | + EXTCON_USB_VBUS_EN, |
---|
1819 | 1760 | &charger->cable_host_nb1); |
---|
1820 | 1761 | if (ret < 0) { |
---|
1821 | 1762 | dev_err(charger->dev, |
---|
.. | .. |
---|
1823 | 1764 | return -1; |
---|
1824 | 1765 | } |
---|
1825 | 1766 | } |
---|
| 1767 | + |
---|
| 1768 | + return 0; |
---|
| 1769 | +} |
---|
| 1770 | + |
---|
| 1771 | +static int bq25700_otg_vbus_enable(struct regulator_dev *dev) |
---|
| 1772 | +{ |
---|
| 1773 | + struct bq25700_device *charger = rdev_get_drvdata(dev); |
---|
| 1774 | + |
---|
| 1775 | + bq25700_set_otg_vbus(charger, true); |
---|
| 1776 | + |
---|
| 1777 | + return 0; |
---|
| 1778 | +} |
---|
| 1779 | + |
---|
| 1780 | +static int bq25700_otg_vbus_disable(struct regulator_dev *dev) |
---|
| 1781 | +{ |
---|
| 1782 | + struct bq25700_device *charger = rdev_get_drvdata(dev); |
---|
| 1783 | + |
---|
| 1784 | + bq25700_set_otg_vbus(charger, false); |
---|
| 1785 | + |
---|
| 1786 | + return 0; |
---|
| 1787 | +} |
---|
| 1788 | + |
---|
| 1789 | +static int bq25700_otg_vbus_is_enabled(struct regulator_dev *dev) |
---|
| 1790 | +{ |
---|
| 1791 | + struct bq25700_device *charger = rdev_get_drvdata(dev); |
---|
| 1792 | + u8 val; |
---|
| 1793 | + int gpio_status = 1; |
---|
| 1794 | + |
---|
| 1795 | + val = bq25700_field_read(charger, EN_OTG); |
---|
| 1796 | + if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) |
---|
| 1797 | + gpio_status = gpiod_get_value(charger->otg_mode_en_io); |
---|
| 1798 | + |
---|
| 1799 | + return val && gpio_status ? 1 : 0; |
---|
| 1800 | +} |
---|
| 1801 | + |
---|
| 1802 | +static const struct regulator_ops bq25700_otg_vbus_ops = { |
---|
| 1803 | + .enable = bq25700_otg_vbus_enable, |
---|
| 1804 | + .disable = bq25700_otg_vbus_disable, |
---|
| 1805 | + .is_enabled = bq25700_otg_vbus_is_enabled, |
---|
| 1806 | +}; |
---|
| 1807 | + |
---|
| 1808 | +static const struct regulator_desc bq25700_otg_vbus_desc = { |
---|
| 1809 | + .name = "otg-vbus", |
---|
| 1810 | + .of_match = "otg-vbus", |
---|
| 1811 | + .regulators_node = of_match_ptr("regulators"), |
---|
| 1812 | + .owner = THIS_MODULE, |
---|
| 1813 | + .ops = &bq25700_otg_vbus_ops, |
---|
| 1814 | + .type = REGULATOR_VOLTAGE, |
---|
| 1815 | + .fixed_uV = 5000000, |
---|
| 1816 | + .n_voltages = 1, |
---|
| 1817 | +}; |
---|
| 1818 | + |
---|
| 1819 | +static int bq25700_register_otg_vbus_regulator(struct bq25700_device *charger) |
---|
| 1820 | +{ |
---|
| 1821 | + struct device_node *np; |
---|
| 1822 | + struct regulator_config config = { }; |
---|
| 1823 | + |
---|
| 1824 | + np = of_get_child_by_name(charger->dev->of_node, "regulators"); |
---|
| 1825 | + if (!np) { |
---|
| 1826 | + dev_warn(charger->dev, "cannot find regulators node\n"); |
---|
| 1827 | + return -ENXIO; |
---|
| 1828 | + } |
---|
| 1829 | + |
---|
| 1830 | + config.dev = charger->dev; |
---|
| 1831 | + config.driver_data = charger; |
---|
| 1832 | + |
---|
| 1833 | + charger->otg_vbus_reg = devm_regulator_register(charger->dev, |
---|
| 1834 | + &bq25700_otg_vbus_desc, |
---|
| 1835 | + &config); |
---|
| 1836 | + if (IS_ERR(charger->otg_vbus_reg)) |
---|
| 1837 | + return PTR_ERR(charger->otg_vbus_reg); |
---|
1826 | 1838 | |
---|
1827 | 1839 | return 0; |
---|
1828 | 1840 | } |
---|
.. | .. |
---|
1854 | 1866 | } else { |
---|
1855 | 1867 | charger->cable_edev_1 = edev1; |
---|
1856 | 1868 | } |
---|
| 1869 | + /*set power_on input current*/ |
---|
| 1870 | + bq25700_field_write(charger, INPUT_CURRENT, |
---|
| 1871 | + charger->init_data.input_current_sdp); |
---|
| 1872 | + |
---|
1857 | 1873 | if (!charger->pd_charge_only) |
---|
1858 | 1874 | bq25700_register_cg_nb(charger); |
---|
1859 | | - bq25700_register_host_nb(charger); |
---|
1860 | | - bq25700_register_discnt_nb(charger); |
---|
| 1875 | + |
---|
| 1876 | + if (bq25700_register_otg_vbus_regulator(charger) < 0) { |
---|
| 1877 | + dev_warn(charger->dev, |
---|
| 1878 | + "Cannot register otg vbus regulator\n"); |
---|
| 1879 | + charger->otg_vbus_reg = NULL; |
---|
| 1880 | + bq25700_register_host_nb(charger); |
---|
| 1881 | + } |
---|
| 1882 | + |
---|
1861 | 1883 | bq25700_register_pd_nb(charger); |
---|
1862 | 1884 | |
---|
1863 | 1885 | if (charger->cable_edev) { |
---|
1864 | | - schedule_delayed_work(&charger->host_work, 0); |
---|
1865 | | - schedule_delayed_work(&charger->pd_work, 0); |
---|
| 1886 | + if (!charger->otg_vbus_reg) |
---|
| 1887 | + schedule_delayed_work(&charger->host_work, 0); |
---|
1866 | 1888 | if (!charger->pd_charge_only) |
---|
1867 | 1889 | schedule_delayed_work(&charger->usb_work, 0); |
---|
1868 | 1890 | } |
---|
1869 | 1891 | if (charger->cable_edev_1) { |
---|
1870 | | - schedule_delayed_work(&charger->host_work1, 0); |
---|
1871 | | - schedule_delayed_work(&charger->pd_work1, 0); |
---|
| 1892 | + if (!charger->otg_vbus_reg) |
---|
| 1893 | + schedule_delayed_work(&charger->host_work1, 0); |
---|
1872 | 1894 | if (!charger->pd_charge_only) |
---|
1873 | 1895 | schedule_delayed_work(&charger->usb_work1, 0); |
---|
1874 | 1896 | } |
---|
.. | .. |
---|
1918 | 1940 | charger->usb_bc = 0; |
---|
1919 | 1941 | else |
---|
1920 | 1942 | charger->usb_bc = 1; |
---|
| 1943 | + of_node_put(temp_np); |
---|
1921 | 1944 | |
---|
| 1945 | + if (np) |
---|
| 1946 | + charger->notify_node = of_parse_phandle(np, |
---|
| 1947 | + "ti,usb-charger-detection", 0); |
---|
1922 | 1948 | return 0; |
---|
1923 | 1949 | } |
---|
1924 | 1950 | |
---|
.. | .. |
---|
1944 | 1970 | charger->dev = dev; |
---|
1945 | 1971 | |
---|
1946 | 1972 | charger_np = of_find_compatible_node(NULL, NULL, "ti,bq25700"); |
---|
| 1973 | + if (!charger_np) |
---|
| 1974 | + charger_np = of_find_compatible_node(NULL, NULL, "southchip,sc8885"); |
---|
1947 | 1975 | if (charger_np) { |
---|
1948 | 1976 | charger->regmap = devm_regmap_init_i2c(client, |
---|
1949 | 1977 | &bq25700_regmap_config); |
---|
.. | .. |
---|
2006 | 2034 | return -ENODEV; |
---|
2007 | 2035 | } |
---|
2008 | 2036 | |
---|
| 2037 | + /* |
---|
| 2038 | + * Make sure battery online, otherwise, writing INPUT_CURRENT and |
---|
| 2039 | + * CHARGE_CURRENT would make system power off |
---|
| 2040 | + */ |
---|
| 2041 | + if (of_parse_phandle(charger->dev->of_node, "ti,battery", 0)) { |
---|
| 2042 | + if (IS_ERR_OR_NULL(power_supply_get_by_phandle( |
---|
| 2043 | + charger->dev->of_node, |
---|
| 2044 | + "ti,battery"))) { |
---|
| 2045 | + dev_info(charger->dev, "No battery found\n"); |
---|
| 2046 | + return -EPROBE_DEFER; |
---|
| 2047 | + } |
---|
| 2048 | + dev_info(charger->dev, "Battery found\n"); |
---|
| 2049 | + } |
---|
| 2050 | + |
---|
2009 | 2051 | ret = bq25700_hw_init(charger); |
---|
2010 | 2052 | if (ret < 0) { |
---|
2011 | 2053 | dev_err(dev, "Cannot initialize the chip.\n"); |
---|
.. | .. |
---|
2044 | 2086 | return ret; |
---|
2045 | 2087 | } |
---|
2046 | 2088 | |
---|
2047 | | -void bq25700_shutdown(struct i2c_client *client) |
---|
| 2089 | +static void bq25700_shutdown(struct i2c_client *client) |
---|
2048 | 2090 | { |
---|
2049 | 2091 | int vol_idx; |
---|
2050 | 2092 | struct bq25700_device *charger = i2c_get_clientdata(client); |
---|
.. | .. |
---|
2053 | 2095 | bq25700_field_write(charger, INPUT_VOLTAGE, vol_idx); |
---|
2054 | 2096 | bq25700_field_write(charger, INPUT_CURRENT, |
---|
2055 | 2097 | charger->init_data.input_current_sdp); |
---|
| 2098 | + |
---|
| 2099 | + if (!bq25700_field_read(charger, AC_STAT)) |
---|
| 2100 | + bq25700_field_write(charger, EN_LWPWR, 1); |
---|
2056 | 2101 | } |
---|
2057 | 2102 | |
---|
2058 | 2103 | #ifdef CONFIG_PM_SLEEP |
---|
2059 | 2104 | static int bq25700_pm_suspend(struct device *dev) |
---|
2060 | 2105 | { |
---|
| 2106 | + struct bq25700_device *charger = dev_get_drvdata(dev); |
---|
| 2107 | + |
---|
| 2108 | + if (!bq25700_field_read(charger, AC_STAT)) |
---|
| 2109 | + bq25700_field_write(charger, EN_LWPWR, 1); |
---|
| 2110 | + |
---|
2061 | 2111 | return 0; |
---|
2062 | 2112 | } |
---|
2063 | 2113 | |
---|
2064 | 2114 | static int bq25700_pm_resume(struct device *dev) |
---|
2065 | 2115 | { |
---|
| 2116 | + struct bq25700_device *charger = dev_get_drvdata(dev); |
---|
| 2117 | + |
---|
| 2118 | + bq25700_field_write(charger, EN_LWPWR, 0); |
---|
| 2119 | + |
---|
2066 | 2120 | return 0; |
---|
2067 | 2121 | } |
---|
2068 | 2122 | #endif |
---|
.. | .. |
---|
2079 | 2133 | static const struct of_device_id bq25700_of_match[] = { |
---|
2080 | 2134 | { .compatible = "ti,bq25700", }, |
---|
2081 | 2135 | { .compatible = "ti,bq25703", }, |
---|
| 2136 | + { .compatible = "southchip,sc8885", }, |
---|
| 2137 | + { .compatible = "southchip,sc8886", }, |
---|
2082 | 2138 | { }, |
---|
2083 | 2139 | }; |
---|
2084 | 2140 | MODULE_DEVICE_TABLE(of, bq25700_of_match); |
---|