diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/Kconfig | 16 | ||||
-rw-r--r-- | drivers/misc/Makefile | 2 | ||||
-rw-r--r-- | drivers/misc/acer-wmi.c | 6 | ||||
-rw-r--r-- | drivers/misc/asus-laptop.c | 10 | ||||
-rw-r--r-- | drivers/misc/c2port/Kconfig | 35 | ||||
-rw-r--r-- | drivers/misc/c2port/Makefile | 3 | ||||
-rw-r--r-- | drivers/misc/c2port/c2port-duramar2150.c | 158 | ||||
-rw-r--r-- | drivers/misc/c2port/core.c | 1003 | ||||
-rw-r--r-- | drivers/misc/compal-laptop.c | 12 | ||||
-rw-r--r-- | drivers/misc/eeepc-laptop.c | 12 | ||||
-rw-r--r-- | drivers/misc/fujitsu-laptop.c | 43 | ||||
-rw-r--r-- | drivers/misc/ics932s401.c | 515 | ||||
-rw-r--r-- | drivers/misc/intel_menlow.c | 10 | ||||
-rw-r--r-- | drivers/misc/msi-laptop.c | 16 | ||||
-rw-r--r-- | drivers/misc/sgi-gru/Makefile | 4 | ||||
-rw-r--r-- | drivers/misc/sgi-gru/grufault.c | 4 | ||||
-rw-r--r-- | drivers/misc/sgi-gru/grufile.c | 2 | ||||
-rw-r--r-- | drivers/misc/sony-laptop.c | 6 | ||||
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 86 |
19 files changed, 1886 insertions, 57 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9494400e8fd0..fee7304102af 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig | |||
@@ -227,10 +227,20 @@ config HP_WMI | |||
227 | To compile this driver as a module, choose M here: the module will | 227 | To compile this driver as a module, choose M here: the module will |
228 | be called hp-wmi. | 228 | be called hp-wmi. |
229 | 229 | ||
230 | config ICS932S401 | ||
231 | tristate "Integrated Circuits ICS932S401" | ||
232 | depends on I2C && EXPERIMENTAL | ||
233 | help | ||
234 | If you say yes here you get support for the Integrated Circuits | ||
235 | ICS932S401 clock control chips. | ||
236 | |||
237 | This driver can also be built as a module. If so, the module | ||
238 | will be called ics932s401. | ||
239 | |||
230 | config MSI_LAPTOP | 240 | config MSI_LAPTOP |
231 | tristate "MSI Laptop Extras" | 241 | tristate "MSI Laptop Extras" |
232 | depends on X86 | 242 | depends on X86 |
233 | depends on ACPI_EC | 243 | depends on ACPI |
234 | depends on BACKLIGHT_CLASS_DEVICE | 244 | depends on BACKLIGHT_CLASS_DEVICE |
235 | ---help--- | 245 | ---help--- |
236 | This is a driver for laptops built by MSI (MICRO-STAR | 246 | This is a driver for laptops built by MSI (MICRO-STAR |
@@ -260,7 +270,7 @@ config PANASONIC_LAPTOP | |||
260 | config COMPAL_LAPTOP | 270 | config COMPAL_LAPTOP |
261 | tristate "Compal Laptop Extras" | 271 | tristate "Compal Laptop Extras" |
262 | depends on X86 | 272 | depends on X86 |
263 | depends on ACPI_EC | 273 | depends on ACPI |
264 | depends on BACKLIGHT_CLASS_DEVICE | 274 | depends on BACKLIGHT_CLASS_DEVICE |
265 | ---help--- | 275 | ---help--- |
266 | This is a driver for laptops built by Compal: | 276 | This is a driver for laptops built by Compal: |
@@ -488,4 +498,6 @@ config SGI_GRU_DEBUG | |||
488 | This option enables addition debugging code for the SGI GRU driver. If | 498 | This option enables addition debugging code for the SGI GRU driver. If |
489 | you are unsure, say N. | 499 | you are unsure, say N. |
490 | 500 | ||
501 | source "drivers/misc/c2port/Kconfig" | ||
502 | |||
491 | endif # MISC_DEVICES | 503 | endif # MISC_DEVICES |
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 909e2468cdc9..817f7f5ab3bd 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile | |||
@@ -14,6 +14,7 @@ obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o | |||
14 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o | 14 | obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o |
15 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o | 15 | obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o |
16 | obj-$(CONFIG_HP_WMI) += hp-wmi.o | 16 | obj-$(CONFIG_HP_WMI) += hp-wmi.o |
17 | obj-$(CONFIG_ICS932S401) += ics932s401.o | ||
17 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o | 18 | obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o |
18 | obj-$(CONFIG_LKDTM) += lkdtm.o | 19 | obj-$(CONFIG_LKDTM) += lkdtm.o |
19 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o | 20 | obj-$(CONFIG_TIFM_CORE) += tifm_core.o |
@@ -31,3 +32,4 @@ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o | |||
31 | obj-$(CONFIG_SGI_XP) += sgi-xp/ | 32 | obj-$(CONFIG_SGI_XP) += sgi-xp/ |
32 | obj-$(CONFIG_SGI_GRU) += sgi-gru/ | 33 | obj-$(CONFIG_SGI_GRU) += sgi-gru/ |
33 | obj-$(CONFIG_HP_ILO) += hpilo.o | 34 | obj-$(CONFIG_HP_ILO) += hpilo.o |
35 | obj-$(CONFIG_C2PORT) += c2port/ | ||
diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index 0532a2de2ce4..94c9f911824e 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c | |||
@@ -1297,6 +1297,12 @@ static int __init acer_wmi_init(void) | |||
1297 | 1297 | ||
1298 | set_quirks(); | 1298 | set_quirks(); |
1299 | 1299 | ||
1300 | if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { | ||
1301 | interface->capability &= ~ACER_CAP_BRIGHTNESS; | ||
1302 | printk(ACER_INFO "Brightness must be controlled by " | ||
1303 | "generic video driver\n"); | ||
1304 | } | ||
1305 | |||
1300 | if (platform_driver_register(&acer_platform_driver)) { | 1306 | if (platform_driver_register(&acer_platform_driver)) { |
1301 | printk(ACER_ERR "Unable to register platform driver.\n"); | 1307 | printk(ACER_ERR "Unable to register platform driver.\n"); |
1302 | goto error_platform_register; | 1308 | goto error_platform_register; |
diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index a9d5228724a6..8fb8b3591048 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c | |||
@@ -1208,9 +1208,13 @@ static int __init asus_laptop_init(void) | |||
1208 | 1208 | ||
1209 | dev = acpi_get_physical_device(hotk->device->handle); | 1209 | dev = acpi_get_physical_device(hotk->device->handle); |
1210 | 1210 | ||
1211 | result = asus_backlight_init(dev); | 1211 | if (!acpi_video_backlight_support()) { |
1212 | if (result) | 1212 | result = asus_backlight_init(dev); |
1213 | goto fail_backlight; | 1213 | if (result) |
1214 | goto fail_backlight; | ||
1215 | } else | ||
1216 | printk(ASUS_INFO "Brightness ignored, must be controlled by " | ||
1217 | "ACPI video driver\n"); | ||
1214 | 1218 | ||
1215 | result = asus_led_init(dev); | 1219 | result = asus_led_init(dev); |
1216 | if (result) | 1220 | if (result) |
diff --git a/drivers/misc/c2port/Kconfig b/drivers/misc/c2port/Kconfig new file mode 100644 index 000000000000..e46af9a5810d --- /dev/null +++ b/drivers/misc/c2port/Kconfig | |||
@@ -0,0 +1,35 @@ | |||
1 | # | ||
2 | # C2 port devices | ||
3 | # | ||
4 | |||
5 | menuconfig C2PORT | ||
6 | tristate "Silicon Labs C2 port support (EXPERIMENTAL)" | ||
7 | depends on EXPERIMENTAL | ||
8 | default no | ||
9 | help | ||
10 | This option enables support for Silicon Labs C2 port used to | ||
11 | program Silicon micro controller chips (and other 8051 compatible). | ||
12 | |||
13 | If your board have no such micro controllers you don't need this | ||
14 | interface at all. | ||
15 | |||
16 | To compile this driver as a module, choose M here: the module will | ||
17 | be called c2port_core. Note that you also need a client module | ||
18 | usually called c2port-*. | ||
19 | |||
20 | If you are not sure, say N here. | ||
21 | |||
22 | if C2PORT | ||
23 | |||
24 | config C2PORT_DURAMAR_2150 | ||
25 | tristate "C2 port support for Eurotech's Duramar 2150 (EXPERIMENTAL)" | ||
26 | depends on X86 && C2PORT | ||
27 | default no | ||
28 | help | ||
29 | This option enables C2 support for the Eurotech's Duramar 2150 | ||
30 | on board micro controller. | ||
31 | |||
32 | To compile this driver as a module, choose M here: the module will | ||
33 | be called c2port-duramar2150. | ||
34 | |||
35 | endif # C2PORT | ||
diff --git a/drivers/misc/c2port/Makefile b/drivers/misc/c2port/Makefile new file mode 100644 index 000000000000..3b2cf43d60f5 --- /dev/null +++ b/drivers/misc/c2port/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | obj-$(CONFIG_C2PORT) += core.o | ||
2 | |||
3 | obj-$(CONFIG_C2PORT_DURAMAR_2150) += c2port-duramar2150.o | ||
diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c new file mode 100644 index 000000000000..338dcc121507 --- /dev/null +++ b/drivers/misc/c2port/c2port-duramar2150.c | |||
@@ -0,0 +1,158 @@ | |||
1 | /* | ||
2 | * Silicon Labs C2 port Linux support for Eurotech Duramar 2150 | ||
3 | * | ||
4 | * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> | ||
5 | * Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation | ||
10 | */ | ||
11 | |||
12 | #include <linux/errno.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/c2port.h> | ||
19 | |||
20 | #define DATA_PORT 0x325 | ||
21 | #define DIR_PORT 0x326 | ||
22 | #define C2D (1 << 0) | ||
23 | #define C2CK (1 << 1) | ||
24 | |||
25 | static DEFINE_MUTEX(update_lock); | ||
26 | |||
27 | /* | ||
28 | * C2 port operations | ||
29 | */ | ||
30 | |||
31 | static void duramar2150_c2port_access(struct c2port_device *dev, int status) | ||
32 | { | ||
33 | u8 v; | ||
34 | |||
35 | mutex_lock(&update_lock); | ||
36 | |||
37 | v = inb(DIR_PORT); | ||
38 | |||
39 | /* 0 = input, 1 = output */ | ||
40 | if (status) | ||
41 | outb(v | (C2D | C2CK), DIR_PORT); | ||
42 | else | ||
43 | /* When access is "off" is important that both lines are set | ||
44 | * as inputs or hi-impedence */ | ||
45 | outb(v & ~(C2D | C2CK), DIR_PORT); | ||
46 | |||
47 | mutex_unlock(&update_lock); | ||
48 | } | ||
49 | |||
50 | static void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir) | ||
51 | { | ||
52 | u8 v; | ||
53 | |||
54 | mutex_lock(&update_lock); | ||
55 | |||
56 | v = inb(DIR_PORT); | ||
57 | |||
58 | if (dir) | ||
59 | outb(v & ~C2D, DIR_PORT); | ||
60 | else | ||
61 | outb(v | C2D, DIR_PORT); | ||
62 | |||
63 | mutex_unlock(&update_lock); | ||
64 | } | ||
65 | |||
66 | static int duramar2150_c2port_c2d_get(struct c2port_device *dev) | ||
67 | { | ||
68 | return inb(DATA_PORT) & C2D; | ||
69 | } | ||
70 | |||
71 | static void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status) | ||
72 | { | ||
73 | u8 v; | ||
74 | |||
75 | mutex_lock(&update_lock); | ||
76 | |||
77 | v = inb(DATA_PORT); | ||
78 | |||
79 | if (status) | ||
80 | outb(v | C2D, DATA_PORT); | ||
81 | else | ||
82 | outb(v & ~C2D, DATA_PORT); | ||
83 | |||
84 | mutex_unlock(&update_lock); | ||
85 | } | ||
86 | |||
87 | static void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status) | ||
88 | { | ||
89 | u8 v; | ||
90 | |||
91 | mutex_lock(&update_lock); | ||
92 | |||
93 | v = inb(DATA_PORT); | ||
94 | |||
95 | if (status) | ||
96 | outb(v | C2CK, DATA_PORT); | ||
97 | else | ||
98 | outb(v & ~C2CK, DATA_PORT); | ||
99 | |||
100 | mutex_unlock(&update_lock); | ||
101 | } | ||
102 | |||
103 | static struct c2port_ops duramar2150_c2port_ops = { | ||
104 | .block_size = 512, /* bytes */ | ||
105 | .blocks_num = 30, /* total flash size: 15360 bytes */ | ||
106 | |||
107 | .access = duramar2150_c2port_access, | ||
108 | .c2d_dir = duramar2150_c2port_c2d_dir, | ||
109 | .c2d_get = duramar2150_c2port_c2d_get, | ||
110 | .c2d_set = duramar2150_c2port_c2d_set, | ||
111 | .c2ck_set = duramar2150_c2port_c2ck_set, | ||
112 | }; | ||
113 | |||
114 | static struct c2port_device *duramar2150_c2port_dev; | ||
115 | |||
116 | /* | ||
117 | * Module stuff | ||
118 | */ | ||
119 | |||
120 | static int __init duramar2150_c2port_init(void) | ||
121 | { | ||
122 | struct resource *res; | ||
123 | int ret = 0; | ||
124 | |||
125 | res = request_region(0x325, 2, "c2port"); | ||
126 | if (!res) | ||
127 | return -EBUSY; | ||
128 | |||
129 | duramar2150_c2port_dev = c2port_device_register("uc", | ||
130 | &duramar2150_c2port_ops, NULL); | ||
131 | if (!duramar2150_c2port_dev) { | ||
132 | ret = -ENODEV; | ||
133 | goto free_region; | ||
134 | } | ||
135 | |||
136 | return 0; | ||
137 | |||
138 | free_region: | ||
139 | release_region(0x325, 2); | ||
140 | return ret; | ||
141 | } | ||
142 | |||
143 | static void __exit duramar2150_c2port_exit(void) | ||
144 | { | ||
145 | /* Setup the GPIOs as input by default (access = 0) */ | ||
146 | duramar2150_c2port_access(duramar2150_c2port_dev, 0); | ||
147 | |||
148 | c2port_device_unregister(duramar2150_c2port_dev); | ||
149 | |||
150 | release_region(0x325, 2); | ||
151 | } | ||
152 | |||
153 | module_init(duramar2150_c2port_init); | ||
154 | module_exit(duramar2150_c2port_exit); | ||
155 | |||
156 | MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); | ||
157 | MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150"); | ||
158 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c new file mode 100644 index 000000000000..0207dd59090d --- /dev/null +++ b/drivers/misc/c2port/core.c | |||
@@ -0,0 +1,1003 @@ | |||
1 | /* | ||
2 | * Silicon Labs C2 port core Linux support | ||
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 it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/ctype.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/idr.h> | ||
21 | #include <linux/sched.h> | ||
22 | |||
23 | #include <linux/c2port.h> | ||
24 | |||
25 | #define DRIVER_NAME "c2port" | ||
26 | #define DRIVER_VERSION "0.51.0" | ||
27 | |||
28 | static DEFINE_SPINLOCK(c2port_idr_lock); | ||
29 | static DEFINE_IDR(c2port_idr); | ||
30 | |||
31 | /* | ||
32 | * Local variables | ||
33 | */ | ||
34 | |||
35 | static struct class *c2port_class; | ||
36 | |||
37 | /* | ||
38 | * C2 registers & commands defines | ||
39 | */ | ||
40 | |||
41 | /* C2 registers */ | ||
42 | #define C2PORT_DEVICEID 0x00 | ||
43 | #define C2PORT_REVID 0x01 | ||
44 | #define C2PORT_FPCTL 0x02 | ||
45 | #define C2PORT_FPDAT 0xB4 | ||
46 | |||
47 | /* C2 interface commands */ | ||
48 | #define C2PORT_GET_VERSION 0x01 | ||
49 | #define C2PORT_DEVICE_ERASE 0x03 | ||
50 | #define C2PORT_BLOCK_READ 0x06 | ||
51 | #define C2PORT_BLOCK_WRITE 0x07 | ||
52 | #define C2PORT_PAGE_ERASE 0x08 | ||
53 | |||
54 | /* C2 status return codes */ | ||
55 | #define C2PORT_INVALID_COMMAND 0x00 | ||
56 | #define C2PORT_COMMAND_FAILED 0x02 | ||
57 | #define C2PORT_COMMAND_OK 0x0d | ||
58 | |||
59 | /* | ||
60 | * C2 port low level signal managements | ||
61 | */ | ||
62 | |||
63 | static void c2port_reset(struct c2port_device *dev) | ||
64 | { | ||
65 | struct c2port_ops *ops = dev->ops; | ||
66 | |||
67 | /* To reset the device we have to keep clock line low for at least | ||
68 | * 20us. | ||
69 | */ | ||
70 | local_irq_disable(); | ||
71 | ops->c2ck_set(dev, 0); | ||
72 | udelay(25); | ||
73 | ops->c2ck_set(dev, 1); | ||
74 | local_irq_enable(); | ||
75 | |||
76 | udelay(1); | ||
77 | } | ||
78 | |||
79 | static void c2port_strobe_ck(struct c2port_device *dev) | ||
80 | { | ||
81 | struct c2port_ops *ops = dev->ops; | ||
82 | |||
83 | /* During hi-low-hi transition we disable local IRQs to avoid | ||
84 | * interructions since C2 port specification says that it must be | ||
85 | * shorter than 5us, otherwise the microcontroller may consider | ||
86 | * it as a reset signal! | ||
87 | */ | ||
88 | local_irq_disable(); | ||
89 | ops->c2ck_set(dev, 0); | ||
90 | udelay(1); | ||
91 | ops->c2ck_set(dev, 1); | ||
92 | local_irq_enable(); | ||
93 | |||
94 | udelay(1); | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * C2 port basic functions | ||
99 | */ | ||
100 | |||
101 | static void c2port_write_ar(struct c2port_device *dev, u8 addr) | ||
102 | { | ||
103 | struct c2port_ops *ops = dev->ops; | ||
104 | int i; | ||
105 | |||
106 | /* START field */ | ||
107 | c2port_strobe_ck(dev); | ||
108 | |||
109 | /* INS field (11b, LSB first) */ | ||
110 | ops->c2d_dir(dev, 0); | ||
111 | ops->c2d_set(dev, 1); | ||
112 | c2port_strobe_ck(dev); | ||
113 | ops->c2d_set(dev, 1); | ||
114 | c2port_strobe_ck(dev); | ||
115 | |||
116 | /* ADDRESS field */ | ||
117 | for (i = 0; i < 8; i++) { | ||
118 | ops->c2d_set(dev, addr & 0x01); | ||
119 | c2port_strobe_ck(dev); | ||
120 | |||
121 | addr >>= 1; | ||
122 | } | ||
123 | |||
124 | /* STOP field */ | ||
125 | ops->c2d_dir(dev, 1); | ||
126 | c2port_strobe_ck(dev); | ||
127 | } | ||
128 | |||
129 | static int c2port_read_ar(struct c2port_device *dev, u8 *addr) | ||
130 | { | ||
131 | struct c2port_ops *ops = dev->ops; | ||
132 | int i; | ||
133 | |||
134 | /* START field */ | ||
135 | c2port_strobe_ck(dev); | ||
136 | |||
137 | /* INS field (10b, LSB first) */ | ||
138 | ops->c2d_dir(dev, 0); | ||
139 | ops->c2d_set(dev, 0); | ||
140 | c2port_strobe_ck(dev); | ||
141 | ops->c2d_set(dev, 1); | ||
142 | c2port_strobe_ck(dev); | ||
143 | |||
144 | /* ADDRESS field */ | ||
145 | ops->c2d_dir(dev, 1); | ||
146 | *addr = 0; | ||
147 | for (i = 0; i < 8; i++) { | ||
148 | *addr >>= 1; /* shift in 8-bit ADDRESS field LSB first */ | ||
149 | |||
150 | c2port_strobe_ck(dev); | ||
151 | if (ops->c2d_get(dev)) | ||
152 | *addr |= 0x80; | ||
153 | } | ||
154 | |||
155 | /* STOP field */ | ||
156 | c2port_strobe_ck(dev); | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int c2port_write_dr(struct c2port_device *dev, u8 data) | ||
162 | { | ||
163 | struct c2port_ops *ops = dev->ops; | ||
164 | int timeout, i; | ||
165 | |||
166 | /* START field */ | ||
167 | c2port_strobe_ck(dev); | ||
168 | |||
169 | /* INS field (01b, LSB first) */ | ||
170 | ops->c2d_dir(dev, 0); | ||
171 | ops->c2d_set(dev, 1); | ||
172 | c2port_strobe_ck(dev); | ||
173 | ops->c2d_set(dev, 0); | ||
174 | c2port_strobe_ck(dev); | ||
175 | |||
176 | /* LENGTH field (00b, LSB first -> 1 byte) */ | ||
177 | ops->c2d_set(dev, 0); | ||
178 | c2port_strobe_ck(dev); | ||
179 | ops->c2d_set(dev, 0); | ||
180 | c2port_strobe_ck(dev); | ||
181 | |||
182 | /* DATA field */ | ||
183 | for (i = 0; i < 8; i++) { | ||
184 | ops->c2d_set(dev, data & 0x01); | ||
185 | c2port_strobe_ck(dev); | ||
186 | |||
187 | data >>= 1; | ||
188 | } | ||
189 | |||
190 | /* WAIT field */ | ||
191 | ops->c2d_dir(dev, 1); | ||
192 | timeout = 20; | ||
193 | do { | ||
194 | c2port_strobe_ck(dev); | ||
195 | if (ops->c2d_get(dev)) | ||
196 | break; | ||
197 | |||
198 | udelay(1); | ||
199 | } while (--timeout > 0); | ||
200 | if (timeout == 0) | ||
201 | return -EIO; | ||
202 | |||
203 | /* STOP field */ | ||
204 | c2port_strobe_ck(dev); | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int c2port_read_dr(struct c2port_device *dev, u8 *data) | ||
210 | { | ||
211 | struct c2port_ops *ops = dev->ops; | ||
212 | int timeout, i; | ||
213 | |||
214 | /* START field */ | ||
215 | c2port_strobe_ck(dev); | ||
216 | |||
217 | /* INS field (00b, LSB first) */ | ||
218 | ops->c2d_dir(dev, 0); | ||
219 | ops->c2d_set(dev, 0); | ||
220 | c2port_strobe_ck(dev); | ||
221 | ops->c2d_set(dev, 0); | ||
222 | c2port_strobe_ck(dev); | ||
223 | |||
224 | /* LENGTH field (00b, LSB first -> 1 byte) */ | ||
225 | ops->c2d_set(dev, 0); | ||
226 | c2port_strobe_ck(dev); | ||
227 | ops->c2d_set(dev, 0); | ||
228 | c2port_strobe_ck(dev); | ||
229 | |||
230 | /* WAIT field */ | ||
231 | ops->c2d_dir(dev, 1); | ||
232 | timeout = 20; | ||
233 | do { | ||
234 | c2port_strobe_ck(dev); | ||
235 | if (ops->c2d_get(dev)) | ||
236 | break; | ||
237 | |||
238 | udelay(1); | ||
239 | } while (--timeout > 0); | ||
240 | if (timeout == 0) | ||
241 | return -EIO; | ||
242 | |||
243 | /* DATA field */ | ||
244 | *data = 0; | ||
245 | for (i = 0; i < 8; i++) { | ||
246 | *data >>= 1; /* shift in 8-bit DATA field LSB first */ | ||
247 | |||
248 | c2port_strobe_ck(dev); | ||
249 | if (ops->c2d_get(dev)) | ||
250 | *data |= 0x80; | ||
251 | } | ||
252 | |||
253 | /* STOP field */ | ||
254 | c2port_strobe_ck(dev); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static int c2port_poll_in_busy(struct c2port_device *dev) | ||
260 | { | ||
261 | u8 addr; | ||
262 | int ret, timeout = 20; | ||
263 | |||
264 | do { | ||
265 | ret = (c2port_read_ar(dev, &addr)); | ||
266 | if (ret < 0) | ||
267 | return -EIO; | ||
268 | |||
269 | if (!(addr & 0x02)) | ||
270 | break; | ||
271 | |||
272 | udelay(1); | ||
273 | } while (--timeout > 0); | ||
274 | if (timeout == 0) | ||
275 | return -EIO; | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int c2port_poll_out_ready(struct c2port_device *dev) | ||
281 | { | ||
282 | u8 addr; | ||
283 | int ret, timeout = 10000; /* erase flash needs long time... */ | ||
284 | |||
285 | do { | ||
286 | ret = (c2port_read_ar(dev, &addr)); | ||
287 | if (ret < 0) | ||
288 | return -EIO; | ||
289 | |||
290 | if (addr & 0x01) | ||
291 | break; | ||
292 | |||
293 | udelay(1); | ||
294 | } while (--timeout > 0); | ||
295 | if (timeout == 0) | ||
296 | return -EIO; | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | /* | ||
302 | * sysfs methods | ||
303 | */ | ||
304 | |||
305 | static ssize_t c2port_show_name(struct device *dev, | ||
306 | struct device_attribute *attr, char *buf) | ||
307 | { | ||
308 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
309 | |||
310 | return sprintf(buf, "%s\n", c2dev->name); | ||
311 | } | ||
312 | |||
313 | static ssize_t c2port_show_flash_blocks_num(struct device *dev, | ||
314 | struct device_attribute *attr, char *buf) | ||
315 | { | ||
316 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
317 | struct c2port_ops *ops = c2dev->ops; | ||
318 | |||
319 | return sprintf(buf, "%d\n", ops->blocks_num); | ||
320 | } | ||
321 | |||
322 | static ssize_t c2port_show_flash_block_size(struct device *dev, | ||
323 | struct device_attribute *attr, char *buf) | ||
324 | { | ||
325 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
326 | struct c2port_ops *ops = c2dev->ops; | ||
327 | |||
328 | return sprintf(buf, "%d\n", ops->block_size); | ||
329 | } | ||
330 | |||
331 | static ssize_t c2port_show_flash_size(struct device *dev, | ||
332 | struct device_attribute *attr, char *buf) | ||
333 | { | ||
334 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
335 | struct c2port_ops *ops = c2dev->ops; | ||
336 | |||
337 | return sprintf(buf, "%d\n", ops->blocks_num * ops->block_size); | ||
338 | } | ||
339 | |||
340 | static ssize_t c2port_show_access(struct device *dev, | ||
341 | struct device_attribute *attr, char *buf) | ||
342 | { | ||
343 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
344 | |||
345 | return sprintf(buf, "%d\n", c2dev->access); | ||
346 | } | ||
347 | |||
348 | static ssize_t c2port_store_access(struct device *dev, | ||
349 | struct device_attribute *attr, | ||
350 | const char *buf, size_t count) | ||
351 | { | ||
352 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
353 | struct c2port_ops *ops = c2dev->ops; | ||
354 | int status, ret; | ||
355 | |||
356 | ret = sscanf(buf, "%d", &status); | ||
357 | if (ret != 1) | ||
358 | return -EINVAL; | ||
359 | |||
360 | mutex_lock(&c2dev->mutex); | ||
361 | |||
362 | c2dev->access = !!status; | ||
363 | |||
364 | /* If access is "on" clock should be HIGH _before_ setting the line | ||
365 | * as output and data line should be set as INPUT anyway */ | ||
366 | if (c2dev->access) | ||
367 | ops->c2ck_set(c2dev, 1); | ||
368 | ops->access(c2dev, c2dev->access); | ||
369 | if (c2dev->access) | ||
370 | ops->c2d_dir(c2dev, 1); | ||
371 | |||
372 | mutex_unlock(&c2dev->mutex); | ||
373 | |||
374 | return count; | ||
375 | } | ||
376 | |||
377 | static ssize_t c2port_store_reset(struct device *dev, | ||
378 | struct device_attribute *attr, | ||
379 | const char *buf, size_t count) | ||
380 | { | ||
381 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
382 | |||
383 | /* Check the device access status */ | ||
384 | if (!c2dev->access) | ||
385 | return -EBUSY; | ||
386 | |||
387 | mutex_lock(&c2dev->mutex); | ||
388 | |||
389 | c2port_reset(c2dev); | ||
390 | c2dev->flash_access = 0; | ||
391 | |||
392 | mutex_unlock(&c2dev->mutex); | ||
393 | |||
394 | return count; | ||
395 | } | ||
396 | |||
397 | static ssize_t __c2port_show_dev_id(struct c2port_device *dev, char *buf) | ||
398 | { | ||
399 | u8 data; | ||
400 | int ret; | ||
401 | |||
402 | /* Select DEVICEID register for C2 data register accesses */ | ||
403 | c2port_write_ar(dev, C2PORT_DEVICEID); | ||
404 | |||
405 | /* Read and return the device ID register */ | ||
406 | ret = c2port_read_dr(dev, &data); | ||
407 | if (ret < 0) | ||
408 | return ret; | ||
409 | |||
410 | return sprintf(buf, "%d\n", data); | ||
411 | } | ||
412 | |||
413 | static ssize_t c2port_show_dev_id(struct device *dev, | ||
414 | struct device_attribute *attr, char *buf) | ||
415 | { | ||
416 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
417 | ssize_t ret; | ||
418 | |||
419 | /* Check the device access status */ | ||
420 | if (!c2dev->access) | ||
421 | return -EBUSY; | ||
422 | |||
423 | mutex_lock(&c2dev->mutex); | ||
424 | ret = __c2port_show_dev_id(c2dev, buf); | ||
425 | mutex_unlock(&c2dev->mutex); | ||
426 | |||
427 | if (ret < 0) | ||
428 | dev_err(dev, "cannot read from %s\n", c2dev->name); | ||
429 | |||
430 | return ret; | ||
431 | } | ||
432 | |||
433 | static ssize_t __c2port_show_rev_id(struct c2port_device *dev, char *buf) | ||
434 | { | ||
435 | u8 data; | ||
436 | int ret; | ||
437 | |||
438 | /* Select REVID register for C2 data register accesses */ | ||
439 | c2port_write_ar(dev, C2PORT_REVID); | ||
440 | |||
441 | /* Read and return the revision ID register */ | ||
442 | ret = c2port_read_dr(dev, &data); | ||
443 | if (ret < 0) | ||
444 | return ret; | ||
445 | |||
446 | return sprintf(buf, "%d\n", data); | ||
447 | } | ||
448 | |||
449 | static ssize_t c2port_show_rev_id(struct device *dev, | ||
450 | struct device_attribute *attr, char *buf) | ||
451 | { | ||
452 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
453 | ssize_t ret; | ||
454 | |||
455 | /* Check the device access status */ | ||
456 | if (!c2dev->access) | ||
457 | return -EBUSY; | ||
458 | |||
459 | mutex_lock(&c2dev->mutex); | ||
460 | ret = __c2port_show_rev_id(c2dev, buf); | ||
461 | mutex_unlock(&c2dev->mutex); | ||
462 | |||
463 | if (ret < 0) | ||
464 | dev_err(c2dev->dev, "cannot read from %s\n", c2dev->name); | ||
465 | |||
466 | return ret; | ||
467 | } | ||
468 | |||
469 | static ssize_t c2port_show_flash_access(struct device *dev, | ||
470 | struct device_attribute *attr, char *buf) | ||
471 | { | ||
472 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
473 | |||
474 | return sprintf(buf, "%d\n", c2dev->flash_access); | ||
475 | } | ||
476 | |||
477 | static ssize_t __c2port_store_flash_access(struct c2port_device *dev, | ||
478 | int status) | ||
479 | { | ||
480 | int ret; | ||
481 | |||
482 | /* Check the device access status */ | ||
483 | if (!dev->access) | ||
484 | return -EBUSY; | ||
485 | |||
486 | dev->flash_access = !!status; | ||
487 | |||
488 | /* If flash_access is off we have nothing to do... */ | ||
489 | if (dev->flash_access == 0) | ||
490 | return 0; | ||
491 | |||
492 | /* Target the C2 flash programming control register for C2 data | ||
493 | * register access */ | ||
494 | c2port_write_ar(dev, C2PORT_FPCTL); | ||
495 | |||
496 | /* Write the first keycode to enable C2 Flash programming */ | ||
497 | ret = c2port_write_dr(dev, 0x02); | ||
498 | if (ret < 0) | ||
499 | return ret; | ||
500 | |||
501 | /* Write the second keycode to enable C2 Flash programming */ | ||
502 | ret = c2port_write_dr(dev, 0x01); | ||
503 | if (ret < 0) | ||
504 | return ret; | ||
505 | |||
506 | /* Delay for at least 20ms to ensure the target is ready for | ||
507 | * C2 flash programming */ | ||
508 | mdelay(25); | ||
509 | |||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static ssize_t c2port_store_flash_access(struct device *dev, | ||
514 | struct device_attribute *attr, | ||
515 | const char *buf, size_t count) | ||
516 | { | ||
517 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
518 | int status; | ||
519 | ssize_t ret; | ||
520 | |||
521 | ret = sscanf(buf, "%d", &status); | ||
522 | if (ret != 1) | ||
523 | return -EINVAL; | ||
524 | |||
525 | mutex_lock(&c2dev->mutex); | ||
526 | ret = __c2port_store_flash_access(c2dev, status); | ||
527 | mutex_unlock(&c2dev->mutex); | ||
528 | |||
529 | if (ret < 0) { | ||
530 | dev_err(c2dev->dev, "cannot enable %s flash programming\n", | ||
531 | c2dev->name); | ||
532 | return ret; | ||
533 | } | ||
534 | |||
535 | return count; | ||
536 | } | ||
537 | |||
538 | static ssize_t __c2port_write_flash_erase(struct c2port_device *dev) | ||
539 | { | ||
540 | u8 status; | ||
541 | int ret; | ||
542 | |||
543 | /* Target the C2 flash programming data register for C2 data register | ||
544 | * access. | ||
545 | */ | ||
546 | c2port_write_ar(dev, C2PORT_FPDAT); | ||
547 | |||
548 | /* Send device erase command */ | ||
549 | c2port_write_dr(dev, C2PORT_DEVICE_ERASE); | ||
550 | |||
551 | /* Wait for input acknowledge */ | ||
552 | ret = c2port_poll_in_busy(dev); | ||
553 | if (ret < 0) | ||
554 | return ret; | ||
555 | |||
556 | /* Should check status before starting FLASH access sequence */ | ||
557 | |||
558 | /* Wait for status information */ | ||
559 | ret = c2port_poll_out_ready(dev); | ||
560 | if (ret < 0) | ||
561 | return ret; | ||
562 | |||
563 | /* Read flash programming interface status */ | ||
564 | ret = c2port_read_dr(dev, &status); | ||
565 | if (ret < 0) | ||
566 | return ret; | ||
567 | if (status != C2PORT_COMMAND_OK) | ||
568 | return -EBUSY; | ||
569 | |||
570 | /* Send a three-byte arming sequence to enable the device erase. | ||
571 | * If the sequence is not received correctly, the command will be | ||
572 | * ignored. | ||
573 | * Sequence is: 0xde, 0xad, 0xa5. | ||
574 | */ | ||
575 | c2port_write_dr(dev, 0xde); | ||
576 | ret = c2port_poll_in_busy(dev); | ||
577 | if (ret < 0) | ||
578 | return ret; | ||
579 | c2port_write_dr(dev, 0xad); | ||
580 | ret = c2port_poll_in_busy(dev); | ||
581 | if (ret < 0) | ||
582 | return ret; | ||
583 | c2port_write_dr(dev, 0xa5); | ||
584 | ret = c2port_poll_in_busy(dev); | ||
585 | if (ret < 0) | ||
586 | return ret; | ||
587 | |||
588 | ret = c2port_poll_out_ready(dev); | ||
589 | if (ret < 0) | ||
590 | return ret; | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | static ssize_t c2port_store_flash_erase(struct device *dev, | ||
596 | struct device_attribute *attr, | ||
597 | const char *buf, size_t count) | ||
598 | { | ||
599 | struct c2port_device *c2dev = dev_get_drvdata(dev); | ||
600 | int ret; | ||
601 | |||
602 | /* Check the device and flash access status */ | ||
603 | if (!c2dev->access || !c2dev->flash_access) | ||
604 | return -EBUSY; | ||
605 | |||
606 | mutex_lock(&c2dev->mutex); | ||
607 | ret = __c2port_write_flash_erase(c2dev); | ||
608 | mutex_unlock(&c2dev->mutex); | ||
609 | |||
610 | if (ret < 0) { | ||
611 | dev_err(c2dev->dev, "cannot erase %s flash\n", c2dev->name); | ||
612 | return ret; | ||
613 | } | ||
614 | |||
615 | return count; | ||
616 | } | ||
617 | |||
618 | static ssize_t __c2port_read_flash_data(struct c2port_device *dev, | ||
619 | char *buffer, loff_t offset, size_t count) | ||
620 | { | ||
621 | struct c2port_ops *ops = dev->ops; | ||
622 | u8 status, nread = 128; | ||
623 | int i, ret; | ||
624 | |||
625 | /* Check for flash end */ | ||
626 | if (offset >= ops->block_size * ops->blocks_num) | ||
627 | return 0; | ||
628 | |||
629 | if (ops->block_size * ops->blocks_num - offset < nread) | ||
630 | nread = ops->block_size * ops->blocks_num - offset; | ||
631 | if (count < nread) | ||
632 | nread = count; | ||
633 | if (nread == 0) | ||
634 | return nread; | ||
635 | |||
636 | /* Target the C2 flash programming data register for C2 data register | ||
637 | * access */ | ||
638 | c2port_write_ar(dev, C2PORT_FPDAT); | ||
639 | |||
640 | /* Send flash block read command */ | ||
641 | c2port_write_dr(dev, C2PORT_BLOCK_READ); | ||
642 | |||
643 | /* Wait for input acknowledge */ | ||
644 | ret = c2port_poll_in_busy(dev); | ||
645 | if (ret < 0) | ||
646 | return ret; | ||
647 | |||
648 | /* Should check status before starting FLASH access sequence */ | ||
649 | |||
650 | /* Wait for status information */ | ||
651 | ret = c2port_poll_out_ready(dev); | ||
652 | if (ret < 0) | ||
653 | return ret; | ||
654 | |||
655 | /* Read flash programming interface status */ | ||
656 | ret = c2port_read_dr(dev, &status); | ||
657 | if (ret < 0) | ||
658 | return ret; | ||
659 | if (status != C2PORT_COMMAND_OK) | ||
660 | return -EBUSY; | ||
661 | |||
662 | /* Send address high byte */ | ||
663 | c2port_write_dr(dev, offset >> 8); | ||
664 | ret = c2port_poll_in_busy(dev); | ||
665 | if (ret < 0) | ||
666 | return ret; | ||
667 | |||
668 | /* Send address low byte */ | ||
669 | c2port_write_dr(dev, offset & 0x00ff); | ||
670 | ret = c2port_poll_in_busy(dev); | ||
671 | if (ret < 0) | ||
672 | return ret; | ||
673 | |||
674 | /* Send address block size */ | ||
675 | c2port_write_dr(dev, nread); | ||
676 | ret = c2port_poll_in_busy(dev); | ||
677 | if (ret < 0) | ||
678 | return ret; | ||
679 | |||
680 | /* Should check status before reading FLASH block */ | ||
681 | |||
682 | /* Wait for status information */ | ||
683 | ret = c2port_poll_out_ready(dev); | ||
684 | if (ret < 0) | ||
685 | return ret; | ||
686 | |||
687 | /* Read flash programming interface status */ | ||
688 | ret = c2port_read_dr(dev, &status); | ||
689 | if (ret < 0) | ||
690 | return ret; | ||
691 | if (status != C2PORT_COMMAND_OK) | ||
692 | return -EBUSY; | ||
693 | |||
694 | /* Read flash block */ | ||
695 | for (i = 0; i < nread; i++) { | ||
696 | ret = c2port_poll_out_ready(dev); | ||
697 | if (ret < 0) | ||
698 | return ret; | ||
699 | |||
700 | ret = c2port_read_dr(dev, buffer+i); | ||
701 | if (ret < 0) | ||
702 | return ret; | ||
703 | } | ||
704 | |||
705 | return nread; | ||
706 | } | ||
707 | |||
708 | static ssize_t c2port_read_flash_data(struct kobject *kobj, | ||
709 | struct bin_attribute *attr, | ||
710 | char *buffer, loff_t offset, size_t count) | ||
711 | { | ||
712 | struct c2port_device *c2dev = | ||
713 | dev_get_drvdata(container_of(kobj, | ||
714 | struct device, kobj)); | ||
715 | ssize_t ret; | ||
716 | |||
717 | /* Check the device and flash access status */ | ||
718 | if (!c2dev->access || !c2dev->flash_access) | ||
719 | return -EBUSY; | ||
720 | |||
721 | mutex_lock(&c2dev->mutex); | ||
722 | ret = __c2port_read_flash_data(c2dev, buffer, offset, count); | ||
723 | mutex_unlock(&c2dev->mutex); | ||
724 | |||
725 | if (ret < 0) | ||
726 | dev_err(c2dev->dev, "cannot read %s flash\n", c2dev->name); | ||
727 | |||
728 | return ret; | ||
729 | } | ||
730 | |||
731 | static ssize_t __c2port_write_flash_data(struct c2port_device *dev, | ||
732 | char *buffer, loff_t offset, size_t count) | ||
733 | { | ||
734 | struct c2port_ops *ops = dev->ops; | ||
735 | u8 status, nwrite = 128; | ||
736 | int i, ret; | ||
737 | |||
738 | if (nwrite > count) | ||
739 | nwrite = count; | ||
740 | if (ops->block_size * ops->blocks_num - offset < nwrite) | ||
741 | nwrite = ops->block_size * ops->blocks_num - offset; | ||
742 | |||
743 | /* Check for flash end */ | ||
744 | if (offset >= ops->block_size * ops->blocks_num) | ||
745 | return -EINVAL; | ||
746 | |||
747 | /* Target the C2 flash programming data register for C2 data register | ||
748 | * access */ | ||
749 | c2port_write_ar(dev, C2PORT_FPDAT); | ||
750 | |||
751 | /* Send flash block write command */ | ||
752 | c2port_write_dr(dev, C2PORT_BLOCK_WRITE); | ||
753 | |||
754 | /* Wait for input acknowledge */ | ||
755 | ret = c2port_poll_in_busy(dev); | ||
756 | if (ret < 0) | ||
757 | return ret; | ||
758 | |||
759 | /* Should check status before starting FLASH access sequence */ | ||
760 | |||
761 | /* Wait for status information */ | ||
762 | ret = c2port_poll_out_ready(dev); | ||
763 | if (ret < 0) | ||
764 | return ret; | ||
765 | |||
766 | /* Read flash programming interface status */ | ||
767 | ret = c2port_read_dr(dev, &status); | ||
768 | if (ret < 0) | ||
769 | return ret; | ||
770 | if (status != C2PORT_COMMAND_OK) | ||
771 | return -EBUSY; | ||
772 | |||
773 | /* Send address high byte */ | ||
774 | c2port_write_dr(dev, offset >> 8); | ||
775 | ret = c2port_poll_in_busy(dev); | ||
776 | if (ret < 0) | ||
777 | return ret; | ||
778 | |||
779 | /* Send address low byte */ | ||
780 | c2port_write_dr(dev, offset & 0x00ff); | ||
781 | ret = c2port_poll_in_busy(dev); | ||
782 | if (ret < 0) | ||
783 | return ret; | ||
784 | |||
785 | /* Send address block size */ | ||
786 | c2port_write_dr(dev, nwrite); | ||
787 | ret = c2port_poll_in_busy(dev); | ||
788 | if (ret < 0) | ||
789 | return ret; | ||
790 | |||
791 | /* Should check status before writing FLASH block */ | ||
792 | |||
793 | /* Wait for status information */ | ||
794 | ret = c2port_poll_out_ready(dev); | ||
795 | if (ret < 0) | ||
796 | return ret; | ||
797 | |||
798 | /* Read flash programming interface status */ | ||
799 | ret = c2port_read_dr(dev, &status); | ||
800 | if (ret < 0) | ||
801 | return ret; | ||
802 | if (status != C2PORT_COMMAND_OK) | ||
803 | return -EBUSY; | ||
804 | |||
805 | /* Write flash block */ | ||
806 | for (i = 0; i < nwrite; i++) { | ||
807 | ret = c2port_write_dr(dev, *(buffer+i)); | ||
808 | if (ret < 0) | ||
809 | return ret; | ||
810 | |||
811 | ret = c2port_poll_in_busy(dev); | ||
812 | if (ret < 0) | ||
813 | return ret; | ||
814 | |||
815 | } | ||
816 | |||
817 | /* Wait for last flash write to complete */ | ||
818 | ret = c2port_poll_out_ready(dev); | ||
819 | if (ret < 0) | ||
820 | return ret; | ||
821 | |||
822 | return nwrite; | ||
823 | } | ||
824 | |||
825 | static ssize_t c2port_write_flash_data(struct kobject *kobj, | ||
826 | struct bin_attribute *attr, | ||
827 | char *buffer, loff_t offset, size_t count) | ||
828 | { | ||
829 | struct c2port_device *c2dev = | ||
830 | dev_get_drvdata(container_of(kobj, | ||
831 | struct device, kobj)); | ||
832 | int ret; | ||
833 | |||
834 | /* Check the device access status */ | ||
835 | if (!c2dev->access || !c2dev->flash_access) | ||
836 | return -EBUSY; | ||
837 | |||
838 | mutex_lock(&c2dev->mutex); | ||
839 | ret = __c2port_write_flash_data(c2dev, buffer, offset, count); | ||
840 | mutex_unlock(&c2dev->mutex); | ||
841 | |||
842 | if (ret < 0) | ||
843 | dev_err(c2dev->dev, "cannot write %s flash\n", c2dev->name); | ||
844 | |||
845 | return ret; | ||
846 | } | ||
847 | |||
848 | /* | ||
849 | * Class attributes | ||
850 | */ | ||
851 | |||
852 | static struct device_attribute c2port_attrs[] = { | ||
853 | __ATTR(name, 0444, c2port_show_name, NULL), | ||
854 | __ATTR(flash_blocks_num, 0444, c2port_show_flash_blocks_num, NULL), | ||
855 | __ATTR(flash_block_size, 0444, c2port_show_flash_block_size, NULL), | ||
856 | __ATTR(flash_size, 0444, c2port_show_flash_size, NULL), | ||
857 | __ATTR(access, 0644, c2port_show_access, c2port_store_access), | ||
858 | __ATTR(reset, 0200, NULL, c2port_store_reset), | ||
859 | __ATTR(dev_id, 0444, c2port_show_dev_id, NULL), | ||
860 | __ATTR(rev_id, 0444, c2port_show_rev_id, NULL), | ||
861 | |||
862 | __ATTR(flash_access, 0644, c2port_show_flash_access, | ||
863 | c2port_store_flash_access), | ||
864 | __ATTR(flash_erase, 0200, NULL, c2port_store_flash_erase), | ||
865 | __ATTR_NULL, | ||
866 | }; | ||
867 | |||
868 | static struct bin_attribute c2port_bin_attrs = { | ||
869 | .attr = { | ||
870 | .name = "flash_data", | ||
871 | .mode = 0644 | ||
872 | }, | ||
873 | .read = c2port_read_flash_data, | ||
874 | .write = c2port_write_flash_data, | ||
875 | /* .size is computed at run-time */ | ||
876 | }; | ||
877 | |||
878 | /* | ||
879 | * Exported functions | ||
880 | */ | ||
881 | |||
882 | struct c2port_device *c2port_device_register(char *name, | ||
883 | struct c2port_ops *ops, void *devdata) | ||
884 | { | ||
885 | struct c2port_device *c2dev; | ||
886 | int id, ret; | ||
887 | |||
888 | if (unlikely(!ops) || unlikely(!ops->access) || \ | ||
889 | unlikely(!ops->c2d_dir) || unlikely(!ops->c2ck_set) || \ | ||
890 | unlikely(!ops->c2d_get) || unlikely(!ops->c2d_set)) | ||
891 | return ERR_PTR(-EINVAL); | ||
892 | |||
893 | c2dev = kmalloc(sizeof(struct c2port_device), GFP_KERNEL); | ||
894 | if (unlikely(!c2dev)) | ||
895 | return ERR_PTR(-ENOMEM); | ||
896 | |||
897 | ret = idr_pre_get(&c2port_idr, GFP_KERNEL); | ||
898 | if (!ret) { | ||
899 | ret = -ENOMEM; | ||
900 | goto error_idr_get_new; | ||
901 | } | ||
902 | |||
903 | spin_lock_irq(&c2port_idr_lock); | ||
904 | ret = idr_get_new(&c2port_idr, c2dev, &id); | ||
905 | spin_unlock_irq(&c2port_idr_lock); | ||
906 | |||
907 | if (ret < 0) | ||
908 | goto error_idr_get_new; | ||
909 | c2dev->id = id; | ||
910 | |||
911 | c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, | ||
912 | "c2port%d", id); | ||
913 | if (unlikely(!c2dev->dev)) { | ||
914 | ret = -ENOMEM; | ||
915 | goto error_device_create; | ||
916 | } | ||
917 | dev_set_drvdata(c2dev->dev, c2dev); | ||
918 | |||
919 | strncpy(c2dev->name, name, C2PORT_NAME_LEN); | ||
920 | c2dev->ops = ops; | ||
921 | mutex_init(&c2dev->mutex); | ||
922 | |||
923 | /* Create binary file */ | ||
924 | c2port_bin_attrs.size = ops->blocks_num * ops->block_size; | ||
925 | ret = device_create_bin_file(c2dev->dev, &c2port_bin_attrs); | ||
926 | if (unlikely(ret)) | ||
927 | goto error_device_create_bin_file; | ||
928 | |||
929 | /* By default C2 port access is off */ | ||
930 | c2dev->access = c2dev->flash_access = 0; | ||
931 | ops->access(c2dev, 0); | ||
932 | |||
933 | dev_info(c2dev->dev, "C2 port %s added\n", name); | ||
934 | dev_info(c2dev->dev, "%s flash has %d blocks x %d bytes " | ||
935 | "(%d bytes total)\n", | ||
936 | name, ops->blocks_num, ops->block_size, | ||
937 | ops->blocks_num * ops->block_size); | ||
938 | |||
939 | return c2dev; | ||
940 | |||
941 | error_device_create_bin_file: | ||
942 | device_destroy(c2port_class, 0); | ||
943 | |||
944 | error_device_create: | ||
945 | spin_lock_irq(&c2port_idr_lock); | ||
946 | idr_remove(&c2port_idr, id); | ||
947 | spin_unlock_irq(&c2port_idr_lock); | ||
948 | |||
949 | error_idr_get_new: | ||
950 | kfree(c2dev); | ||
951 | |||
952 | return ERR_PTR(ret); | ||
953 | } | ||
954 | EXPORT_SYMBOL(c2port_device_register); | ||
955 | |||
956 | void c2port_device_unregister(struct c2port_device *c2dev) | ||
957 | { | ||
958 | if (!c2dev) | ||
959 | return; | ||
960 | |||
961 | dev_info(c2dev->dev, "C2 port %s removed\n", c2dev->name); | ||
962 | |||
963 | device_remove_bin_file(c2dev->dev, &c2port_bin_attrs); | ||
964 | spin_lock_irq(&c2port_idr_lock); | ||
965 | idr_remove(&c2port_idr, c2dev->id); | ||
966 | spin_unlock_irq(&c2port_idr_lock); | ||
967 | |||
968 | device_destroy(c2port_class, c2dev->id); | ||
969 | |||
970 | kfree(c2dev); | ||
971 | } | ||
972 | EXPORT_SYMBOL(c2port_device_unregister); | ||
973 | |||
974 | /* | ||
975 | * Module stuff | ||
976 | */ | ||
977 | |||
978 | static int __init c2port_init(void) | ||
979 | { | ||
980 | printk(KERN_INFO "Silicon Labs C2 port support v. " DRIVER_VERSION | ||
981 | " - (C) 2007 Rodolfo Giometti\n"); | ||
982 | |||
983 | c2port_class = class_create(THIS_MODULE, "c2port"); | ||
984 | if (!c2port_class) { | ||
985 | printk(KERN_ERR "c2port: failed to allocate class\n"); | ||
986 | return -ENOMEM; | ||
987 | } | ||
988 | c2port_class->dev_attrs = c2port_attrs; | ||
989 | |||
990 | return 0; | ||
991 | } | ||
992 | |||
993 | static void __exit c2port_exit(void) | ||
994 | { | ||
995 | class_destroy(c2port_class); | ||
996 | } | ||
997 | |||
998 | module_init(c2port_init); | ||
999 | module_exit(c2port_exit); | ||
1000 | |||
1001 | MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); | ||
1002 | MODULE_DESCRIPTION("Silicon Labs C2 port support v. " DRIVER_VERSION); | ||
1003 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c index 344b790a6253..11003bba10d3 100644 --- a/drivers/misc/compal-laptop.c +++ b/drivers/misc/compal-laptop.c | |||
@@ -326,12 +326,14 @@ static int __init compal_init(void) | |||
326 | 326 | ||
327 | /* Register backlight stuff */ | 327 | /* Register backlight stuff */ |
328 | 328 | ||
329 | compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, | 329 | if (!acpi_video_backlight_support()) { |
330 | &compalbl_ops); | 330 | compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, |
331 | if (IS_ERR(compalbl_device)) | 331 | &compalbl_ops); |
332 | return PTR_ERR(compalbl_device); | 332 | if (IS_ERR(compalbl_device)) |
333 | return PTR_ERR(compalbl_device); | ||
333 | 334 | ||
334 | compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; | 335 | compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; |
336 | } | ||
335 | 337 | ||
336 | ret = platform_driver_register(&compal_driver); | 338 | ret = platform_driver_register(&compal_driver); |
337 | if (ret) | 339 | if (ret) |
diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c index 9ef98b2d5039..02fe2b8b8939 100644 --- a/drivers/misc/eeepc-laptop.c +++ b/drivers/misc/eeepc-laptop.c | |||
@@ -825,9 +825,15 @@ static int __init eeepc_laptop_init(void) | |||
825 | return -ENODEV; | 825 | return -ENODEV; |
826 | } | 826 | } |
827 | dev = acpi_get_physical_device(ehotk->device->handle); | 827 | dev = acpi_get_physical_device(ehotk->device->handle); |
828 | result = eeepc_backlight_init(dev); | 828 | |
829 | if (result) | 829 | if (!acpi_video_backlight_support()) { |
830 | goto fail_backlight; | 830 | result = eeepc_backlight_init(dev); |
831 | if (result) | ||
832 | goto fail_backlight; | ||
833 | } else | ||
834 | printk(EEEPC_INFO "Backlight controlled by ACPI video " | ||
835 | "driver\n"); | ||
836 | |||
831 | result = eeepc_hwmon_init(dev); | 837 | result = eeepc_hwmon_init(dev); |
832 | if (result) | 838 | if (result) |
833 | goto fail_hwmon; | 839 | goto fail_hwmon; |
diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c index d2cf0bfe3163..a7dd3e9fb79d 100644 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/misc/fujitsu-laptop.c | |||
@@ -464,6 +464,14 @@ static int dmi_check_cb_s6410(const struct dmi_system_id *id) | |||
464 | return 0; | 464 | return 0; |
465 | } | 465 | } |
466 | 466 | ||
467 | static int dmi_check_cb_s6420(const struct dmi_system_id *id) | ||
468 | { | ||
469 | dmi_check_cb_common(id); | ||
470 | fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ | ||
471 | fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ | ||
472 | return 0; | ||
473 | } | ||
474 | |||
467 | static int dmi_check_cb_p8010(const struct dmi_system_id *id) | 475 | static int dmi_check_cb_p8010(const struct dmi_system_id *id) |
468 | { | 476 | { |
469 | dmi_check_cb_common(id); | 477 | dmi_check_cb_common(id); |
@@ -473,7 +481,7 @@ static int dmi_check_cb_p8010(const struct dmi_system_id *id) | |||
473 | return 0; | 481 | return 0; |
474 | } | 482 | } |
475 | 483 | ||
476 | static struct dmi_system_id __initdata fujitsu_dmi_table[] = { | 484 | static struct dmi_system_id fujitsu_dmi_table[] = { |
477 | { | 485 | { |
478 | .ident = "Fujitsu Siemens S6410", | 486 | .ident = "Fujitsu Siemens S6410", |
479 | .matches = { | 487 | .matches = { |
@@ -482,6 +490,13 @@ static struct dmi_system_id __initdata fujitsu_dmi_table[] = { | |||
482 | }, | 490 | }, |
483 | .callback = dmi_check_cb_s6410}, | 491 | .callback = dmi_check_cb_s6410}, |
484 | { | 492 | { |
493 | .ident = "Fujitsu Siemens S6420", | ||
494 | .matches = { | ||
495 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), | ||
496 | DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"), | ||
497 | }, | ||
498 | .callback = dmi_check_cb_s6420}, | ||
499 | { | ||
485 | .ident = "Fujitsu LifeBook P8010", | 500 | .ident = "Fujitsu LifeBook P8010", |
486 | .matches = { | 501 | .matches = { |
487 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | 502 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), |
@@ -990,16 +1005,16 @@ static int __init fujitsu_init(void) | |||
990 | 1005 | ||
991 | /* Register backlight stuff */ | 1006 | /* Register backlight stuff */ |
992 | 1007 | ||
993 | fujitsu->bl_device = | 1008 | if (!acpi_video_backlight_support()) { |
994 | backlight_device_register("fujitsu-laptop", NULL, NULL, | 1009 | fujitsu->bl_device = |
995 | &fujitsubl_ops); | 1010 | backlight_device_register("fujitsu-laptop", NULL, NULL, |
996 | if (IS_ERR(fujitsu->bl_device)) | 1011 | &fujitsubl_ops); |
997 | return PTR_ERR(fujitsu->bl_device); | 1012 | if (IS_ERR(fujitsu->bl_device)) |
998 | 1013 | return PTR_ERR(fujitsu->bl_device); | |
999 | max_brightness = fujitsu->max_brightness; | 1014 | max_brightness = fujitsu->max_brightness; |
1000 | 1015 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; | |
1001 | fujitsu->bl_device->props.max_brightness = max_brightness - 1; | 1016 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; |
1002 | fujitsu->bl_device->props.brightness = fujitsu->brightness_level; | 1017 | } |
1003 | 1018 | ||
1004 | ret = platform_driver_register(&fujitsupf_driver); | 1019 | ret = platform_driver_register(&fujitsupf_driver); |
1005 | if (ret) | 1020 | if (ret) |
@@ -1035,7 +1050,8 @@ fail_hotkey: | |||
1035 | 1050 | ||
1036 | fail_backlight: | 1051 | fail_backlight: |
1037 | 1052 | ||
1038 | backlight_device_unregister(fujitsu->bl_device); | 1053 | if (fujitsu->bl_device) |
1054 | backlight_device_unregister(fujitsu->bl_device); | ||
1039 | 1055 | ||
1040 | fail_platform_device2: | 1056 | fail_platform_device2: |
1041 | 1057 | ||
@@ -1062,7 +1078,8 @@ static void __exit fujitsu_cleanup(void) | |||
1062 | &fujitsupf_attribute_group); | 1078 | &fujitsupf_attribute_group); |
1063 | platform_device_unregister(fujitsu->pf_device); | 1079 | platform_device_unregister(fujitsu->pf_device); |
1064 | platform_driver_unregister(&fujitsupf_driver); | 1080 | platform_driver_unregister(&fujitsupf_driver); |
1065 | backlight_device_unregister(fujitsu->bl_device); | 1081 | if (fujitsu->bl_device) |
1082 | backlight_device_unregister(fujitsu->bl_device); | ||
1066 | 1083 | ||
1067 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); | 1084 | acpi_bus_unregister_driver(&acpi_fujitsu_driver); |
1068 | 1085 | ||
diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c new file mode 100644 index 000000000000..6e43ab4231ae --- /dev/null +++ b/drivers/misc/ics932s401.c | |||
@@ -0,0 +1,515 @@ | |||
1 | /* | ||
2 | * A driver for the Integrated Circuits ICS932S401 | ||
3 | * Copyright (C) 2008 IBM | ||
4 | * | ||
5 | * Author: Darrick J. Wong <djwong@us.ibm.com> | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/jiffies.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/err.h> | ||
26 | #include <linux/mutex.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/log2.h> | ||
29 | |||
30 | /* Addresses to scan */ | ||
31 | static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END }; | ||
32 | |||
33 | /* Insmod parameters */ | ||
34 | I2C_CLIENT_INSMOD_1(ics932s401); | ||
35 | |||
36 | /* ICS932S401 registers */ | ||
37 | #define ICS932S401_REG_CFG2 0x01 | ||
38 | #define ICS932S401_CFG1_SPREAD 0x01 | ||
39 | #define ICS932S401_REG_CFG7 0x06 | ||
40 | #define ICS932S401_FS_MASK 0x07 | ||
41 | #define ICS932S401_REG_VENDOR_REV 0x07 | ||
42 | #define ICS932S401_VENDOR 1 | ||
43 | #define ICS932S401_VENDOR_MASK 0x0F | ||
44 | #define ICS932S401_REV 4 | ||
45 | #define ICS932S401_REV_SHIFT 4 | ||
46 | #define ICS932S401_REG_DEVICE 0x09 | ||
47 | #define ICS932S401_DEVICE 11 | ||
48 | #define ICS932S401_REG_CTRL 0x0A | ||
49 | #define ICS932S401_MN_ENABLED 0x80 | ||
50 | #define ICS932S401_CPU_ALT 0x04 | ||
51 | #define ICS932S401_SRC_ALT 0x08 | ||
52 | #define ICS932S401_REG_CPU_M_CTRL 0x0B | ||
53 | #define ICS932S401_M_MASK 0x3F | ||
54 | #define ICS932S401_REG_CPU_N_CTRL 0x0C | ||
55 | #define ICS932S401_REG_CPU_SPREAD1 0x0D | ||
56 | #define ICS932S401_REG_CPU_SPREAD2 0x0E | ||
57 | #define ICS932S401_SPREAD_MASK 0x7FFF | ||
58 | #define ICS932S401_REG_SRC_M_CTRL 0x0F | ||
59 | #define ICS932S401_REG_SRC_N_CTRL 0x10 | ||
60 | #define ICS932S401_REG_SRC_SPREAD1 0x11 | ||
61 | #define ICS932S401_REG_SRC_SPREAD2 0x12 | ||
62 | #define ICS932S401_REG_CPU_DIVISOR 0x13 | ||
63 | #define ICS932S401_CPU_DIVISOR_SHIFT 4 | ||
64 | #define ICS932S401_REG_PCISRC_DIVISOR 0x14 | ||
65 | #define ICS932S401_SRC_DIVISOR_MASK 0x0F | ||
66 | #define ICS932S401_PCI_DIVISOR_SHIFT 4 | ||
67 | |||
68 | /* Base clock is 14.318MHz */ | ||
69 | #define BASE_CLOCK 14318 | ||
70 | |||
71 | #define NUM_REGS 21 | ||
72 | #define NUM_MIRRORED_REGS 15 | ||
73 | |||
74 | static int regs_to_copy[NUM_MIRRORED_REGS] = { | ||
75 | ICS932S401_REG_CFG2, | ||
76 | ICS932S401_REG_CFG7, | ||
77 | ICS932S401_REG_VENDOR_REV, | ||
78 | ICS932S401_REG_DEVICE, | ||
79 | ICS932S401_REG_CTRL, | ||
80 | ICS932S401_REG_CPU_M_CTRL, | ||
81 | ICS932S401_REG_CPU_N_CTRL, | ||
82 | ICS932S401_REG_CPU_SPREAD1, | ||
83 | ICS932S401_REG_CPU_SPREAD2, | ||
84 | ICS932S401_REG_SRC_M_CTRL, | ||
85 | ICS932S401_REG_SRC_N_CTRL, | ||
86 | ICS932S401_REG_SRC_SPREAD1, | ||
87 | ICS932S401_REG_SRC_SPREAD2, | ||
88 | ICS932S401_REG_CPU_DIVISOR, | ||
89 | ICS932S401_REG_PCISRC_DIVISOR, | ||
90 | }; | ||
91 | |||
92 | /* How often do we reread sensors values? (In jiffies) */ | ||
93 | #define SENSOR_REFRESH_INTERVAL (2 * HZ) | ||
94 | |||
95 | /* How often do we reread sensor limit values? (In jiffies) */ | ||
96 | #define LIMIT_REFRESH_INTERVAL (60 * HZ) | ||
97 | |||
98 | struct ics932s401_data { | ||
99 | struct attribute_group attrs; | ||
100 | struct mutex lock; | ||
101 | char sensors_valid; | ||
102 | unsigned long sensors_last_updated; /* In jiffies */ | ||
103 | |||
104 | u8 regs[NUM_REGS]; | ||
105 | }; | ||
106 | |||
107 | static int ics932s401_probe(struct i2c_client *client, | ||
108 | const struct i2c_device_id *id); | ||
109 | static int ics932s401_detect(struct i2c_client *client, int kind, | ||
110 | struct i2c_board_info *info); | ||
111 | static int ics932s401_remove(struct i2c_client *client); | ||
112 | |||
113 | static const struct i2c_device_id ics932s401_id[] = { | ||
114 | { "ics932s401", ics932s401 }, | ||
115 | { } | ||
116 | }; | ||
117 | MODULE_DEVICE_TABLE(i2c, ics932s401_id); | ||
118 | |||
119 | static struct i2c_driver ics932s401_driver = { | ||
120 | .class = I2C_CLASS_HWMON, | ||
121 | .driver = { | ||
122 | .name = "ics932s401", | ||
123 | }, | ||
124 | .probe = ics932s401_probe, | ||
125 | .remove = ics932s401_remove, | ||
126 | .id_table = ics932s401_id, | ||
127 | .detect = ics932s401_detect, | ||
128 | .address_data = &addr_data, | ||
129 | }; | ||
130 | |||
131 | static struct ics932s401_data *ics932s401_update_device(struct device *dev) | ||
132 | { | ||
133 | struct i2c_client *client = to_i2c_client(dev); | ||
134 | struct ics932s401_data *data = i2c_get_clientdata(client); | ||
135 | unsigned long local_jiffies = jiffies; | ||
136 | int i, temp; | ||
137 | |||
138 | mutex_lock(&data->lock); | ||
139 | if (time_before(local_jiffies, data->sensors_last_updated + | ||
140 | SENSOR_REFRESH_INTERVAL) | ||
141 | && data->sensors_valid) | ||
142 | goto out; | ||
143 | |||
144 | /* | ||
145 | * Each register must be read as a word and then right shifted 8 bits. | ||
146 | * Not really sure why this is; setting the "byte count programming" | ||
147 | * register to 1 does not fix this problem. | ||
148 | */ | ||
149 | for (i = 0; i < NUM_MIRRORED_REGS; i++) { | ||
150 | temp = i2c_smbus_read_word_data(client, regs_to_copy[i]); | ||
151 | data->regs[regs_to_copy[i]] = temp >> 8; | ||
152 | } | ||
153 | |||
154 | data->sensors_last_updated = local_jiffies; | ||
155 | data->sensors_valid = 1; | ||
156 | |||
157 | out: | ||
158 | mutex_unlock(&data->lock); | ||
159 | return data; | ||
160 | } | ||
161 | |||
162 | static ssize_t show_spread_enabled(struct device *dev, | ||
163 | struct device_attribute *devattr, | ||
164 | char *buf) | ||
165 | { | ||
166 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
167 | |||
168 | if (data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD) | ||
169 | return sprintf(buf, "1\n"); | ||
170 | |||
171 | return sprintf(buf, "0\n"); | ||
172 | } | ||
173 | |||
174 | /* bit to cpu khz map */ | ||
175 | static const int fs_speeds[] = { | ||
176 | 266666, | ||
177 | 133333, | ||
178 | 200000, | ||
179 | 166666, | ||
180 | 333333, | ||
181 | 100000, | ||
182 | 400000, | ||
183 | 0, | ||
184 | }; | ||
185 | |||
186 | /* clock divisor map */ | ||
187 | static const int divisors[] = {2, 3, 5, 15, 4, 6, 10, 30, 8, 12, 20, 60, 16, | ||
188 | 24, 40, 120}; | ||
189 | |||
190 | /* Calculate CPU frequency from the M/N registers. */ | ||
191 | static int calculate_cpu_freq(struct ics932s401_data *data) | ||
192 | { | ||
193 | int m, n, freq; | ||
194 | |||
195 | m = data->regs[ICS932S401_REG_CPU_M_CTRL] & ICS932S401_M_MASK; | ||
196 | n = data->regs[ICS932S401_REG_CPU_N_CTRL]; | ||
197 | |||
198 | /* Pull in bits 8 & 9 from the M register */ | ||
199 | n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x80) << 1; | ||
200 | n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x40) << 3; | ||
201 | |||
202 | freq = BASE_CLOCK * (n + 8) / (m + 2); | ||
203 | freq /= divisors[data->regs[ICS932S401_REG_CPU_DIVISOR] >> | ||
204 | ICS932S401_CPU_DIVISOR_SHIFT]; | ||
205 | |||
206 | return freq; | ||
207 | } | ||
208 | |||
209 | static ssize_t show_cpu_clock(struct device *dev, | ||
210 | struct device_attribute *devattr, | ||
211 | char *buf) | ||
212 | { | ||
213 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
214 | |||
215 | return sprintf(buf, "%d\n", calculate_cpu_freq(data)); | ||
216 | } | ||
217 | |||
218 | static ssize_t show_cpu_clock_sel(struct device *dev, | ||
219 | struct device_attribute *devattr, | ||
220 | char *buf) | ||
221 | { | ||
222 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
223 | int freq; | ||
224 | |||
225 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) | ||
226 | freq = calculate_cpu_freq(data); | ||
227 | else { | ||
228 | /* Freq is neatly wrapped up for us */ | ||
229 | int fid = data->regs[ICS932S401_REG_CFG7] & ICS932S401_FS_MASK; | ||
230 | freq = fs_speeds[fid]; | ||
231 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT) { | ||
232 | switch (freq) { | ||
233 | case 166666: | ||
234 | freq = 160000; | ||
235 | break; | ||
236 | case 333333: | ||
237 | freq = 320000; | ||
238 | break; | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | return sprintf(buf, "%d\n", freq); | ||
244 | } | ||
245 | |||
246 | /* Calculate SRC frequency from the M/N registers. */ | ||
247 | static int calculate_src_freq(struct ics932s401_data *data) | ||
248 | { | ||
249 | int m, n, freq; | ||
250 | |||
251 | m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK; | ||
252 | n = data->regs[ICS932S401_REG_SRC_N_CTRL]; | ||
253 | |||
254 | /* Pull in bits 8 & 9 from the M register */ | ||
255 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1; | ||
256 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3; | ||
257 | |||
258 | freq = BASE_CLOCK * (n + 8) / (m + 2); | ||
259 | freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] & | ||
260 | ICS932S401_SRC_DIVISOR_MASK]; | ||
261 | |||
262 | return freq; | ||
263 | } | ||
264 | |||
265 | static ssize_t show_src_clock(struct device *dev, | ||
266 | struct device_attribute *devattr, | ||
267 | char *buf) | ||
268 | { | ||
269 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
270 | |||
271 | return sprintf(buf, "%d\n", calculate_src_freq(data)); | ||
272 | } | ||
273 | |||
274 | static ssize_t show_src_clock_sel(struct device *dev, | ||
275 | struct device_attribute *devattr, | ||
276 | char *buf) | ||
277 | { | ||
278 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
279 | int freq; | ||
280 | |||
281 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) | ||
282 | freq = calculate_src_freq(data); | ||
283 | else | ||
284 | /* Freq is neatly wrapped up for us */ | ||
285 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT && | ||
286 | data->regs[ICS932S401_REG_CTRL] & ICS932S401_SRC_ALT) | ||
287 | freq = 96000; | ||
288 | else | ||
289 | freq = 100000; | ||
290 | |||
291 | return sprintf(buf, "%d\n", freq); | ||
292 | } | ||
293 | |||
294 | /* Calculate PCI frequency from the SRC M/N registers. */ | ||
295 | static int calculate_pci_freq(struct ics932s401_data *data) | ||
296 | { | ||
297 | int m, n, freq; | ||
298 | |||
299 | m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK; | ||
300 | n = data->regs[ICS932S401_REG_SRC_N_CTRL]; | ||
301 | |||
302 | /* Pull in bits 8 & 9 from the M register */ | ||
303 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1; | ||
304 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3; | ||
305 | |||
306 | freq = BASE_CLOCK * (n + 8) / (m + 2); | ||
307 | freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] >> | ||
308 | ICS932S401_PCI_DIVISOR_SHIFT]; | ||
309 | |||
310 | return freq; | ||
311 | } | ||
312 | |||
313 | static ssize_t show_pci_clock(struct device *dev, | ||
314 | struct device_attribute *devattr, | ||
315 | char *buf) | ||
316 | { | ||
317 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
318 | |||
319 | return sprintf(buf, "%d\n", calculate_pci_freq(data)); | ||
320 | } | ||
321 | |||
322 | static ssize_t show_pci_clock_sel(struct device *dev, | ||
323 | struct device_attribute *devattr, | ||
324 | char *buf) | ||
325 | { | ||
326 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
327 | int freq; | ||
328 | |||
329 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) | ||
330 | freq = calculate_pci_freq(data); | ||
331 | else | ||
332 | freq = 33333; | ||
333 | |||
334 | return sprintf(buf, "%d\n", freq); | ||
335 | } | ||
336 | |||
337 | static ssize_t show_value(struct device *dev, | ||
338 | struct device_attribute *devattr, | ||
339 | char *buf); | ||
340 | |||
341 | static ssize_t show_spread(struct device *dev, | ||
342 | struct device_attribute *devattr, | ||
343 | char *buf); | ||
344 | |||
345 | static DEVICE_ATTR(spread_enabled, S_IRUGO, show_spread_enabled, NULL); | ||
346 | static DEVICE_ATTR(cpu_clock_selection, S_IRUGO, show_cpu_clock_sel, NULL); | ||
347 | static DEVICE_ATTR(cpu_clock, S_IRUGO, show_cpu_clock, NULL); | ||
348 | static DEVICE_ATTR(src_clock_selection, S_IRUGO, show_src_clock_sel, NULL); | ||
349 | static DEVICE_ATTR(src_clock, S_IRUGO, show_src_clock, NULL); | ||
350 | static DEVICE_ATTR(pci_clock_selection, S_IRUGO, show_pci_clock_sel, NULL); | ||
351 | static DEVICE_ATTR(pci_clock, S_IRUGO, show_pci_clock, NULL); | ||
352 | static DEVICE_ATTR(usb_clock, S_IRUGO, show_value, NULL); | ||
353 | static DEVICE_ATTR(ref_clock, S_IRUGO, show_value, NULL); | ||
354 | static DEVICE_ATTR(cpu_spread, S_IRUGO, show_spread, NULL); | ||
355 | static DEVICE_ATTR(src_spread, S_IRUGO, show_spread, NULL); | ||
356 | |||
357 | static struct attribute *ics932s401_attr[] = | ||
358 | { | ||
359 | &dev_attr_spread_enabled.attr, | ||
360 | &dev_attr_cpu_clock_selection.attr, | ||
361 | &dev_attr_cpu_clock.attr, | ||
362 | &dev_attr_src_clock_selection.attr, | ||
363 | &dev_attr_src_clock.attr, | ||
364 | &dev_attr_pci_clock_selection.attr, | ||
365 | &dev_attr_pci_clock.attr, | ||
366 | &dev_attr_usb_clock.attr, | ||
367 | &dev_attr_ref_clock.attr, | ||
368 | &dev_attr_cpu_spread.attr, | ||
369 | &dev_attr_src_spread.attr, | ||
370 | NULL | ||
371 | }; | ||
372 | |||
373 | static ssize_t show_value(struct device *dev, | ||
374 | struct device_attribute *devattr, | ||
375 | char *buf) | ||
376 | { | ||
377 | int x; | ||
378 | |||
379 | if (devattr == &dev_attr_usb_clock) | ||
380 | x = 48000; | ||
381 | else if (devattr == &dev_attr_ref_clock) | ||
382 | x = BASE_CLOCK; | ||
383 | else | ||
384 | BUG(); | ||
385 | |||
386 | return sprintf(buf, "%d\n", x); | ||
387 | } | ||
388 | |||
389 | static ssize_t show_spread(struct device *dev, | ||
390 | struct device_attribute *devattr, | ||
391 | char *buf) | ||
392 | { | ||
393 | struct ics932s401_data *data = ics932s401_update_device(dev); | ||
394 | int reg; | ||
395 | unsigned long val; | ||
396 | |||
397 | if (!(data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD)) | ||
398 | return sprintf(buf, "0%%\n"); | ||
399 | |||
400 | if (devattr == &dev_attr_src_spread) | ||
401 | reg = ICS932S401_REG_SRC_SPREAD1; | ||
402 | else if (devattr == &dev_attr_cpu_spread) | ||
403 | reg = ICS932S401_REG_CPU_SPREAD1; | ||
404 | else | ||
405 | BUG(); | ||
406 | |||
407 | val = data->regs[reg] | (data->regs[reg + 1] << 8); | ||
408 | val &= ICS932S401_SPREAD_MASK; | ||
409 | |||
410 | /* Scale 0..2^14 to -0.5. */ | ||
411 | val = 500000 * val / 16384; | ||
412 | return sprintf(buf, "-0.%lu%%\n", val); | ||
413 | } | ||
414 | |||
415 | /* Return 0 if detection is successful, -ENODEV otherwise */ | ||
416 | static int ics932s401_detect(struct i2c_client *client, int kind, | ||
417 | struct i2c_board_info *info) | ||
418 | { | ||
419 | struct i2c_adapter *adapter = client->adapter; | ||
420 | |||
421 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | ||
422 | return -ENODEV; | ||
423 | |||
424 | if (kind <= 0) { | ||
425 | int vendor, device, revision; | ||
426 | |||
427 | vendor = i2c_smbus_read_word_data(client, | ||
428 | ICS932S401_REG_VENDOR_REV); | ||
429 | vendor >>= 8; | ||
430 | revision = vendor >> ICS932S401_REV_SHIFT; | ||
431 | vendor &= ICS932S401_VENDOR_MASK; | ||
432 | if (vendor != ICS932S401_VENDOR) | ||
433 | return -ENODEV; | ||
434 | |||
435 | device = i2c_smbus_read_word_data(client, | ||
436 | ICS932S401_REG_DEVICE); | ||
437 | device >>= 8; | ||
438 | if (device != ICS932S401_DEVICE) | ||
439 | return -ENODEV; | ||
440 | |||
441 | if (revision != ICS932S401_REV) | ||
442 | dev_info(&adapter->dev, "Unknown revision %d\n", | ||
443 | revision); | ||
444 | } else | ||
445 | dev_dbg(&adapter->dev, "detection forced\n"); | ||
446 | |||
447 | strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
452 | static int ics932s401_probe(struct i2c_client *client, | ||
453 | const struct i2c_device_id *id) | ||
454 | { | ||
455 | struct ics932s401_data *data; | ||
456 | int err; | ||
457 | |||
458 | data = kzalloc(sizeof(struct ics932s401_data), GFP_KERNEL); | ||
459 | if (!data) { | ||
460 | err = -ENOMEM; | ||
461 | goto exit; | ||
462 | } | ||
463 | |||
464 | i2c_set_clientdata(client, data); | ||
465 | mutex_init(&data->lock); | ||
466 | |||
467 | dev_info(&client->dev, "%s chip found\n", client->name); | ||
468 | |||
469 | /* Register sysfs hooks */ | ||
470 | data->attrs.attrs = ics932s401_attr; | ||
471 | err = sysfs_create_group(&client->dev.kobj, &data->attrs); | ||
472 | if (err) | ||
473 | goto exit_free; | ||
474 | |||
475 | return 0; | ||
476 | |||
477 | exit_free: | ||
478 | kfree(data); | ||
479 | exit: | ||
480 | return err; | ||
481 | } | ||
482 | |||
483 | static int ics932s401_remove(struct i2c_client *client) | ||
484 | { | ||
485 | struct ics932s401_data *data = i2c_get_clientdata(client); | ||
486 | |||
487 | sysfs_remove_group(&client->dev.kobj, &data->attrs); | ||
488 | kfree(data); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int __init ics932s401_init(void) | ||
493 | { | ||
494 | return i2c_add_driver(&ics932s401_driver); | ||
495 | } | ||
496 | |||
497 | static void __exit ics932s401_exit(void) | ||
498 | { | ||
499 | i2c_del_driver(&ics932s401_driver); | ||
500 | } | ||
501 | |||
502 | MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); | ||
503 | MODULE_DESCRIPTION("ICS932S401 driver"); | ||
504 | MODULE_LICENSE("GPL"); | ||
505 | |||
506 | module_init(ics932s401_init); | ||
507 | module_exit(ics932s401_exit); | ||
508 | |||
509 | /* IBM IntelliStation Z30 */ | ||
510 | MODULE_ALIAS("dmi:bvnIBM:*:rn9228:*"); | ||
511 | MODULE_ALIAS("dmi:bvnIBM:*:rn9232:*"); | ||
512 | |||
513 | /* IBM x3650/x3550 */ | ||
514 | MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650*"); | ||
515 | MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550*"); | ||
diff --git a/drivers/misc/intel_menlow.c b/drivers/misc/intel_menlow.c index e00a2756e97e..27b7662955bb 100644 --- a/drivers/misc/intel_menlow.c +++ b/drivers/misc/intel_menlow.c | |||
@@ -52,6 +52,11 @@ MODULE_LICENSE("GPL"); | |||
52 | #define MEMORY_ARG_CUR_BANDWIDTH 1 | 52 | #define MEMORY_ARG_CUR_BANDWIDTH 1 |
53 | #define MEMORY_ARG_MAX_BANDWIDTH 0 | 53 | #define MEMORY_ARG_MAX_BANDWIDTH 0 |
54 | 54 | ||
55 | /* | ||
56 | * GTHS returning 'n' would mean that [0,n-1] states are supported | ||
57 | * In that case max_cstate would be n-1 | ||
58 | * GTHS returning '0' would mean that no bandwidth control states are supported | ||
59 | */ | ||
55 | static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, | 60 | static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, |
56 | unsigned long *max_state) | 61 | unsigned long *max_state) |
57 | { | 62 | { |
@@ -71,6 +76,9 @@ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, | |||
71 | if (ACPI_FAILURE(status)) | 76 | if (ACPI_FAILURE(status)) |
72 | return -EFAULT; | 77 | return -EFAULT; |
73 | 78 | ||
79 | if (!value) | ||
80 | return -EINVAL; | ||
81 | |||
74 | *max_state = value - 1; | 82 | *max_state = value - 1; |
75 | return 0; | 83 | return 0; |
76 | } | 84 | } |
@@ -121,7 +129,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, | |||
121 | if (memory_get_int_max_bandwidth(cdev, &max_state)) | 129 | if (memory_get_int_max_bandwidth(cdev, &max_state)) |
122 | return -EFAULT; | 130 | return -EFAULT; |
123 | 131 | ||
124 | if (max_state < 0 || state > max_state) | 132 | if (state > max_state) |
125 | return -EINVAL; | 133 | return -EINVAL; |
126 | 134 | ||
127 | arg_list.count = 1; | 135 | arg_list.count = 1; |
diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index de898c6938f3..759763d18e4c 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c | |||
@@ -347,12 +347,16 @@ static int __init msi_init(void) | |||
347 | 347 | ||
348 | /* Register backlight stuff */ | 348 | /* Register backlight stuff */ |
349 | 349 | ||
350 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, NULL, | 350 | if (acpi_video_backlight_support()) { |
351 | &msibl_ops); | 351 | printk(KERN_INFO "MSI: Brightness ignored, must be controlled " |
352 | if (IS_ERR(msibl_device)) | 352 | "by ACPI video driver\n"); |
353 | return PTR_ERR(msibl_device); | 353 | } else { |
354 | 354 | msibl_device = backlight_device_register("msi-laptop-bl", NULL, | |
355 | msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; | 355 | NULL, &msibl_ops); |
356 | if (IS_ERR(msibl_device)) | ||
357 | return PTR_ERR(msibl_device); | ||
358 | msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; | ||
359 | } | ||
356 | 360 | ||
357 | ret = platform_driver_register(&msipf_driver); | 361 | ret = platform_driver_register(&msipf_driver); |
358 | if (ret) | 362 | if (ret) |
diff --git a/drivers/misc/sgi-gru/Makefile b/drivers/misc/sgi-gru/Makefile index d03597a521b0..9e9170b3599a 100644 --- a/drivers/misc/sgi-gru/Makefile +++ b/drivers/misc/sgi-gru/Makefile | |||
@@ -1,3 +1,7 @@ | |||
1 | ifdef CONFIG_SGI_GRU_DEBUG | ||
2 | EXTRA_CFLAGS += -DDEBUG | ||
3 | endif | ||
4 | |||
1 | obj-$(CONFIG_SGI_GRU) := gru.o | 5 | obj-$(CONFIG_SGI_GRU) := gru.o |
2 | gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o | 6 | gru-y := grufile.o grumain.o grufault.o grutlbpurge.o gruprocfs.o grukservices.o |
3 | 7 | ||
diff --git a/drivers/misc/sgi-gru/grufault.c b/drivers/misc/sgi-gru/grufault.c index 8c389d606c30..3ee698ad8599 100644 --- a/drivers/misc/sgi-gru/grufault.c +++ b/drivers/misc/sgi-gru/grufault.c | |||
@@ -254,7 +254,11 @@ static int atomic_pte_lookup(struct vm_area_struct *vma, unsigned long vaddr, | |||
254 | return 1; | 254 | return 1; |
255 | 255 | ||
256 | *paddr = pte_pfn(pte) << PAGE_SHIFT; | 256 | *paddr = pte_pfn(pte) << PAGE_SHIFT; |
257 | #ifdef CONFIG_HUGETLB_PAGE | ||
257 | *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; | 258 | *pageshift = is_vm_hugetlb_page(vma) ? HPAGE_SHIFT : PAGE_SHIFT; |
259 | #else | ||
260 | *pageshift = PAGE_SHIFT; | ||
261 | #endif | ||
258 | return 0; | 262 | return 0; |
259 | 263 | ||
260 | err: | 264 | err: |
diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c index 5c027b6b4e5a..650983806392 100644 --- a/drivers/misc/sgi-gru/grufile.c +++ b/drivers/misc/sgi-gru/grufile.c | |||
@@ -481,7 +481,7 @@ struct vm_operations_struct gru_vm_ops = { | |||
481 | .fault = gru_fault, | 481 | .fault = gru_fault, |
482 | }; | 482 | }; |
483 | 483 | ||
484 | module_init(gru_init); | 484 | fs_initcall(gru_init); |
485 | module_exit(gru_exit); | 485 | module_exit(gru_exit); |
486 | 486 | ||
487 | module_param(gru_options, ulong, 0644); | 487 | module_param(gru_options, ulong, 0644); |
diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index 06f07e19dc70..571b211608d1 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c | |||
@@ -1038,7 +1038,11 @@ static int sony_nc_add(struct acpi_device *device) | |||
1038 | goto outinput; | 1038 | goto outinput; |
1039 | } | 1039 | } |
1040 | 1040 | ||
1041 | if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { | 1041 | if (acpi_video_backlight_support()) { |
1042 | printk(KERN_INFO DRV_PFX "brightness ignored, must be " | ||
1043 | "controlled by ACPI video driver\n"); | ||
1044 | } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", | ||
1045 | &handle))) { | ||
1042 | sony_backlight_device = backlight_device_register("sony", NULL, | 1046 | sony_backlight_device = backlight_device_register("sony", NULL, |
1043 | NULL, | 1047 | NULL, |
1044 | &sony_backlight_ops); | 1048 | &sony_backlight_ops); |
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 4db1cf9078d9..899766e16fa8 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -4932,16 +4932,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4932 | */ | 4932 | */ |
4933 | b = tpacpi_check_std_acpi_brightness_support(); | 4933 | b = tpacpi_check_std_acpi_brightness_support(); |
4934 | if (b > 0) { | 4934 | if (b > 0) { |
4935 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | 4935 | |
4936 | printk(TPACPI_NOTICE | 4936 | if (acpi_video_backlight_support()) { |
4937 | "Lenovo BIOS switched to ACPI backlight " | 4937 | if (brightness_enable > 1) { |
4938 | "control mode\n"); | 4938 | printk(TPACPI_NOTICE |
4939 | } | 4939 | "Standard ACPI backlight interface " |
4940 | if (brightness_enable > 1) { | 4940 | "available, not loading native one.\n"); |
4941 | printk(TPACPI_NOTICE | 4941 | return 1; |
4942 | "standard ACPI backlight interface " | 4942 | } else if (brightness_enable == 1) { |
4943 | "available, not loading native one...\n"); | 4943 | printk(TPACPI_NOTICE |
4944 | return 1; | 4944 | "Backlight control force enabled, even if standard " |
4945 | "ACPI backlight interface is available\n"); | ||
4946 | } | ||
4947 | } else { | ||
4948 | if (brightness_enable > 1) { | ||
4949 | printk(TPACPI_NOTICE | ||
4950 | "Standard ACPI backlight interface not " | ||
4951 | "available, thinkpad_acpi native " | ||
4952 | "brightness control enabled\n"); | ||
4953 | } | ||
4945 | } | 4954 | } |
4946 | } | 4955 | } |
4947 | 4956 | ||
@@ -5309,6 +5318,7 @@ static enum fan_control_commands fan_control_commands; | |||
5309 | 5318 | ||
5310 | static u8 fan_control_initial_status; | 5319 | static u8 fan_control_initial_status; |
5311 | static u8 fan_control_desired_level; | 5320 | static u8 fan_control_desired_level; |
5321 | static u8 fan_control_resume_level; | ||
5312 | static int fan_watchdog_maxinterval; | 5322 | static int fan_watchdog_maxinterval; |
5313 | 5323 | ||
5314 | static struct mutex fan_mutex; | 5324 | static struct mutex fan_mutex; |
@@ -5431,8 +5441,8 @@ static int fan_set_level(int level) | |||
5431 | 5441 | ||
5432 | case TPACPI_FAN_WR_ACPI_FANS: | 5442 | case TPACPI_FAN_WR_ACPI_FANS: |
5433 | case TPACPI_FAN_WR_TPEC: | 5443 | case TPACPI_FAN_WR_TPEC: |
5434 | if ((level != TP_EC_FAN_AUTO) && | 5444 | if (!(level & TP_EC_FAN_AUTO) && |
5435 | (level != TP_EC_FAN_FULLSPEED) && | 5445 | !(level & TP_EC_FAN_FULLSPEED) && |
5436 | ((level < 0) || (level > 7))) | 5446 | ((level < 0) || (level > 7))) |
5437 | return -EINVAL; | 5447 | return -EINVAL; |
5438 | 5448 | ||
@@ -5996,38 +6006,67 @@ static void fan_exit(void) | |||
5996 | 6006 | ||
5997 | static void fan_suspend(pm_message_t state) | 6007 | static void fan_suspend(pm_message_t state) |
5998 | { | 6008 | { |
6009 | int rc; | ||
6010 | |||
5999 | if (!fan_control_allowed) | 6011 | if (!fan_control_allowed) |
6000 | return; | 6012 | return; |
6001 | 6013 | ||
6002 | /* Store fan status in cache */ | 6014 | /* Store fan status in cache */ |
6003 | fan_get_status_safe(NULL); | 6015 | fan_control_resume_level = 0; |
6016 | rc = fan_get_status_safe(&fan_control_resume_level); | ||
6017 | if (rc < 0) | ||
6018 | printk(TPACPI_NOTICE | ||
6019 | "failed to read fan level for later " | ||
6020 | "restore during resume: %d\n", rc); | ||
6021 | |||
6022 | /* if it is undefined, don't attempt to restore it. | ||
6023 | * KEEP THIS LAST */ | ||
6004 | if (tp_features.fan_ctrl_status_undef) | 6024 | if (tp_features.fan_ctrl_status_undef) |
6005 | fan_control_desired_level = TP_EC_FAN_AUTO; | 6025 | fan_control_resume_level = 0; |
6006 | } | 6026 | } |
6007 | 6027 | ||
6008 | static void fan_resume(void) | 6028 | static void fan_resume(void) |
6009 | { | 6029 | { |
6010 | u8 saved_fan_level; | ||
6011 | u8 current_level = 7; | 6030 | u8 current_level = 7; |
6012 | bool do_set = false; | 6031 | bool do_set = false; |
6032 | int rc; | ||
6013 | 6033 | ||
6014 | /* DSDT *always* updates status on resume */ | 6034 | /* DSDT *always* updates status on resume */ |
6015 | tp_features.fan_ctrl_status_undef = 0; | 6035 | tp_features.fan_ctrl_status_undef = 0; |
6016 | 6036 | ||
6017 | saved_fan_level = fan_control_desired_level; | ||
6018 | if (!fan_control_allowed || | 6037 | if (!fan_control_allowed || |
6038 | !fan_control_resume_level || | ||
6019 | (fan_get_status_safe(¤t_level) < 0)) | 6039 | (fan_get_status_safe(¤t_level) < 0)) |
6020 | return; | 6040 | return; |
6021 | 6041 | ||
6022 | switch (fan_control_access_mode) { | 6042 | switch (fan_control_access_mode) { |
6023 | case TPACPI_FAN_WR_ACPI_SFAN: | 6043 | case TPACPI_FAN_WR_ACPI_SFAN: |
6024 | do_set = (saved_fan_level > current_level); | 6044 | /* never decrease fan level */ |
6045 | do_set = (fan_control_resume_level > current_level); | ||
6025 | break; | 6046 | break; |
6026 | case TPACPI_FAN_WR_ACPI_FANS: | 6047 | case TPACPI_FAN_WR_ACPI_FANS: |
6027 | case TPACPI_FAN_WR_TPEC: | 6048 | case TPACPI_FAN_WR_TPEC: |
6028 | do_set = ((saved_fan_level & TP_EC_FAN_FULLSPEED) || | 6049 | /* never decrease fan level, scale is: |
6029 | (saved_fan_level == 7 && | 6050 | * TP_EC_FAN_FULLSPEED > 7 >= TP_EC_FAN_AUTO |
6030 | !(current_level & TP_EC_FAN_FULLSPEED))); | 6051 | * |
6052 | * We expect the firmware to set either 7 or AUTO, but we | ||
6053 | * handle FULLSPEED out of paranoia. | ||
6054 | * | ||
6055 | * So, we can safely only restore FULLSPEED or 7, anything | ||
6056 | * else could slow the fan. Restoring AUTO is useless, at | ||
6057 | * best that's exactly what the DSDT already set (it is the | ||
6058 | * slower it uses). | ||
6059 | * | ||
6060 | * Always keep in mind that the DSDT *will* have set the | ||
6061 | * fans to what the vendor supposes is the best level. We | ||
6062 | * muck with it only to speed the fan up. | ||
6063 | */ | ||
6064 | if (fan_control_resume_level != 7 && | ||
6065 | !(fan_control_resume_level & TP_EC_FAN_FULLSPEED)) | ||
6066 | return; | ||
6067 | else | ||
6068 | do_set = !(current_level & TP_EC_FAN_FULLSPEED) && | ||
6069 | (current_level != fan_control_resume_level); | ||
6031 | break; | 6070 | break; |
6032 | default: | 6071 | default: |
6033 | return; | 6072 | return; |
@@ -6035,8 +6074,11 @@ static void fan_resume(void) | |||
6035 | if (do_set) { | 6074 | if (do_set) { |
6036 | printk(TPACPI_NOTICE | 6075 | printk(TPACPI_NOTICE |
6037 | "restoring fan level to 0x%02x\n", | 6076 | "restoring fan level to 0x%02x\n", |
6038 | saved_fan_level); | 6077 | fan_control_resume_level); |
6039 | fan_set_level_safe(saved_fan_level); | 6078 | rc = fan_set_level_safe(fan_control_resume_level); |
6079 | if (rc < 0) | ||
6080 | printk(TPACPI_NOTICE | ||
6081 | "failed to restore fan level: %d\n", rc); | ||
6040 | } | 6082 | } |
6041 | } | 6083 | } |
6042 | 6084 | ||