1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Charging control driver for the Wilco EC |
4 | * |
5 | * Copyright 2019 Google LLC |
6 | * |
7 | * See Documentation/ABI/testing/sysfs-class-power and |
8 | * Documentation/ABI/testing/sysfs-class-power-wilco for userspace interface |
9 | * and other info. |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/platform_data/wilco-ec.h> |
15 | #include <linux/power_supply.h> |
16 | |
17 | #define DRV_NAME "wilco-charger" |
18 | |
19 | /* Property IDs and related EC constants */ |
20 | #define PID_CHARGE_MODE 0x0710 |
21 | #define PID_CHARGE_LOWER_LIMIT 0x0711 |
22 | #define PID_CHARGE_UPPER_LIMIT 0x0712 |
23 | |
24 | enum charge_mode { |
25 | CHARGE_MODE_STD = 1, /* Used for Standard */ |
26 | CHARGE_MODE_EXP = 2, /* Express Charge, used for Fast */ |
27 | CHARGE_MODE_AC = 3, /* Mostly AC use, used for Trickle */ |
28 | CHARGE_MODE_AUTO = 4, /* Used for Adaptive */ |
29 | CHARGE_MODE_CUSTOM = 5, /* Used for Custom */ |
30 | CHARGE_MODE_LONGLIFE = 6, /* Used for Long Life */ |
31 | }; |
32 | |
33 | #define CHARGE_LOWER_LIMIT_MIN 50 |
34 | #define CHARGE_LOWER_LIMIT_MAX 95 |
35 | #define CHARGE_UPPER_LIMIT_MIN 55 |
36 | #define CHARGE_UPPER_LIMIT_MAX 100 |
37 | |
38 | /* Convert from POWER_SUPPLY_PROP_CHARGE_TYPE value to the EC's charge mode */ |
39 | static int psp_val_to_charge_mode(int psp_val) |
40 | { |
41 | switch (psp_val) { |
42 | case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: |
43 | return CHARGE_MODE_AC; |
44 | case POWER_SUPPLY_CHARGE_TYPE_FAST: |
45 | return CHARGE_MODE_EXP; |
46 | case POWER_SUPPLY_CHARGE_TYPE_STANDARD: |
47 | return CHARGE_MODE_STD; |
48 | case POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE: |
49 | return CHARGE_MODE_AUTO; |
50 | case POWER_SUPPLY_CHARGE_TYPE_CUSTOM: |
51 | return CHARGE_MODE_CUSTOM; |
52 | case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: |
53 | return CHARGE_MODE_LONGLIFE; |
54 | default: |
55 | return -EINVAL; |
56 | } |
57 | } |
58 | |
59 | /* Convert from EC's charge mode to POWER_SUPPLY_PROP_CHARGE_TYPE value */ |
60 | static int charge_mode_to_psp_val(enum charge_mode mode) |
61 | { |
62 | switch (mode) { |
63 | case CHARGE_MODE_AC: |
64 | return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; |
65 | case CHARGE_MODE_EXP: |
66 | return POWER_SUPPLY_CHARGE_TYPE_FAST; |
67 | case CHARGE_MODE_STD: |
68 | return POWER_SUPPLY_CHARGE_TYPE_STANDARD; |
69 | case CHARGE_MODE_AUTO: |
70 | return POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE; |
71 | case CHARGE_MODE_CUSTOM: |
72 | return POWER_SUPPLY_CHARGE_TYPE_CUSTOM; |
73 | case CHARGE_MODE_LONGLIFE: |
74 | return POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; |
75 | default: |
76 | return -EINVAL; |
77 | } |
78 | } |
79 | |
80 | static enum power_supply_property wilco_charge_props[] = { |
81 | POWER_SUPPLY_PROP_CHARGE_TYPE, |
82 | POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD, |
83 | POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, |
84 | }; |
85 | |
86 | static int wilco_charge_get_property(struct power_supply *psy, |
87 | enum power_supply_property psp, |
88 | union power_supply_propval *val) |
89 | { |
90 | struct wilco_ec_device *ec = power_supply_get_drvdata(psy); |
91 | u32 property_id; |
92 | int ret; |
93 | u8 raw; |
94 | |
95 | switch (psp) { |
96 | case POWER_SUPPLY_PROP_CHARGE_TYPE: |
97 | property_id = PID_CHARGE_MODE; |
98 | break; |
99 | case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: |
100 | property_id = PID_CHARGE_LOWER_LIMIT; |
101 | break; |
102 | case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: |
103 | property_id = PID_CHARGE_UPPER_LIMIT; |
104 | break; |
105 | default: |
106 | return -EINVAL; |
107 | } |
108 | |
109 | ret = wilco_ec_get_byte_property(ec, property_id, val: &raw); |
110 | if (ret < 0) |
111 | return ret; |
112 | if (property_id == PID_CHARGE_MODE) { |
113 | ret = charge_mode_to_psp_val(mode: raw); |
114 | if (ret < 0) |
115 | return -EBADMSG; |
116 | raw = ret; |
117 | } |
118 | val->intval = raw; |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static int wilco_charge_set_property(struct power_supply *psy, |
124 | enum power_supply_property psp, |
125 | const union power_supply_propval *val) |
126 | { |
127 | struct wilco_ec_device *ec = power_supply_get_drvdata(psy); |
128 | int mode; |
129 | |
130 | switch (psp) { |
131 | case POWER_SUPPLY_PROP_CHARGE_TYPE: |
132 | mode = psp_val_to_charge_mode(psp_val: val->intval); |
133 | if (mode < 0) |
134 | return -EINVAL; |
135 | return wilco_ec_set_byte_property(ec, PID_CHARGE_MODE, val: mode); |
136 | case POWER_SUPPLY_PROP_CHARGE_CONTROL_START_THRESHOLD: |
137 | if (val->intval < CHARGE_LOWER_LIMIT_MIN || |
138 | val->intval > CHARGE_LOWER_LIMIT_MAX) |
139 | return -EINVAL; |
140 | return wilco_ec_set_byte_property(ec, PID_CHARGE_LOWER_LIMIT, |
141 | val: val->intval); |
142 | case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: |
143 | if (val->intval < CHARGE_UPPER_LIMIT_MIN || |
144 | val->intval > CHARGE_UPPER_LIMIT_MAX) |
145 | return -EINVAL; |
146 | return wilco_ec_set_byte_property(ec, PID_CHARGE_UPPER_LIMIT, |
147 | val: val->intval); |
148 | default: |
149 | return -EINVAL; |
150 | } |
151 | } |
152 | |
153 | static int wilco_charge_property_is_writeable(struct power_supply *psy, |
154 | enum power_supply_property psp) |
155 | { |
156 | return 1; |
157 | } |
158 | |
159 | static const struct power_supply_desc wilco_ps_desc = { |
160 | .properties = wilco_charge_props, |
161 | .num_properties = ARRAY_SIZE(wilco_charge_props), |
162 | .get_property = wilco_charge_get_property, |
163 | .set_property = wilco_charge_set_property, |
164 | .property_is_writeable = wilco_charge_property_is_writeable, |
165 | .name = DRV_NAME, |
166 | .type = POWER_SUPPLY_TYPE_MAINS, |
167 | }; |
168 | |
169 | static int wilco_charge_probe(struct platform_device *pdev) |
170 | { |
171 | struct wilco_ec_device *ec = dev_get_drvdata(dev: pdev->dev.parent); |
172 | struct power_supply_config psy_cfg = {}; |
173 | struct power_supply *psy; |
174 | |
175 | psy_cfg.drv_data = ec; |
176 | psy = devm_power_supply_register(parent: &pdev->dev, desc: &wilco_ps_desc, cfg: &psy_cfg); |
177 | |
178 | return PTR_ERR_OR_ZERO(ptr: psy); |
179 | } |
180 | |
181 | static struct platform_driver wilco_charge_driver = { |
182 | .probe = wilco_charge_probe, |
183 | .driver = { |
184 | .name = DRV_NAME, |
185 | } |
186 | }; |
187 | module_platform_driver(wilco_charge_driver); |
188 | |
189 | MODULE_ALIAS("platform:" DRV_NAME); |
190 | MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>" ); |
191 | MODULE_LICENSE("GPL v2" ); |
192 | MODULE_DESCRIPTION("Wilco EC charge control driver" ); |
193 | |