xref: /linux/drivers/power/supply/s2m-charger.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
1*7e541f6dSKaustabh Chakraborty // SPDX-License-Identifier: GPL-2.0
2*7e541f6dSKaustabh Chakraborty /*
3*7e541f6dSKaustabh Chakraborty  * Battery Charger Driver for Samsung S2M series PMICs.
4*7e541f6dSKaustabh Chakraborty  *
5*7e541f6dSKaustabh Chakraborty  * Copyright (c) 2015 Samsung Electronics Co., Ltd
6*7e541f6dSKaustabh Chakraborty  * Copyright (c) 2026 Kaustabh Chakraborty <kauschluss@disroot.org>
7*7e541f6dSKaustabh Chakraborty  * Copyright (c) 2026 Łukasz Lebiedziński <kernel@lvkasz.us>
8*7e541f6dSKaustabh Chakraborty  */
9*7e541f6dSKaustabh Chakraborty 
10*7e541f6dSKaustabh Chakraborty #include <linux/devm-helpers.h>
11*7e541f6dSKaustabh Chakraborty #include <linux/extcon.h>
12*7e541f6dSKaustabh Chakraborty #include <linux/mfd/samsung/core.h>
13*7e541f6dSKaustabh Chakraborty #include <linux/mfd/samsung/s2mu005.h>
14*7e541f6dSKaustabh Chakraborty #include <linux/module.h>
15*7e541f6dSKaustabh Chakraborty #include <linux/of.h>
16*7e541f6dSKaustabh Chakraborty #include <linux/of_graph.h>
17*7e541f6dSKaustabh Chakraborty #include <linux/platform_device.h>
18*7e541f6dSKaustabh Chakraborty #include <linux/power_supply.h>
19*7e541f6dSKaustabh Chakraborty #include <linux/regmap.h>
20*7e541f6dSKaustabh Chakraborty 
21*7e541f6dSKaustabh Chakraborty struct s2m_chgr {
22*7e541f6dSKaustabh Chakraborty 	struct device *dev;
23*7e541f6dSKaustabh Chakraborty 	struct regmap *regmap;
24*7e541f6dSKaustabh Chakraborty 	struct power_supply *psy;
25*7e541f6dSKaustabh Chakraborty 	struct extcon_dev *extcon;
26*7e541f6dSKaustabh Chakraborty 	struct work_struct extcon_work;
27*7e541f6dSKaustabh Chakraborty 	struct notifier_block extcon_nb;
28*7e541f6dSKaustabh Chakraborty };
29*7e541f6dSKaustabh Chakraborty 
30*7e541f6dSKaustabh Chakraborty static int s2mu005_chgr_get_online(struct s2m_chgr *priv, int *value)
31*7e541f6dSKaustabh Chakraborty {
32*7e541f6dSKaustabh Chakraborty 	u32 val;
33*7e541f6dSKaustabh Chakraborty 	int ret;
34*7e541f6dSKaustabh Chakraborty 
35*7e541f6dSKaustabh Chakraborty 	ret = regmap_read(priv->regmap, S2MU005_REG_CHGR_STATUS0, &val);
36*7e541f6dSKaustabh Chakraborty 	if (ret) {
37*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to read register (%d)\n", ret);
38*7e541f6dSKaustabh Chakraborty 		return ret;
39*7e541f6dSKaustabh Chakraborty 	}
40*7e541f6dSKaustabh Chakraborty 
41*7e541f6dSKaustabh Chakraborty 	*value = !!(val & S2MU005_CHGR_CHG);
42*7e541f6dSKaustabh Chakraborty 
43*7e541f6dSKaustabh Chakraborty 	return 0;
44*7e541f6dSKaustabh Chakraborty }
45*7e541f6dSKaustabh Chakraborty 
46*7e541f6dSKaustabh Chakraborty static void s2mu005_chgr_get_usb_type(struct s2m_chgr *priv, int *value)
47*7e541f6dSKaustabh Chakraborty {
48*7e541f6dSKaustabh Chakraborty 	if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_CDP) > 0)
49*7e541f6dSKaustabh Chakraborty 		*value = POWER_SUPPLY_USB_TYPE_CDP;
50*7e541f6dSKaustabh Chakraborty 	else if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_SDP) > 0)
51*7e541f6dSKaustabh Chakraborty 		*value = POWER_SUPPLY_USB_TYPE_SDP;
52*7e541f6dSKaustabh Chakraborty 	else if (extcon_get_state(priv->extcon, EXTCON_CHG_USB_DCP) > 0)
53*7e541f6dSKaustabh Chakraborty 		*value = POWER_SUPPLY_USB_TYPE_DCP;
54*7e541f6dSKaustabh Chakraborty 	else
55*7e541f6dSKaustabh Chakraborty 		*value = POWER_SUPPLY_USB_TYPE_UNKNOWN;
56*7e541f6dSKaustabh Chakraborty }
57*7e541f6dSKaustabh Chakraborty 
58*7e541f6dSKaustabh Chakraborty static int s2mu005_chgr_get_property(struct power_supply *psy,
59*7e541f6dSKaustabh Chakraborty 				     enum power_supply_property psp,
60*7e541f6dSKaustabh Chakraborty 				     union power_supply_propval *val)
61*7e541f6dSKaustabh Chakraborty {
62*7e541f6dSKaustabh Chakraborty 	struct s2m_chgr *priv = power_supply_get_drvdata(psy);
63*7e541f6dSKaustabh Chakraborty 	int ret;
64*7e541f6dSKaustabh Chakraborty 
65*7e541f6dSKaustabh Chakraborty 	switch (psp) {
66*7e541f6dSKaustabh Chakraborty 	case POWER_SUPPLY_PROP_ONLINE:
67*7e541f6dSKaustabh Chakraborty 		ret = s2mu005_chgr_get_online(priv, &val->intval);
68*7e541f6dSKaustabh Chakraborty 		if (ret)
69*7e541f6dSKaustabh Chakraborty 			return ret;
70*7e541f6dSKaustabh Chakraborty 		break;
71*7e541f6dSKaustabh Chakraborty 	case POWER_SUPPLY_PROP_USB_TYPE:
72*7e541f6dSKaustabh Chakraborty 		s2mu005_chgr_get_usb_type(priv, &val->intval);
73*7e541f6dSKaustabh Chakraborty 		break;
74*7e541f6dSKaustabh Chakraborty 	default:
75*7e541f6dSKaustabh Chakraborty 		return -EINVAL;
76*7e541f6dSKaustabh Chakraborty 	}
77*7e541f6dSKaustabh Chakraborty 
78*7e541f6dSKaustabh Chakraborty 	return 0;
79*7e541f6dSKaustabh Chakraborty }
80*7e541f6dSKaustabh Chakraborty 
81*7e541f6dSKaustabh Chakraborty static int s2mu005_chgr_mode_set_host(struct s2m_chgr *priv)
82*7e541f6dSKaustabh Chakraborty {
83*7e541f6dSKaustabh Chakraborty 	int ret;
84*7e541f6dSKaustabh Chakraborty 
85*7e541f6dSKaustabh Chakraborty 	/* set mode to OTG */
86*7e541f6dSKaustabh Chakraborty 	ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0,
87*7e541f6dSKaustabh Chakraborty 				 S2MU005_CHGR_OP_MODE,
88*7e541f6dSKaustabh Chakraborty 				 FIELD_PREP(S2MU005_CHGR_OP_MODE,
89*7e541f6dSKaustabh Chakraborty 					    S2MU005_CHGR_OP_MODE_OTG));
90*7e541f6dSKaustabh Chakraborty 	if (ret) {
91*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to set OTG mode (%d)\n", ret);
92*7e541f6dSKaustabh Chakraborty 		return ret;
93*7e541f6dSKaustabh Chakraborty 	}
94*7e541f6dSKaustabh Chakraborty 
95*7e541f6dSKaustabh Chakraborty 	/* set boost frequency to 2MHz */
96*7e541f6dSKaustabh Chakraborty 	ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL11,
97*7e541f6dSKaustabh Chakraborty 				 S2MU005_CHGR_OSC_BOOST,
98*7e541f6dSKaustabh Chakraborty 				 FIELD_PREP(S2MU005_CHGR_OSC_BOOST,
99*7e541f6dSKaustabh Chakraborty 					    S2MU005_CHGR_OSC_BOOST_2MHZ));
100*7e541f6dSKaustabh Chakraborty 	if (ret) {
101*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to set boost frequency (%d)\n", ret);
102*7e541f6dSKaustabh Chakraborty 		return ret;
103*7e541f6dSKaustabh Chakraborty 	}
104*7e541f6dSKaustabh Chakraborty 
105*7e541f6dSKaustabh Chakraborty 	/* set OTG current limit to 1.5 A */
106*7e541f6dSKaustabh Chakraborty 	ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL4,
107*7e541f6dSKaustabh Chakraborty 				 S2MU005_CHGR_OTG_OCP,
108*7e541f6dSKaustabh Chakraborty 				 FIELD_PREP(S2MU005_CHGR_OTG_OCP,
109*7e541f6dSKaustabh Chakraborty 					    S2MU005_CHGR_OTG_OCP_1P5A));
110*7e541f6dSKaustabh Chakraborty 	if (ret) {
111*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to set OTG current limit (%d)\n", ret);
112*7e541f6dSKaustabh Chakraborty 		return ret;
113*7e541f6dSKaustabh Chakraborty 	}
114*7e541f6dSKaustabh Chakraborty 
115*7e541f6dSKaustabh Chakraborty 	/* VBUS switches are OFF when OTG over-current happens */
116*7e541f6dSKaustabh Chakraborty 	ret = regmap_set_bits(priv->regmap, S2MU005_REG_CHGR_CTRL4,
117*7e541f6dSKaustabh Chakraborty 			      S2MU005_CHGR_OTG_OCP_OFF);
118*7e541f6dSKaustabh Chakraborty 	if (ret) {
119*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to set OTG OCP switch (%d)\n", ret);
120*7e541f6dSKaustabh Chakraborty 		return ret;
121*7e541f6dSKaustabh Chakraborty 	}
122*7e541f6dSKaustabh Chakraborty 
123*7e541f6dSKaustabh Chakraborty 	/* set OTG voltage to 5.1 V */
124*7e541f6dSKaustabh Chakraborty 	ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL5,
125*7e541f6dSKaustabh Chakraborty 				 S2MU005_CHGR_VMID_BOOST,
126*7e541f6dSKaustabh Chakraborty 				 FIELD_PREP(S2MU005_CHGR_VMID_BOOST,
127*7e541f6dSKaustabh Chakraborty 					    S2MU005_CHGR_VMID_BOOST_5P1V));
128*7e541f6dSKaustabh Chakraborty 	if (ret) {
129*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to set OTG voltage (%d)\n", ret);
130*7e541f6dSKaustabh Chakraborty 		return ret;
131*7e541f6dSKaustabh Chakraborty 	}
132*7e541f6dSKaustabh Chakraborty 
133*7e541f6dSKaustabh Chakraborty 	/* turn on OTG */
134*7e541f6dSKaustabh Chakraborty 	ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL15,
135*7e541f6dSKaustabh Chakraborty 				 S2MU005_CHGR_OTG_EN,
136*7e541f6dSKaustabh Chakraborty 				 FIELD_PREP(S2MU005_CHGR_OTG_EN,
137*7e541f6dSKaustabh Chakraborty 					    S2MU005_CHGR_OTG_EN_ON));
138*7e541f6dSKaustabh Chakraborty 	if (ret) {
139*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to turn on OTG (%d)\n", ret);
140*7e541f6dSKaustabh Chakraborty 		return ret;
141*7e541f6dSKaustabh Chakraborty 	}
142*7e541f6dSKaustabh Chakraborty 
143*7e541f6dSKaustabh Chakraborty 	return 0;
144*7e541f6dSKaustabh Chakraborty }
145*7e541f6dSKaustabh Chakraborty 
146*7e541f6dSKaustabh Chakraborty static int s2mu005_chgr_mode_set_charger(struct s2m_chgr *priv)
147*7e541f6dSKaustabh Chakraborty {
148*7e541f6dSKaustabh Chakraborty 	int ret;
149*7e541f6dSKaustabh Chakraborty 
150*7e541f6dSKaustabh Chakraborty 	/* first reset to mode 0 */
151*7e541f6dSKaustabh Chakraborty 	ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0,
152*7e541f6dSKaustabh Chakraborty 				S2MU005_CHGR_OP_MODE);
153*7e541f6dSKaustabh Chakraborty 	if (ret) {
154*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to reset opmode (%d)\n", ret);
155*7e541f6dSKaustabh Chakraborty 		return ret;
156*7e541f6dSKaustabh Chakraborty 	}
157*7e541f6dSKaustabh Chakraborty 
158*7e541f6dSKaustabh Chakraborty 	/* wait for the charger to settle before switching to charging mode */
159*7e541f6dSKaustabh Chakraborty 	msleep(50);
160*7e541f6dSKaustabh Chakraborty 	/* then set to charging mode */
161*7e541f6dSKaustabh Chakraborty 	ret = regmap_update_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0,
162*7e541f6dSKaustabh Chakraborty 				 S2MU005_CHGR_OP_MODE,
163*7e541f6dSKaustabh Chakraborty 				 FIELD_PREP(S2MU005_CHGR_OP_MODE,
164*7e541f6dSKaustabh Chakraborty 					    S2MU005_CHGR_OP_MODE_CHG));
165*7e541f6dSKaustabh Chakraborty 	if (ret) {
166*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to set opmode to charging (%d)\n", ret);
167*7e541f6dSKaustabh Chakraborty 		return ret;
168*7e541f6dSKaustabh Chakraborty 	}
169*7e541f6dSKaustabh Chakraborty 
170*7e541f6dSKaustabh Chakraborty 	return 0;
171*7e541f6dSKaustabh Chakraborty }
172*7e541f6dSKaustabh Chakraborty 
173*7e541f6dSKaustabh Chakraborty static int s2mu005_chgr_mode_unset(struct s2m_chgr *priv)
174*7e541f6dSKaustabh Chakraborty {
175*7e541f6dSKaustabh Chakraborty 	int ret;
176*7e541f6dSKaustabh Chakraborty 
177*7e541f6dSKaustabh Chakraborty 	/* turn off OTG */
178*7e541f6dSKaustabh Chakraborty 	ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL15,
179*7e541f6dSKaustabh Chakraborty 				S2MU005_CHGR_OTG_EN);
180*7e541f6dSKaustabh Chakraborty 	if (ret) {
181*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to turn off OTG (%d)\n", ret);
182*7e541f6dSKaustabh Chakraborty 		return ret;
183*7e541f6dSKaustabh Chakraborty 	}
184*7e541f6dSKaustabh Chakraborty 
185*7e541f6dSKaustabh Chakraborty 	/* reset operation mode */
186*7e541f6dSKaustabh Chakraborty 	ret = regmap_clear_bits(priv->regmap, S2MU005_REG_CHGR_CTRL0,
187*7e541f6dSKaustabh Chakraborty 				S2MU005_CHGR_OP_MODE);
188*7e541f6dSKaustabh Chakraborty 	if (ret) {
189*7e541f6dSKaustabh Chakraborty 		dev_err(priv->dev, "failed to reset opmode (%d)\n", ret);
190*7e541f6dSKaustabh Chakraborty 		return ret;
191*7e541f6dSKaustabh Chakraborty 	}
192*7e541f6dSKaustabh Chakraborty 
193*7e541f6dSKaustabh Chakraborty 	return 0;
194*7e541f6dSKaustabh Chakraborty }
195*7e541f6dSKaustabh Chakraborty 
196*7e541f6dSKaustabh Chakraborty static void s2mu005_chgr_extcon_work(struct work_struct *work)
197*7e541f6dSKaustabh Chakraborty {
198*7e541f6dSKaustabh Chakraborty 	struct s2m_chgr *priv = container_of(work, struct s2m_chgr, extcon_work);
199*7e541f6dSKaustabh Chakraborty 
200*7e541f6dSKaustabh Chakraborty 	if (extcon_get_state(priv->extcon, EXTCON_USB_HOST) > 0)
201*7e541f6dSKaustabh Chakraborty 		s2mu005_chgr_mode_set_host(priv);
202*7e541f6dSKaustabh Chakraborty 	else if (extcon_get_state(priv->extcon, EXTCON_USB) > 0)
203*7e541f6dSKaustabh Chakraborty 		s2mu005_chgr_mode_set_charger(priv);
204*7e541f6dSKaustabh Chakraborty 	else
205*7e541f6dSKaustabh Chakraborty 		s2mu005_chgr_mode_unset(priv);
206*7e541f6dSKaustabh Chakraborty 
207*7e541f6dSKaustabh Chakraborty 	power_supply_changed(priv->psy);
208*7e541f6dSKaustabh Chakraborty }
209*7e541f6dSKaustabh Chakraborty 
210*7e541f6dSKaustabh Chakraborty static const enum power_supply_property s2mu005_chgr_properties[] = {
211*7e541f6dSKaustabh Chakraborty 	POWER_SUPPLY_PROP_ONLINE,
212*7e541f6dSKaustabh Chakraborty 	POWER_SUPPLY_PROP_USB_TYPE,
213*7e541f6dSKaustabh Chakraborty };
214*7e541f6dSKaustabh Chakraborty 
215*7e541f6dSKaustabh Chakraborty static const struct power_supply_desc s2mu005_chgr_psy_desc = {
216*7e541f6dSKaustabh Chakraborty 	.name = "s2mu005-charger",
217*7e541f6dSKaustabh Chakraborty 	.type = POWER_SUPPLY_TYPE_USB,
218*7e541f6dSKaustabh Chakraborty 	.properties = s2mu005_chgr_properties,
219*7e541f6dSKaustabh Chakraborty 	.num_properties = ARRAY_SIZE(s2mu005_chgr_properties),
220*7e541f6dSKaustabh Chakraborty 	.get_property = s2mu005_chgr_get_property,
221*7e541f6dSKaustabh Chakraborty 	.usb_types = BIT(POWER_SUPPLY_USB_TYPE_CDP) |
222*7e541f6dSKaustabh Chakraborty 		     BIT(POWER_SUPPLY_USB_TYPE_SDP) |
223*7e541f6dSKaustabh Chakraborty 		     BIT(POWER_SUPPLY_USB_TYPE_DCP) |
224*7e541f6dSKaustabh Chakraborty 		     BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN),
225*7e541f6dSKaustabh Chakraborty };
226*7e541f6dSKaustabh Chakraborty 
227*7e541f6dSKaustabh Chakraborty static int s2m_chgr_extcon_notifier(struct notifier_block *nb,
228*7e541f6dSKaustabh Chakraborty 					unsigned long event, void *param)
229*7e541f6dSKaustabh Chakraborty {
230*7e541f6dSKaustabh Chakraborty 	struct s2m_chgr *priv = container_of(nb, struct s2m_chgr, extcon_nb);
231*7e541f6dSKaustabh Chakraborty 
232*7e541f6dSKaustabh Chakraborty 	schedule_work(&priv->extcon_work);
233*7e541f6dSKaustabh Chakraborty 
234*7e541f6dSKaustabh Chakraborty 	return NOTIFY_OK;
235*7e541f6dSKaustabh Chakraborty }
236*7e541f6dSKaustabh Chakraborty 
237*7e541f6dSKaustabh Chakraborty static int s2m_chgr_probe(struct platform_device *pdev)
238*7e541f6dSKaustabh Chakraborty {
239*7e541f6dSKaustabh Chakraborty 	struct device *dev = &pdev->dev;
240*7e541f6dSKaustabh Chakraborty 	struct sec_pmic_dev *pmic_drvdata = dev_get_drvdata(dev->parent);
241*7e541f6dSKaustabh Chakraborty 	struct s2m_chgr *priv;
242*7e541f6dSKaustabh Chakraborty 	struct device_node *extcon_node __free(device_node) = NULL;
243*7e541f6dSKaustabh Chakraborty 	struct power_supply_config psy_cfg = {};
244*7e541f6dSKaustabh Chakraborty 	const struct power_supply_desc *psy_desc;
245*7e541f6dSKaustabh Chakraborty 	work_func_t extcon_work_func;
246*7e541f6dSKaustabh Chakraborty 	int ret;
247*7e541f6dSKaustabh Chakraborty 
248*7e541f6dSKaustabh Chakraborty 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
249*7e541f6dSKaustabh Chakraborty 	if (!priv)
250*7e541f6dSKaustabh Chakraborty 		return -ENOMEM;
251*7e541f6dSKaustabh Chakraborty 
252*7e541f6dSKaustabh Chakraborty 	platform_set_drvdata(pdev, priv);
253*7e541f6dSKaustabh Chakraborty 	priv->dev = dev;
254*7e541f6dSKaustabh Chakraborty 	priv->regmap = pmic_drvdata->regmap_pmic;
255*7e541f6dSKaustabh Chakraborty 
256*7e541f6dSKaustabh Chakraborty 	switch (platform_get_device_id(pdev)->driver_data) {
257*7e541f6dSKaustabh Chakraborty 	case S2MU005:
258*7e541f6dSKaustabh Chakraborty 		psy_desc = &s2mu005_chgr_psy_desc;
259*7e541f6dSKaustabh Chakraborty 		extcon_work_func = s2mu005_chgr_extcon_work;
260*7e541f6dSKaustabh Chakraborty 		break;
261*7e541f6dSKaustabh Chakraborty 	default:
262*7e541f6dSKaustabh Chakraborty 		return dev_err_probe(dev, -ENODEV,
263*7e541f6dSKaustabh Chakraborty 				     "device type %d is not supported by driver\n",
264*7e541f6dSKaustabh Chakraborty 				     pmic_drvdata->device_type);
265*7e541f6dSKaustabh Chakraborty 	}
266*7e541f6dSKaustabh Chakraborty 
267*7e541f6dSKaustabh Chakraborty 	/* MUIC is mandatory. If unavailable, request probe deferral */
268*7e541f6dSKaustabh Chakraborty 	extcon_node = of_get_child_by_name(dev->parent->of_node, "muic");
269*7e541f6dSKaustabh Chakraborty 	if (!extcon_node)
270*7e541f6dSKaustabh Chakraborty 		return dev_err_probe(dev, -ENODEV, "MUIC node required but not found\n");
271*7e541f6dSKaustabh Chakraborty 
272*7e541f6dSKaustabh Chakraborty 	priv->extcon = extcon_find_edev_by_node(extcon_node);
273*7e541f6dSKaustabh Chakraborty 	if (IS_ERR(priv->extcon))
274*7e541f6dSKaustabh Chakraborty 		return -EPROBE_DEFER;
275*7e541f6dSKaustabh Chakraborty 
276*7e541f6dSKaustabh Chakraborty 	psy_cfg.drv_data = priv;
277*7e541f6dSKaustabh Chakraborty 	psy_cfg.fwnode = dev_fwnode(dev->parent);
278*7e541f6dSKaustabh Chakraborty 	priv->psy = devm_power_supply_register(dev, psy_desc, &psy_cfg);
279*7e541f6dSKaustabh Chakraborty 	if (IS_ERR(priv->psy))
280*7e541f6dSKaustabh Chakraborty 		return dev_err_probe(dev, PTR_ERR(priv->psy),
281*7e541f6dSKaustabh Chakraborty 				     "failed to register power supply subsystem\n");
282*7e541f6dSKaustabh Chakraborty 
283*7e541f6dSKaustabh Chakraborty 	ret = devm_work_autocancel(dev, &priv->extcon_work, extcon_work_func);
284*7e541f6dSKaustabh Chakraborty 	if (ret)
285*7e541f6dSKaustabh Chakraborty 		return dev_err_probe(dev, ret, "failed to initialize extcon work\n");
286*7e541f6dSKaustabh Chakraborty 
287*7e541f6dSKaustabh Chakraborty 	priv->extcon_nb.notifier_call = s2m_chgr_extcon_notifier;
288*7e541f6dSKaustabh Chakraborty 	ret = devm_extcon_register_notifier_all(dev, priv->extcon, &priv->extcon_nb);
289*7e541f6dSKaustabh Chakraborty 	if (ret)
290*7e541f6dSKaustabh Chakraborty 		return dev_err_probe(dev, ret, "failed to register extcon notifier\n");
291*7e541f6dSKaustabh Chakraborty 
292*7e541f6dSKaustabh Chakraborty 	return 0;
293*7e541f6dSKaustabh Chakraborty }
294*7e541f6dSKaustabh Chakraborty 
295*7e541f6dSKaustabh Chakraborty static const struct platform_device_id s2m_chgr_id_table[] = {
296*7e541f6dSKaustabh Chakraborty 	{ "s2mu005-charger", S2MU005 },
297*7e541f6dSKaustabh Chakraborty 	{ /* sentinel */ },
298*7e541f6dSKaustabh Chakraborty };
299*7e541f6dSKaustabh Chakraborty MODULE_DEVICE_TABLE(platform, s2m_chgr_id_table);
300*7e541f6dSKaustabh Chakraborty 
301*7e541f6dSKaustabh Chakraborty static struct platform_driver s2m_chgr_driver = {
302*7e541f6dSKaustabh Chakraborty 	.driver = {
303*7e541f6dSKaustabh Chakraborty 		.name = "s2m-charger",
304*7e541f6dSKaustabh Chakraborty 	},
305*7e541f6dSKaustabh Chakraborty 	.probe = s2m_chgr_probe,
306*7e541f6dSKaustabh Chakraborty 	.id_table = s2m_chgr_id_table,
307*7e541f6dSKaustabh Chakraborty };
308*7e541f6dSKaustabh Chakraborty module_platform_driver(s2m_chgr_driver);
309*7e541f6dSKaustabh Chakraborty 
310*7e541f6dSKaustabh Chakraborty MODULE_DESCRIPTION("Battery Charger Driver For Samsung S2M Series PMICs");
311*7e541f6dSKaustabh Chakraborty MODULE_AUTHOR("Kaustabh Chakraborty <kauschluss@disroot.org>");
312*7e541f6dSKaustabh Chakraborty MODULE_AUTHOR("Łukasz Lebiedziński <kernel@lvkasz.us>");
313*7e541f6dSKaustabh Chakraborty MODULE_LICENSE("GPL");
314