Blame view

drivers/hwmon/applesmc.c 33 KB
21eb0be9a   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
6f2fad748   Nicolas Boichat   Apple SMC driver ...
2
3
4
5
6
7
  /*
   * 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)...
8
   * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
9
10
11
   *
   * Based on hdaps.c driver:
   * Copyright (C) 2005 Robert Love <rml@novell.com>
aa8521ec2   Jesper Juhl   hwmon: (applesmc)...
12
   * Copyright (C) 2005 Jesper Juhl <jj@chaosbits.net>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
13
14
15
   *
   * Fan control based on smcFanControl:
   * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
16
   */
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
17
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6f2fad748   Nicolas Boichat   Apple SMC driver ...
18
19
  #include <linux/delay.h>
  #include <linux/platform_device.h>
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
20
  #include <linux/input.h>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
21
  #include <linux/kernel.h>
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
22
  #include <linux/slab.h>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
23
24
25
26
27
  #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...
28
  #include <linux/io.h>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
29
30
31
  #include <linux/leds.h>
  #include <linux/hwmon.h>
  #include <linux/workqueue.h>
fa845740c   Jean Delvare   hwmon: Add missin...
32
  #include <linux/err.h>
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
33
  #include <linux/bits.h>
6f2fad748   Nicolas Boichat   Apple SMC driver ...
34
35
36
37
38
39
40
41
42
  
  /* 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
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
43
44
45
46
47
48
49
  /* Apple SMC status bits */
  #define SMC_STATUS_AWAITING_DATA  BIT(0) /* SMC has data waiting to be read */
  #define SMC_STATUS_IB_CLOSED      BIT(1) /* Will ignore any input */
  #define SMC_STATUS_BUSY           BIT(2) /* Command in progress */
  
  /* Initial wait is 8us */
  #define APPLESMC_MIN_WAIT      0x0008
8c9398d1e   Henrik Rydberg   hwmon: applesmc: ...
50

6f2fad748   Nicolas Boichat   Apple SMC driver ...
51
52
53
54
55
56
  #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: ...
57
58
  #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 -...
59
  #define BACKLIGHT_KEY		"LKSB" /* w-o {lkb (2 bytes) */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
60

d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
61
  #define CLAMSHELL_KEY		"MSLD" /* r-o ui8 (unused) */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
62
63
64
65
66
67
68
69
  
  #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)...
70
  #define FAN_ID_FMT		"F%dID" /* r-o char[16] */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
71

b6e5122f0   Henrik Rydberg   hwmon: (applesmc)...
72
  #define TEMP_SENSOR_TYPE	"sp78"
6f2fad748   Nicolas Boichat   Apple SMC driver ...
73
  /* List of keys used to read/write fan speeds */
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
74
75
76
77
78
79
  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 ...
80
81
82
83
  };
  
  #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 -...
84
  #define APPLESMC_POLL_INTERVAL	50	/* msecs */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
85
86
  #define APPLESMC_INPUT_FUZZ	4	/* input event threshold */
  #define APPLESMC_INPUT_FLAT	4
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
87
88
  #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)...
89

9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
90
91
92
93
94
95
96
97
98
99
100
  /* 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)...
101
  	int option;				/* function argument */
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
102
103
  	struct applesmc_dev_attr *nodes;	/* dynamic node array */
  };
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
104
105
106
107
108
109
110
111
112
113
114
115
116
  /* 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)...
117
  	unsigned int fan_count;		/* number of fans */
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
118
119
120
  	unsigned int temp_count;	/* number of temperature registers */
  	unsigned int temp_begin;	/* temperature lower index bound */
  	unsigned int temp_end;		/* temperature upper index bound */
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
121
  	unsigned int index_count;	/* size of temperature index array */
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
122
123
124
  	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)...
125
126
  	bool init_complete;		/* true when fully initialized */
  	struct applesmc_entry *cache;	/* cached key entries */
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
127
  	const char **index;		/* temperature key index */
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
128
129
130
  } smcreg = {
  	.mutex = __MUTEX_INITIALIZER(smcreg.mutex),
  };
6f2fad748   Nicolas Boichat   Apple SMC driver ...
131
132
133
134
  static const int debug;
  static struct platform_device *pdev;
  static s16 rest_x;
  static s16 rest_y;
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
135
  static u8 backlight_state[2];
1beeffe43   Tony Jones   hwmon: Convert fr...
136
  static struct device *hwmon_dev;
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
137
  static struct input_dev *applesmc_idev;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
138

6f2fad748   Nicolas Boichat   Apple SMC driver ...
139
140
141
142
143
144
145
146
147
  /*
   * 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;
  
  /*
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
148
149
150
151
152
   * Wait for specific status bits with a mask on the SMC.
   * Used before all transactions.
   * This does 10 fast loops of 8us then exponentially backs off for a
   * minimum total wait of 262ms. Depending on usleep_range this could
   * run out past 500ms.
6f2fad748   Nicolas Boichat   Apple SMC driver ...
153
   */
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
154
155
  
  static int wait_status(u8 val, u8 mask)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
