1 // SPDX-License-Identifier: GPL-2.0
3 * Power supply driver for ChromeOS EC based Peripheral Device Charger.
5 * Copyright 2020 Google LLC.
8 #include <linux/mod_devicetable.h>
9 #include <linux/module.h>
10 #include <linux/notifier.h>
11 #include <linux/platform_data/cros_ec_commands.h>
12 #include <linux/platform_data/cros_ec_proto.h>
13 #include <linux/platform_device.h>
14 #include <linux/power_supply.h>
15 #include <linux/slab.h>
16 #include <linux/stringify.h>
17 #include <linux/types.h>
18 #include <linux/unaligned.h>
20 #define DRV_NAME "cros-ec-pchg"
21 #define PCHG_DIR_PREFIX "peripheral"
22 #define PCHG_DIR_NAME PCHG_DIR_PREFIX "%d"
23 #define PCHG_DIR_NAME_LENGTH \
24 sizeof(PCHG_DIR_PREFIX __stringify(EC_PCHG_MAX_PORTS))
25 #define PCHG_CACHE_UPDATE_DELAY msecs_to_jiffies(500)
29 char name
[PCHG_DIR_NAME_LENGTH
];
30 struct power_supply
*psy
;
31 struct power_supply_desc psy_desc
;
33 int battery_percentage
;
35 struct charger_data
*charger
;
36 unsigned long last_update
;
41 struct cros_ec_dev
*ec_dev
;
42 struct cros_ec_device
*ec_device
;
43 int num_registered_psy
;
44 struct port_data
*ports
[EC_PCHG_MAX_PORTS
];
45 struct notifier_block notifier
;
48 static enum power_supply_property cros_pchg_props
[] = {
49 POWER_SUPPLY_PROP_STATUS
,
50 POWER_SUPPLY_PROP_CHARGE_TYPE
,
51 POWER_SUPPLY_PROP_CAPACITY
,
52 POWER_SUPPLY_PROP_SCOPE
,
55 static int cros_pchg_ec_command(const struct charger_data
*charger
,
63 struct cros_ec_dev
*ec_dev
= charger
->ec_dev
;
64 struct cros_ec_command
*msg
;
67 msg
= kzalloc(struct_size(msg
, data
, max(outsize
, insize
)), GFP_KERNEL
);
71 msg
->version
= version
;
72 msg
->command
= ec_dev
->cmd_offset
+ command
;
73 msg
->outsize
= outsize
;
77 memcpy(msg
->data
, outdata
, outsize
);
79 ret
= cros_ec_cmd_xfer_status(charger
->ec_device
, msg
);
80 if (ret
>= 0 && insize
)
81 memcpy(indata
, msg
->data
, insize
);
87 static const unsigned int pchg_cmd_version
= 1;
89 static bool cros_pchg_cmd_ver_check(const struct charger_data
*charger
)
91 struct ec_params_get_cmd_versions_v1 req
;
92 struct ec_response_get_cmd_versions rsp
;
95 req
.cmd
= EC_CMD_PCHG
;
96 ret
= cros_pchg_ec_command(charger
, 1, EC_CMD_GET_CMD_VERSIONS
,
97 &req
, sizeof(req
), &rsp
, sizeof(rsp
));
99 dev_warn(charger
->dev
,
100 "Unable to get versions of EC_CMD_PCHG (err:%d)\n",
105 return !!(rsp
.version_mask
& BIT(pchg_cmd_version
));
108 static int cros_pchg_port_count(const struct charger_data
*charger
)
110 struct ec_response_pchg_count rsp
;
113 ret
= cros_pchg_ec_command(charger
, 0, EC_CMD_PCHG_COUNT
,
114 NULL
, 0, &rsp
, sizeof(rsp
));
116 dev_warn(charger
->dev
,
117 "Unable to get number or ports (err:%d)\n", ret
);
121 return rsp
.port_count
;
124 static int cros_pchg_get_status(struct port_data
*port
)
126 struct charger_data
*charger
= port
->charger
;
127 struct ec_params_pchg req
;
128 struct ec_response_pchg rsp
;
129 struct device
*dev
= charger
->dev
;
130 int old_status
= port
->psy_status
;
131 int old_percentage
= port
->battery_percentage
;
134 req
.port
= port
->port_number
;
135 ret
= cros_pchg_ec_command(charger
, pchg_cmd_version
, EC_CMD_PCHG
,
136 &req
, sizeof(req
), &rsp
, sizeof(rsp
));
138 dev_err(dev
, "Unable to get port.%d status (err:%d)\n",
139 port
->port_number
, ret
);
144 case PCHG_STATE_RESET
:
145 case PCHG_STATE_INITIALIZED
:
146 case PCHG_STATE_ENABLED
:
148 port
->psy_status
= POWER_SUPPLY_STATUS_UNKNOWN
;
149 port
->charge_type
= POWER_SUPPLY_CHARGE_TYPE_NONE
;
151 case PCHG_STATE_DETECTED
:
152 port
->psy_status
= POWER_SUPPLY_STATUS_CHARGING
;
153 port
->charge_type
= POWER_SUPPLY_CHARGE_TYPE_TRICKLE
;
155 case PCHG_STATE_CHARGING
:
156 port
->psy_status
= POWER_SUPPLY_STATUS_CHARGING
;
157 port
->charge_type
= POWER_SUPPLY_CHARGE_TYPE_STANDARD
;
159 case PCHG_STATE_FULL
:
160 port
->psy_status
= POWER_SUPPLY_STATUS_FULL
;
161 port
->charge_type
= POWER_SUPPLY_CHARGE_TYPE_NONE
;
165 port
->battery_percentage
= rsp
.battery_percentage
;
167 if (port
->psy_status
!= old_status
||
168 port
->battery_percentage
!= old_percentage
)
169 power_supply_changed(port
->psy
);
172 "Port %d: state=%d battery=%d%%\n",
173 port
->port_number
, rsp
.state
, rsp
.battery_percentage
);
178 static int cros_pchg_get_port_status(struct port_data
*port
, bool ratelimit
)
183 time_is_after_jiffies(port
->last_update
+ PCHG_CACHE_UPDATE_DELAY
))
186 ret
= cros_pchg_get_status(port
);
190 port
->last_update
= jiffies
;
195 static int cros_pchg_get_prop(struct power_supply
*psy
,
196 enum power_supply_property psp
,
197 union power_supply_propval
*val
)
199 struct port_data
*port
= power_supply_get_drvdata(psy
);
202 case POWER_SUPPLY_PROP_STATUS
:
203 case POWER_SUPPLY_PROP_CAPACITY
:
204 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
205 cros_pchg_get_port_status(port
, true);
212 case POWER_SUPPLY_PROP_STATUS
:
213 val
->intval
= port
->psy_status
;
215 case POWER_SUPPLY_PROP_CAPACITY
:
216 val
->intval
= port
->battery_percentage
;
218 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
219 val
->intval
= port
->charge_type
;
221 case POWER_SUPPLY_PROP_SCOPE
:
222 val
->intval
= POWER_SUPPLY_SCOPE_DEVICE
;
231 static int cros_pchg_event(const struct charger_data
*charger
)
235 for (i
= 0; i
< charger
->num_registered_psy
; i
++)
236 cros_pchg_get_port_status(charger
->ports
[i
], false);
241 static int cros_ec_notify(struct notifier_block
*nb
,
242 unsigned long queued_during_suspend
,
245 struct cros_ec_device
*ec_dev
= data
;
246 struct charger_data
*charger
=
247 container_of(nb
, struct charger_data
, notifier
);
250 if (ec_dev
->event_data
.event_type
!= EC_MKBP_EVENT_PCHG
||
251 ec_dev
->event_size
!= sizeof(host_event
))
254 host_event
= get_unaligned_le32(&ec_dev
->event_data
.data
.host_event
);
256 if (!(host_event
& EC_MKBP_PCHG_DEVICE_EVENT
))
259 return cros_pchg_event(charger
);
262 static int cros_pchg_probe(struct platform_device
*pdev
)
264 struct device
*dev
= &pdev
->dev
;
265 struct cros_ec_dev
*ec_dev
= dev_get_drvdata(dev
->parent
);
266 struct cros_ec_device
*ec_device
= ec_dev
->ec_dev
;
267 struct power_supply_desc
*psy_desc
;
268 struct charger_data
*charger
;
269 struct power_supply
*psy
;
270 struct port_data
*port
;
271 struct notifier_block
*nb
;
276 charger
= devm_kzalloc(dev
, sizeof(*charger
), GFP_KERNEL
);
281 charger
->ec_dev
= ec_dev
;
282 charger
->ec_device
= ec_device
;
284 platform_set_drvdata(pdev
, charger
);
286 ret
= cros_pchg_port_count(charger
);
289 * This feature is enabled by the EC and the kernel driver is
290 * included by default for CrOS devices. Don't need to be loud
291 * since this error can be normal.
293 dev_info(dev
, "No peripheral charge ports (err:%d)\n", ret
);
297 if (!cros_pchg_cmd_ver_check(charger
)) {
298 dev_err(dev
, "EC_CMD_PCHG version %d isn't available.\n",
304 if (num_ports
> EC_PCHG_MAX_PORTS
) {
305 dev_err(dev
, "Too many peripheral charge ports (%d)\n",
310 dev_info(dev
, "%d peripheral charge ports found\n", num_ports
);
312 for (i
= 0; i
< num_ports
; i
++) {
313 struct power_supply_config psy_cfg
= {};
315 port
= devm_kzalloc(dev
, sizeof(*port
), GFP_KERNEL
);
319 port
->charger
= charger
;
320 port
->port_number
= i
;
321 snprintf(port
->name
, sizeof(port
->name
), PCHG_DIR_NAME
, i
);
323 psy_desc
= &port
->psy_desc
;
324 psy_desc
->name
= port
->name
;
325 psy_desc
->type
= POWER_SUPPLY_TYPE_BATTERY
;
326 psy_desc
->get_property
= cros_pchg_get_prop
;
327 psy_desc
->external_power_changed
= NULL
;
328 psy_desc
->properties
= cros_pchg_props
;
329 psy_desc
->num_properties
= ARRAY_SIZE(cros_pchg_props
);
330 psy_cfg
.drv_data
= port
;
332 psy
= devm_power_supply_register(dev
, psy_desc
, &psy_cfg
);
334 return dev_err_probe(dev
, PTR_ERR(psy
),
335 "Failed to register power supply\n");
338 charger
->ports
[charger
->num_registered_psy
++] = port
;
341 if (!charger
->num_registered_psy
)
344 nb
= &charger
->notifier
;
345 nb
->notifier_call
= cros_ec_notify
;
346 ret
= blocking_notifier_chain_register(&ec_dev
->ec_dev
->event_notifier
,
349 dev_err(dev
, "Failed to register notifier (err:%d)\n", ret
);
354 #ifdef CONFIG_PM_SLEEP
355 static int __maybe_unused
cros_pchg_resume(struct device
*dev
)
357 struct charger_data
*charger
= dev_get_drvdata(dev
);
360 * Sync all ports on resume in case reports from EC are lost during
363 cros_pchg_event(charger
);
369 static SIMPLE_DEV_PM_OPS(cros_pchg_pm_ops
, NULL
, cros_pchg_resume
);
371 static const struct platform_device_id cros_pchg_id
[] = {
375 MODULE_DEVICE_TABLE(platform
, cros_pchg_id
);
377 static struct platform_driver cros_pchg_driver
= {
380 .pm
= &cros_pchg_pm_ops
,
382 .probe
= cros_pchg_probe
,
383 .id_table
= cros_pchg_id
,
386 module_platform_driver(cros_pchg_driver
);
388 MODULE_LICENSE("GPL");
389 MODULE_DESCRIPTION("ChromeOS EC peripheral device charger");