diff options
author | David S. Miller <davem@davemloft.net> | 2010-04-03 18:49:14 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-04-03 18:49:14 -0400 |
commit | 87e8b821ed8db3dab03d96cd542e29666bf210aa (patch) | |
tree | 0027060473aafbbb125655ba027319c8a1a665fc /drivers/misc | |
parent | 33cd9dfa3a13e3d8e41aef225a9f98169816723b (diff) | |
parent | 5e11611a5d22252f3f9c169a3c9377eac0c32033 (diff) |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 19 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/c2port/core.c | 4 | ||||
-rw-r--r-- | drivers/misc/eeprom/at24.c | 1 | ||||
-rw-r--r-- | drivers/misc/eeprom/at25.c | 1 | ||||
-rw-r--r-- | drivers/misc/iwmc3200top/main.c | 2 | ||||
-rw-r--r-- | drivers/misc/kgdbts.c | 6 | ||||
-rw-r--r-- | drivers/misc/lkdtm.c | 472 | ||||
-rw-r--r-- | drivers/misc/phantom.c | 13 | ||||
-rw-r--r-- | drivers/misc/sgi-gru/grutables.h | 15 | ||||
-rw-r--r-- | drivers/misc/sgi-xp/xpnet.c | 2 | ||||
-rw-r--r-- | drivers/misc/tsl2550.c | 473 |
12 files changed, 902 insertions, 107 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index e3551d20464f..2191c8d896a0 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -212,6 +212,15 @@ config CS5535_MFGPT_DEFAULT_IRQ | |||
212 | want to use a different IRQ by default. This is here for | 212 | want to use a different IRQ by default. This is here for |
213 | architectures to set as necessary. | 213 | architectures to set as necessary. |
214 | 214 | ||
215 | config CS5535_CLOCK_EVENT_SRC | ||
216 | tristate "CS5535/CS5536 high-res timer (MFGPT) events" | ||
217 | depends on GENERIC_TIME && GENERIC_CLOCKEVENTS && CS5535_MFGPT | ||
218 | help | ||
219 | This driver provides a clock event source based on the MFGPT | ||
220 | timer(s) in the CS5535 and CS5536 companion chips. | ||
221 | MFGPTs have a better resolution and max interval than the | ||
222 | generic PIT, and are suitable for use as high-res timers. | ||
223 | |||
215 | config HP_ILO | 224 | config HP_ILO |
216 | tristate "Channel interface driver for HP iLO/iLO2 processor" | 225 | tristate "Channel interface driver for HP iLO/iLO2 processor" |
217 | depends on PCI | 226 | depends on PCI |
@@ -259,6 +268,16 @@ config ISL29003 | |||
259 | This driver can also be built as a module. If so, the module | 268 | This driver can also be built as a module. If so, the module |
260 | will be called isl29003. | 269 | will be called isl29003. |
261 | 270 | ||
271 | config SENSORS_TSL2550 | ||
272 | tristate "Taos TSL2550 ambient light sensor" | ||
273 | depends on I2C && SYSFS | ||
274 | help | ||
275 | If you say yes here you get support for the Taos TSL2550 | ||
276 | ambient light sensor. | ||
277 | |||
278 | This driver can also be built as a module. If so, the module | ||
279 | will be called tsl2550. | ||
280 | |||
262 | config EP93XX_PWM | 281 | config EP93XX_PWM |
263 | tristate "EP93xx PWM support" | 282 | tristate "EP93xx PWM support" |
264 | depends on ARCH_EP93XX | 283 | depends on ARCH_EP93XX |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 049ff2482f30..27c484355414 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -21,6 +21,7 @@ obj-$(CONFIG_SGI_GRU) += sgi-gru/ | |||
21 | obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o | 21 | obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o |
22 | obj-$(CONFIG_HP_ILO) += hpilo.o | 22 | obj-$(CONFIG_HP_ILO) += hpilo.o |
23 | obj-$(CONFIG_ISL29003) += isl29003.o | 23 | obj-$(CONFIG_ISL29003) += isl29003.o |
24 | obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o | ||
24 | obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o | 25 | obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o |
25 | obj-$(CONFIG_DS1682) += ds1682.o | 26 | obj-$(CONFIG_DS1682) += ds1682.o |
26 | obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o | 27 | obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o |
diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c index b5346b4db91a..b7a85f46a6c2 100644 --- a/drivers/misc/c2port/core.c +++ b/drivers/misc/c2port/core.c | |||
@@ -912,8 +912,8 @@ struct c2port_device *c2port_device_register(char *name, | |||
912 | 912 | ||
913 | c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, | 913 | c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, |
914 | "c2port%d", id); | 914 | "c2port%d", id); |
915 | if (unlikely(!c2dev->dev)) { | 915 | if (unlikely(IS_ERR(c2dev->dev))) { |
916 | ret = -ENOMEM; | 916 | ret = PTR_ERR(c2dev->dev); |
917 | goto error_device_create; | 917 | goto error_device_create; |
918 | } | 918 | } |
919 | dev_set_drvdata(c2dev->dev, c2dev); | 919 | dev_set_drvdata(c2dev->dev, c2dev); |
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 2cb2736d65aa..db7d0f21b65d 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c | |||
@@ -505,6 +505,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) | |||
505 | * Export the EEPROM bytes through sysfs, since that's convenient. | 505 | * Export the EEPROM bytes through sysfs, since that's convenient. |
506 | * By default, only root should see the data (maybe passwords etc) | 506 | * By default, only root should see the data (maybe passwords etc) |
507 | */ | 507 | */ |
508 | sysfs_bin_attr_init(&at24->bin); | ||
508 | at24->bin.attr.name = "eeprom"; | 509 | at24->bin.attr.name = "eeprom"; |
509 | at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR; | 510 | at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR; |
510 | at24->bin.read = at24_bin_read; | 511 | at24->bin.read = at24_bin_read; |
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index d902d81dde39..d194212a41f6 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c | |||
@@ -347,6 +347,7 @@ static int at25_probe(struct spi_device *spi) | |||
347 | * that's sensitive for read and/or write, like ethernet addresses, | 347 | * that's sensitive for read and/or write, like ethernet addresses, |
348 | * security codes, board-specific manufacturing calibrations, etc. | 348 | * security codes, board-specific manufacturing calibrations, etc. |
349 | */ | 349 | */ |
350 | sysfs_bin_attr_init(&at25->bin); | ||
350 | at25->bin.attr.name = "eeprom"; | 351 | at25->bin.attr.name = "eeprom"; |
351 | at25->bin.attr.mode = S_IRUSR; | 352 | at25->bin.attr.mode = S_IRUSR; |
352 | at25->bin.read = at25_bin_read; | 353 | at25->bin.read = at25_bin_read; |
diff --git a/drivers/misc/iwmc3200top/main.c b/drivers/misc/iwmc3200top/main.c index dd0a3913bf6d..3b7292a5cea9 100644 --- a/drivers/misc/iwmc3200top/main.c +++ b/drivers/misc/iwmc3200top/main.c | |||
@@ -597,8 +597,6 @@ static void iwmct_remove(struct sdio_func *func) | |||
597 | struct iwmct_work_struct *read_req; | 597 | struct iwmct_work_struct *read_req; |
598 | struct iwmct_priv *priv = sdio_get_drvdata(func); | 598 | struct iwmct_priv *priv = sdio_get_drvdata(func); |
599 | 599 | ||
600 | priv = sdio_get_drvdata(func); | ||
601 | |||
602 | LOG_INFO(priv, INIT, "enter\n"); | 600 | LOG_INFO(priv, INIT, "enter\n"); |
603 | 601 | ||
604 | sdio_claim_host(func); | 602 | sdio_claim_host(func); |
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index fcb6ec1af173..72450237a0f4 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c | |||
@@ -295,6 +295,10 @@ static int check_and_rewind_pc(char *put_str, char *arg) | |||
295 | /* On x86 a breakpoint stop requires it to be decremented */ | 295 | /* On x86 a breakpoint stop requires it to be decremented */ |
296 | if (addr + 1 == kgdbts_regs.ip) | 296 | if (addr + 1 == kgdbts_regs.ip) |
297 | offset = -1; | 297 | offset = -1; |
298 | #elif defined(CONFIG_SUPERH) | ||
299 | /* On SUPERH a breakpoint stop requires it to be decremented */ | ||
300 | if (addr + 2 == kgdbts_regs.pc) | ||
301 | offset = -2; | ||
298 | #endif | 302 | #endif |
299 | if (strcmp(arg, "silent") && | 303 | if (strcmp(arg, "silent") && |
300 | instruction_pointer(&kgdbts_regs) + offset != addr) { | 304 | instruction_pointer(&kgdbts_regs) + offset != addr) { |
@@ -305,6 +309,8 @@ static int check_and_rewind_pc(char *put_str, char *arg) | |||
305 | #ifdef CONFIG_X86 | 309 | #ifdef CONFIG_X86 |
306 | /* On x86 adjust the instruction pointer if needed */ | 310 | /* On x86 adjust the instruction pointer if needed */ |
307 | kgdbts_regs.ip += offset; | 311 | kgdbts_regs.ip += offset; |
312 | #elif defined(CONFIG_SUPERH) | ||
313 | kgdbts_regs.pc += offset; | ||
308 | #endif | 314 | #endif |
309 | return 0; | 315 | return 0; |
310 | } | 316 | } |
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c index 3648b23d5c92..4a0648301fdf 100644 --- a/drivers/misc/lkdtm.c +++ b/drivers/misc/lkdtm.c | |||
@@ -26,21 +26,9 @@ | |||
26 | * It is adapted from the Linux Kernel Dump Test Tool by | 26 | * It is adapted from the Linux Kernel Dump Test Tool by |
27 | * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net> | 27 | * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net> |
28 | * | 28 | * |
29 | * Usage : insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<> | 29 | * Debugfs support added by Simon Kagstrom <simon.kagstrom@netinsight.net> |
30 | * [cpoint_count={>0}] | ||
31 | * | 30 | * |
32 | * recur_count : Recursion level for the stack overflow test. Default is 10. | 31 | * See Documentation/fault-injection/provoke-crashes.txt for instructions |
33 | * | ||
34 | * cpoint_name : Crash point where the kernel is to be crashed. It can be | ||
35 | * one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY, | ||
36 | * FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD, | ||
37 | * IDE_CORE_CP | ||
38 | * | ||
39 | * cpoint_type : Indicates the action to be taken on hitting the crash point. | ||
40 | * It can be one of PANIC, BUG, EXCEPTION, LOOP, OVERFLOW | ||
41 | * | ||
42 | * cpoint_count : Indicates the number of times the crash point is to be hit | ||
43 | * to trigger an action. The default is 10. | ||
44 | */ | 32 | */ |
45 | 33 | ||
46 | #include <linux/kernel.h> | 34 | #include <linux/kernel.h> |
@@ -53,13 +41,12 @@ | |||
53 | #include <linux/interrupt.h> | 41 | #include <linux/interrupt.h> |
54 | #include <linux/hrtimer.h> | 42 | #include <linux/hrtimer.h> |
55 | #include <scsi/scsi_cmnd.h> | 43 | #include <scsi/scsi_cmnd.h> |
44 | #include <linux/debugfs.h> | ||
56 | 45 | ||
57 | #ifdef CONFIG_IDE | 46 | #ifdef CONFIG_IDE |
58 | #include <linux/ide.h> | 47 | #include <linux/ide.h> |
59 | #endif | 48 | #endif |
60 | 49 | ||
61 | #define NUM_CPOINTS 8 | ||
62 | #define NUM_CPOINT_TYPES 5 | ||
63 | #define DEFAULT_COUNT 10 | 50 | #define DEFAULT_COUNT 10 |
64 | #define REC_NUM_DEFAULT 10 | 51 | #define REC_NUM_DEFAULT 10 |
65 | 52 | ||
@@ -72,7 +59,8 @@ enum cname { | |||
72 | MEM_SWAPOUT, | 59 | MEM_SWAPOUT, |
73 | TIMERADD, | 60 | TIMERADD, |
74 | SCSI_DISPATCH_CMD, | 61 | SCSI_DISPATCH_CMD, |
75 | IDE_CORE_CP | 62 | IDE_CORE_CP, |
63 | DIRECT, | ||
76 | }; | 64 | }; |
77 | 65 | ||
78 | enum ctype { | 66 | enum ctype { |
@@ -81,7 +69,11 @@ enum ctype { | |||
81 | BUG, | 69 | BUG, |
82 | EXCEPTION, | 70 | EXCEPTION, |
83 | LOOP, | 71 | LOOP, |
84 | OVERFLOW | 72 | OVERFLOW, |
73 | CORRUPT_STACK, | ||
74 | UNALIGNED_LOAD_STORE_WRITE, | ||
75 | OVERWRITE_ALLOCATION, | ||
76 | WRITE_AFTER_FREE, | ||
85 | }; | 77 | }; |
86 | 78 | ||
87 | static char* cp_name[] = { | 79 | static char* cp_name[] = { |
@@ -92,7 +84,8 @@ static char* cp_name[] = { | |||
92 | "MEM_SWAPOUT", | 84 | "MEM_SWAPOUT", |
93 | "TIMERADD", | 85 | "TIMERADD", |
94 | "SCSI_DISPATCH_CMD", | 86 | "SCSI_DISPATCH_CMD", |
95 | "IDE_CORE_CP" | 87 | "IDE_CORE_CP", |
88 | "DIRECT", | ||
96 | }; | 89 | }; |
97 | 90 | ||
98 | static char* cp_type[] = { | 91 | static char* cp_type[] = { |
@@ -100,7 +93,11 @@ static char* cp_type[] = { | |||
100 | "BUG", | 93 | "BUG", |
101 | "EXCEPTION", | 94 | "EXCEPTION", |
102 | "LOOP", | 95 | "LOOP", |
103 | "OVERFLOW" | 96 | "OVERFLOW", |
97 | "CORRUPT_STACK", | ||
98 | "UNALIGNED_LOAD_STORE_WRITE", | ||
99 | "OVERWRITE_ALLOCATION", | ||
100 | "WRITE_AFTER_FREE", | ||
104 | }; | 101 | }; |
105 | 102 | ||
106 | static struct jprobe lkdtm; | 103 | static struct jprobe lkdtm; |
@@ -193,34 +190,66 @@ int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file, | |||
193 | } | 190 | } |
194 | #endif | 191 | #endif |
195 | 192 | ||
193 | /* Return the crashpoint number or NONE if the name is invalid */ | ||
194 | static enum ctype parse_cp_type(const char *what, size_t count) | ||
195 | { | ||
196 | int i; | ||
197 | |||
198 | for (i = 0; i < ARRAY_SIZE(cp_type); i++) { | ||
199 | if (!strcmp(what, cp_type[i])) | ||
200 | return i + 1; | ||
201 | } | ||
202 | |||
203 | return NONE; | ||
204 | } | ||
205 | |||
206 | static const char *cp_type_to_str(enum ctype type) | ||
207 | { | ||
208 | if (type == NONE || type < 0 || type > ARRAY_SIZE(cp_type)) | ||
209 | return "None"; | ||
210 | |||
211 | return cp_type[type - 1]; | ||
212 | } | ||
213 | |||
214 | static const char *cp_name_to_str(enum cname name) | ||
215 | { | ||
216 | if (name == INVALID || name < 0 || name > ARRAY_SIZE(cp_name)) | ||
217 | return "INVALID"; | ||
218 | |||
219 | return cp_name[name - 1]; | ||
220 | } | ||
221 | |||
222 | |||
196 | static int lkdtm_parse_commandline(void) | 223 | static int lkdtm_parse_commandline(void) |
197 | { | 224 | { |
198 | int i; | 225 | int i; |
199 | 226 | ||
200 | if (cpoint_name == NULL || cpoint_type == NULL || | 227 | if (cpoint_count < 1 || recur_count < 1) |
201 | cpoint_count < 1 || recur_count < 1) | ||
202 | return -EINVAL; | 228 | return -EINVAL; |
203 | 229 | ||
204 | for (i = 0; i < NUM_CPOINTS; ++i) { | 230 | count = cpoint_count; |
231 | |||
232 | /* No special parameters */ | ||
233 | if (!cpoint_type && !cpoint_name) | ||
234 | return 0; | ||
235 | |||
236 | /* Neither or both of these need to be set */ | ||
237 | if (!cpoint_type || !cpoint_name) | ||
238 | return -EINVAL; | ||
239 | |||
240 | cptype = parse_cp_type(cpoint_type, strlen(cpoint_type)); | ||
241 | if (cptype == NONE) | ||
242 | return -EINVAL; | ||
243 | |||
244 | for (i = 0; i < ARRAY_SIZE(cp_name); i++) { | ||
205 | if (!strcmp(cpoint_name, cp_name[i])) { | 245 | if (!strcmp(cpoint_name, cp_name[i])) { |
206 | cpoint = i + 1; | 246 | cpoint = i + 1; |
207 | break; | 247 | return 0; |
208 | } | ||
209 | } | ||
210 | |||
211 | for (i = 0; i < NUM_CPOINT_TYPES; ++i) { | ||
212 | if (!strcmp(cpoint_type, cp_type[i])) { | ||
213 | cptype = i + 1; | ||
214 | break; | ||
215 | } | 248 | } |
216 | } | 249 | } |
217 | 250 | ||
218 | if (cpoint == INVALID || cptype == NONE) | 251 | /* Could not find a valid crash point */ |
219 | return -EINVAL; | 252 | return -EINVAL; |
220 | |||
221 | count = cpoint_count; | ||
222 | |||
223 | return 0; | ||
224 | } | 253 | } |
225 | 254 | ||
226 | static int recursive_loop(int a) | 255 | static int recursive_loop(int a) |
@@ -235,53 +264,92 @@ static int recursive_loop(int a) | |||
235 | return recursive_loop(a); | 264 | return recursive_loop(a); |
236 | } | 265 | } |
237 | 266 | ||
238 | void lkdtm_handler(void) | 267 | static void lkdtm_do_action(enum ctype which) |
239 | { | 268 | { |
240 | printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n", | 269 | switch (which) { |
241 | cpoint_name, cpoint_type); | 270 | case PANIC: |
242 | --count; | 271 | panic("dumptest"); |
272 | break; | ||
273 | case BUG: | ||
274 | BUG(); | ||
275 | break; | ||
276 | case EXCEPTION: | ||
277 | *((int *) 0) = 0; | ||
278 | break; | ||
279 | case LOOP: | ||
280 | for (;;) | ||
281 | ; | ||
282 | break; | ||
283 | case OVERFLOW: | ||
284 | (void) recursive_loop(0); | ||
285 | break; | ||
286 | case CORRUPT_STACK: { | ||
287 | volatile u32 data[8]; | ||
288 | volatile u32 *p = data; | ||
289 | |||
290 | p[12] = 0x12345678; | ||
291 | break; | ||
292 | } | ||
293 | case UNALIGNED_LOAD_STORE_WRITE: { | ||
294 | static u8 data[5] __attribute__((aligned(4))) = {1, 2, | ||
295 | 3, 4, 5}; | ||
296 | u32 *p; | ||
297 | u32 val = 0x12345678; | ||
298 | |||
299 | p = (u32 *)(data + 1); | ||
300 | if (*p == 0) | ||
301 | val = 0x87654321; | ||
302 | *p = val; | ||
303 | break; | ||
304 | } | ||
305 | case OVERWRITE_ALLOCATION: { | ||
306 | size_t len = 1020; | ||
307 | u32 *data = kmalloc(len, GFP_KERNEL); | ||
308 | |||
309 | data[1024 / sizeof(u32)] = 0x12345678; | ||
310 | kfree(data); | ||
311 | break; | ||
312 | } | ||
313 | case WRITE_AFTER_FREE: { | ||
314 | size_t len = 1024; | ||
315 | u32 *data = kmalloc(len, GFP_KERNEL); | ||
316 | |||
317 | kfree(data); | ||
318 | schedule(); | ||
319 | memset(data, 0x78, len); | ||
320 | break; | ||
321 | } | ||
322 | case NONE: | ||
323 | default: | ||
324 | break; | ||
325 | } | ||
326 | |||
327 | } | ||
328 | |||
329 | static void lkdtm_handler(void) | ||
330 | { | ||
331 | count--; | ||
332 | printk(KERN_INFO "lkdtm: Crash point %s of type %s hit, trigger in %d rounds\n", | ||
333 | cp_name_to_str(cpoint), cp_type_to_str(cptype), count); | ||
243 | 334 | ||
244 | if (count == 0) { | 335 | if (count == 0) { |
245 | switch (cptype) { | 336 | lkdtm_do_action(cptype); |
246 | case NONE: | ||
247 | break; | ||
248 | case PANIC: | ||
249 | printk(KERN_INFO "lkdtm : PANIC\n"); | ||
250 | panic("dumptest"); | ||
251 | break; | ||
252 | case BUG: | ||
253 | printk(KERN_INFO "lkdtm : BUG\n"); | ||
254 | BUG(); | ||
255 | break; | ||
256 | case EXCEPTION: | ||
257 | printk(KERN_INFO "lkdtm : EXCEPTION\n"); | ||
258 | *((int *) 0) = 0; | ||
259 | break; | ||
260 | case LOOP: | ||
261 | printk(KERN_INFO "lkdtm : LOOP\n"); | ||
262 | for (;;); | ||
263 | break; | ||
264 | case OVERFLOW: | ||
265 | printk(KERN_INFO "lkdtm : OVERFLOW\n"); | ||
266 | (void) recursive_loop(0); | ||
267 | break; | ||
268 | default: | ||
269 | break; | ||
270 | } | ||
271 | count = cpoint_count; | 337 | count = cpoint_count; |
272 | } | 338 | } |
273 | } | 339 | } |
274 | 340 | ||
275 | static int __init lkdtm_module_init(void) | 341 | static int lkdtm_register_cpoint(enum cname which) |
276 | { | 342 | { |
277 | int ret; | 343 | int ret; |
278 | 344 | ||
279 | if (lkdtm_parse_commandline() == -EINVAL) { | 345 | cpoint = INVALID; |
280 | printk(KERN_INFO "lkdtm : Invalid command\n"); | 346 | if (lkdtm.entry != NULL) |
281 | return -EINVAL; | 347 | unregister_jprobe(&lkdtm); |
282 | } | ||
283 | 348 | ||
284 | switch (cpoint) { | 349 | switch (which) { |
350 | case DIRECT: | ||
351 | lkdtm_do_action(cptype); | ||
352 | return 0; | ||
285 | case INT_HARDWARE_ENTRY: | 353 | case INT_HARDWARE_ENTRY: |
286 | lkdtm.kp.symbol_name = "do_IRQ"; | 354 | lkdtm.kp.symbol_name = "do_IRQ"; |
287 | lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; | 355 | lkdtm.entry = (kprobe_opcode_t*) jp_do_irq; |
@@ -315,28 +383,268 @@ static int __init lkdtm_module_init(void) | |||
315 | lkdtm.kp.symbol_name = "generic_ide_ioctl"; | 383 | lkdtm.kp.symbol_name = "generic_ide_ioctl"; |
316 | lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; | 384 | lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl; |
317 | #else | 385 | #else |
318 | printk(KERN_INFO "lkdtm : Crash point not available\n"); | 386 | printk(KERN_INFO "lkdtm: Crash point not available\n"); |
387 | return -EINVAL; | ||
319 | #endif | 388 | #endif |
320 | break; | 389 | break; |
321 | default: | 390 | default: |
322 | printk(KERN_INFO "lkdtm : Invalid Crash Point\n"); | 391 | printk(KERN_INFO "lkdtm: Invalid Crash Point\n"); |
323 | break; | 392 | return -EINVAL; |
324 | } | 393 | } |
325 | 394 | ||
395 | cpoint = which; | ||
326 | if ((ret = register_jprobe(&lkdtm)) < 0) { | 396 | if ((ret = register_jprobe(&lkdtm)) < 0) { |
327 | printk(KERN_INFO "lkdtm : Couldn't register jprobe\n"); | 397 | printk(KERN_INFO "lkdtm: Couldn't register jprobe\n"); |
328 | return ret; | 398 | cpoint = INVALID; |
399 | } | ||
400 | |||
401 | return ret; | ||
402 | } | ||
403 | |||
404 | static ssize_t do_register_entry(enum cname which, struct file *f, | ||
405 | const char __user *user_buf, size_t count, loff_t *off) | ||
406 | { | ||
407 | char *buf; | ||
408 | int err; | ||
409 | |||
410 | if (count >= PAGE_SIZE) | ||
411 | return -EINVAL; | ||
412 | |||
413 | buf = (char *)__get_free_page(GFP_KERNEL); | ||
414 | if (!buf) | ||
415 | return -ENOMEM; | ||
416 | if (copy_from_user(buf, user_buf, count)) { | ||
417 | free_page((unsigned long) buf); | ||
418 | return -EFAULT; | ||
419 | } | ||
420 | /* NULL-terminate and remove enter */ | ||
421 | buf[count] = '\0'; | ||
422 | strim(buf); | ||
423 | |||
424 | cptype = parse_cp_type(buf, count); | ||
425 | free_page((unsigned long) buf); | ||
426 | |||
427 | if (cptype == NONE) | ||
428 | return -EINVAL; | ||
429 | |||
430 | err = lkdtm_register_cpoint(which); | ||
431 | if (err < 0) | ||
432 | return err; | ||
433 | |||
434 | *off += count; | ||
435 | |||
436 | return count; | ||
437 | } | ||
438 | |||
439 | /* Generic read callback that just prints out the available crash types */ | ||
440 | static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, | ||
441 | size_t count, loff_t *off) | ||
442 | { | ||
443 | char *buf; | ||
444 | int i, n, out; | ||
445 | |||
446 | buf = (char *)__get_free_page(GFP_KERNEL); | ||
447 | |||
448 | n = snprintf(buf, PAGE_SIZE, "Available crash types:\n"); | ||
449 | for (i = 0; i < ARRAY_SIZE(cp_type); i++) | ||
450 | n += snprintf(buf + n, PAGE_SIZE - n, "%s\n", cp_type[i]); | ||
451 | buf[n] = '\0'; | ||
452 | |||
453 | out = simple_read_from_buffer(user_buf, count, off, | ||
454 | buf, n); | ||
455 | free_page((unsigned long) buf); | ||
456 | |||
457 | return out; | ||
458 | } | ||
459 | |||
460 | static int lkdtm_debugfs_open(struct inode *inode, struct file *file) | ||
461 | { | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | |||
466 | static ssize_t int_hardware_entry(struct file *f, const char __user *buf, | ||
467 | size_t count, loff_t *off) | ||
468 | { | ||
469 | return do_register_entry(INT_HARDWARE_ENTRY, f, buf, count, off); | ||
470 | } | ||
471 | |||
472 | static ssize_t int_hw_irq_en(struct file *f, const char __user *buf, | ||
473 | size_t count, loff_t *off) | ||
474 | { | ||
475 | return do_register_entry(INT_HW_IRQ_EN, f, buf, count, off); | ||
476 | } | ||
477 | |||
478 | static ssize_t int_tasklet_entry(struct file *f, const char __user *buf, | ||
479 | size_t count, loff_t *off) | ||
480 | { | ||
481 | return do_register_entry(INT_TASKLET_ENTRY, f, buf, count, off); | ||
482 | } | ||
483 | |||
484 | static ssize_t fs_devrw_entry(struct file *f, const char __user *buf, | ||
485 | size_t count, loff_t *off) | ||
486 | { | ||
487 | return do_register_entry(FS_DEVRW, f, buf, count, off); | ||
488 | } | ||
489 | |||
490 | static ssize_t mem_swapout_entry(struct file *f, const char __user *buf, | ||
491 | size_t count, loff_t *off) | ||
492 | { | ||
493 | return do_register_entry(MEM_SWAPOUT, f, buf, count, off); | ||
494 | } | ||
495 | |||
496 | static ssize_t timeradd_entry(struct file *f, const char __user *buf, | ||
497 | size_t count, loff_t *off) | ||
498 | { | ||
499 | return do_register_entry(TIMERADD, f, buf, count, off); | ||
500 | } | ||
501 | |||
502 | static ssize_t scsi_dispatch_cmd_entry(struct file *f, | ||
503 | const char __user *buf, size_t count, loff_t *off) | ||
504 | { | ||
505 | return do_register_entry(SCSI_DISPATCH_CMD, f, buf, count, off); | ||
506 | } | ||
507 | |||
508 | static ssize_t ide_core_cp_entry(struct file *f, const char __user *buf, | ||
509 | size_t count, loff_t *off) | ||
510 | { | ||
511 | return do_register_entry(IDE_CORE_CP, f, buf, count, off); | ||
512 | } | ||
513 | |||
514 | /* Special entry to just crash directly. Available without KPROBEs */ | ||
515 | static ssize_t direct_entry(struct file *f, const char __user *user_buf, | ||
516 | size_t count, loff_t *off) | ||
517 | { | ||
518 | enum ctype type; | ||
519 | char *buf; | ||
520 | |||
521 | if (count >= PAGE_SIZE) | ||
522 | return -EINVAL; | ||
523 | if (count < 1) | ||
524 | return -EINVAL; | ||
525 | |||
526 | buf = (char *)__get_free_page(GFP_KERNEL); | ||
527 | if (!buf) | ||
528 | return -ENOMEM; | ||
529 | if (copy_from_user(buf, user_buf, count)) { | ||
530 | free_page((unsigned long) buf); | ||
531 | return -EFAULT; | ||
532 | } | ||
533 | /* NULL-terminate and remove enter */ | ||
534 | buf[count] = '\0'; | ||
535 | strim(buf); | ||
536 | |||
537 | type = parse_cp_type(buf, count); | ||
538 | free_page((unsigned long) buf); | ||
539 | if (type == NONE) | ||
540 | return -EINVAL; | ||
541 | |||
542 | printk(KERN_INFO "lkdtm: Performing direct entry %s\n", | ||
543 | cp_type_to_str(type)); | ||
544 | lkdtm_do_action(type); | ||
545 | *off += count; | ||
546 | |||
547 | return count; | ||
548 | } | ||
549 | |||
550 | struct crash_entry { | ||
551 | const char *name; | ||
552 | const struct file_operations fops; | ||
553 | }; | ||
554 | |||
555 | static const struct crash_entry crash_entries[] = { | ||
556 | {"DIRECT", {.read = lkdtm_debugfs_read, | ||
557 | .open = lkdtm_debugfs_open, | ||
558 | .write = direct_entry} }, | ||
559 | {"INT_HARDWARE_ENTRY", {.read = lkdtm_debugfs_read, | ||
560 | .open = lkdtm_debugfs_open, | ||
561 | .write = int_hardware_entry} }, | ||
562 | {"INT_HW_IRQ_EN", {.read = lkdtm_debugfs_read, | ||
563 | .open = lkdtm_debugfs_open, | ||
564 | .write = int_hw_irq_en} }, | ||
565 | {"INT_TASKLET_ENTRY", {.read = lkdtm_debugfs_read, | ||
566 | .open = lkdtm_debugfs_open, | ||
567 | .write = int_tasklet_entry} }, | ||
568 | {"FS_DEVRW", {.read = lkdtm_debugfs_read, | ||
569 | .open = lkdtm_debugfs_open, | ||
570 | .write = fs_devrw_entry} }, | ||
571 | {"MEM_SWAPOUT", {.read = lkdtm_debugfs_read, | ||
572 | .open = lkdtm_debugfs_open, | ||
573 | .write = mem_swapout_entry} }, | ||
574 | {"TIMERADD", {.read = lkdtm_debugfs_read, | ||
575 | .open = lkdtm_debugfs_open, | ||
576 | .write = timeradd_entry} }, | ||
577 | {"SCSI_DISPATCH_CMD", {.read = lkdtm_debugfs_read, | ||
578 | .open = lkdtm_debugfs_open, | ||
579 | .write = scsi_dispatch_cmd_entry} }, | ||
580 | {"IDE_CORE_CP", {.read = lkdtm_debugfs_read, | ||
581 | .open = lkdtm_debugfs_open, | ||
582 | .write = ide_core_cp_entry} }, | ||
583 | }; | ||
584 | |||
585 | static struct dentry *lkdtm_debugfs_root; | ||
586 | |||
587 | static int __init lkdtm_module_init(void) | ||
588 | { | ||
589 | int ret = -EINVAL; | ||
590 | int n_debugfs_entries = 1; /* Assume only the direct entry */ | ||
591 | int i; | ||
592 | |||
593 | /* Register debugfs interface */ | ||
594 | lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL); | ||
595 | if (!lkdtm_debugfs_root) { | ||
596 | printk(KERN_ERR "lkdtm: creating root dir failed\n"); | ||
597 | return -ENODEV; | ||
598 | } | ||
599 | |||
600 | #ifdef CONFIG_KPROBES | ||
601 | n_debugfs_entries = ARRAY_SIZE(crash_entries); | ||
602 | #endif | ||
603 | |||
604 | for (i = 0; i < n_debugfs_entries; i++) { | ||
605 | const struct crash_entry *cur = &crash_entries[i]; | ||
606 | struct dentry *de; | ||
607 | |||
608 | de = debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root, | ||
609 | NULL, &cur->fops); | ||
610 | if (de == NULL) { | ||
611 | printk(KERN_ERR "lkdtm: could not create %s\n", | ||
612 | cur->name); | ||
613 | goto out_err; | ||
614 | } | ||
615 | } | ||
616 | |||
617 | if (lkdtm_parse_commandline() == -EINVAL) { | ||
618 | printk(KERN_INFO "lkdtm: Invalid command\n"); | ||
619 | goto out_err; | ||
620 | } | ||
621 | |||
622 | if (cpoint != INVALID && cptype != NONE) { | ||
623 | ret = lkdtm_register_cpoint(cpoint); | ||
624 | if (ret < 0) { | ||
625 | printk(KERN_INFO "lkdtm: Invalid crash point %d\n", | ||
626 | cpoint); | ||
627 | goto out_err; | ||
628 | } | ||
629 | printk(KERN_INFO "lkdtm: Crash point %s of type %s registered\n", | ||
630 | cpoint_name, cpoint_type); | ||
631 | } else { | ||
632 | printk(KERN_INFO "lkdtm: No crash points registered, enable through debugfs\n"); | ||
329 | } | 633 | } |
330 | 634 | ||
331 | printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n", | ||
332 | cpoint_name, cpoint_type); | ||
333 | return 0; | 635 | return 0; |
636 | |||
637 | out_err: | ||
638 | debugfs_remove_recursive(lkdtm_debugfs_root); | ||
639 | return ret; | ||
334 | } | 640 | } |
335 | 641 | ||
336 | static void __exit lkdtm_module_exit(void) | 642 | static void __exit lkdtm_module_exit(void) |
337 | { | 643 | { |
338 | unregister_jprobe(&lkdtm); | 644 | debugfs_remove_recursive(lkdtm_debugfs_root); |
339 | printk(KERN_INFO "lkdtm : Crash point unregistered\n"); | 645 | |
646 | unregister_jprobe(&lkdtm); | ||
647 | printk(KERN_INFO "lkdtm: Crash point unregistered\n"); | ||
340 | } | 648 | } |
341 | 649 | ||
342 | module_init(lkdtm_module_init); | 650 | module_init(lkdtm_module_init); |
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 04c27266f567..779aa8ebe4cf 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c | |||
@@ -497,12 +497,7 @@ static struct pci_driver phantom_pci_driver = { | |||
497 | .resume = phantom_resume | 497 | .resume = phantom_resume |
498 | }; | 498 | }; |
499 | 499 | ||
500 | static ssize_t phantom_show_version(struct class *cls, char *buf) | 500 | static CLASS_ATTR_STRING(version, 0444, PHANTOM_VERSION); |
501 | { | ||
502 | return sprintf(buf, PHANTOM_VERSION "\n"); | ||
503 | } | ||
504 | |||
505 | static CLASS_ATTR(version, 0444, phantom_show_version, NULL); | ||
506 | 501 | ||
507 | static int __init phantom_init(void) | 502 | static int __init phantom_init(void) |
508 | { | 503 | { |
@@ -515,7 +510,7 @@ static int __init phantom_init(void) | |||
515 | printk(KERN_ERR "phantom: can't register phantom class\n"); | 510 | printk(KERN_ERR "phantom: can't register phantom class\n"); |
516 | goto err; | 511 | goto err; |
517 | } | 512 | } |
518 | retval = class_create_file(phantom_class, &class_attr_version); | 513 | retval = class_create_file(phantom_class, &class_attr_version.attr); |
519 | if (retval) { | 514 | if (retval) { |
520 | printk(KERN_ERR "phantom: can't create sysfs version file\n"); | 515 | printk(KERN_ERR "phantom: can't create sysfs version file\n"); |
521 | goto err_class; | 516 | goto err_class; |
@@ -541,7 +536,7 @@ static int __init phantom_init(void) | |||
541 | err_unchr: | 536 | err_unchr: |
542 | unregister_chrdev_region(dev, PHANTOM_MAX_MINORS); | 537 | unregister_chrdev_region(dev, PHANTOM_MAX_MINORS); |
543 | err_attr: | 538 | err_attr: |
544 | class_remove_file(phantom_class, &class_attr_version); | 539 | class_remove_file(phantom_class, &class_attr_version.attr); |
545 | err_class: | 540 | err_class: |
546 | class_destroy(phantom_class); | 541 | class_destroy(phantom_class); |
547 | err: | 542 | err: |
@@ -554,7 +549,7 @@ static void __exit phantom_exit(void) | |||
554 | 549 | ||
555 | unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS); | 550 | unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS); |
556 | 551 | ||
557 | class_remove_file(phantom_class, &class_attr_version); | 552 | class_remove_file(phantom_class, &class_attr_version.attr); |
558 | class_destroy(phantom_class); | 553 | class_destroy(phantom_class); |
559 | 554 | ||
560 | pr_debug("phantom: module successfully removed\n"); | 555 | pr_debug("phantom: module successfully removed\n"); |
diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index 02a77b8b8eef..7a8b9068ea03 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h | |||
@@ -516,8 +516,7 @@ struct gru_blade_state { | |||
516 | 516 | ||
517 | /* Scan all active GRUs in a GRU bitmap */ | 517 | /* Scan all active GRUs in a GRU bitmap */ |
518 | #define for_each_gru_in_bitmap(gid, map) \ | 518 | #define for_each_gru_in_bitmap(gid, map) \ |
519 | for ((gid) = find_first_bit((map), GRU_MAX_GRUS); (gid) < GRU_MAX_GRUS;\ | 519 | for_each_set_bit((gid), (map), GRU_MAX_GRUS) |
520 | (gid)++, (gid) = find_next_bit((map), GRU_MAX_GRUS, (gid))) | ||
521 | 520 | ||
522 | /* Scan all active GRUs on a specific blade */ | 521 | /* Scan all active GRUs on a specific blade */ |
523 | #define for_each_gru_on_blade(gru, nid, i) \ | 522 | #define for_each_gru_on_blade(gru, nid, i) \ |
@@ -536,23 +535,17 @@ struct gru_blade_state { | |||
536 | 535 | ||
537 | /* Scan each CBR whose bit is set in a TFM (or copy of) */ | 536 | /* Scan each CBR whose bit is set in a TFM (or copy of) */ |
538 | #define for_each_cbr_in_tfm(i, map) \ | 537 | #define for_each_cbr_in_tfm(i, map) \ |
539 | for ((i) = find_first_bit(map, GRU_NUM_CBE); \ | 538 | for_each_set_bit((i), (map), GRU_NUM_CBE) |
540 | (i) < GRU_NUM_CBE; \ | ||
541 | (i)++, (i) = find_next_bit(map, GRU_NUM_CBE, i)) | ||
542 | 539 | ||
543 | /* Scan each CBR in a CBR bitmap. Note: multiple CBRs in an allocation unit */ | 540 | /* Scan each CBR in a CBR bitmap. Note: multiple CBRs in an allocation unit */ |
544 | #define for_each_cbr_in_allocation_map(i, map, k) \ | 541 | #define for_each_cbr_in_allocation_map(i, map, k) \ |
545 | for ((k) = find_first_bit(map, GRU_CBR_AU); (k) < GRU_CBR_AU; \ | 542 | for_each_set_bit((k), (map), GRU_CBR_AU) \ |
546 | (k) = find_next_bit(map, GRU_CBR_AU, (k) + 1)) \ | ||
547 | for ((i) = (k)*GRU_CBR_AU_SIZE; \ | 543 | for ((i) = (k)*GRU_CBR_AU_SIZE; \ |
548 | (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++) | 544 | (i) < ((k) + 1) * GRU_CBR_AU_SIZE; (i)++) |
549 | 545 | ||
550 | /* Scan each DSR in a DSR bitmap. Note: multiple DSRs in an allocation unit */ | 546 | /* Scan each DSR in a DSR bitmap. Note: multiple DSRs in an allocation unit */ |
551 | #define for_each_dsr_in_allocation_map(i, map, k) \ | 547 | #define for_each_dsr_in_allocation_map(i, map, k) \ |
552 | for ((k) = find_first_bit((const unsigned long *)map, GRU_DSR_AU);\ | 548 | for_each_set_bit((k), (const unsigned long *)(map), GRU_DSR_AU) \ |
553 | (k) < GRU_DSR_AU; \ | ||
554 | (k) = find_next_bit((const unsigned long *)map, \ | ||
555 | GRU_DSR_AU, (k) + 1)) \ | ||
556 | for ((i) = (k) * GRU_DSR_AU_CL; \ | 549 | for ((i) = (k) * GRU_DSR_AU_CL; \ |
557 | (i) < ((k) + 1) * GRU_DSR_AU_CL; (i)++) | 550 | (i) < ((k) + 1) * GRU_DSR_AU_CL; (i)++) |
558 | 551 | ||
diff --git a/drivers/misc/sgi-xp/xpnet.c b/drivers/misc/sgi-xp/xpnet.c index 16f0abda1423..57b152f8d1b9 100644 --- a/drivers/misc/sgi-xp/xpnet.c +++ b/drivers/misc/sgi-xp/xpnet.c | |||
@@ -475,7 +475,7 @@ xpnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
475 | 475 | ||
476 | if (skb->data[0] == 0xff) { | 476 | if (skb->data[0] == 0xff) { |
477 | /* we are being asked to broadcast to all partitions */ | 477 | /* we are being asked to broadcast to all partitions */ |
478 | for_each_bit(dest_partid, xpnet_broadcast_partitions, | 478 | for_each_set_bit(dest_partid, xpnet_broadcast_partitions, |
479 | xp_max_npartitions) { | 479 | xp_max_npartitions) { |
480 | 480 | ||
481 | xpnet_send(skb, queued_msg, start_addr, end_addr, | 481 | xpnet_send(skb, queued_msg, start_addr, end_addr, |
diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c new file mode 100644 index 000000000000..483ae5f7f68e --- /dev/null +++ b/drivers/misc/tsl2550.c | |||
@@ -0,0 +1,473 @@ | |||
1 | /* | ||
2 | * tsl2550.c - Linux kernel modules for ambient light sensor | ||
3 | * | ||
4 | * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it> | ||
5 | * Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/slab.h> | ||
25 | #include <linux/i2c.h> | ||
26 | #include <linux/mutex.h> | ||
27 | |||
28 | #define TSL2550_DRV_NAME "tsl2550" | ||
29 | #define DRIVER_VERSION "1.2" | ||
30 | |||
31 | /* | ||
32 | * Defines | ||
33 | */ | ||
34 | |||
35 | #define TSL2550_POWER_DOWN 0x00 | ||
36 | #define TSL2550_POWER_UP 0x03 | ||
37 | #define TSL2550_STANDARD_RANGE 0x18 | ||
38 | #define TSL2550_EXTENDED_RANGE 0x1d | ||
39 | #define TSL2550_READ_ADC0 0x43 | ||
40 | #define TSL2550_READ_ADC1 0x83 | ||
41 | |||
42 | /* | ||
43 | * Structs | ||
44 | */ | ||
45 | |||
46 | struct tsl2550_data { | ||
47 | struct i2c_client *client; | ||
48 | struct mutex update_lock; | ||
49 | |||
50 | unsigned int power_state:1; | ||
51 | unsigned int operating_mode:1; | ||
52 | }; | ||
53 | |||
54 | /* | ||
55 | * Global data | ||
56 | */ | ||
57 | |||
58 | static const u8 TSL2550_MODE_RANGE[2] = { | ||
59 | TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE, | ||
60 | }; | ||
61 | |||
62 | /* | ||
63 | * Management functions | ||
64 | */ | ||
65 | |||
66 | static int tsl2550_set_operating_mode(struct i2c_client *client, int mode) | ||
67 | { | ||
68 | struct tsl2550_data *data = i2c_get_clientdata(client); | ||
69 | |||
70 | int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]); | ||
71 | |||
72 | data->operating_mode = mode; | ||
73 | |||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | static int tsl2550_set_power_state(struct i2c_client *client, int state) | ||
78 | { | ||
79 | struct tsl2550_data *data = i2c_get_clientdata(client); | ||
80 | int ret; | ||
81 | |||
82 | if (state == 0) | ||
83 | ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN); | ||
84 | else { | ||
85 | ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP); | ||
86 | |||
87 | /* On power up we should reset operating mode also... */ | ||
88 | tsl2550_set_operating_mode(client, data->operating_mode); | ||
89 | } | ||
90 | |||
91 | data->power_state = state; | ||
92 | |||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd) | ||
97 | { | ||
98 | int ret; | ||
99 | |||
100 | ret = i2c_smbus_read_byte_data(client, cmd); | ||
101 | if (ret < 0) | ||
102 | return ret; | ||
103 | if (!(ret & 0x80)) | ||
104 | return -EAGAIN; | ||
105 | return ret & 0x7f; /* remove the "valid" bit */ | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * LUX calculation | ||
110 | */ | ||
111 | |||
112 | #define TSL2550_MAX_LUX 1846 | ||
113 | |||
114 | static const u8 ratio_lut[] = { | ||
115 | 100, 100, 100, 100, 100, 100, 100, 100, | ||
116 | 100, 100, 100, 100, 100, 100, 99, 99, | ||
117 | 99, 99, 99, 99, 99, 99, 99, 99, | ||
118 | 99, 99, 99, 98, 98, 98, 98, 98, | ||
119 | 98, 98, 97, 97, 97, 97, 97, 96, | ||
120 | 96, 96, 96, 95, 95, 95, 94, 94, | ||
121 | 93, 93, 93, 92, 92, 91, 91, 90, | ||
122 | 89, 89, 88, 87, 87, 86, 85, 84, | ||
123 | 83, 82, 81, 80, 79, 78, 77, 75, | ||
124 | 74, 73, 71, 69, 68, 66, 64, 62, | ||
125 | 60, 58, 56, 54, 52, 49, 47, 44, | ||
126 | 42, 41, 40, 40, 39, 39, 38, 38, | ||
127 | 37, 37, 37, 36, 36, 36, 35, 35, | ||
128 | 35, 35, 34, 34, 34, 34, 33, 33, | ||
129 | 33, 33, 32, 32, 32, 32, 32, 31, | ||
130 | 31, 31, 31, 31, 30, 30, 30, 30, | ||
131 | 30, | ||
132 | }; | ||
133 | |||
134 | static const u16 count_lut[] = { | ||
135 | 0, 1, 2, 3, 4, 5, 6, 7, | ||
136 | 8, 9, 10, 11, 12, 13, 14, 15, | ||
137 | 16, 18, 20, 22, 24, 26, 28, 30, | ||
138 | 32, 34, 36, 38, 40, 42, 44, 46, | ||
139 | 49, 53, 57, 61, 65, 69, 73, 77, | ||
140 | 81, 85, 89, 93, 97, 101, 105, 109, | ||
141 | 115, 123, 131, 139, 147, 155, 163, 171, | ||
142 | 179, 187, 195, 203, 211, 219, 227, 235, | ||
143 | 247, 263, 279, 295, 311, 327, 343, 359, | ||
144 | 375, 391, 407, 423, 439, 455, 471, 487, | ||
145 | 511, 543, 575, 607, 639, 671, 703, 735, | ||
146 | 767, 799, 831, 863, 895, 927, 959, 991, | ||
147 | 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487, | ||
148 | 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999, | ||
149 | 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991, | ||
150 | 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015, | ||
151 | }; | ||
152 | |||
153 | /* | ||
154 | * This function is described into Taos TSL2550 Designer's Notebook | ||
155 | * pages 2, 3. | ||
156 | */ | ||
157 | static int tsl2550_calculate_lux(u8 ch0, u8 ch1) | ||
158 | { | ||
159 | unsigned int lux; | ||
160 | |||
161 | /* Look up count from channel values */ | ||
162 | u16 c0 = count_lut[ch0]; | ||
163 | u16 c1 = count_lut[ch1]; | ||
164 | |||
165 | /* | ||
166 | * Calculate ratio. | ||
167 | * Note: the "128" is a scaling factor | ||
168 | */ | ||
169 | u8 r = 128; | ||
170 | |||
171 | /* Avoid division by 0 and count 1 cannot be greater than count 0 */ | ||
172 | if (c1 <= c0) | ||
173 | if (c0) { | ||
174 | r = c1 * 128 / c0; | ||
175 | |||
176 | /* Calculate LUX */ | ||
177 | lux = ((c0 - c1) * ratio_lut[r]) / 256; | ||
178 | } else | ||
179 | lux = 0; | ||
180 | else | ||
181 | return -EAGAIN; | ||
182 | |||
183 | /* LUX range check */ | ||
184 | return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux; | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * SysFS support | ||
189 | */ | ||
190 | |||
191 | static ssize_t tsl2550_show_power_state(struct device *dev, | ||
192 | struct device_attribute *attr, char *buf) | ||
193 | { | ||
194 | struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev)); | ||
195 | |||
196 | return sprintf(buf, "%u\n", data->power_state); | ||
197 | } | ||
198 | |||
199 | static ssize_t tsl2550_store_power_state(struct device *dev, | ||
200 | struct device_attribute *attr, const char *buf, size_t count) | ||
201 | { | ||
202 | struct i2c_client *client = to_i2c_client(dev); | ||
203 | struct tsl2550_data *data = i2c_get_clientdata(client); | ||
204 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
205 | int ret; | ||
206 | |||
207 | if (val < 0 || val > 1) | ||
208 | return -EINVAL; | ||
209 | |||
210 | mutex_lock(&data->update_lock); | ||
211 | ret = tsl2550_set_power_state(client, val); | ||
212 | mutex_unlock(&data->update_lock); | ||
213 | |||
214 | if (ret < 0) | ||
215 | return ret; | ||
216 | |||
217 | return count; | ||
218 | } | ||
219 | |||
220 | static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, | ||
221 | tsl2550_show_power_state, tsl2550_store_power_state); | ||
222 | |||
223 | static ssize_t tsl2550_show_operating_mode(struct device *dev, | ||
224 | struct device_attribute *attr, char *buf) | ||
225 | { | ||
226 | struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev)); | ||
227 | |||
228 | return sprintf(buf, "%u\n", data->operating_mode); | ||
229 | } | ||
230 | |||
231 | static ssize_t tsl2550_store_operating_mode(struct device *dev, | ||
232 | struct device_attribute *attr, const char *buf, size_t count) | ||
233 | { | ||
234 | struct i2c_client *client = to_i2c_client(dev); | ||
235 | struct tsl2550_data *data = i2c_get_clientdata(client); | ||
236 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
237 | int ret; | ||
238 | |||
239 | if (val < 0 || val > 1) | ||
240 | return -EINVAL; | ||
241 | |||
242 | if (data->power_state == 0) | ||
243 | return -EBUSY; | ||
244 | |||
245 | mutex_lock(&data->update_lock); | ||
246 | ret = tsl2550_set_operating_mode(client, val); | ||
247 | mutex_unlock(&data->update_lock); | ||
248 | |||
249 | if (ret < 0) | ||
250 | return ret; | ||
251 | |||
252 | return count; | ||
253 | } | ||
254 | |||
255 | static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO, | ||
256 | tsl2550_show_operating_mode, tsl2550_store_operating_mode); | ||
257 | |||
258 | static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf) | ||
259 | { | ||
260 | struct tsl2550_data *data = i2c_get_clientdata(client); | ||
261 | u8 ch0, ch1; | ||
262 | int ret; | ||
263 | |||
264 | ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0); | ||
265 | if (ret < 0) | ||
266 | return ret; | ||
267 | ch0 = ret; | ||
268 | |||
269 | ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1); | ||
270 | if (ret < 0) | ||
271 | return ret; | ||
272 | ch1 = ret; | ||
273 | |||
274 | /* Do the job */ | ||
275 | ret = tsl2550_calculate_lux(ch0, ch1); | ||
276 | if (ret < 0) | ||
277 | return ret; | ||
278 | if (data->operating_mode == 1) | ||
279 | ret *= 5; | ||
280 | |||
281 | return sprintf(buf, "%d\n", ret); | ||
282 | } | ||
283 | |||
284 | static ssize_t tsl2550_show_lux1_input(struct device *dev, | ||
285 | struct device_attribute *attr, char *buf) | ||
286 | { | ||
287 | struct i2c_client *client = to_i2c_client(dev); | ||
288 | struct tsl2550_data *data = i2c_get_clientdata(client); | ||
289 | int ret; | ||
290 | |||
291 | /* No LUX data if not operational */ | ||
292 | if (!data->power_state) | ||
293 | return -EBUSY; | ||
294 | |||
295 | mutex_lock(&data->update_lock); | ||
296 | ret = __tsl2550_show_lux(client, buf); | ||
297 | mutex_unlock(&data->update_lock); | ||
298 | |||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | static DEVICE_ATTR(lux1_input, S_IRUGO, | ||
303 | tsl2550_show_lux1_input, NULL); | ||
304 | |||
305 | static struct attribute *tsl2550_attributes[] = { | ||
306 | &dev_attr_power_state.attr, | ||
307 | &dev_attr_operating_mode.attr, | ||
308 | &dev_attr_lux1_input.attr, | ||
309 | NULL | ||
310 | }; | ||
311 | |||
312 | static const struct attribute_group tsl2550_attr_group = { | ||
313 | .attrs = tsl2550_attributes, | ||
314 | }; | ||
315 | |||
316 | /* | ||
317 | * Initialization function | ||
318 | */ | ||
319 | |||
320 | static int tsl2550_init_client(struct i2c_client *client) | ||
321 | { | ||
322 | struct tsl2550_data *data = i2c_get_clientdata(client); | ||
323 | int err; | ||
324 | |||
325 | /* | ||
326 | * Probe the chip. To do so we try to power up the device and then to | ||
327 | * read back the 0x03 code | ||
328 | */ | ||
329 | err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP); | ||
330 | if (err < 0) | ||
331 | return err; | ||
332 | if (err != TSL2550_POWER_UP) | ||
333 | return -ENODEV; | ||
334 | data->power_state = 1; | ||
335 | |||
336 | /* Set the default operating mode */ | ||
337 | err = i2c_smbus_write_byte(client, | ||
338 | TSL2550_MODE_RANGE[data->operating_mode]); | ||
339 | if (err < 0) | ||
340 | return err; | ||
341 | |||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | /* | ||
346 | * I2C init/probing/exit functions | ||
347 | */ | ||
348 | |||
349 | static struct i2c_driver tsl2550_driver; | ||
350 | static int __devinit tsl2550_probe(struct i2c_client *client, | ||
351 | const struct i2c_device_id *id) | ||
352 | { | ||
353 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||
354 | struct tsl2550_data *data; | ||
355 | int *opmode, err = 0; | ||
356 | |||
357 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE | ||
358 | | I2C_FUNC_SMBUS_READ_BYTE_DATA)) { | ||
359 | err = -EIO; | ||
360 | goto exit; | ||
361 | } | ||
362 | |||
363 | data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL); | ||
364 | if (!data) { | ||
365 | err = -ENOMEM; | ||
366 | goto exit; | ||
367 | } | ||
368 | data->client = client; | ||
369 | i2c_set_clientdata(client, data); | ||
370 | |||
371 | /* Check platform data */ | ||
372 | opmode = client->dev.platform_data; | ||
373 | if (opmode) { | ||
374 | if (*opmode < 0 || *opmode > 1) { | ||
375 | dev_err(&client->dev, "invalid operating_mode (%d)\n", | ||
376 | *opmode); | ||
377 | err = -EINVAL; | ||
378 | goto exit_kfree; | ||
379 | } | ||
380 | data->operating_mode = *opmode; | ||
381 | } else | ||
382 | data->operating_mode = 0; /* default mode is standard */ | ||
383 | dev_info(&client->dev, "%s operating mode\n", | ||
384 | data->operating_mode ? "extended" : "standard"); | ||
385 | |||
386 | mutex_init(&data->update_lock); | ||
387 | |||
388 | /* Initialize the TSL2550 chip */ | ||
389 | err = tsl2550_init_client(client); | ||
390 | if (err) | ||
391 | goto exit_kfree; | ||
392 | |||
393 | /* Register sysfs hooks */ | ||
394 | err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group); | ||
395 | if (err) | ||
396 | goto exit_kfree; | ||
397 | |||
398 | dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION); | ||
399 | |||
400 | return 0; | ||
401 | |||
402 | exit_kfree: | ||
403 | kfree(data); | ||
404 | exit: | ||
405 | return err; | ||
406 | } | ||
407 | |||
408 | static int __devexit tsl2550_remove(struct i2c_client *client) | ||
409 | { | ||
410 | sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group); | ||
411 | |||
412 | /* Power down the device */ | ||
413 | tsl2550_set_power_state(client, 0); | ||
414 | |||
415 | kfree(i2c_get_clientdata(client)); | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | #ifdef CONFIG_PM | ||
421 | |||
422 | static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg) | ||
423 | { | ||
424 | return tsl2550_set_power_state(client, 0); | ||
425 | } | ||
426 | |||
427 | static int tsl2550_resume(struct i2c_client *client) | ||
428 | { | ||
429 | return tsl2550_set_power_state(client, 1); | ||
430 | } | ||
431 | |||
432 | #else | ||
433 | |||
434 | #define tsl2550_suspend NULL | ||
435 | #define tsl2550_resume NULL | ||
436 | |||
437 | #endif /* CONFIG_PM */ | ||
438 | |||
439 | static const struct i2c_device_id tsl2550_id[] = { | ||
440 | { "tsl2550", 0 }, | ||
441 | { } | ||
442 | }; | ||
443 | MODULE_DEVICE_TABLE(i2c, tsl2550_id); | ||
444 | |||
445 | static struct i2c_driver tsl2550_driver = { | ||
446 | .driver = { | ||
447 | .name = TSL2550_DRV_NAME, | ||
448 | .owner = THIS_MODULE, | ||
449 | }, | ||
450 | .suspend = tsl2550_suspend, | ||
451 | .resume = tsl2550_resume, | ||
452 | .probe = tsl2550_probe, | ||
453 | .remove = __devexit_p(tsl2550_remove), | ||
454 | .id_table = tsl2550_id, | ||
455 | }; | ||
456 | |||
457 | static int __init tsl2550_init(void) | ||
458 | { | ||
459 | return i2c_add_driver(&tsl2550_driver); | ||
460 | } | ||
461 | |||
462 | static void __exit tsl2550_exit(void) | ||
463 | { | ||
464 | i2c_del_driver(&tsl2550_driver); | ||
465 | } | ||
466 | |||
467 | MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); | ||
468 | MODULE_DESCRIPTION("TSL2550 ambient light sensor driver"); | ||
469 | MODULE_LICENSE("GPL"); | ||
470 | MODULE_VERSION(DRIVER_VERSION); | ||
471 | |||
472 | module_init(tsl2550_init); | ||
473 | module_exit(tsl2550_exit); | ||