Blame view

drivers/hwmon/applesmc.c 31.5 KB
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1
2
3
4
5
6
  /*
   * drivers/hwmon/applesmc.c - driver for Apple's SMC (accelerometer, temperature
   * sensors, fan control, keyboard backlight control) used in Intel-based Apple
   * computers.
   *
   * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch>
41e71f971   Henrik Rydberg   hwmon: (applesmc)...
7
   * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
   *
   * Based on hdaps.c driver:
   * Copyright (C) 2005 Robert Love <rml@novell.com>
   * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
   *
   * Fan control based on smcFanControl:
   * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com>
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License v2 as published by the
   * Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   * more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
   */
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
29
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6f2fad748   Nicolas Boichat   Apple SMC driver ...
30
31
  #include <linux/delay.h>
  #include <linux/platform_device.h>
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
32
  #include <linux/input-polldev.h>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
33
  #include <linux/kernel.h>
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
34
  #include <linux/slab.h>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
35
36
37
38
39
  #include <linux/module.h>
  #include <linux/timer.h>
  #include <linux/dmi.h>
  #include <linux/mutex.h>
  #include <linux/hwmon-sysfs.h>
6055fae8a   H Hartley Sweeten   hwmon: Include <l...
40
  #include <linux/io.h>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
41
42
43
44
45
46
47
48
49
50
51
52
  #include <linux/leds.h>
  #include <linux/hwmon.h>
  #include <linux/workqueue.h>
  
  /* data port used by Apple SMC */
  #define APPLESMC_DATA_PORT	0x300
  /* command/status port used by Apple SMC */
  #define APPLESMC_CMD_PORT	0x304
  
  #define APPLESMC_NR_PORTS	32 /* 0x300-0x31f */
  
  #define APPLESMC_MAX_DATA_LENGTH 32
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
53
  /* wait up to 32 ms for a status change. */
8c9398d1e   Henrik Rydberg   hwmon: applesmc: ...
54
55
  #define APPLESMC_MIN_WAIT	0x0040
  #define APPLESMC_MAX_WAIT	0x8000
6f2fad748   Nicolas Boichat   Apple SMC driver ...
56
57
58
59
60
61
62
  #define APPLESMC_STATUS_MASK	0x0f
  #define APPLESMC_READ_CMD	0x10
  #define APPLESMC_WRITE_CMD	0x11
  #define APPLESMC_GET_KEY_BY_INDEX_CMD	0x12
  #define APPLESMC_GET_KEY_TYPE_CMD	0x13
  
  #define KEY_COUNT_KEY		"#KEY" /* r-o ui32 */
8bd1a12a5   Henrik Rydberg   hwmon: applesmc: ...
63
64
  #define LIGHT_SENSOR_LEFT_KEY	"ALV0" /* r-o {alv (6-10 bytes) */
  #define LIGHT_SENSOR_RIGHT_KEY	"ALV1" /* r-o {alv (6-10 bytes) */
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
65
  #define BACKLIGHT_KEY		"LKSB" /* w-o {lkb (2 bytes) */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
66

d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
67
  #define CLAMSHELL_KEY		"MSLD" /* r-o ui8 (unused) */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
68
69
70
71
72
73
74
75
  
  #define MOTION_SENSOR_X_KEY	"MO_X" /* r-o sp78 (2 bytes) */
  #define MOTION_SENSOR_Y_KEY	"MO_Y" /* r-o sp78 (2 bytes) */
  #define MOTION_SENSOR_Z_KEY	"MO_Z" /* r-o sp78 (2 bytes) */
  #define MOTION_SENSOR_KEY	"MOCN" /* r/w ui16 */
  
  #define FANS_COUNT		"FNum" /* r-o ui8 */
  #define FANS_MANUAL		"FS! " /* r-w ui16 */
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
76
  #define FAN_ID_FMT		"F%dID" /* r-o char[16] */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
77

6f2fad748   Nicolas Boichat   Apple SMC driver ...
78
  /* List of keys used to read/write fan speeds */
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
79
80
81
82
83
84
  static const char *const fan_speed_fmt[] = {
  	"F%dAc",		/* actual speed */
  	"F%dMn",		/* minimum speed (rw) */
  	"F%dMx",		/* maximum speed */
  	"F%dSf",		/* safe speed - not all models */
  	"F%dTg",		/* target speed (manual: rw) */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
85
86
87
88
  };
  
  #define INIT_TIMEOUT_MSECS	5000	/* wait up to 5s for device init ... */
  #define INIT_WAIT_MSECS		50	/* ... in 50ms increments */
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
89
  #define APPLESMC_POLL_INTERVAL	50	/* msecs */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
90
91
92
93
94
95
  #define APPLESMC_INPUT_FUZZ	4	/* input event threshold */
  #define APPLESMC_INPUT_FLAT	4
  
  #define SENSOR_X 0
  #define SENSOR_Y 1
  #define SENSOR_Z 2
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
96
97
  #define to_index(attr) (to_sensor_dev_attr(attr)->index & 0xffff)
  #define to_option(attr) (to_sensor_dev_attr(attr)->index >> 16)
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
98

9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
99
100
101
102
103
104
105
106
107
108
109
  /* Dynamic device node attributes */
  struct applesmc_dev_attr {
  	struct sensor_device_attribute sda;	/* hwmon attributes */
  	char name[32];				/* room for node file name */
  };
  
  /* Dynamic device node group */
  struct applesmc_node_group {
  	char *format;				/* format string */
  	void *show;				/* show function */
  	void *store;				/* store function */
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
110
  	int option;				/* function argument */
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
111
112
  	struct applesmc_dev_attr *nodes;	/* dynamic node array */
  };
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
113
114
115
116
117
118
119
120
121
122
123
124
125
  /* AppleSMC entry - cached register information */
  struct applesmc_entry {
  	char key[5];		/* four-letter key code */
  	u8 valid;		/* set when entry is successfully read once */
  	u8 len;			/* bounded by APPLESMC_MAX_DATA_LENGTH */
  	char type[5];		/* four-letter type code */
  	u8 flags;		/* 0x10: func; 0x40: write; 0x80: read */
  };
  
  /* Register lookup and registers common to all SMCs */
  static struct applesmc_registers {
  	struct mutex mutex;		/* register read/write mutex */
  	unsigned int key_count;		/* number of SMC registers */
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
126
  	unsigned int fan_count;		/* number of fans */
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
127
128
129
  	unsigned int temp_count;	/* number of temperature registers */
  	unsigned int temp_begin;	/* temperature lower index bound */
  	unsigned int temp_end;		/* temperature upper index bound */
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
130
131
132
  	int num_light_sensors;		/* number of light sensors */
  	bool has_accelerometer;		/* has motion sensor */
  	bool has_key_backlight;		/* has keyboard backlight */
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
133
134
135
136
137
  	bool init_complete;		/* true when fully initialized */
  	struct applesmc_entry *cache;	/* cached key entries */
  } smcreg = {
  	.mutex = __MUTEX_INITIALIZER(smcreg.mutex),
  };