156
  {
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
157
  	u8 status;
8c9398d1e   Henrik Rydberg   hwmon: applesmc: ...
158
  	int us;
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
159
  	int i;
fff2d0f70   Arnd Bergmann   hwmon: (applesmc)...
160

4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
161
162
  	us = APPLESMC_MIN_WAIT;
  	for (i = 0; i < 24 ; i++) {
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
163
  		status = inb(APPLESMC_CMD_PORT);
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
164
  		if ((status & mask) == val)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
165
  			return 0;
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
166
167
168
  		usleep_range(us, us * 2);
  		if (i > 9)
  			us <<= 1;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
169
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
170
171
  	return -EIO;
  }
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
172
  /* send_byte - Write to SMC data port. Callers must hold applesmc_lock. */
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
173
  static int send_byte(u8 cmd, u16 port)
84d2d7f2e   Henrik Rydberg   hwmon: applesmc: ...
174
  {
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  	int status;
  
  	status = wait_status(0, SMC_STATUS_IB_CLOSED);
  	if (status)
  		return status;
  	/*
  	 * This needs to be a separate read looking for bit 0x04
  	 * after bit 0x02 falls. If consolidated with the wait above
  	 * this extra read may not happen if status returns both
  	 * simultaneously and this would appear to be required.
  	 */
  	status = wait_status(SMC_STATUS_BUSY, SMC_STATUS_BUSY);
  	if (status)
  		return status;
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
189
190
  
  	outb(cmd, port);
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
191
  	return 0;
84d2d7f2e   Henrik Rydberg   hwmon: applesmc: ...
192
  }
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
193
  /* send_command - Write a command to the SMC. Callers must hold applesmc_lock. */
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
194
195
  static int send_command(u8 cmd)
  {
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
  	int ret;
  
  	ret = wait_status(0, SMC_STATUS_IB_CLOSED);
  	if (ret)
  		return ret;
  	outb(cmd, APPLESMC_CMD_PORT);
  	return 0;
  }
  
  /*
   * Based on logic from the Apple driver. This is issued before any interaction
   * If busy is stuck high, issue a read command to reset the SMC state machine.
   * If busy is stuck high after the command then the SMC is jammed.
   */
  
  static int smc_sane(void)
  {
  	int ret;
  
  	ret = wait_status(0, SMC_STATUS_BUSY);
  	if (!ret)
  		return ret;
  	ret = send_command(APPLESMC_READ_CMD);
  	if (ret)
  		return ret;
  	return wait_status(0, SMC_STATUS_BUSY);
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
222
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
223
  static int send_argument(const char *key)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
224
225
  {
  	int i;
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
226
227
  	for (i = 0; i < 4; i++)
  		if (send_byte(key[i], APPLESMC_DATA_PORT))
6f2fad748   Nicolas Boichat   Apple SMC driver ...
228
  			return -EIO;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
229
230
231
232
233
  	return 0;
  }
  
  static int read_smc(u8 cmd, const char *key, u8 *buffer, u8 len)
  {
25f2bd7f5   Henrik Rydberg   hwmon: (applesmc)...
234
  	u8 status, data = 0;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
235
  	int i;
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
236
237
238
239
240
  	int ret;
  
  	ret = smc_sane();
  	if (ret)
  		return ret;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
241
242
  
  	if (send_command(cmd) || send_argument(key)) {
ac852edb4   Henrik Rydberg   hwmon: (applesmc)...
243
244
  		pr_warn("%.4s: read arg fail
  ", key);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
245
246
  		return -EIO;
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
247

25f2bd7f5   Henrik Rydberg   hwmon: (applesmc)...
248
  	/* This has no effect on newer (2012) SMCs */
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
249
250
251
252
253
  	if (send_byte(len, APPLESMC_DATA_PORT)) {
  		pr_warn("%.4s: read len fail
  ", key);
  		return -EIO;
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
254
255
  
  	for (i = 0; i < len; i++) {
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
256
257
  		if (wait_status(SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY,
  				SMC_STATUS_AWAITING_DATA | SMC_STATUS_BUSY)) {
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
258
259
  			pr_warn("%.4s: read data[%d] fail
  ", key, i);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
260
  			return -EIO;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
261
  		}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
262
  		buffer[i] = inb(APPLESMC_DATA_PORT);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
263
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
264

25f2bd7f5   Henrik Rydberg   hwmon: (applesmc)...
265
266
267
268
  	/* Read the data port until bit0 is cleared */
  	for (i = 0; i < 16; i++) {
  		udelay(APPLESMC_MIN_WAIT);
  		status = inb(APPLESMC_CMD_PORT);
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
269
  		if (!(status & SMC_STATUS_AWAITING_DATA))
25f2bd7f5   Henrik Rydberg   hwmon: (applesmc)...
270
271
272
273
274
275
  			break;
  		data = inb(APPLESMC_DATA_PORT);
  	}
  	if (i)
  		pr_warn("flushed %d bytes, last value is: %d
  ", i, data);
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
276
  	return wait_status(0, SMC_STATUS_BUSY);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
277
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
278
  static int write_smc(u8 cmd, const char *key, const u8 *buffer, u8 len)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
279
280
  {
  	int i;
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
281
282
283
284
285
  	int ret;
  
  	ret = smc_sane();
  	if (ret)
  		return ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
286

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
287
288
289
  	if (send_command(cmd) || send_argument(key)) {
  		pr_warn("%s: write arg fail
  ", key);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
290
  		return -EIO;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
291
  	}
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
292
293
294
295
296
  	if (send_byte(len, APPLESMC_DATA_PORT)) {
  		pr_warn("%.4s: write len fail
  ", key);
  		return -EIO;
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
297
298
  
  	for (i = 0; i < len; i++) {
829917cd7   Henrik Rydberg   hwmon: (applesmc)...
299
  		if (send_byte(buffer[i], APPLESMC_DATA_PORT)) {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
300
301
  			pr_warn("%s: write data fail
  ", key);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
302
  			return -EIO;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
303
  		}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
304
  	}
4d64bb4ba   Brad Campbell   hwmon: (applesmc)...
305
  	return wait_status(0, SMC_STATUS_BUSY);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
306
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
307
308
309
310
311
312
313
314
315
316
317
318
  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 ...
319
  /*
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
320
321
322
323
   * 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 ...
324
   */
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
325
326
327
  
  static int applesmc_read_entry(const struct applesmc_entry *entry,
  			       u8 *buf, u8 len)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
328
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
329
  	int ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
330

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
331
332
333
334
335
  	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 ...
336

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
  	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);
0fc86eca1   Henrik Rydberg   hwmon: (applesmc)...
396
397
  		if (IS_ERR(entry)) {
  			*lo = 0;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
398
  			return PTR_ERR(entry);
0fc86eca1   Henrik Rydberg   hwmon: (applesmc)...
399
  		}
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
400
401
402
403
  		if (strcmp(entry->key, key) < 0)
  			begin = middle + 1;
  		else
  			end = middle;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
404
  	}
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
405
406
407
  	*lo = begin;
  	return 0;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
408

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
409
410
411
412
413
414
415
416
  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);
0fc86eca1   Henrik Rydberg   hwmon: (applesmc)...
417
418
  		if (IS_ERR(entry)) {
  			*hi = smcreg.key_count;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
419
  			return PTR_ERR(entry);
0fc86eca1   Henrik Rydberg   hwmon: (applesmc)...
420
  		}
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
421
422
423
424
  		if (strcmp(key, entry->key) < 0)
  			end = middle;
  		else
  			begin = middle + 1;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
425
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
426

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
427
  	*hi = begin;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
428
429
  	return 0;
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
430
  static const struct applesmc_entry *applesmc_get_entry_by_key(const char *key)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
431
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
432
433
  	int begin, end;
  	int ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
434

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
435
436
437
438
439
440
441
442
  	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 ...
443

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
444
445
  	return applesmc_get_entry_by_index(begin);
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
446

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

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
451
452
453
  	entry = applesmc_get_entry_by_key(key);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
454

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
455
456
457
458
459
460
461
462
463
464
465
466
  	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 ...
467
  }
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
468
469
470
471
472
473
474
475
476
477
478
  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 ...
479
  /*
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
480
   * applesmc_read_s16 - Read 16-bit signed big endian register
6f2fad748   Nicolas Boichat   Apple SMC driver ...
481
   */
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
482
  static int applesmc_read_s16(const char *key, s16 *value)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
483
484
485
  {
  	u8 buffer[2];
  	int ret;
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
486
487
488
  	ret = applesmc_read_key(key, buffer, 2);
  	if (ret)
  		return ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
489
490
  
  	*value = ((s16)buffer[0] << 8) | buffer[1];
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
491
  	return 0;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
492
493
494
  }
  
  /*
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
495
   * applesmc_device_init - initialize the accelerometer.  Can sleep.
6f2fad748   Nicolas Boichat   Apple SMC driver ...
496
   */
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
497
  static void applesmc_device_init(void)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
498
  {
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
499
  	int total;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
500
  	u8 buffer[2];
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
501
  	if (!smcreg.has_accelerometer)
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
502
  		return;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
503

6f2fad748   Nicolas Boichat   Apple SMC driver ...
504
  	for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
6f2fad748   Nicolas Boichat   Apple SMC driver ...
505
  		if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) &&
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
506
  				(buffer[0] != 0x00 || buffer[1] != 0x00))
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
507
  			return;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
508
509
510
511
512
  		buffer[0] = 0xe0;
  		buffer[1] = 0x00;
  		applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2);
  		msleep(INIT_WAIT_MSECS);
  	}
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
513
514
  	pr_warn("failed to init the device
  ");
6f2fad748   Nicolas Boichat   Apple SMC driver ...
515
  }
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
  static int applesmc_init_index(struct applesmc_registers *s)
  {
  	const struct applesmc_entry *entry;
  	unsigned int i;
  
  	if (s->index)
  		return 0;
  
  	s->index = kcalloc(s->temp_count, sizeof(s->index[0]), GFP_KERNEL);
  	if (!s->index)
  		return -ENOMEM;
  
  	for (i = s->temp_begin; i < s->temp_end; i++) {
  		entry = applesmc_get_entry_by_index(i);
  		if (IS_ERR(entry))
  			continue;
  		if (strcmp(entry->type, TEMP_SENSOR_TYPE))
  			continue;
  		s->index[s->index_count++] = entry->key;
  	}
  
  	return 0;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
539
  /*
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
540
541
542
543
544
   * applesmc_init_smcreg_try - Try to initialize register cache. Idempotent.
   */
  static int applesmc_init_smcreg_try(void)
  {
  	struct applesmc_registers *s = &smcreg;
5e0a0ee4d   Shuah Khan   hwmon : (applesmc...
545
  	bool left_light_sensor = 0, right_light_sensor = 0;
5f4513864   Henrik Rydberg   hwmon: (applesmc)...
546
  	unsigned int count;
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
547
  	u8 tmp[1];
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
548
549
550
551
  	int ret;
  
  	if (s->init_complete)
  		return 0;
5f4513864   Henrik Rydberg   hwmon: (applesmc)...
552
  	ret = read_register_count(&count);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
553
554
  	if (ret)
  		return ret;
5f4513864   Henrik Rydberg   hwmon: (applesmc)...
555
556
557
558
559
560
561
562
  	if (s->cache && s->key_count != count) {
  		pr_warn("key count changed from %d to %d
  ",
  			s->key_count, count);
  		kfree(s->cache);
  		s->cache = NULL;
  	}
  	s->key_count = count;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
563
564
565
566
  	if (!s->cache)
  		s->cache = kcalloc(s->key_count, sizeof(*s->cache), GFP_KERNEL);
  	if (!s->cache)
  		return -ENOMEM;
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
567
568
569
570
  	ret = applesmc_read_key(FANS_COUNT, tmp, 1);
  	if (ret)
  		return ret;
  	s->fan_count = tmp[0];
1009ccdc6   Guenter Roeck   hwmon: (applesmc)...
571
572
  	if (s->fan_count > 10)
  		s->fan_count = 10;
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
573

9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
574
575
576
577
578
579
580
  	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;
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
581
582
583
  	ret = applesmc_init_index(s);
  	if (ret)
  		return ret;
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
584
585
586
587
588
589
590
591
592
593
594
595
596
597
  	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)...
598
  	s->init_complete = true;
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
599
600
601
  	pr_info("key=%d fan=%d temp=%d index=%d acc=%d lux=%d kbd=%d
  ",
  	       s->key_count, s->fan_count, s->temp_count, s->index_count,
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
602
603
604
  	       s->has_accelerometer,
  	       s->num_light_sensors,
  	       s->has_key_backlight);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
605
606
607
  
  	return 0;
  }
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
608
609
610
611
612
613
614
615
  static void applesmc_destroy_smcreg(void)
  {
  	kfree(smcreg.index);
  	smcreg.index = NULL;
  	kfree(smcreg.cache);
  	smcreg.cache = NULL;
  	smcreg.init_complete = false;
  }
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
  /*
   * 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);
  	}
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
636
  	applesmc_destroy_smcreg();
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
637
638
639
  
  	return ret;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
640
641
642
  /* Device model stuff */
  static int applesmc_probe(struct platform_device *dev)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
643
644
645
646
647
  	int ret;
  
  	ret = applesmc_init_smcreg();
  	if (ret)
  		return ret;
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
648
  	applesmc_device_init();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
649

6f2fad748   Nicolas Boichat   Apple SMC driver ...
650
651
  	return 0;
  }
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
652
653
654
  /* Synchronize device with memorized backlight state */
  static int applesmc_pm_resume(struct device *dev)
  {
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
655
  	if (smcreg.has_key_backlight)
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
656
  		applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
657
658
659
660
661
  	return 0;
  }
  
  /* Reinitialize device on resume from hibernation */
  static int applesmc_pm_restore(struct device *dev)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
662
  {
2344cd0c2   Henrik Rydberg   hwmon: (applesmc)...
663
  	applesmc_device_init();
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
664
  	return applesmc_pm_resume(dev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
665
  }
471452104   Alexey Dobriyan   const: constify r...
666
  static const struct dev_pm_ops applesmc_pm_ops = {
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
667
668
669
  	.resume = applesmc_pm_resume,
  	.restore = applesmc_pm_restore,
  };
6f2fad748   Nicolas Boichat   Apple SMC driver ...
670
671
  static struct platform_driver applesmc_driver = {
  	.probe = applesmc_probe,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
672
673
  	.driver	= {
  		.name = "applesmc",
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
674
  		.pm = &applesmc_pm_ops,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
675
676
677
678
679
680
681
682
683
  	},
  };
  
  /*
   * applesmc_calibrate - Set our "resting" values.  Callers must
   * hold applesmc_lock.
   */
  static void applesmc_calibrate(void)
  {
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
684
685
  	applesmc_read_s16(MOTION_SENSOR_X_KEY, &rest_x);
  	applesmc_read_s16(MOTION_SENSOR_Y_KEY, &rest_y);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
686
687
  	rest_x = -rest_x;
  }
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
688
  static void applesmc_idev_poll(struct input_dev *idev)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
689
690
  {
  	s16 x, y;
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
691
  	if (applesmc_read_s16(MOTION_SENSOR_X_KEY, &x))
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
692
  		return;
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
693
  	if (applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y))
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
694
  		return;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
695
696
  
  	x = -x;
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
697
698
699
  	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 ...
700
701
702
  }
  
  /* Sysfs Files */
fa74419bb   Nicolas Boichat   applesmc - sensor...
703
704
705
706
707
708
  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 ...
709
710
711
712
713
  static ssize_t applesmc_position_show(struct device *dev,
  				   struct device_attribute *attr, char *buf)
  {
  	int ret;
  	s16 x, y, z;
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
714
  	ret = applesmc_read_s16(MOTION_SENSOR_X_KEY, &x);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
715
716
  	if (ret)
  		goto out;
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
717
  	ret = applesmc_read_s16(MOTION_SENSOR_Y_KEY, &y);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
718
719
  	if (ret)
  		goto out;
edf48f3a7   Henrik Rydberg   hwmon: (applesmc)...
720
  	ret = applesmc_read_s16(MOTION_SENSOR_Z_KEY, &z);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
721
722
723
724
  	if (ret)
  		goto out;
  
  out:
6f2fad748   Nicolas Boichat   Apple SMC driver ...
725
726
727
728
729
730
731
732
733
734
  	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)...
735
  	const struct applesmc_entry *entry;
8bd1a12a5   Henrik Rydberg   hwmon: applesmc: ...
736
  	static int data_length;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
737
738
  	int ret;
  	u8 left = 0, right = 0;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
739
  	u8 buffer[10];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
740

8bd1a12a5   Henrik Rydberg   hwmon: applesmc: ...
741
  	if (!data_length) {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
742
743
744
745
746
747
  		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)...
748
749
  		pr_info("light sensor data length set to %d
  ", data_length);
8bd1a12a5   Henrik Rydberg   hwmon: applesmc: ...
750
751
752
  	}
  
  	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length);
cecf7560f   Tom Rix   hwmon: (applesmc)...
753
754
  	if (ret)
  		goto out;
c3d6362b8   Alex Murray   hwmon: applesmc: ...
755
756
757
758
759
  	/* 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 ...
760
  	left = buffer[2];
cecf7560f   Tom Rix   hwmon: (applesmc)...
761
762
  
  	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, data_length);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
763
764
  	if (ret)
  		goto out;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
765
766
767
  	right = buffer[2];
  
  out:
6f2fad748   Nicolas Boichat   Apple SMC driver ...
768
769
770
771
772
773
  	if (ret)
  		return ret;
  	else
  		return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)
  ", left, right);
  }
fa5575cff   Alex Murray   hwmon: (applesmc)...
774
775
776
777
  /* Displays sensor key as label */
  static ssize_t applesmc_show_sensor_label(struct device *dev,
  			struct device_attribute *devattr, char *sysfsbuf)
  {
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
778
  	const char *key = smcreg.index[to_index(devattr)];
fa5575cff   Alex Murray   hwmon: (applesmc)...
779

e30bca125   Henrik Rydberg   hwmon: (applesmc)...
780
781
  	return snprintf(sysfsbuf, PAGE_SIZE, "%s
  ", key);
fa5575cff   Alex Murray   hwmon: (applesmc)...
782
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
783
784
785
786
  /* Displays degree Celsius * 1000 */
  static ssize_t applesmc_show_temperature(struct device *dev,
  			struct device_attribute *devattr, char *sysfsbuf)
  {
e30bca125   Henrik Rydberg   hwmon: (applesmc)...
787
  	const char *key = smcreg.index[to_index(devattr)];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
788
  	int ret;
b6e5122f0   Henrik Rydberg   hwmon: (applesmc)...
789
790
  	s16 value;
  	int temp;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
791

e30bca125   Henrik Rydberg   hwmon: (applesmc)...
792
  	ret = applesmc_read_s16(key, &value);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
793
794
  	if (ret)
  		return ret;
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
795

b6e5122f0   Henrik Rydberg   hwmon: (applesmc)...
796
  	temp = 250 * (value >> 6);
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
797

b6e5122f0   Henrik Rydberg   hwmon: (applesmc)...
798
799
  	return snprintf(sysfsbuf, PAGE_SIZE, "%d
  ", temp);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
800
801
802
803
804
805
806
807
808
  }
  
  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 ...
809

1009ccdc6   Guenter Roeck   hwmon: (applesmc)...
810
811
  	scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
  		  to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
812

6f2fad748   Nicolas Boichat   Apple SMC driver ...
813
  	ret = applesmc_read_key(newkey, buffer, 2);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
814
815
  	if (ret)
  		return ret;
cecf7560f   Tom Rix   hwmon: (applesmc)...
816
817
818
819
  
  	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
  	return snprintf(sysfsbuf, PAGE_SIZE, "%u
  ", speed);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
820
821
822
823
824
825
826
  }
  
  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)...
827
  	unsigned long speed;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
828
829
  	char newkey[5];
  	u8 buffer[2];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
830

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

1009ccdc6   Guenter Roeck   hwmon: (applesmc)...
834
835
  	scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
  		  to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
836

6f2fad748   Nicolas Boichat   Apple SMC driver ...
837
838
839
  	buffer[0] = (speed >> 6) & 0xff;
  	buffer[1] = (speed << 2) & 0xff;
  	ret = applesmc_write_key(newkey, buffer, 2);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
840
841
842
843
844
845
846
  	if (ret)
  		return ret;
  	else
  		return count;
  }
  
  static ssize_t applesmc_show_fan_manual(struct device *dev,
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
847
  			struct device_attribute *attr, char *sysfsbuf)
6f2fad748   Nicolas Boichat   Apple SMC driver ...
848
849
850
851
  {
  	int ret;
  	u16 manual = 0;
  	u8 buffer[2];
6f2fad748   Nicolas Boichat   Apple SMC driver ...
852

6f2fad748   Nicolas Boichat   Apple SMC driver ...
853
  	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
854
855
  	if (ret)
  		return ret;
cecf7560f   Tom Rix   hwmon: (applesmc)...
856
857
858
859
  
  	manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
  	return snprintf(sysfsbuf, PAGE_SIZE, "%d
  ", manual);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
860
861
862
  }
  
  static ssize_t applesmc_store_fan_manual(struct device *dev,
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
863
  					 struct device_attribute *attr,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
864
865
866
867
  					 const char *sysfsbuf, size_t count)
  {
  	int ret;
  	u8 buffer[2];
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
868
  	unsigned long input;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
869
  	u16 val;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
870

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

6f2fad748   Nicolas Boichat   Apple SMC driver ...
874
  	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
875
876
  	if (ret)
  		goto out;
cecf7560f   Tom Rix   hwmon: (applesmc)...
877
  	val = (buffer[0] << 8 | buffer[1]);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
878
  	if (input)
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
879
  		val = val | (0x01 << to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
880
  	else
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
881
  		val = val & ~(0x01 << to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
882
883
884
885
886
887
888
  
  	buffer[0] = (val >> 8) & 0xFF;
  	buffer[1] = val & 0xFF;
  
  	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
  
  out:
6f2fad748   Nicolas Boichat   Apple SMC driver ...
889
890
891
892
893
894
895
896
897
898
899
900
  	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 ...
901

1009ccdc6   Guenter Roeck   hwmon: (applesmc)...
902
  	scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, to_index(attr));
6f2fad748   Nicolas Boichat   Apple SMC driver ...
903

6f2fad748   Nicolas Boichat   Apple SMC driver ...
904
905
  	ret = applesmc_read_key(newkey, buffer, 16);
  	buffer[16] = 0;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
  	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 ...
923
  	applesmc_calibrate();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
924
925
926
  
  	return count;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
927
928
  static void applesmc_backlight_set(struct work_struct *work)
  {
a976f150a   Henrik Rydberg   hwmon: applesmc: ...
929
  	applesmc_write_key(BACKLIGHT_KEY, backlight_state, 2);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
930
931
932
933
934
935
936
  }
  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: ...
937
  	backlight_state[0] = value;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
938
939
940
  	ret = queue_work(applesmc_led_wq, &backlight_work);
  
  	if (debug && (!ret))
692fe501d   Guenter Roeck   hwmon: checkpatch...
941
942
  		dev_dbg(led_cdev->dev, "work was already on the queue.
  ");
6f2fad748   Nicolas Boichat   Apple SMC driver ...
943
944
945
946
947
948
949
950
  }
  
  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 ...
951
  	ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
952
953
  	if (ret)
  		return ret;
cecf7560f   Tom Rix   hwmon: (applesmc)...
954
955
956
957
958
  
  	count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
  						((u32)buffer[2]<<8) + buffer[3];
  	return snprintf(sysfsbuf, PAGE_SIZE, "%d
  ", count);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
959
960
961
962
963
  }
  
  static ssize_t applesmc_key_at_index_read_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
964
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
965
  	int ret;
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
966
967
968
969
970
  	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 ...
971
  		return ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
972

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
973
  	return entry->len;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
974
975
976
977
978
  }
  
  static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
979
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
980

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
981
982
983
  	entry = applesmc_get_entry_by_index(key_at_index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
984

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
985
986
  	return snprintf(sysfsbuf, PAGE_SIZE, "%d
  ", entry->len);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
987
988
989
990
991
  }
  
  static ssize_t applesmc_key_at_index_type_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
992
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
993

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
994
995
996
  	entry = applesmc_get_entry_by_index(key_at_index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
997

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
998
999
  	return snprintf(sysfsbuf, PAGE_SIZE, "%s
  ", entry->type);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1000
1001
1002
1003
1004
  }
  
  static ssize_t applesmc_key_at_index_name_show(struct device *dev,
  				struct device_attribute *attr, char *sysfsbuf)
  {
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1005
  	const struct applesmc_entry *entry;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1006

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1007
1008
1009
  	entry = applesmc_get_entry_by_index(key_at_index);
  	if (IS_ERR(entry))
  		return PTR_ERR(entry);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1010

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1011
1012
  	return snprintf(sysfsbuf, PAGE_SIZE, "%s
  ", entry->key);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
  }
  
  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)...
1025
  	unsigned long newkey;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1026

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

5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1031
  	key_at_index = newkey;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1032
1033
1034
1035
  	return count;
  }
  
  static struct led_classdev applesmc_backlight = {
6c152beef   Richard Purdie   leds: Standardise...
1036
  	.name			= "smc::kbd_backlight",
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1037
1038
1039
  	.default_trigger	= "nand-disk",
  	.brightness_set		= applesmc_brightness_set,
  };
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1040
1041
1042
1043
1044
1045
1046
1047
1048
  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 ...
1049
  };
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1050
1051
1052
1053
  static struct applesmc_node_group accelerometer_group[] = {
  	{ "position", applesmc_position_show },
  	{ "calibrate", applesmc_calibrate_show, applesmc_calibrate_store },
  	{ }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1054
  };
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1055
1056
1057
1058
  static struct applesmc_node_group light_sensor_group[] = {
  	{ "light", applesmc_light_show },
  	{ }
  };
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1059

3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1060
1061
1062
1063
1064
1065
1066
1067
1068
  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 ...
1069
  };
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1070
1071
1072
1073
  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)...
1074
  };
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1075
1076
1077
  /* Module stuff */
  
  /*
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
   * 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];
1009ccdc6   Guenter Roeck   hwmon: (applesmc)...
1112
1113
  			scnprintf(node->name, sizeof(node->name), grp->format,
  				  i + 1);
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1114
  			node->sda.index = (grp->option << 16) | (i & 0xffff);
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1115
1116
1117
  			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)...
1118
  			sysfs_attr_init(attr);
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1119
  			attr->name = node->name;
71ee4a400   Guenter Roeck   hwmon: (applesmc)...
1120
  			attr->mode = 0444 | (grp->store ? 0200 : 0);
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
  			ret = sysfs_create_file(&pdev->dev.kobj, attr);
  			if (ret) {
  				attr->name = NULL;
  				goto out;
  			}
  		}
  	}
  
  	return 0;
  out:
  	applesmc_destroy_nodes(groups);
  	return ret;
  }
0c6cac7ab   Bastien Nocera   hwmon: applesmc: ...
1134
  /* Create accelerometer resources */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1135
1136
1137
  static int applesmc_create_accelerometer(void)
  {
  	int ret;
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1138
1139
1140
1141
  	if (!smcreg.has_accelerometer)
  		return 0;
  
  	ret = applesmc_create_nodes(accelerometer_group, 1);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1142
1143
  	if (ret)
  		goto out;
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
1144
  	applesmc_idev = input_allocate_device();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1145
1146
1147
1148
1149
1150
1151
  	if (!applesmc_idev) {
  		ret = -ENOMEM;
  		goto out_sysfs;
  	}
  
  	/* initial calibrate for the input device */
  	applesmc_calibrate();
d5cf2b99d   Dmitry Torokhov   HWMON: applesmc -...
1152
  	/* initialize the input device */
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
1153
1154
1155
1156
  	applesmc_idev->name = "applesmc";
  	applesmc_idev->id.bustype = BUS_HOST;
  	applesmc_idev->dev.parent = &pdev->dev;
  	input_set_abs_params(applesmc_idev, ABS_X,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1157
  			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
1158
  	input_set_abs_params(applesmc_idev, ABS_Y,
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1159
  			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
1160
1161
1162
1163
1164
1165
1166
  	ret = input_setup_polling(applesmc_idev, applesmc_idev_poll);
  	if (ret)
  		goto out_idev;
  
  	input_set_poll_interval(applesmc_idev, APPLESMC_POLL_INTERVAL);
  
  	ret = input_register_device(applesmc_idev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1167
1168
  	if (ret)
  		goto out_idev;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1169
1170
1171
  	return 0;
  
  out_idev:
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
1172
  	input_free_device(applesmc_idev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1173
1174
  
  out_sysfs:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1175
  	applesmc_destroy_nodes(accelerometer_group);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1176
1177
  
  out:
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
1178
1179
  	pr_warn("driver init failed (ret=%d)!
  ", ret);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1180
1181
  	return ret;
  }
0c6cac7ab   Bastien Nocera   hwmon: applesmc: ...
1182
  /* Release all resources used by the accelerometer */
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1183
1184
  static void applesmc_release_accelerometer(void)
  {
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1185
1186
  	if (!smcreg.has_accelerometer)
  		return;
58d5aa5c7   Dmitry Torokhov   hwmon: (applesmc)...
1187
  	input_unregister_device(applesmc_idev);
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1188
  	applesmc_destroy_nodes(accelerometer_group);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1189
  }
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
  
  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)...
1222
1223
1224
1225
  static int applesmc_dmi_match(const struct dmi_system_id *id)
  {
  	return 1;
  }
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1226

85ebfd3ef   Guenter Roeck   hwmon: (applesmc)...
1227
1228
1229
1230
  /*
   * Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
   * So we need to put "Apple MacBook Pro" before "Apple MacBook".
   */
6faadbbb7   Christoph Hellwig   dmi: Mark all str...
1231
  static const struct dmi_system_id applesmc_whitelist[] __initconst = {
f5274c972   Henrik Rydberg   hwmon: applesmc: ...
1232
1233
1234
  	{ applesmc_dmi_match, "Apple MacBook Air", {
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1235
  	},
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1236
  	{ applesmc_dmi_match, "Apple MacBook Pro", {
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
1237
1238
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1239
  	},
cd19ba139   Riki Oktarianto   hwmon: (applesmc)...
1240
  	{ applesmc_dmi_match, "Apple MacBook", {
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
1241
1242
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "MacBook") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1243
  	},
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1244
  	{ applesmc_dmi_match, "Apple Macmini", {
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
1245
1246
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "Macmini") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1247
  	},
45a3a36b6   Henrik Rydberg   drivers/hwmon/app...
1248
1249
1250
  	{ applesmc_dmi_match, "Apple MacPro", {
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1251
  	},
9f86f28df   Roberto De Ioris   applesmc: support...
1252
  	{ applesmc_dmi_match, "Apple iMac", {
2bfe81482   Guenter Roeck   hwmon: (applesmc)...
1253
1254
  	  DMI_MATCH(DMI_BOARD_VENDOR, "Apple"),
  	  DMI_MATCH(DMI_PRODUCT_NAME, "iMac") },
40ef06f11   Henrik Rydberg   hwmon: (applesmc)...
1255
  	},
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1256
1257
1258
1259
1260
1261
  	{ .ident = NULL }
  };
  
  static int __init applesmc_init(void)
  {
  	int ret;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1262

6f2fad748   Nicolas Boichat   Apple SMC driver ...
1263
  	if (!dmi_check_system(applesmc_whitelist)) {
1ee7c71bd   Joe Perches   hwmon: (applesmc)...
1264
1265
  		pr_warn("supported laptop not found!
  ");
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
  		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...
1279
1280
  	pdev = platform_device_register_simple("applesmc", APPLESMC_DATA_PORT,
  					       NULL, 0);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1281
1282
1283
1284
  	if (IS_ERR(pdev)) {
  		ret = PTR_ERR(pdev);
  		goto out_driver;
  	}
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1285
1286
  	/* create register cache */
  	ret = applesmc_init_smcreg();
6996abf09   Nicolas Boichat   hwmon/applesmc: H...
1287
1288
  	if (ret)
  		goto out_device;
fa74419bb   Nicolas Boichat   applesmc - sensor...
1289

0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1290
  	ret = applesmc_create_nodes(info_group, 1);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1291
1292
  	if (ret)
  		goto out_smcreg;
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1293
1294
1295
  	ret = applesmc_create_nodes(fan_group, smcreg.fan_count);
  	if (ret)
  		goto out_info;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1296

e30bca125   Henrik Rydberg   hwmon: (applesmc)...
1297
  	ret = applesmc_create_nodes(temp_group, smcreg.index_count);
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1298
1299
  	if (ret)
  		goto out_fans;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1300

0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1301
1302
1303
  	ret = applesmc_create_accelerometer();
  	if (ret)
  		goto out_temperature;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1304

0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1305
1306
1307
  	ret = applesmc_create_light_sensor();
  	if (ret)
  		goto out_accelerometer;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1308

0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1309
1310
1311
  	ret = applesmc_create_key_backlight();
  	if (ret)
  		goto out_light_sysfs;
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1312

1beeffe43   Tony Jones   hwmon: Convert fr...
1313
1314
1315
  	hwmon_dev = hwmon_device_register(&pdev->dev);
  	if (IS_ERR(hwmon_dev)) {
  		ret = PTR_ERR(hwmon_dev);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1316
1317
  		goto out_light_ledclass;
  	}
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1318
1319
1320
  	return 0;
  
  out_light_ledclass:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1321
  	applesmc_release_key_backlight();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1322
  out_light_sysfs:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1323
  	applesmc_release_light_sensor();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1324
  out_accelerometer:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1325
  	applesmc_release_accelerometer();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1326
  out_temperature:
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1327
  	applesmc_destroy_nodes(temp_group);
0559a5388   Henrik Rydberg   hwmon: (applesmc)...
1328
  out_fans:
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1329
1330
  	applesmc_destroy_nodes(fan_group);
  out_info:
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1331
  	applesmc_destroy_nodes(info_group);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1332
1333
  out_smcreg:
  	applesmc_destroy_smcreg();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1334
1335
1336
1337
1338
1339
1340
  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)...
1341
1342
  	pr_warn("driver init failed (ret=%d)!
  ", ret);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1343
1344
1345
1346
1347
  	return ret;
  }
  
  static void __exit applesmc_exit(void)
  {
1beeffe43   Tony Jones   hwmon: Convert fr...
1348
  	hwmon_device_unregister(hwmon_dev);
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1349
1350
1351
  	applesmc_release_key_backlight();
  	applesmc_release_light_sensor();
  	applesmc_release_accelerometer();
9792dadfc   Henrik Rydberg   hwmon: (applesmc)...
1352
  	applesmc_destroy_nodes(temp_group);
3eba2bf7c   Henrik Rydberg   hwmon: (applesmc)...
1353
  	applesmc_destroy_nodes(fan_group);
0b0b5dff8   Henrik Rydberg   hwmon: (applesmc)...
1354
  	applesmc_destroy_nodes(info_group);
5874583d5   Henrik Rydberg   hwmon: (applesmc)...
1355
  	applesmc_destroy_smcreg();
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1356
1357
1358
  	platform_device_unregister(pdev);
  	platform_driver_unregister(&applesmc_driver);
  	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
6f2fad748   Nicolas Boichat   Apple SMC driver ...
1359
1360
1361
1362
1363
1364
1365
1366
  }
  
  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: ...
1367
  MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);