66 * Author: Beomho Seo <beomho.seo@samsung.com>
77 */
88
9+ #include <linux/devm-helpers.h>
10+ #include <linux/extcon.h>
911#include <linux/mod_devicetable.h>
1012#include <linux/module.h>
13+ #include <linux/mutex.h>
14+ #include <linux/of.h>
1115#include <linux/platform_device.h>
1216#include <linux/power_supply.h>
1317#include <linux/regmap.h>
@@ -26,6 +30,14 @@ struct rt5033_charger {
2630 struct regmap * regmap ;
2731 struct power_supply * psy ;
2832 struct rt5033_charger_data * chg ;
33+ struct extcon_dev * edev ;
34+ struct notifier_block extcon_nb ;
35+ struct work_struct extcon_work ;
36+ struct mutex lock ;
37+ bool online ;
38+ bool otg ;
39+ bool mivr_enabled ;
40+ u8 cv_regval ;
2941};
3042
3143static int rt5033_get_charger_state (struct rt5033_charger * charger )
@@ -56,6 +68,10 @@ static int rt5033_get_charger_state(struct rt5033_charger *charger)
5668 state = POWER_SUPPLY_STATUS_UNKNOWN ;
5769 }
5870
71+ /* For OTG mode, RT5033 would still report "charging" */
72+ if (charger -> otg )
73+ state = POWER_SUPPLY_STATUS_DISCHARGING ;
74+
5975 return state ;
6076}
6177
@@ -147,6 +163,9 @@ static inline int rt5033_init_const_charge(struct rt5033_charger *charger)
147163 return - EINVAL ;
148164 }
149165
166+ /* Store that value for later usage */
167+ charger -> cv_regval = reg_data ;
168+
150169 /* Set end of charge current */
151170 if (chg -> eoc_uamp < RT5033_CHARGER_EOC_MIN ||
152171 chg -> eoc_uamp > RT5033_CHARGER_EOC_MAX ) {
@@ -330,6 +349,162 @@ static int rt5033_charger_reg_init(struct rt5033_charger *charger)
330349 return 0 ;
331350}
332351
352+ static int rt5033_charger_set_otg (struct rt5033_charger * charger )
353+ {
354+ int ret ;
355+
356+ mutex_lock (& charger -> lock );
357+
358+ /* Set OTG boost v_out to 5 volts */
359+ ret = regmap_update_bits (charger -> regmap , RT5033_REG_CHG_CTRL2 ,
360+ RT5033_CHGCTRL2_CV_MASK ,
361+ 0x37 << RT5033_CHGCTRL2_CV_SHIFT );
362+ if (ret ) {
363+ dev_err (charger -> dev , "Failed set OTG boost v_out\n" );
364+ ret = - EINVAL ;
365+ goto out_unlock ;
366+ }
367+
368+ /* Set operation mode to OTG */
369+ ret = regmap_update_bits (charger -> regmap , RT5033_REG_CHG_CTRL1 ,
370+ RT5033_CHGCTRL1_MODE_MASK , RT5033_BOOST_MODE );
371+ if (ret ) {
372+ dev_err (charger -> dev , "Failed to update OTG mode.\n" );
373+ ret = - EINVAL ;
374+ goto out_unlock ;
375+ }
376+
377+ /* In case someone switched from charging to OTG directly */
378+ if (charger -> online )
379+ charger -> online = false;
380+
381+ charger -> otg = true;
382+
383+ out_unlock :
384+ mutex_unlock (& charger -> lock );
385+
386+ return ret ;
387+ }
388+
389+ static int rt5033_charger_unset_otg (struct rt5033_charger * charger )
390+ {
391+ int ret ;
392+ u8 data ;
393+
394+ /* Restore constant voltage for charging */
395+ data = charger -> cv_regval ;
396+ ret = regmap_update_bits (charger -> regmap , RT5033_REG_CHG_CTRL2 ,
397+ RT5033_CHGCTRL2_CV_MASK ,
398+ data << RT5033_CHGCTRL2_CV_SHIFT );
399+ if (ret ) {
400+ dev_err (charger -> dev , "Failed to restore constant voltage\n" );
401+ return - EINVAL ;
402+ }
403+
404+ /* Set operation mode to charging */
405+ ret = regmap_update_bits (charger -> regmap , RT5033_REG_CHG_CTRL1 ,
406+ RT5033_CHGCTRL1_MODE_MASK , RT5033_CHARGER_MODE );
407+ if (ret ) {
408+ dev_err (charger -> dev , "Failed to update charger mode.\n" );
409+ return - EINVAL ;
410+ }
411+
412+ charger -> otg = false;
413+
414+ return 0 ;
415+ }
416+
417+ static int rt5033_charger_set_charging (struct rt5033_charger * charger )
418+ {
419+ int ret ;
420+
421+ mutex_lock (& charger -> lock );
422+
423+ /* In case someone switched from OTG to charging directly */
424+ if (charger -> otg ) {
425+ ret = rt5033_charger_unset_otg (charger );
426+ if (ret ) {
427+ mutex_unlock (& charger -> lock );
428+ return - EINVAL ;
429+ }
430+ }
431+
432+ charger -> online = true;
433+
434+ mutex_unlock (& charger -> lock );
435+
436+ return 0 ;
437+ }
438+
439+ static int rt5033_charger_set_mivr (struct rt5033_charger * charger )
440+ {
441+ int ret ;
442+
443+ mutex_lock (& charger -> lock );
444+
445+ /*
446+ * When connected via USB connector type SDP (Standard Downstream Port),
447+ * the minimum input voltage regulation (MIVR) should be enabled. It
448+ * prevents an input voltage drop due to insufficient current provided
449+ * by the adapter or USB input. As a downside, it may reduces the
450+ * charging current and thus slows the charging.
451+ */
452+ ret = regmap_update_bits (charger -> regmap , RT5033_REG_CHG_CTRL4 ,
453+ RT5033_CHGCTRL4_MIVR_MASK , RT5033_CHARGER_MIVR_4600MV );
454+ if (ret ) {
455+ dev_err (charger -> dev , "Failed to set MIVR level.\n" );
456+ mutex_unlock (& charger -> lock );
457+ return - EINVAL ;
458+ }
459+
460+ charger -> mivr_enabled = true;
461+
462+ mutex_unlock (& charger -> lock );
463+
464+ /* Beyond this, do the same steps like setting charging */
465+ rt5033_charger_set_charging (charger );
466+
467+ return 0 ;
468+ }
469+
470+ static int rt5033_charger_set_disconnect (struct rt5033_charger * charger )
471+ {
472+ int ret = 0 ;
473+
474+ mutex_lock (& charger -> lock );
475+
476+ /* Disable MIVR if enabled */
477+ if (charger -> mivr_enabled ) {
478+ ret = regmap_update_bits (charger -> regmap ,
479+ RT5033_REG_CHG_CTRL4 ,
480+ RT5033_CHGCTRL4_MIVR_MASK ,
481+ RT5033_CHARGER_MIVR_DISABLE );
482+ if (ret ) {
483+ dev_err (charger -> dev , "Failed to disable MIVR.\n" );
484+ ret = - EINVAL ;
485+ goto out_unlock ;
486+ }
487+
488+ charger -> mivr_enabled = false;
489+ }
490+
491+ if (charger -> otg ) {
492+ ret = rt5033_charger_unset_otg (charger );
493+ if (ret ) {
494+ ret = - EINVAL ;
495+ goto out_unlock ;
496+ }
497+ }
498+
499+ if (charger -> online )
500+ charger -> online = false;
501+
502+ out_unlock :
503+ mutex_unlock (& charger -> lock );
504+
505+ return ret ;
506+ }
507+
333508static enum power_supply_property rt5033_charger_props [] = {
334509 POWER_SUPPLY_PROP_STATUS ,
335510 POWER_SUPPLY_PROP_CHARGE_TYPE ,
@@ -366,8 +541,7 @@ static int rt5033_charger_get_property(struct power_supply *psy,
366541 val -> strval = RT5033_MANUFACTURER ;
367542 break ;
368543 case POWER_SUPPLY_PROP_ONLINE :
369- val -> intval = (rt5033_get_charger_state (charger ) ==
370- POWER_SUPPLY_STATUS_CHARGING );
544+ val -> intval = charger -> online ;
371545 break ;
372546 default :
373547 return - EINVAL ;
@@ -402,6 +576,86 @@ static struct rt5033_charger_data *rt5033_charger_dt_init(
402576 return chg ;
403577}
404578
579+ static void rt5033_charger_extcon_work (struct work_struct * work )
580+ {
581+ struct rt5033_charger * charger =
582+ container_of (work , struct rt5033_charger , extcon_work );
583+ struct extcon_dev * edev = charger -> edev ;
584+ int connector , state ;
585+ int ret ;
586+
587+ for (connector = EXTCON_USB_HOST ; connector <= EXTCON_CHG_USB_PD ;
588+ connector ++ ) {
589+ state = extcon_get_state (edev , connector );
590+ if (state == 1 )
591+ break ;
592+ }
593+
594+ /*
595+ * Adding a delay between extcon notification and extcon action. This
596+ * makes extcon action execution more reliable. Without the delay the
597+ * execution sometimes fails, possibly because the chip is busy or not
598+ * ready.
599+ */
600+ msleep (100 );
601+
602+ switch (connector ) {
603+ case EXTCON_CHG_USB_SDP :
604+ ret = rt5033_charger_set_mivr (charger );
605+ if (ret ) {
606+ dev_err (charger -> dev , "failed to set USB mode\n" );
607+ break ;
608+ }
609+ dev_info (charger -> dev , "USB mode. connector type: %d\n" ,
610+ connector );
611+ break ;
612+ case EXTCON_CHG_USB_DCP :
613+ case EXTCON_CHG_USB_CDP :
614+ case EXTCON_CHG_USB_ACA :
615+ case EXTCON_CHG_USB_FAST :
616+ case EXTCON_CHG_USB_SLOW :
617+ case EXTCON_CHG_WPT :
618+ case EXTCON_CHG_USB_PD :
619+ ret = rt5033_charger_set_charging (charger );
620+ if (ret ) {
621+ dev_err (charger -> dev , "failed to set charging\n" );
622+ break ;
623+ }
624+ dev_info (charger -> dev , "charging. connector type: %d\n" ,
625+ connector );
626+ break ;
627+ case EXTCON_USB_HOST :
628+ ret = rt5033_charger_set_otg (charger );
629+ if (ret ) {
630+ dev_err (charger -> dev , "failed to set OTG\n" );
631+ break ;
632+ }
633+ dev_info (charger -> dev , "OTG enabled\n" );
634+ break ;
635+ default :
636+ ret = rt5033_charger_set_disconnect (charger );
637+ if (ret ) {
638+ dev_err (charger -> dev , "failed to set disconnect\n" );
639+ break ;
640+ }
641+ dev_info (charger -> dev , "disconnected\n" );
642+ break ;
643+ }
644+
645+ power_supply_changed (charger -> psy );
646+ }
647+
648+ static int rt5033_charger_extcon_notifier (struct notifier_block * nb ,
649+ unsigned long event , void * param )
650+ {
651+ struct rt5033_charger * charger =
652+ container_of (nb , struct rt5033_charger , extcon_nb );
653+
654+ schedule_work (& charger -> extcon_work );
655+
656+ return NOTIFY_OK ;
657+ }
658+
405659static const struct power_supply_desc rt5033_charger_desc = {
406660 .name = "rt5033-charger" ,
407661 .type = POWER_SUPPLY_TYPE_USB ,
@@ -414,6 +668,7 @@ static int rt5033_charger_probe(struct platform_device *pdev)
414668{
415669 struct rt5033_charger * charger ;
416670 struct power_supply_config psy_cfg = {};
671+ struct device_node * np_conn , * np_edev ;
417672 int ret ;
418673
419674 charger = devm_kzalloc (& pdev -> dev , sizeof (* charger ), GFP_KERNEL );
@@ -423,6 +678,7 @@ static int rt5033_charger_probe(struct platform_device *pdev)
423678 platform_set_drvdata (pdev , charger );
424679 charger -> dev = & pdev -> dev ;
425680 charger -> regmap = dev_get_regmap (pdev -> dev .parent , NULL );
681+ mutex_init (& charger -> lock );
426682
427683 psy_cfg .of_node = pdev -> dev .of_node ;
428684 psy_cfg .drv_data = charger ;
@@ -442,6 +698,33 @@ static int rt5033_charger_probe(struct platform_device *pdev)
442698 if (ret )
443699 return ret ;
444700
701+ /*
702+ * Extcon support is not vital for the charger to work. If no extcon
703+ * is available, just emit a warning and leave the probe function.
704+ */
705+ np_conn = of_parse_phandle (pdev -> dev .of_node , "richtek,usb-connector" , 0 );
706+ np_edev = of_get_parent (np_conn );
707+ charger -> edev = extcon_find_edev_by_node (np_edev );
708+ if (IS_ERR (charger -> edev )) {
709+ dev_warn (& pdev -> dev , "no extcon device found in device-tree\n" );
710+ goto out ;
711+ }
712+
713+ ret = devm_work_autocancel (& pdev -> dev , & charger -> extcon_work ,
714+ rt5033_charger_extcon_work );
715+ if (ret ) {
716+ dev_err (& pdev -> dev , "failed to initialize extcon work\n" );
717+ return ret ;
718+ }
719+
720+ charger -> extcon_nb .notifier_call = rt5033_charger_extcon_notifier ;
721+ ret = devm_extcon_register_notifier_all (& pdev -> dev , charger -> edev ,
722+ & charger -> extcon_nb );
723+ if (ret ) {
724+ dev_err (& pdev -> dev , "failed to register extcon notifier\n" );
725+ return ret ;
726+ }
727+ out :
445728 return 0 ;
446729}
447730
0 commit comments