6f2fad748   Nicolas Boichat   Apple SMC driver ...
138
139
140
141
  static const int debug;
  static struct platform_device *pdev;
  static s16 rest_x;
  static s16 rest_y;
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
142
  static u8 backlight_state[2];
1beeffe43   Tony Jones   hwmon: Convert fr...
143
  static struct device *hwmon_dev;
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
144
  static struct input_polled_dev *applesmc_idev;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
145

6f2fad748   Nicolas Boichat   Apple SMC driver ...
146
147
148
149
150
151
152
153
154
  /*
   * Last index written to key_at_index sysfs file, and value to use for all other
   * key_at_index_* sysfs files.
   */
  static unsigned int key_at_index;
  
  static struct workqueue_struct *applesmc_led_wq;
  
  /*
8c9398d1e   Henrik Rydberg   hwmon: applesmc: ...
155
   * __wait_status - Wait up to 32ms for the status port to get a certain value
6f2fad748   Nicolas Boichat   Apple SMC driver ...
156
157
158
159
160
   * (masked with 0x0f), returning zero if the value is obtained.  Callers must
   * hold applesmc_lock.
   */
  static int __wait_status(u8 val)
  {
8c9398d1e   Henrik Rydberg   hwmon: applesmc: ...
161
  	int us;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
162
163
  
  	val = val & APPLESMC_STATUS_MASK;
8c9398d1e   Henrik Rydberg   hwmon: applesmc: ...
164
165
  	for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
  		udelay(us);
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
166
  		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
167
  			return 0;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
168
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
169
170
171
172
  	return -EIO;
  }
  
  /*
84d2d7f2e   Henrik Rydberg   hwmon: applesmc: ...
173
174
175
176
177
178
   * special treatment of command port - on newer macbooks, it seems necessary
   * to resend the command byte before polling the status again. Callers must
   * hold applesmc_lock.
   */
  static int send_command(u8 cmd)
  {
8c9398d1e   Henrik Rydberg   hwmon: applesmc: ...
179
180
  	int us;
  	for (us = APPLESMC_MIN_WAIT; us < APPLESMC_MAX_WAIT; us <<= 1) {
84d2d7f2e   Henrik Rydberg   hwmon: applesmc: ...
181
  		outb(cmd, APPLESMC_CMD_PORT);
8c9398d1e   Henrik Rydberg   hwmon: applesmc: ...
182
  		udelay(us);
84d2d7f2e   Henrik Rydberg   hwmon: applesmc: ...
183
184
  		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 0x0c)
  			return 0;
84d2d7f2e   Henrik Rydberg   hwmon: applesmc: ...
185
  	}
84d2d7f2e   Henrik Rydberg   hwmon: applesmc: ...
186
187
  	return -EIO;
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
188
  static int send_argument(const char *key)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
