1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * dptf_power: DPTF platform power driver |
4 | * Copyright (c) 2016, Intel Corporation. |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/module.h> |
9 | #include <linux/acpi.h> |
10 | #include <linux/platform_device.h> |
11 | |
12 | /* |
13 | * Presentation of attributes which are defined for INT3407 and INT3532. |
14 | * They are: |
15 | * PMAX : Maximum platform power |
16 | * PSRC : Platform power source |
17 | * ARTG : Adapter rating |
18 | * CTYP : Charger type |
19 | * PROP : Rest of worst case platform Power |
20 | * PBSS : Power Battery Steady State |
21 | * RBHF : High Frequency Impedance |
22 | * VBNL : Instantaneous No-Load Voltage |
23 | * CMPP : Current Discharge Capability |
24 | */ |
25 | #define DPTF_POWER_SHOW(name, object) \ |
26 | static ssize_t name##_show(struct device *dev,\ |
27 | struct device_attribute *attr,\ |
28 | char *buf)\ |
29 | {\ |
30 | struct acpi_device *acpi_dev = dev_get_drvdata(dev);\ |
31 | unsigned long long val;\ |
32 | acpi_status status;\ |
33 | \ |
34 | status = acpi_evaluate_integer(acpi_dev->handle, #object,\ |
35 | NULL, &val);\ |
36 | if (ACPI_SUCCESS(status))\ |
37 | return sprintf(buf, "%d\n", (int)val);\ |
38 | else \ |
39 | return -EINVAL;\ |
40 | } |
41 | |
42 | DPTF_POWER_SHOW(max_platform_power_mw, PMAX) |
43 | DPTF_POWER_SHOW(platform_power_source, PSRC) |
44 | DPTF_POWER_SHOW(adapter_rating_mw, ARTG) |
45 | DPTF_POWER_SHOW(battery_steady_power_mw, PBSS) |
46 | DPTF_POWER_SHOW(charger_type, CTYP) |
47 | DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP) |
48 | DPTF_POWER_SHOW(max_steady_state_power_mw, PBSS) |
49 | DPTF_POWER_SHOW(high_freq_impedance_mohm, RBHF) |
50 | DPTF_POWER_SHOW(no_load_voltage_mv, VBNL) |
51 | DPTF_POWER_SHOW(current_discharge_capbility_ma, CMPP); |
52 | |
53 | static DEVICE_ATTR_RO(max_platform_power_mw); |
54 | static DEVICE_ATTR_RO(platform_power_source); |
55 | static DEVICE_ATTR_RO(adapter_rating_mw); |
56 | static DEVICE_ATTR_RO(battery_steady_power_mw); |
57 | static DEVICE_ATTR_RO(charger_type); |
58 | static DEVICE_ATTR_RO(rest_of_platform_power_mw); |
59 | static DEVICE_ATTR_RO(max_steady_state_power_mw); |
60 | static DEVICE_ATTR_RO(high_freq_impedance_mohm); |
61 | static DEVICE_ATTR_RO(no_load_voltage_mv); |
62 | static DEVICE_ATTR_RO(current_discharge_capbility_ma); |
63 | |
64 | static ssize_t prochot_confirm_store(struct device *dev, |
65 | struct device_attribute *attr, |
66 | const char *buf, size_t count) |
67 | { |
68 | struct acpi_device *acpi_dev = dev_get_drvdata(dev); |
69 | acpi_status status; |
70 | int seq_no; |
71 | |
72 | if (kstrtouint(s: buf, base: 0, res: &seq_no) < 0) |
73 | return -EINVAL; |
74 | |
75 | status = acpi_execute_simple_method(handle: acpi_dev->handle, method: "PBOK" , arg: seq_no); |
76 | if (ACPI_SUCCESS(status)) |
77 | return count; |
78 | |
79 | return -EINVAL; |
80 | } |
81 | |
82 | static DEVICE_ATTR_WO(prochot_confirm); |
83 | |
84 | static struct attribute *dptf_power_attrs[] = { |
85 | &dev_attr_max_platform_power_mw.attr, |
86 | &dev_attr_platform_power_source.attr, |
87 | &dev_attr_adapter_rating_mw.attr, |
88 | &dev_attr_battery_steady_power_mw.attr, |
89 | &dev_attr_charger_type.attr, |
90 | &dev_attr_rest_of_platform_power_mw.attr, |
91 | &dev_attr_prochot_confirm.attr, |
92 | NULL |
93 | }; |
94 | |
95 | static const struct attribute_group dptf_power_attribute_group = { |
96 | .attrs = dptf_power_attrs, |
97 | .name = "dptf_power" |
98 | }; |
99 | |
100 | static struct attribute *dptf_battery_attrs[] = { |
101 | &dev_attr_max_platform_power_mw.attr, |
102 | &dev_attr_max_steady_state_power_mw.attr, |
103 | &dev_attr_high_freq_impedance_mohm.attr, |
104 | &dev_attr_no_load_voltage_mv.attr, |
105 | &dev_attr_current_discharge_capbility_ma.attr, |
106 | NULL |
107 | }; |
108 | |
109 | static const struct attribute_group dptf_battery_attribute_group = { |
110 | .attrs = dptf_battery_attrs, |
111 | .name = "dptf_battery" |
112 | }; |
113 | |
114 | #define MAX_POWER_CHANGED 0x80 |
115 | #define POWER_STATE_CHANGED 0x81 |
116 | #define STEADY_STATE_POWER_CHANGED 0x83 |
117 | #define POWER_PROP_CHANGE_EVENT 0x84 |
118 | #define IMPEDANCE_CHANGED 0x85 |
119 | #define VOLTAGE_CURRENT_CHANGED 0x86 |
120 | |
121 | static long long dptf_participant_type(acpi_handle handle) |
122 | { |
123 | unsigned long long ptype; |
124 | acpi_status status; |
125 | |
126 | status = acpi_evaluate_integer(handle, pathname: "PTYP" , NULL, data: &ptype); |
127 | if (ACPI_FAILURE(status)) |
128 | return -ENODEV; |
129 | |
130 | return ptype; |
131 | } |
132 | |
133 | static void dptf_power_notify(acpi_handle handle, u32 event, void *data) |
134 | { |
135 | struct platform_device *pdev = data; |
136 | char *attr; |
137 | |
138 | switch (event) { |
139 | case POWER_STATE_CHANGED: |
140 | attr = "platform_power_source" ; |
141 | break; |
142 | case POWER_PROP_CHANGE_EVENT: |
143 | attr = "rest_of_platform_power_mw" ; |
144 | break; |
145 | case MAX_POWER_CHANGED: |
146 | attr = "max_platform_power_mw" ; |
147 | break; |
148 | case STEADY_STATE_POWER_CHANGED: |
149 | attr = "max_steady_state_power_mw" ; |
150 | break; |
151 | case IMPEDANCE_CHANGED: |
152 | attr = "high_freq_impedance_mohm" ; |
153 | break; |
154 | case VOLTAGE_CURRENT_CHANGED: |
155 | attr = "no_load_voltage_mv" ; |
156 | break; |
157 | default: |
158 | dev_err(&pdev->dev, "Unsupported event [0x%x]\n" , event); |
159 | return; |
160 | } |
161 | |
162 | /* |
163 | * Notify that an attribute is changed, so that user space can read |
164 | * again. |
165 | */ |
166 | if (dptf_participant_type(handle) == 0x0CULL) |
167 | sysfs_notify(kobj: &pdev->dev.kobj, dir: "dptf_battery" , attr); |
168 | else |
169 | sysfs_notify(kobj: &pdev->dev.kobj, dir: "dptf_power" , attr); |
170 | } |
171 | |
172 | static int dptf_power_add(struct platform_device *pdev) |
173 | { |
174 | const struct attribute_group *attr_group; |
175 | struct acpi_device *acpi_dev; |
176 | unsigned long long ptype; |
177 | int result; |
178 | |
179 | acpi_dev = ACPI_COMPANION(&(pdev->dev)); |
180 | if (!acpi_dev) |
181 | return -ENODEV; |
182 | |
183 | ptype = dptf_participant_type(handle: acpi_dev->handle); |
184 | if (ptype == 0x11) |
185 | attr_group = &dptf_power_attribute_group; |
186 | else if (ptype == 0x0C) |
187 | attr_group = &dptf_battery_attribute_group; |
188 | else |
189 | return -ENODEV; |
190 | |
191 | result = acpi_install_notify_handler(device: acpi_dev->handle, |
192 | ACPI_DEVICE_NOTIFY, |
193 | handler: dptf_power_notify, |
194 | context: (void *)pdev); |
195 | if (result) |
196 | return result; |
197 | |
198 | result = sysfs_create_group(kobj: &pdev->dev.kobj, |
199 | grp: attr_group); |
200 | if (result) { |
201 | acpi_remove_notify_handler(device: acpi_dev->handle, |
202 | ACPI_DEVICE_NOTIFY, |
203 | handler: dptf_power_notify); |
204 | return result; |
205 | } |
206 | |
207 | platform_set_drvdata(pdev, data: acpi_dev); |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | static int dptf_power_remove(struct platform_device *pdev) |
213 | { |
214 | struct acpi_device *acpi_dev = platform_get_drvdata(pdev); |
215 | |
216 | acpi_remove_notify_handler(device: acpi_dev->handle, |
217 | ACPI_DEVICE_NOTIFY, |
218 | handler: dptf_power_notify); |
219 | |
220 | if (dptf_participant_type(handle: acpi_dev->handle) == 0x0CULL) |
221 | sysfs_remove_group(kobj: &pdev->dev.kobj, grp: &dptf_battery_attribute_group); |
222 | else |
223 | sysfs_remove_group(kobj: &pdev->dev.kobj, grp: &dptf_power_attribute_group); |
224 | |
225 | return 0; |
226 | } |
227 | |
228 | static const struct acpi_device_id int3407_device_ids[] = { |
229 | {"INT3407" , 0}, |
230 | {"INT3532" , 0}, |
231 | {"INTC1047" , 0}, |
232 | {"INTC1050" , 0}, |
233 | {"INTC1060" , 0}, |
234 | {"INTC1061" , 0}, |
235 | {"INTC1065" , 0}, |
236 | {"INTC1066" , 0}, |
237 | {"INTC10A4" , 0}, |
238 | {"INTC10A5" , 0}, |
239 | {"" , 0}, |
240 | }; |
241 | MODULE_DEVICE_TABLE(acpi, int3407_device_ids); |
242 | |
243 | static struct platform_driver dptf_power_driver = { |
244 | .probe = dptf_power_add, |
245 | .remove = dptf_power_remove, |
246 | .driver = { |
247 | .name = "dptf_power" , |
248 | .acpi_match_table = int3407_device_ids, |
249 | }, |
250 | }; |
251 | |
252 | module_platform_driver(dptf_power_driver); |
253 | |
254 | MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>" ); |
255 | MODULE_LICENSE("GPL v2" ); |
256 | MODULE_DESCRIPTION("ACPI DPTF platform power driver" ); |
257 | |