.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * drivers/hwmon/applesmc.c - driver for Apple's SMC (accelerometer, temperature |
---|
3 | 4 | * sensors, fan control, keyboard backlight control) used in Intel-based Apple |
---|
.. | .. |
---|
12 | 13 | * |
---|
13 | 14 | * Fan control based on smcFanControl: |
---|
14 | 15 | * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com> |
---|
15 | | - * |
---|
16 | | - * This program is free software; you can redistribute it and/or modify it |
---|
17 | | - * under the terms of the GNU General Public License v2 as published by the |
---|
18 | | - * Free Software Foundation. |
---|
19 | | - * |
---|
20 | | - * This program is distributed in the hope that it will be useful, but WITHOUT |
---|
21 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
---|
22 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
---|
23 | | - * more details. |
---|
24 | | - * |
---|
25 | | - * You should have received a copy of the GNU General Public License along with |
---|
26 | | - * this program; if not, write to the Free Software Foundation, Inc., |
---|
27 | | - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
---|
28 | 16 | */ |
---|
29 | 17 | |
---|
30 | 18 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
31 | 19 | |
---|
32 | 20 | #include <linux/delay.h> |
---|
33 | 21 | #include <linux/platform_device.h> |
---|
34 | | -#include <linux/input-polldev.h> |
---|
| 22 | +#include <linux/input.h> |
---|
35 | 23 | #include <linux/kernel.h> |
---|
36 | 24 | #include <linux/slab.h> |
---|
37 | 25 | #include <linux/module.h> |
---|
.. | .. |
---|
44 | 32 | #include <linux/hwmon.h> |
---|
45 | 33 | #include <linux/workqueue.h> |
---|
46 | 34 | #include <linux/err.h> |
---|
| 35 | +#include <linux/bits.h> |
---|
47 | 36 | |
---|
48 | 37 | /* data port used by Apple SMC */ |
---|
49 | 38 | #define APPLESMC_DATA_PORT 0x300 |
---|
.. | .. |
---|
54 | 43 | |
---|
55 | 44 | #define APPLESMC_MAX_DATA_LENGTH 32 |
---|
56 | 45 | |
---|
57 | | -/* wait up to 128 ms for a status change. */ |
---|
58 | | -#define APPLESMC_MIN_WAIT 0x0010 |
---|
59 | | -#define APPLESMC_RETRY_WAIT 0x0100 |
---|
60 | | -#define APPLESMC_MAX_WAIT 0x20000 |
---|
| 46 | +/* Apple SMC status bits */ |
---|
| 47 | +#define SMC_STATUS_AWAITING_DATA BIT(0) /* SMC has data waiting to be read */ |
---|
| 48 | +#define SMC_STATUS_IB_CLOSED BIT(1) /* Will ignore any input */ |
---|
| 49 | +#define SMC_STATUS_BUSY BIT(2) /* Command in progress */ |
---|
| 50 | + |
---|
| 51 | +/* Initial wait is 8us */ |
---|
| 52 | +#define APPLESMC_MIN_WAIT 0x0008 |
---|
61 | 53 | |
---|
62 | 54 | #define APPLESMC_READ_CMD 0x10 |
---|
63 | 55 | #define APPLESMC_WRITE_CMD 0x11 |
---|
.. | .. |
---|
152 | 144 | static u8 backlight_state[2]; |
---|
153 | 145 | |
---|
154 | 146 | static struct device *hwmon_dev; |
---|
155 | | -static struct input_polled_dev *applesmc_idev; |
---|
| 147 | +static struct input_dev *applesmc_idev; |
---|
156 | 148 | |
---|
157 | 149 | /* |
---|
158 | 150 | * Last index written to key_at_index sysfs file, and value to use for all other |
---|
.. | .. |
---|
163 | 155 | static struct workqueue_struct *applesmc_led_wq; |
---|
164 | 156 | |
---|
165 | 157 | /* |
---|
166 | | - * wait_read - Wait for a byte to appear on SMC port. Callers must |
---|
167 | | - * hold applesmc_lock. |
---|
| 158 | + * Wait for specific status bits with a mask on the SMC. |
---|
| 159 | + * Used before all transactions. |
---|
| 160 | + * This does 10 fast loops of 8us then exponentially backs off for a |
---|
| 161 | + * minimum total wait of 262ms. Depending on usleep_range this could |
---|
| 162 | + * run out past 500ms. |
---|
168 | 163 | */ |
---|
169 | | -static int wait_read(void) |
---|
| 164 | + |
---|
| 165 | +static int wait_status(u8 val, u8 mask) |
---|
170 | 166 | { |
---|
171 | 167 | u8 status; |
---|
172 | 168 | int us; |
---|
173 | | - for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { |
---|
174 | | - udelay(us); |
---|
175 | | - status = inb(APPLESMC_CMD_PORT); |
---|
176 | | - /* read: wait for smc to settle */ |
---|
177 | | - if (status & 0x01) |
---|
178 | | - return 0; |
---|
179 | | - } |
---|
| 169 | + int i; |
---|
180 | 170 | |
---|
181 | | - pr_warn("wait_read() fail: 0x%02x\n", status); |
---|
| 171 | + us = APPLESMC_MIN_WAIT; |
---|
| 172 | + for (i = 0; i < 24 ; i++) { |
---|
| 173 | + status = inb(APPLESMC_CMD_PORT); |
---|
| 174 | + if ((status & mask) == val) |
---|
| 175 | + return 0; |
---|
| 176 | + usleep_range(us, us * 2); |
---|
| 177 | + if (i > 9) |
---|
| 178 | + us <<= 1; |
---|
| 179 | + } |
---|
182 | 180 | return -EIO; |
---|
183 | 181 | } |
---|
184 | 182 | |
---|
185 | | -/* |
---|
186 | | - * send_byte - Write to SMC port, retrying when necessary. Callers |
---|
187 | | - * must hold applesmc_lock. |
---|
188 | | - */ |
---|
| 183 | +/* send_byte - Write to SMC data port. Callers must hold applesmc_lock. */ |
---|
| 184 | + |
---|
189 | 185 | static int send_byte(u8 cmd, u16 port) |
---|
190 | 186 | { |
---|
191 | | - u8 status; |
---|
192 | | - int us; |
---|
| 187 | + int status; |
---|
| 188 | + |
---|
| 189 | + status = wait_status(0, SMC_STATUS_IB_CLOSED); |
---|
| 190 | + if (status) |
---|
| 191 | + return status; |
---|
| 192 | + /* |
---|
| 193 | + * This needs to be a separate read looking for bit 0x04 |
---|
| 194 | + * after bit 0x02 falls. If consolidated with the wait above |
---|
| 195 | + * this extra read may not happen if status returns both |
---|
| 196 | + * simultaneously and this would appear to be required. |
---|
| 197 | + */ |
---|
| 198 | + status = wait_status(SMC_STATUS_BUSY, SMC_STATUS_BUSY); |
---|
| 199 | + if (status) |
---|
| 200 | + return status; |
---|
193 | 201 | |
---|
194 | 202 | outb(cmd, port); |
---|
195 | | - for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) { |
---|
196 | | - udelay(us); |
---|
197 | | - status = inb(APPLESMC_CMD_PORT); |
---|
198 | | - /* write: wait for smc to settle */ |
---|
199 | | - if (status & 0x02) |
---|
200 | | - continue; |
---|
201 | | - /* ready: cmd accepted, return */ |
---|
202 | | - if (status & 0x04) |
---|
203 | | - return 0; |
---|
204 | | - /* timeout: give up */ |
---|
205 | | - if (us << 1 == APPLESMC_MAX_WAIT) |
---|
206 | | - break; |
---|
207 | | - /* busy: long wait and resend */ |
---|
208 | | - udelay(APPLESMC_RETRY_WAIT); |
---|
209 | | - outb(cmd, port); |
---|
210 | | - } |
---|
211 | | - |
---|
212 | | - pr_warn("send_byte(0x%02x, 0x%04x) fail: 0x%02x\n", cmd, port, status); |
---|
213 | | - return -EIO; |
---|
| 203 | + return 0; |
---|
214 | 204 | } |
---|
| 205 | + |
---|
| 206 | +/* send_command - Write a command to the SMC. Callers must hold applesmc_lock. */ |
---|
215 | 207 | |
---|
216 | 208 | static int send_command(u8 cmd) |
---|
217 | 209 | { |
---|
218 | | - return send_byte(cmd, APPLESMC_CMD_PORT); |
---|
| 210 | + int ret; |
---|
| 211 | + |
---|
| 212 | + ret = wait_status(0, SMC_STATUS_IB_CLOSED); |
---|
| 213 | + if (ret) |
---|
| 214 | + return ret; |
---|
| 215 | + outb(cmd, APPLESMC_CMD_PORT); |
---|
| 216 | + return 0; |
---|
| 217 | +} |
---|
| 218 | + |
---|
| 219 | +/* |
---|
| 220 | + * Based on logic from the Apple driver. This is issued before any interaction |
---|
| 221 | + * If busy is stuck high, issue a read command to reset the SMC state machine. |
---|
| 222 | + * If busy is stuck high after the command then the SMC is jammed. |
---|
| 223 | + */ |
---|
| 224 | + |
---|
| 225 | +static int smc_sane(void) |
---|
| 226 | +{ |
---|
| 227 | + int ret; |
---|
| 228 | + |
---|
| 229 | + ret = wait_status(0, SMC_STATUS_BUSY); |
---|
| 230 | + if (!ret) |
---|
| 231 | + return ret; |
---|
| 232 | + ret = send_command(APPLESMC_READ_CMD); |
---|
| 233 | + if (ret) |
---|
| 234 | + return ret; |
---|
| 235 | + return wait_status(0, SMC_STATUS_BUSY); |
---|
219 | 236 | } |
---|
220 | 237 | |
---|
221 | 238 | static int send_argument(const char *key) |
---|
.. | .. |
---|
232 | 249 | { |
---|
233 | 250 | u8 status, data = 0; |
---|
234 | 251 | int i; |
---|
| 252 | + int ret; |
---|
| 253 | + |
---|
| 254 | + ret = smc_sane(); |
---|
| 255 | + if (ret) |
---|
| 256 | + return ret; |
---|
235 | 257 | |
---|
236 | 258 | if (send_command(cmd) || send_argument(key)) { |
---|
237 | 259 | pr_warn("%.4s: read arg fail\n", key); |
---|
.. | .. |
---|
245 | 267 | } |
---|
246 | 268 | |
---|
247 | 269 | for (i = 0; i < len; i++) { |
---|
248 | | - if (wait_read()) { |
---|
| 270 | + if (wait_status(SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY, |
---|
| 271 | + SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY)) { |
---|
249 | 272 | pr_warn("%.4s: read data[%d] fail\n", key, i); |
---|
250 | 273 | return -EIO; |
---|
251 | 274 | } |
---|
.. | .. |
---|
256 | 279 | for (i = 0; i < 16; i++) { |
---|
257 | 280 | udelay(APPLESMC_MIN_WAIT); |
---|
258 | 281 | status = inb(APPLESMC_CMD_PORT); |
---|
259 | | - if (!(status & 0x01)) |
---|
| 282 | + if (!(status & SMC_STATUS_AWAITING_DATA)) |
---|
260 | 283 | break; |
---|
261 | 284 | data = inb(APPLESMC_DATA_PORT); |
---|
262 | 285 | } |
---|
263 | 286 | if (i) |
---|
264 | 287 | pr_warn("flushed %d bytes, last value is: %d\n", i, data); |
---|
265 | 288 | |
---|
266 | | - return 0; |
---|
| 289 | + return wait_status(0, SMC_STATUS_BUSY); |
---|
267 | 290 | } |
---|
268 | 291 | |
---|
269 | 292 | static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len) |
---|
270 | 293 | { |
---|
271 | 294 | int i; |
---|
| 295 | + int ret; |
---|
| 296 | + |
---|
| 297 | + ret = smc_sane(); |
---|
| 298 | + if (ret) |
---|
| 299 | + return ret; |
---|
272 | 300 | |
---|
273 | 301 | if (send_command(cmd) || send_argument(key)) { |
---|
274 | 302 | pr_warn("%s: write arg fail\n", key); |
---|
.. | .. |
---|
287 | 315 | } |
---|
288 | 316 | } |
---|
289 | 317 | |
---|
290 | | - return 0; |
---|
| 318 | + return wait_status(0, SMC_STATUS_BUSY); |
---|
291 | 319 | } |
---|
292 | 320 | |
---|
293 | 321 | static int read_register_count(unsigned int *count) |
---|
.. | .. |
---|
693 | 721 | rest_x = -rest_x; |
---|
694 | 722 | } |
---|
695 | 723 | |
---|
696 | | -static void applesmc_idev_poll(struct input_polled_dev *dev) |
---|
| 724 | +static void applesmc_idev_poll(struct input_dev *idev) |
---|
697 | 725 | { |
---|
698 | | - struct input_dev *idev = dev->input; |
---|
699 | 726 | s16 x, y; |
---|
700 | 727 | |
---|
701 | 728 | if (applesmc_read_s16(MOTION_SENSOR_X_KEY, &x)) |
---|
.. | .. |
---|
1129 | 1156 | attr = &node->sda.dev_attr.attr; |
---|
1130 | 1157 | sysfs_attr_init(attr); |
---|
1131 | 1158 | attr->name = node->name; |
---|
1132 | | - attr->mode = S_IRUGO | (grp->store ? S_IWUSR : 0); |
---|
| 1159 | + attr->mode = 0444 | (grp->store ? 0200 : 0); |
---|
1133 | 1160 | ret = sysfs_create_file(&pdev->dev.kobj, attr); |
---|
1134 | 1161 | if (ret) { |
---|
1135 | 1162 | attr->name = NULL; |
---|
.. | .. |
---|
1147 | 1174 | /* Create accelerometer resources */ |
---|
1148 | 1175 | static int applesmc_create_accelerometer(void) |
---|
1149 | 1176 | { |
---|
1150 | | - struct input_dev *idev; |
---|
1151 | 1177 | int ret; |
---|
1152 | 1178 | |
---|
1153 | 1179 | if (!smcreg.has_accelerometer) |
---|
.. | .. |
---|
1157 | 1183 | if (ret) |
---|
1158 | 1184 | goto out; |
---|
1159 | 1185 | |
---|
1160 | | - applesmc_idev = input_allocate_polled_device(); |
---|
| 1186 | + applesmc_idev = input_allocate_device(); |
---|
1161 | 1187 | if (!applesmc_idev) { |
---|
1162 | 1188 | ret = -ENOMEM; |
---|
1163 | 1189 | goto out_sysfs; |
---|
1164 | 1190 | } |
---|
1165 | 1191 | |
---|
1166 | | - applesmc_idev->poll = applesmc_idev_poll; |
---|
1167 | | - applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL; |
---|
1168 | | - |
---|
1169 | 1192 | /* initial calibrate for the input device */ |
---|
1170 | 1193 | applesmc_calibrate(); |
---|
1171 | 1194 | |
---|
1172 | 1195 | /* initialize the input device */ |
---|
1173 | | - idev = applesmc_idev->input; |
---|
1174 | | - idev->name = "applesmc"; |
---|
1175 | | - idev->id.bustype = BUS_HOST; |
---|
1176 | | - idev->dev.parent = &pdev->dev; |
---|
1177 | | - idev->evbit[0] = BIT_MASK(EV_ABS); |
---|
1178 | | - input_set_abs_params(idev, ABS_X, |
---|
| 1196 | + applesmc_idev->name = "applesmc"; |
---|
| 1197 | + applesmc_idev->id.bustype = BUS_HOST; |
---|
| 1198 | + applesmc_idev->dev.parent = &pdev->dev; |
---|
| 1199 | + input_set_abs_params(applesmc_idev, ABS_X, |
---|
1179 | 1200 | -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT); |
---|
1180 | | - input_set_abs_params(idev, ABS_Y, |
---|
| 1201 | + input_set_abs_params(applesmc_idev, ABS_Y, |
---|
1181 | 1202 | -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT); |
---|
1182 | 1203 | |
---|
1183 | | - ret = input_register_polled_device(applesmc_idev); |
---|
| 1204 | + ret = input_setup_polling(applesmc_idev, applesmc_idev_poll); |
---|
| 1205 | + if (ret) |
---|
| 1206 | + goto out_idev; |
---|
| 1207 | + |
---|
| 1208 | + input_set_poll_interval(applesmc_idev, APPLESMC_POLL_INTERVAL); |
---|
| 1209 | + |
---|
| 1210 | + ret = input_register_device(applesmc_idev); |
---|
1184 | 1211 | if (ret) |
---|
1185 | 1212 | goto out_idev; |
---|
1186 | 1213 | |
---|
1187 | 1214 | return 0; |
---|
1188 | 1215 | |
---|
1189 | 1216 | out_idev: |
---|
1190 | | - input_free_polled_device(applesmc_idev); |
---|
| 1217 | + input_free_device(applesmc_idev); |
---|
1191 | 1218 | |
---|
1192 | 1219 | out_sysfs: |
---|
1193 | 1220 | applesmc_destroy_nodes(accelerometer_group); |
---|
.. | .. |
---|
1202 | 1229 | { |
---|
1203 | 1230 | if (!smcreg.has_accelerometer) |
---|
1204 | 1231 | return; |
---|
1205 | | - input_unregister_polled_device(applesmc_idev); |
---|
1206 | | - input_free_polled_device(applesmc_idev); |
---|
| 1232 | + input_unregister_device(applesmc_idev); |
---|
1207 | 1233 | applesmc_destroy_nodes(accelerometer_group); |
---|
1208 | 1234 | } |
---|
1209 | 1235 | |
---|