189
190
  {
  	int i;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
191
192
193
194
195
  	for (i = 0; i < 4; i++) {
  		outb(key[i], APPLESMC_DATA_PORT);
  		if (__wait_status(0x04))
  			return -EIO;
  	}
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
196
197
198
199
200
201
202
203
204
205
206
207
  	return 0;
  }
  
  static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
  {
  	int i;
  
  	if (send_command(cmd) || send_argument(key)) {
  		pr_warn("%s: read arg fail
  ", key);
  		return -EIO;
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
208
209
  
  	outb(len, APPLESMC_DATA_PORT);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
210
211
  
  	for (i = 0; i < len; i++) {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
212
213
214
  		if (__wait_status(0x05)) {
  			pr_warn("%s: read data fail
  ", key);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
215
  			return -EIO;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
216
  		}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
217
  		buffer[i] = inb(APPLESMC_DATA_PORT);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
218
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
219
220
221
  
  	return 0;
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
222
  static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
223
224
  {
  	int i;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
225
226
227
  	if (send_command(cmd) || send_argument(key)) {
  		pr_warn("%s: write arg fail
  ", key);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
228
  		return -EIO;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
229
230
231
232
233
  	}
  
  	outb(len, APPLESMC_DATA_PORT);
  
  	for (i = 0; i < len; i++) {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
234
235
236
  		if (__wait_status(0x04)) {
  			pr_warn("%s: write data fail
  ", key);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
237
  			return -EIO;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
238
  		}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
239
240
241
242
243
  		outb(buffer[i], APPLESMC_DATA_PORT);
  	}
  
  	return 0;
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
244
245
246
247
248
249
250
251
252
253
254
255
  static int read_register_count(unsigned int *count)
  {
  	__be32 be;
  	int ret;
  
  	ret = read_smc(APPLESMC_READ_CMD, KEY_COUNT_KEY, (u8 *)&be, 4);
  	if (ret)
  		return ret;
  
  	*count = be32_to_cpu(be);
  	return 0;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
256
  /*
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
257
258
259
260
   * Serialized I/O
   *
   * Returns zero on success or a negative error on failure.
   * All functions below are concurrency safe - callers should NOT hold lock.
6f2fad748   Nicolas Boichat   Apple SMC driver ...
261
   */
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
262
263
264
  
  static int applesmc_read_entry(const struct applesmc_entry *entry,
  			       u8 *buf, u8 len)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
265
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
266
  	int ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
267

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
268
269
270
271
272
  	if (entry->len != len)
  		return -EINVAL;
  	mutex_lock(&smcreg.mutex);
  	ret = read_smc(APPLESMC_READ_CMD, entry->key, buf, len);
  	mutex_unlock(&smcreg.mutex);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
273

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
  	return ret;
  }
  
  static int applesmc_write_entry(const struct applesmc_entry *entry,
  				const u8 *buf, u8 len)
  {
  	int ret;
  
  	if (entry->len != len)
  		return -EINVAL;
  	mutex_lock(&smcreg.mutex);
  	ret = write_smc(APPLESMC_WRITE_CMD, entry->key, buf, len);
  	mutex_unlock(&smcreg.mutex);
  	return ret;
  }
  
  static const struct applesmc_entry *applesmc_get_entry_by_index(int index)
  {
  	struct applesmc_entry *cache = &smcreg.cache[index];
  	u8 key[4], info[6];
  	__be32 be;
  	int ret = 0;
  
  	if (cache->valid)
  		return cache;
  
  	mutex_lock(&smcreg.mutex);
  
  	if (cache->valid)
  		goto out;
  	be = cpu_to_be32(index);
  	ret = read_smc(APPLESMC_GET_KEY_BY_INDEX_CMD, (u8 *)&be, key, 4);
  	if (ret)
  		goto out;
  	ret = read_smc(APPLESMC_GET_KEY_TYPE_CMD, key, info, 6);
  	if (ret)
  		goto out;
  
  	memcpy(cache->key, key, 4);
  	cache->len = info[0];
  	memcpy(cache->type, &info[1], 4);
  	cache->flags = info[5];
  	cache->valid = 1;
  
  out:
  	mutex_unlock(&smcreg.mutex);
  	if (ret)
  		return ERR_PTR(ret);
  	return cache;
  }
  
  static int applesmc_get_lower_bound(unsigned int *lo, const char *key)
  {
  	int begin = 0, end = smcreg.key_count;
  	const struct applesmc_entry *entry;
  
  	while (begin != end) {
  		int middle = begin + (end - begin) / 2;
  		entry = applesmc_get_entry_by_index(middle);
  		if (IS_ERR(entry))
  			return PTR_ERR(entry);
  		if (strcmp(entry->key, key) < 0)
  			begin = middle + 1;
  		else
  			end = middle;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
339
  	}
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
340
341
342
  	*lo = begin;
  	return 0;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
343

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
  static int applesmc_get_upper_bound(unsigned int *hi, const char *key)
  {
  	int begin = 0, end = smcreg.key_count;
  	const struct applesmc_entry *entry;
  
  	while (begin != end) {
  		int middle = begin + (end - begin) / 2;
  		entry = applesmc_get_entry_by_index(middle);
  		if (IS_ERR(entry))
  			return PTR_ERR(entry);
  		if (strcmp(key, entry->key) < 0)
  			end = middle;
  		else
  			begin = middle + 1;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
358
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
359

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
360
  	*hi = begin;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
361
362
  	return 0;
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
363
  static const struct applesmc_entry *applesmc_get_entry_by_key(const char *key)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
364
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
365
366
  	int begin, end;
  	int ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
367

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
368
369
370
371
372
373
374
375
  	ret = applesmc_get_lower_bound(&begin, key);
  	if (ret)
  		return ERR_PTR(ret);
  	ret = applesmc_get_upper_bound(&end, key);
  	if (ret)
  		return ERR_PTR(ret);
  	if (end - begin != 1)
  		return ERR_PTR(-EINVAL);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
376

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
377
378
  	return applesmc_get_entry_by_index(begin);
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
379

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
380
381
382
  static int applesmc_read_key(const char *key, u8 *buffer, u8 len)
  {
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
383

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
384
385
386
  	entry = applesmc_get_entry_by_key(key);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
387

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
388
389
390
391
392
393
394
395
396
397
398
399
  	return applesmc_read_entry(entry, buffer, len);
  }
  
  static int applesmc_write_key(const char *key, const u8 *buffer, u8 len)
  {
  	const struct applesmc_entry *entry;
  
  	entry = applesmc_get_entry_by_key(key);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
  
  	return applesmc_write_entry(entry, buffer, len);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
400
  }
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
401
402
403
404
405
406
407
408
409
410
411
  static int applesmc_has_key(const char *key, bool *value)
  {
  	const struct applesmc_entry *entry;
  
  	entry = applesmc_get_entry_by_key(key);
  	if (IS_ERR(entry) && PTR_ERR(entry) != -EINVAL)
  		return PTR_ERR(entry);
  
  	*value = !IS_ERR(entry);
  	return 0;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
412
  /*
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
413
   * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z).
6f2fad748   Nicolas Boichat   Apple SMC driver ...
414
   */
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
415
  static int applesmc_read_motion_sensor(int index, s16 *value)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
  {
  	u8 buffer[2];
  	int ret;
  
  	switch (index) {
  	case SENSOR_X:
  		ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2);
  		break;
  	case SENSOR_Y:
  		ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2);
  		break;
  	case SENSOR_Z:
  		ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2);
  		break;
  	default:
  		ret = -EINVAL;
  	}
  
  	*value = ((s16)buffer[0] << 8) | buffer[1];
  
  	return ret;
  }
  
  /*
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
440
   * applesmc_device_init - initialize the accelerometer.  Can sleep.
6f2fad748   Nicolas Boichat   Apple SMC driver ...
441
   */
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
442
  static void applesmc_device_init(void)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
443
  {
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
444
  	int total;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
445
  	u8 buffer[2];
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
446
  	if (!smcreg.has_accelerometer)
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
447
  		return;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
448

6f2fad748   Nicolas Boichat   Apple SMC driver ...
449
  	for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
6f2fad748   Nicolas Boichat   Apple SMC driver ...
450
  		if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
451
  				(buffer[0] != 0x00 || buffer[1] != 0x00))
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
452
  			return;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
453
454
455
456
457
  		buffer[0] = 0xe0;
  		buffer[1] = 0x00;
  		applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
  		msleep(INIT_WAIT_MSECS);
  	}
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
458
459
  	pr_warn("failed to init the device
  ");
6f2fad748   Nicolas Boichat   Apple SMC driver ...
460
461
462
  }
  
  /*
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
463
464
465
466
467
   * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent.
   */
  static int applesmc_init_smcreg_try(void)
  {
  	struct applesmc_registers *s = &smcreg;
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
468
  	bool left_light_sensor, right_light_sensor;
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
469
  	u8 tmp[1];
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
470
471
472
473
474
475
476
477
478
479
480
481
482
  	int ret;
  
  	if (s->init_complete)
  		return 0;
  
  	ret = read_register_count(&s->key_count);
  	if (ret)
  		return ret;
  
  	if (!s->cache)
  		s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL);
  	if (!s->cache)
  		return -ENOMEM;
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
483
484
485
486
  	ret = applesmc_read_key(FANS_COUNT, tmp, 1);
  	if (ret)
  		return ret;
  	s->fan_count = tmp[0];
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
487
488
489
490
491
492
493
  	ret = applesmc_get_lower_bound(&s->temp_begin, "T");
  	if (ret)
  		return ret;
  	ret = applesmc_get_lower_bound(&s->temp_end, "U");
  	if (ret)
  		return ret;
  	s->temp_count = s->temp_end - s->temp_begin;
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
494
495
496
497
498
499
500
501
502
503
504
505
506
507
  	ret = applesmc_has_key(LIGHT_SENSOR_LEFT_KEY, &left_light_sensor);
  	if (ret)
  		return ret;
  	ret = applesmc_has_key(LIGHT_SENSOR_RIGHT_KEY, &right_light_sensor);
  	if (ret)
  		return ret;
  	ret = applesmc_has_key(MOTION_SENSOR_KEY, &s->has_accelerometer);
  	if (ret)
  		return ret;
  	ret = applesmc_has_key(BACKLIGHT_KEY, &s->has_key_backlight);
  	if (ret)
  		return ret;
  
  	s->num_light_sensors = left_light_sensor + right_light_sensor;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
508
  	s->init_complete = true;
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
509
510
511
  	pr_info("key=%d fan=%d temp=%d acc=%d lux=%d kbd=%d
  ",
  	       s->key_count, s->fan_count, s->temp_count,
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
512
513
514
  	       s->has_accelerometer,
  	       s->num_light_sensors,
  	       s->has_key_backlight);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
  
  	return 0;
  }
  
  /*
   * applesmc_init_smcreg - Initialize register cache.
   *
   * Retries until initialization is successful, or the operation times out.
   *
   */
  static int applesmc_init_smcreg(void)
  {
  	int ms, ret;
  
  	for (ms = 0; ms < INIT_TIMEOUT_MSECS; ms += INIT_WAIT_MSECS) {
  		ret = applesmc_init_smcreg_try();
  		if (!ret) {
  			if (ms)
  				pr_info("init_smcreg() took %d ms
  ", ms);
  			return 0;
  		}
  		msleep(INIT_WAIT_MSECS);
  	}
  
  	kfree(smcreg.cache);
  	smcreg.cache = NULL;
  
  	return ret;
  }
  
  static void applesmc_destroy_smcreg(void)
  {
  	kfree(smcreg.cache);
  	smcreg.cache = NULL;
  	smcreg.init_complete = false;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
552
553
554
  /* Device model stuff */
  static int applesmc_probe(struct platform_device *dev)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
555
556
557
558
559
  	int ret;
  
  	ret = applesmc_init_smcreg();
  	if (ret)
  		return ret;
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
560
  	applesmc_device_init();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
561

6f2fad748   Nicolas Boichat   Apple SMC driver ...
562
563
  	return 0;
  }
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
564
565
566
  /* Synchronize device with memorized backlight state */
  static int applesmc_pm_resume(struct device *dev)
  {
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
567
  	if (smcreg.has_key_backlight)
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
568
  		applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
569
570
571
572
573
  	return 0;
  }
  
  /* Reinitialize device on resume from hibernation */
  static int applesmc_pm_restore(struct device *dev)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
574
  {
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
575
  	applesmc_device_init();
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
576
  	return applesmc_pm_resume(dev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
577
  }
471452104   Alexey Dobriyan   const: constify r...
578
  static const struct dev_pm_ops applesmc_pm_ops = {
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
579
580
581
  	.resume = applesmc_pm_resume,
  	.restore = applesmc_pm_restore,
  };
6f2fad748   Nicolas Boichat   Apple SMC driver ...
582
583
  static struct platform_driver applesmc_driver = {
  	.probe = applesmc_probe,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
584
585
586
  	.driver	= {
  		.name = "applesmc",
  		.owner = THIS_MODULE,
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
587
  		.pm = &applesmc_pm_ops,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
588
589
590
591
592
593
594
595
596
597
598
599
600
  	},
  };
  
  /*
   * applesmc_calibrate - Set our "resting" values.  Callers must
   * hold applesmc_lock.
   */
  static void applesmc_calibrate(void)
  {
  	applesmc_read_motion_sensor(SENSOR_X, &rest_x);
  	applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
  	rest_x = -rest_x;
  }
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
601
  static void applesmc_idev_poll(struct input_polled_dev *dev)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
602
  {
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
603
  	struct input_dev *idev = dev->input;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
604
  	s16 x, y;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
605
  	if (applesmc_read_motion_sensor(SENSOR_X, &x))
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
606
  		return;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
607
  	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
608
  		return;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
609
610
  
  	x = -x;
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
611
612
613
  	input_report_abs(idev, ABS_X, x - rest_x);
  	input_report_abs(idev, ABS_Y, y - rest_y);
  	input_sync(idev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
614
615
616
  }
  
  /* Sysfs Files */
fa74419bb   Nicolas Boichat   applesmc - sensor...
617
618
619
620
621
622
  static ssize_t applesmc_name_show(struct device *dev,
  				   struct device_attribute *attr, char *buf)
  {
  	return snprintf(buf, PAGE_SIZE, "applesmc
  ");
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
623
624
625
626
627
  static ssize_t applesmc_position_show(struct device *dev,
  				   struct device_attribute *attr, char *buf)
  {
  	int ret;
  	s16 x, y, z;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
628
629
630
631
632
633
634
635
636
637
638
  	ret = applesmc_read_motion_sensor(SENSOR_X, &x);
  	if (ret)
  		goto out;
  	ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
  	if (ret)
  		goto out;
  	ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
  	if (ret)
  		goto out;
  
  out:
6f2fad748   Nicolas Boichat   Apple SMC driver ...
639
640
641
642
643
644
645
646
647
648
  	if (ret)
  		return ret;
  	else
  		return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)
  ", x, y, z);
  }
  
  static ssize_t applesmc_light_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
649
  	const struct applesmc_entry *entry;
8bd1a12a5   Henrik Rydberg   hwmon: applesmc: ...
650
  	static int data_length;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
651
652
  	int ret;
  	u8 left = 0, right = 0;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
653
  	u8 buffer[10];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
654

8bd1a12a5   Henrik Rydberg   hwmon: applesmc: ...
655
  	if (!data_length) {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
656
657
658
659
660
661
  		entry = applesmc_get_entry_by_key(LIGHT_SENSOR_LEFT_KEY);
  		if (IS_ERR(entry))
  			return PTR_ERR(entry);
  		if (entry->len > 10)
  			return -ENXIO;
  		data_length = entry->len;
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
662
663
  		pr_info("light sensor data length set to %d
  ", data_length);
8bd1a12a5   Henrik Rydberg   hwmon: applesmc: ...
664
665
666
  	}
  
  	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
c3d6362b8   Alex Murray   hwmon: applesmc: ...
667
668
669
670
671
  	/* newer macbooks report a single 10-bit bigendian value */
  	if (data_length == 10) {
  		left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2;
  		goto out;
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
672
673
674
  	left = buffer[2];
  	if (ret)
  		goto out;
8bd1a12a5   Henrik Rydberg   hwmon: applesmc: ...
675
  	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
676
677
678
  	right = buffer[2];
  
  out:
6f2fad748   Nicolas Boichat   Apple SMC driver ...
679
680
681
682
683
684
  	if (ret)
  		return ret;
  	else
  		return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)
  ", left, right);
  }
fa5575cff   Alex Murray   hwmon: (applesmc)...
685
686
687
688
  /* Displays sensor key as label */
  static ssize_t applesmc_show_sensor_label(struct device *dev,
  			struct device_attribute *devattr, char *sysfsbuf)
  {
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
689
690
  	int index = smcreg.temp_begin + to_index(devattr);
  	const struct applesmc_entry *entry;
fa5575cff   Alex Murray   hwmon: (applesmc)...
691

9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
692
693
694
695
696
697
  	entry = applesmc_get_entry_by_index(index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
  
  	return snprintf(sysfsbuf, PAGE_SIZE, "%s
  ", entry->key);
fa5575cff   Alex Murray   hwmon: (applesmc)...
698
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
699
700
701
702
  /* Displays degree Celsius * 1000 */
  static ssize_t applesmc_show_temperature(struct device *dev,
  			struct device_attribute *devattr, char *sysfsbuf)
  {
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
703
704
  	int index = smcreg.temp_begin + to_index(devattr);
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
705
706
707
  	int ret;
  	u8 buffer[2];
  	unsigned int temp;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
708

9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
709
710
711
  	entry = applesmc_get_entry_by_index(index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
dcdea2614   Henrik Rydberg   hwmon: (applesmc)...
712
713
  	if (entry->len > 2)
  		return -EINVAL;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
714

dcdea2614   Henrik Rydberg   hwmon: (applesmc)...
715
  	ret = applesmc_read_entry(entry, buffer, entry->len);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
716
717
  	if (ret)
  		return ret;
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
718

dcdea2614   Henrik Rydberg   hwmon: (applesmc)...
719
720
721
722
723
724
  	if (entry->len == 2) {
  		temp = buffer[0] * 1000;
  		temp += (buffer[1] >> 6) * 250;
  	} else {
  		temp = buffer[0] * 4000;
  	}
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
725
726
727
  
  	return snprintf(sysfsbuf, PAGE_SIZE, "%u
  ", temp);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
728
729
730
731
732
733
734
735
736
  }
  
  static ssize_t applesmc_show_fan_speed(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
  	int ret;
  	unsigned int speed = 0;
  	char newkey[5];
  	u8 buffer[2];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
737

3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
738
  	sprintf(newkey, fan_speed_fmt[to_option(attr)], to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
739

6f2fad748   Nicolas Boichat   Apple SMC driver ...
740
741
  	ret = applesmc_read_key(newkey, buffer, 2);
  	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
742
743
744
745
746
747
748
749
750
751
752
753
  	if (ret)
  		return ret;
  	else
  		return snprintf(sysfsbuf, PAGE_SIZE, "%u
  ", speed);
  }
  
  static ssize_t applesmc_store_fan_speed(struct device *dev,
  					struct device_attribute *attr,
  					const char *sysfsbuf, size_t count)
  {
  	int ret;
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
754
  	unsigned long speed;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
755
756
  	char newkey[5];
  	u8 buffer[2];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
757

179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
758
  	if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000)
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
759
  		return -EINVAL;		/* Bigger than a 14-bit value */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
760

3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
761
  	sprintf(newkey, fan_speed_fmt[to_option(attr)], to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
762

6f2fad748   Nicolas Boichat   Apple SMC driver ...
763
764
765
  	buffer[0] = (speed >> 6) & 0xff;
  	buffer[1] = (speed << 2) & 0xff;
  	ret = applesmc_write_key(newkey, buffer, 2);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
766
767
768
769
770
771
772
  	if (ret)
  		return ret;
  	else
  		return count;
  }
  
  static ssize_t applesmc_show_fan_manual(struct device *dev,
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
773
  			struct device_attribute *attr, char *sysfsbuf)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
774
775
776
777
  {
  	int ret;
  	u16 manual = 0;
  	u8 buffer[2];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
778

6f2fad748   Nicolas Boichat   Apple SMC driver ...
779
  	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
780
  	manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
781

6f2fad748   Nicolas Boichat   Apple SMC driver ...
782
783
784
785
786
787
788
789
  	if (ret)
  		return ret;
  	else
  		return snprintf(sysfsbuf, PAGE_SIZE, "%d
  ", manual);
  }
  
  static ssize_t applesmc_store_fan_manual(struct device *dev,
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
790
  					 struct device_attribute *attr,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
791
792
793
794
  					 const char *sysfsbuf, size_t count)
  {
  	int ret;
  	u8 buffer[2];
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
795
  	unsigned long input;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
796
  	u16 val;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
797

179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
798
  	if (kstrtoul(sysfsbuf, 10, &input) < 0)
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
799
  		return -EINVAL;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
800

6f2fad748   Nicolas Boichat   Apple SMC driver ...
801
802
803
804
805
806
  	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
  	val = (buffer[0] << 8 | buffer[1]);
  	if (ret)
  		goto out;
  
  	if (input)
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
807
  		val = val | (0x01 << to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
808
  	else
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
809
  		val = val & ~(0x01 << to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
810
811
812
813
814
815
816
  
  	buffer[0] = (val >> 8) & 0xFF;
  	buffer[1] = val & 0xFF;
  
  	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
  
  out:
6f2fad748   Nicolas Boichat   Apple SMC driver ...
817
818
819
820
821
822
823
824
825
826
827
828
  	if (ret)
  		return ret;
  	else
  		return count;
  }
  
  static ssize_t applesmc_show_fan_position(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
  	int ret;
  	char newkey[5];
  	u8 buffer[17];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
829

3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
830
  	sprintf(newkey, FAN_ID_FMT, to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
831

6f2fad748   Nicolas Boichat   Apple SMC driver ...
832
833
  	ret = applesmc_read_key(newkey, buffer, 16);
  	buffer[16] = 0;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
  	if (ret)
  		return ret;
  	else
  		return snprintf(sysfsbuf, PAGE_SIZE, "%s
  ", buffer+4);
  }
  
  static ssize_t applesmc_calibrate_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
  	return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)
  ", rest_x, rest_y);
  }
  
  static ssize_t applesmc_calibrate_store(struct device *dev,
  	struct device_attribute *attr, const char *sysfsbuf, size_t count)
  {
6f2fad748   Nicolas Boichat   Apple SMC driver ...
851
  	applesmc_calibrate();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
852
853
854
  
  	return count;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
855
856
  static void applesmc_backlight_set(struct work_struct *work)
  {
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
857
  	applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
858
859
860
861
862
863
864
  }
  static DECLARE_WORK(backlight_work, &applesmc_backlight_set);
  
  static void applesmc_brightness_set(struct led_classdev *led_cdev,
  						enum led_brightness value)
  {
  	int ret;
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
865
  	backlight_state[0] = value;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
866
867
868
869
870
871
872
873
874
875
876
877
878
  	ret = queue_work(applesmc_led_wq, &backlight_work);
  
  	if (debug && (!ret))
  		printk(KERN_DEBUG "applesmc: work was already on the queue.
  ");
  }
  
  static ssize_t applesmc_key_count_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
  	int ret;
  	u8 buffer[4];
  	u32 count;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
879
880
881
  	ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
  	count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
  						((u32)buffer[2]<<8) + buffer[3];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
882
883
884
885
886
887
888
889
890
891
  	if (ret)
  		return ret;
  	else
  		return snprintf(sysfsbuf, PAGE_SIZE, "%d
  ", count);
  }
  
  static ssize_t applesmc_key_at_index_read_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
892
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
893
  	int ret;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
894
895
896
897
898
  	entry = applesmc_get_entry_by_index(key_at_index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
  	ret = applesmc_read_entry(entry, sysfsbuf, entry->len);
  	if (ret)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
899
  		return ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
900

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
901
  	return entry->len;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
902
903
904
905
906
  }
  
  static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
907
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
908

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
909
910
911
  	entry = applesmc_get_entry_by_index(key_at_index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
912

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
913
914
  	return snprintf(sysfsbuf, PAGE_SIZE, "%d
  ", entry->len);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
915
916
917
918
919
  }
  
  static ssize_t applesmc_key_at_index_type_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
920
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
921

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
922
923
924
  	entry = applesmc_get_entry_by_index(key_at_index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
925

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
926
927
  	return snprintf(sysfsbuf, PAGE_SIZE, "%s
  ", entry->type);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
928
929
930
931
932
  }
  
  static ssize_t applesmc_key_at_index_name_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
933
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
934

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
935
936
937
  	entry = applesmc_get_entry_by_index(key_at_index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
938

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
939
940
  	return snprintf(sysfsbuf, PAGE_SIZE, "%s
  ", entry->key);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
941
942
943
944
945
946
947
948
949
950
951
952
  }
  
  static ssize_t applesmc_key_at_index_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
  	return snprintf(sysfsbuf, PAGE_SIZE, "%d
  ", key_at_index);
  }
  
  static ssize_t applesmc_key_at_index_store(struct device *dev,
  	struct device_attribute *attr, const char *sysfsbuf, size_t count)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
953
  	unsigned long newkey;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
954

179c4fdb5   Frans Meulenbroeks   hwmon: replaced s...
955
  	if (kstrtoul(sysfsbuf, 10, &newkey) < 0
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
956
957
  	    || newkey >= smcreg.key_count)
  		return -EINVAL;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
958

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
959
  	key_at_index = newkey;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
960
961
962
963
  	return count;
  }
  
  static struct led_classdev applesmc_backlight = {
6c152beef   Richard Purdie   leds: Standardise...
964
  	.name			= "smc::kbd_backlight",
6f2fad748   Nicolas Boichat   Apple SMC driver ...
965
966
967
  	.default_trigger	= "nand-disk",
  	.brightness_set		= applesmc_brightness_set,
  };
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
968
969
970
971
972
973
974
975
976
  static struct applesmc_node_group info_group[] = {
  	{ "name", applesmc_name_show },
  	{ "key_count", applesmc_key_count_show },
  	{ "key_at_index", applesmc_key_at_index_show, applesmc_key_at_index_store },
  	{ "key_at_index_name", applesmc_key_at_index_name_show },
  	{ "key_at_index_type", applesmc_key_at_index_type_show },
  	{ "key_at_index_data_length", applesmc_key_at_index_data_length_show },
  	{ "key_at_index_data", applesmc_key_at_index_read_show },
  	{ }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
977
  };
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
978
979
980
981
  static struct applesmc_node_group accelerometer_group[] = {
  	{ "position", applesmc_position_show },
  	{ "calibrate", applesmc_calibrate_show, applesmc_calibrate_store },
  	{ }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
982
  };
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
983
984
985
986
  static struct applesmc_node_group light_sensor_group[] = {
  	{ "light", applesmc_light_show },
  	{ }
  };
6f2fad748   Nicolas Boichat   Apple SMC driver ...
987

3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
988
989
990
991
992
993
994
995
996
  static struct applesmc_node_group fan_group[] = {
  	{ "fan%d_label", applesmc_show_fan_position },
  	{ "fan%d_input", applesmc_show_fan_speed, NULL, 0 },
  	{ "fan%d_min", applesmc_show_fan_speed, applesmc_store_fan_speed, 1 },
  	{ "fan%d_max", applesmc_show_fan_speed, NULL, 2 },
  	{ "fan%d_safe", applesmc_show_fan_speed, NULL, 3 },
  	{ "fan%d_output", applesmc_show_fan_speed, applesmc_store_fan_speed, 4 },
  	{ "fan%d_manual", applesmc_show_fan_manual, applesmc_store_fan_manual },
  	{ }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
997
  };
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
998
999
1000
1001
  static struct applesmc_node_group temp_group[] = {
  	{ "temp%d_label", applesmc_show_sensor_label },
  	{ "temp%d_input", applesmc_show_temperature },
  	{ }
fa5575cff   Alex Murray   hwmon: (applesmc)...
1002
  };
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1003
1004
1005
  /* Module stuff */
  
  /*
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
   * applesmc_destroy_nodes - remove files and free associated memory
   */
  static void applesmc_destroy_nodes(struct applesmc_node_group *groups)
  {
  	struct applesmc_node_group *grp;
  	struct applesmc_dev_attr *node;
  
  	for (grp = groups; grp->nodes; grp++) {
  		for (node = grp->nodes; node->sda.dev_attr.attr.name; node++)
  			sysfs_remove_file(&pdev->dev.kobj,
  					  &node->sda.dev_attr.attr);
  		kfree(grp->nodes);
  		grp->nodes = NULL;
  	}
  }
  
  /*
   * applesmc_create_nodes - create a two-dimensional group of sysfs files
   */
  static int applesmc_create_nodes(struct applesmc_node_group *groups, int num)
  {
  	struct applesmc_node_group *grp;
  	struct applesmc_dev_attr *node;
  	struct attribute *attr;
  	int ret, i;
  
  	for (grp = groups; grp->format; grp++) {
  		grp->nodes = kcalloc(num + 1, sizeof(*node), GFP_KERNEL);
  		if (!grp->nodes) {
  			ret = -ENOMEM;
  			goto out;
  		}
  		for (i = 0; i < num; i++) {
  			node = &grp->nodes[i];
  			sprintf(node->name, grp->format, i + 1);
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1041
  			node->sda.index = (grp->option << 16) | (i & 0xffff);
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1042
1043
1044
  			node->sda.dev_attr.show = grp->show;
  			node->sda.dev_attr.store = grp->store;
  			attr = &node->sda.dev_attr.attr;
9d1f8a40d   Henrik Rydberg   hwmon: (applesmc)...
1045
  			sysfs_attr_init(attr);
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
  			attr->name = node->name;
  			attr->mode = S_IRUGO | (grp->store ? S_IWUSR : 0);
  			ret = sysfs_create_file(&pdev->dev.kobj, attr);
  			if (ret) {
  				attr->name = NULL;
  				goto out;
  			}
  		}
  	}
  
  	return 0;
  out:
  	applesmc_destroy_nodes(groups);
  	return ret;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1061
1062
1063
  /* Create accelerometer ressources */
  static int applesmc_create_accelerometer(void)
  {
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1064
  	struct input_dev *idev;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1065
  	int ret;
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1066
1067
1068
1069
  	if (!smcreg.has_accelerometer)
  		return 0;
  
  	ret = applesmc_create_nodes(accelerometer_group, 1);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1070
1071
  	if (ret)
  		goto out;
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1072
  	applesmc_idev = input_allocate_polled_device();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1073
1074
1075
1076
  	if (!applesmc_idev) {
  		ret = -ENOMEM;
  		goto out_sysfs;
  	}
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1077
1078
  	applesmc_idev->poll = applesmc_idev_poll;
  	applesmc_idev->poll_interval = APPLESMC_POLL_INTERVAL;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1079
1080
  	/* initial calibrate for the input device */
  	applesmc_calibrate();
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1081
1082
1083
1084
1085
  	/* initialize the input device */
  	idev = applesmc_idev->input;
  	idev->name = "applesmc";
  	idev->id.bustype = BUS_HOST;
  	idev->dev.parent = &pdev->dev;
7b19ada2e   Jiri Slaby   get rid of input ...
1086
  	idev->evbit[0] = BIT_MASK(EV_ABS);
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1087
  	input_set_abs_params(idev, ABS_X,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1088
  			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1089
  	input_set_abs_params(idev, ABS_Y,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1090
  			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1091
  	ret = input_register_polled_device(applesmc_idev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1092
1093
  	if (ret)
  		goto out_idev;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1094
1095
1096
  	return 0;
  
  out_idev:
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1097
  	input_free_polled_device(applesmc_idev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1098
1099
  
  out_sysfs:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1100
  	applesmc_destroy_nodes(accelerometer_group);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1101
1102
  
  out:
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
1103
1104
  	pr_warn("driver init failed (ret=%d)!
  ", ret);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1105
1106
1107
1108
1109
1110
  	return ret;
  }
  
  /* Release all ressources used by the accelerometer */
  static void applesmc_release_accelerometer(void)
  {
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1111
1112
  	if (!smcreg.has_accelerometer)
  		return;
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1113
1114
  	input_unregister_polled_device(applesmc_idev);
  	input_free_polled_device(applesmc_idev);
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1115
  	applesmc_destroy_nodes(accelerometer_group);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1116
  }
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
  
  static int applesmc_create_light_sensor(void)
  {
  	if (!smcreg.num_light_sensors)
  		return 0;
  	return applesmc_create_nodes(light_sensor_group, 1);
  }
  
  static void applesmc_release_light_sensor(void)
  {
  	if (!smcreg.num_light_sensors)
  		return;
  	applesmc_destroy_nodes(light_sensor_group);
  }
  
  static int applesmc_create_key_backlight(void)
  {
  	if (!smcreg.has_key_backlight)
  		return 0;
  	applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
  	if (!applesmc_led_wq)
  		return -ENOMEM;
  	return led_classdev_register(&pdev->dev, &applesmc_backlight);
  }
  
  static void applesmc_release_key_backlight(void)
  {
  	if (!smcreg.has_key_backlight)
  		return;
  	led_classdev_unregister(&applesmc_backlight);
  	destroy_workqueue(applesmc_led_wq);
  }
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1149
1150
1151
1152
  static int applesmc_dmi_match(const struct dmi_system_id *id)
  {
  	return 1;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1153
1154
1155
1156
  
  /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
   * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
  static __initdata struct dmi_system_id applesmc_whitelist[] = {
f5274c972   Henrik Rydberg   hwmon: applesmc: ...
1157
1158
1159
  	{ applesmc_dmi_match, "Apple MacBook Air", {
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1160
  	},
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1161
  	{ applesmc_dmi_match, "Apple MacBook Pro", {
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
1162
1163
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1164
  	},
cd19ba139   Riki Oktarianto   hwmon: (applesmc)...
1165
  	{ applesmc_dmi_match, "Apple MacBook", {
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
1166
1167
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBook") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1168
  	},
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1169
  	{ applesmc_dmi_match, "Apple Macmini", {
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
1170
1171
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "Macmini") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1172
  	},
45a3a36b6   Henrik Rydberg   drivers/hwmon/app...
1173
1174
1175
  	{ applesmc_dmi_match, "Apple MacPro", {
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1176
  	},
9f86f28df   Roberto De Ioris   applesmc: support...
1177
  	{ applesmc_dmi_match, "Apple iMac", {
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
1178
1179
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "iMac") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1180
  	},
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1181
1182
1183
1184
1185
1186
  	{ .ident = NULL }
  };
  
  static int __init applesmc_init(void)
  {
  	int ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1187

6f2fad748   Nicolas Boichat   Apple SMC driver ...
1188
  	if (!dmi_check_system(applesmc_whitelist)) {
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
1189
1190
  		pr_warn("supported laptop not found!
  ");
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
  		ret = -ENODEV;
  		goto out;
  	}
  
  	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
  								"applesmc")) {
  		ret = -ENXIO;
  		goto out;
  	}
  
  	ret = platform_driver_register(&applesmc_driver);
  	if (ret)
  		goto out_region;
ddfbf2afd   Jean Delvare   applesmc: Use the...
1204
1205
  	pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
  					       NULL, 0);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1206
1207
1208
1209
  	if (IS_ERR(pdev)) {
  		ret = PTR_ERR(pdev);
  		goto out_driver;
  	}
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1210
1211
  	/* create register cache */
  	ret = applesmc_init_smcreg();
6996abf09   Nicolas Boichat   hwmon/applesmc: H...
1212
1213
  	if (ret)
  		goto out_device;
fa74419bb   Nicolas Boichat   applesmc - sensor...
1214

0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1215
  	ret = applesmc_create_nodes(info_group, 1);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1216
1217
  	if (ret)
  		goto out_smcreg;
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1218
1219
1220
  	ret = applesmc_create_nodes(fan_group, smcreg.fan_count);
  	if (ret)
  		goto out_info;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1221

9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1222
1223
1224
  	ret = applesmc_create_nodes(temp_group, smcreg.temp_count);
  	if (ret)
  		goto out_fans;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1225

0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1226
1227
1228
  	ret = applesmc_create_accelerometer();
  	if (ret)
  		goto out_temperature;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1229

0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1230
1231
1232
  	ret = applesmc_create_light_sensor();
  	if (ret)
  		goto out_accelerometer;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1233

0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1234
1235
1236
  	ret = applesmc_create_key_backlight();
  	if (ret)
  		goto out_light_sysfs;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1237

1beeffe43   Tony Jones   hwmon: Convert fr...
1238
1239
1240
  	hwmon_dev = hwmon_device_register(&pdev->dev);
  	if (IS_ERR(hwmon_dev)) {
  		ret = PTR_ERR(hwmon_dev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1241
1242
  		goto out_light_ledclass;
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1243
1244
1245
  	return 0;
  
  out_light_ledclass:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1246
  	applesmc_release_key_backlight();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1247
  out_light_sysfs:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1248
  	applesmc_release_light_sensor();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1249
  out_accelerometer:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1250
  	applesmc_release_accelerometer();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1251
  out_temperature:
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1252
  	applesmc_destroy_nodes(temp_group);
0559a5388   Henrik Rydberg   hwmon: (applesmc)...
1253
  out_fans:
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1254
1255
  	applesmc_destroy_nodes(fan_group);
  out_info:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1256
  	applesmc_destroy_nodes(info_group);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1257
1258
  out_smcreg:
  	applesmc_destroy_smcreg();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1259
1260
1261
1262
1263
1264
1265
  out_device:
  	platform_device_unregister(pdev);
  out_driver:
  	platform_driver_unregister(&applesmc_driver);
  out_region:
  	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
  out:
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
1266
1267
  	pr_warn("driver init failed (ret=%d)!
  ", ret);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1268
1269
1270
1271
1272
  	return ret;
  }
  
  static void __exit applesmc_exit(void)
  {
1beeffe43   Tony Jones   hwmon: Convert fr...
1273
  	hwmon_device_unregister(hwmon_dev);
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1274
1275
1276
  	applesmc_release_key_backlight();
  	applesmc_release_light_sensor();
  	applesmc_release_accelerometer();
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1277
  	applesmc_destroy_nodes(temp_group);
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1278
  	applesmc_destroy_nodes(fan_group);
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1279
  	applesmc_destroy_nodes(info_group);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1280
  	applesmc_destroy_smcreg();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1281
1282
1283
  	platform_device_unregister(pdev);
  	platform_driver_unregister(&applesmc_driver);
  	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1284
1285
1286
1287
1288
1289
1290
1291
  }
  
  module_init(applesmc_init);
  module_exit(applesmc_exit);
  
  MODULE_AUTHOR("Nicolas Boichat");
  MODULE_DESCRIPTION("Apple SMC");
  MODULE_LICENSE("GPL v2");
dc924efb5   Henrik Rydberg   hwmon: applesmc: ...
1292
  MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);