Commit | Line | Data |
---|---|---|
2025cf9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6256ebd5 SP |
2 | /* |
3 | * dptf_power: DPTF platform power driver | |
4 | * Copyright (c) 2016, Intel Corporation. | |
6256ebd5 SP |
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 | /* | |
7b52b200 SP |
13 | * Presentation of attributes which are defined for INT3407 and INT3532. |
14 | * They are: | |
290a2078 | 15 | * PMAX : Maximum platform power |
6256ebd5 SP |
16 | * PSRC : Platform power source |
17 | * ARTG : Adapter rating | |
18 | * CTYP : Charger type | |
668ce99e | 19 | * PROP : Rest of worst case platform Power |
7b52b200 | 20 | * PBSS : Power Battery Steady State |
7b52b200 SP |
21 | * RBHF : High Frequency Impedance |
22 | * VBNL : Instantaneous No-Load Voltage | |
23 | * CMPP : Current Discharge Capability | |
6256ebd5 SP |
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 | {\ | |
b0f65b91 | 30 | struct acpi_device *acpi_dev = dev_get_drvdata(dev);\ |
6256ebd5 SP |
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) | |
668ce99e | 47 | DPTF_POWER_SHOW(rest_of_platform_power_mw, PROP) |
7b52b200 SP |
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); | |
6256ebd5 SP |
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); | |
668ce99e | 58 | static DEVICE_ATTR_RO(rest_of_platform_power_mw); |
7b52b200 SP |
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); | |
668ce99e SP |
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(buf, 0, &seq_no) < 0) | |
73 | return -EINVAL; | |
74 | ||
75 | status = acpi_execute_simple_method(acpi_dev->handle, "PBOK", seq_no); | |
76 | if (ACPI_SUCCESS(status)) | |
77 | return count; | |
78 | ||
79 | return -EINVAL; | |
80 | } | |
81 | ||
82 | static DEVICE_ATTR_WO(prochot_confirm); | |
6256ebd5 SP |
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, | |
668ce99e SP |
90 | &dev_attr_rest_of_platform_power_mw.attr, |
91 | &dev_attr_prochot_confirm.attr, | |
6256ebd5 SP |
92 | NULL |
93 | }; | |
94 | ||
9e4de6a8 | 95 | static const struct attribute_group dptf_power_attribute_group = { |
6256ebd5 SP |
96 | .attrs = dptf_power_attrs, |
97 | .name = "dptf_power" | |
98 | }; | |
99 | ||
7b52b200 SP |
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 | |
668ce99e | 115 | #define POWER_STATE_CHANGED 0x81 |
7b52b200 | 116 | #define STEADY_STATE_POWER_CHANGED 0x83 |
668ce99e | 117 | #define POWER_PROP_CHANGE_EVENT 0x84 |
42e5ed06 | 118 | #define IMPEDANCE_CHANGED 0x85 |
7b52b200 SP |
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, "PTYP", NULL, &ptype); | |
127 | if (ACPI_FAILURE(status)) | |
128 | return -ENODEV; | |
129 | ||
130 | return ptype; | |
131 | } | |
668ce99e SP |
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; | |
7b52b200 SP |
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; | |
42e5ed06 SP |
151 | case IMPEDANCE_CHANGED: |
152 | attr = "high_freq_impedance_mohm"; | |
153 | break; | |
7b52b200 SP |
154 | case VOLTAGE_CURRENT_CHANGED: |
155 | attr = "no_load_voltage_mv"; | |
156 | break; | |
668ce99e SP |
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 | */ | |
7b52b200 SP |
166 | if (dptf_participant_type(handle) == 0x0CULL) |
167 | sysfs_notify(&pdev->dev.kobj, "dptf_battery", attr); | |
168 | else | |
169 | sysfs_notify(&pdev->dev.kobj, "dptf_power", attr); | |
668ce99e SP |
170 | } |
171 | ||
6256ebd5 SP |
172 | static int dptf_power_add(struct platform_device *pdev) |
173 | { | |
7b52b200 | 174 | const struct attribute_group *attr_group; |
6256ebd5 | 175 | struct acpi_device *acpi_dev; |
6256ebd5 SP |
176 | unsigned long long ptype; |
177 | int result; | |
178 | ||
179 | acpi_dev = ACPI_COMPANION(&(pdev->dev)); | |
180 | if (!acpi_dev) | |
181 | return -ENODEV; | |
182 | ||
7b52b200 SP |
183 | ptype = dptf_participant_type(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 | |
6256ebd5 SP |
189 | return -ENODEV; |
190 | ||
668ce99e SP |
191 | result = acpi_install_notify_handler(acpi_dev->handle, |
192 | ACPI_DEVICE_NOTIFY, | |
193 | dptf_power_notify, | |
194 | (void *)pdev); | |
195 | if (result) | |
196 | return result; | |
197 | ||
6256ebd5 | 198 | result = sysfs_create_group(&pdev->dev.kobj, |
7b52b200 | 199 | attr_group); |
668ce99e SP |
200 | if (result) { |
201 | acpi_remove_notify_handler(acpi_dev->handle, | |
202 | ACPI_DEVICE_NOTIFY, | |
203 | dptf_power_notify); | |
6256ebd5 | 204 | return result; |
668ce99e | 205 | } |
6256ebd5 SP |
206 | |
207 | platform_set_drvdata(pdev, acpi_dev); | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
da22084d | 212 | static void dptf_power_remove(struct platform_device *pdev) |
6256ebd5 | 213 | { |
668ce99e | 214 | struct acpi_device *acpi_dev = platform_get_drvdata(pdev); |
6256ebd5 | 215 | |
668ce99e SP |
216 | acpi_remove_notify_handler(acpi_dev->handle, |
217 | ACPI_DEVICE_NOTIFY, | |
218 | dptf_power_notify); | |
7b52b200 SP |
219 | |
220 | if (dptf_participant_type(acpi_dev->handle) == 0x0CULL) | |
221 | sysfs_remove_group(&pdev->dev.kobj, &dptf_battery_attribute_group); | |
222 | else | |
223 | sysfs_remove_group(&pdev->dev.kobj, &dptf_power_attribute_group); | |
6256ebd5 SP |
224 | } |
225 | ||
226 | static const struct acpi_device_id int3407_device_ids[] = { | |
227 | {"INT3407", 0}, | |
7b52b200 | 228 | {"INT3532", 0}, |
b62c770f | 229 | {"INTC1047", 0}, |
1e05daca | 230 | {"INTC1050", 0}, |
38748bcb SP |
231 | {"INTC1060", 0}, |
232 | {"INTC1061", 0}, | |
657b95d3 SP |
233 | {"INTC1065", 0}, |
234 | {"INTC1066", 0}, | |
79b510c4 SP |
235 | {"INTC106C", 0}, |
236 | {"INTC106D", 0}, | |
a510c78e SP |
237 | {"INTC10A4", 0}, |
238 | {"INTC10A5", 0}, | |
6256ebd5 SP |
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, | |
da22084d | 245 | .remove_new = dptf_power_remove, |
6256ebd5 | 246 | .driver = { |
ff44fe3e | 247 | .name = "dptf_power", |
6256ebd5 SP |
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"); |