diff options
author | Lennart Poettering <mzxreary@0pointer.de> | 2007-05-04 08:16:19 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-05-10 03:52:22 -0400 |
commit | 00eb43a1897a8845d0edb198cec69ac5f1f299dd (patch) | |
tree | 32cab402e51882c7854eab20556f0da75525cdc7 /drivers | |
parent | a64e62a07097f67108f0b68bc15216c3a4a5299b (diff) |
acpi,msi-laptop: Fall back to EC polling mode for MSI laptop specific EC commands
The ACPI EC that is used in MSI laptops knows some non-standard
commands for changing the screen brighntess and a few other things,
which are used by the msi-laptop.c driver. Unfortunately for these
commands no GPE events for IBF and OBF are triggered. Since nowadays
the EC code uses the ec_intr=1 mode by default, this causes these
operations to timeout, although they don't fail. In result, all
operations that you can do with the msi-laptop.c driver take more or
less 1s to complete, which is awfully slow.
In one of the more recent kernels (2.6.20?) the EC subsystem has been
revamped. With that change the EC timeout has been increased. before
that increase the MSI EC accesses were slow -- but not *that* slow,
hence I took notice of this limitation of the MSI EC hardware only very
recently.
The standard EC operations on the MSI EC as defined in the ACPI spec
support GPE events properly.
The following patch adds a new argument "force_poll" to the
ec_transaction() function (and friends). If set to 1, the function
will poll for IBF/OBF even if ec_intr=1 is enabled. If set to 0 the
current behaviour is used. The msi-laptop driver is modified to make
use of this new flag, so that OBF/IBF is polled for the special MSI EC
transactions -- but only for them.
Signed-off-by: Lennart Poettering <mzxreary@0pointer.de>
Acked-by: Alexey Starikovskiy <aystarik@gmail.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/ec.c | 39 | ||||
-rw-r--r-- | drivers/misc/msi-laptop.c | 12 |
2 files changed, 29 insertions, 22 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e08cf98f504f..82f496c07675 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -147,9 +147,10 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event, | |||
147 | return 0; | 147 | return 0; |
148 | } | 148 | } |
149 | 149 | ||
150 | static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count) | 150 | static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, |
151 | unsigned count, int force_poll) | ||
151 | { | 152 | { |
152 | if (acpi_ec_mode == EC_POLL) { | 153 | if (unlikely(force_poll) || acpi_ec_mode == EC_POLL) { |
153 | unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); | 154 | unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); |
154 | while (time_before(jiffies, delay)) { | 155 | while (time_before(jiffies, delay)) { |
155 | if (acpi_ec_check_status(ec, event, 0)) | 156 | if (acpi_ec_check_status(ec, event, 0)) |
@@ -173,14 +174,15 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count) | |||
173 | 174 | ||
174 | static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, | 175 | static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, |
175 | const u8 * wdata, unsigned wdata_len, | 176 | const u8 * wdata, unsigned wdata_len, |
176 | u8 * rdata, unsigned rdata_len) | 177 | u8 * rdata, unsigned rdata_len, |
178 | int force_poll) | ||
177 | { | 179 | { |
178 | int result = 0; | 180 | int result = 0; |
179 | unsigned count = atomic_read(&ec->event_count); | 181 | unsigned count = atomic_read(&ec->event_count); |
180 | acpi_ec_write_cmd(ec, command); | 182 | acpi_ec_write_cmd(ec, command); |
181 | 183 | ||
182 | for (; wdata_len > 0; --wdata_len) { | 184 | for (; wdata_len > 0; --wdata_len) { |
183 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count); | 185 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count, force_poll); |
184 | if (result) { | 186 | if (result) { |
185 | printk(KERN_ERR PREFIX | 187 | printk(KERN_ERR PREFIX |
186 | "write_cmd timeout, command = %d\n", command); | 188 | "write_cmd timeout, command = %d\n", command); |
@@ -191,7 +193,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, | |||
191 | } | 193 | } |
192 | 194 | ||
193 | if (!rdata_len) { | 195 | if (!rdata_len) { |
194 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count); | 196 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count, force_poll); |
195 | if (result) { | 197 | if (result) { |
196 | printk(KERN_ERR PREFIX | 198 | printk(KERN_ERR PREFIX |
197 | "finish-write timeout, command = %d\n", command); | 199 | "finish-write timeout, command = %d\n", command); |
@@ -202,7 +204,7 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, | |||
202 | } | 204 | } |
203 | 205 | ||
204 | for (; rdata_len > 0; --rdata_len) { | 206 | for (; rdata_len > 0; --rdata_len) { |
205 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, count); | 207 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, count, force_poll); |
206 | if (result) { | 208 | if (result) { |
207 | printk(KERN_ERR PREFIX "read timeout, command = %d\n", | 209 | printk(KERN_ERR PREFIX "read timeout, command = %d\n", |
208 | command); | 210 | command); |
@@ -217,7 +219,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, | |||
217 | 219 | ||
218 | static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, | 220 | static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, |
219 | const u8 * wdata, unsigned wdata_len, | 221 | const u8 * wdata, unsigned wdata_len, |
220 | u8 * rdata, unsigned rdata_len) | 222 | u8 * rdata, unsigned rdata_len, |
223 | int force_poll) | ||
221 | { | 224 | { |
222 | int status; | 225 | int status; |
223 | u32 glk; | 226 | u32 glk; |
@@ -240,7 +243,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, | |||
240 | /* Make sure GPE is enabled before doing transaction */ | 243 | /* Make sure GPE is enabled before doing transaction */ |
241 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); | 244 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); |
242 | 245 | ||
243 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0); | 246 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0, 0); |
244 | if (status) { | 247 | if (status) { |
245 | printk(KERN_DEBUG PREFIX | 248 | printk(KERN_DEBUG PREFIX |
246 | "input buffer is not empty, aborting transaction\n"); | 249 | "input buffer is not empty, aborting transaction\n"); |
@@ -249,7 +252,8 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, | |||
249 | 252 | ||
250 | status = acpi_ec_transaction_unlocked(ec, command, | 253 | status = acpi_ec_transaction_unlocked(ec, command, |
251 | wdata, wdata_len, | 254 | wdata, wdata_len, |
252 | rdata, rdata_len); | 255 | rdata, rdata_len, |
256 | force_poll); | ||
253 | 257 | ||
254 | end: | 258 | end: |
255 | 259 | ||
@@ -267,12 +271,12 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, | |||
267 | int acpi_ec_burst_enable(struct acpi_ec *ec) | 271 | int acpi_ec_burst_enable(struct acpi_ec *ec) |
268 | { | 272 | { |
269 | u8 d; | 273 | u8 d; |
270 | return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1); | 274 | return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1, 0); |
271 | } | 275 | } |
272 | 276 | ||
273 | int acpi_ec_burst_disable(struct acpi_ec *ec) | 277 | int acpi_ec_burst_disable(struct acpi_ec *ec) |
274 | { | 278 | { |
275 | return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0); | 279 | return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0, 0); |
276 | } | 280 | } |
277 | 281 | ||
278 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) | 282 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) |
@@ -281,7 +285,7 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) | |||
281 | u8 d; | 285 | u8 d; |
282 | 286 | ||
283 | result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_READ, | 287 | result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_READ, |
284 | &address, 1, &d, 1); | 288 | &address, 1, &d, 1, 0); |
285 | *data = d; | 289 | *data = d; |
286 | return result; | 290 | return result; |
287 | } | 291 | } |
@@ -290,7 +294,7 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) | |||
290 | { | 294 | { |
291 | u8 wdata[2] = { address, data }; | 295 | u8 wdata[2] = { address, data }; |
292 | return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, | 296 | return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, |
293 | wdata, 2, NULL, 0); | 297 | wdata, 2, NULL, 0, 0); |
294 | } | 298 | } |
295 | 299 | ||
296 | /* | 300 | /* |
@@ -349,13 +353,15 @@ EXPORT_SYMBOL(ec_write); | |||
349 | 353 | ||
350 | int ec_transaction(u8 command, | 354 | int ec_transaction(u8 command, |
351 | const u8 * wdata, unsigned wdata_len, | 355 | const u8 * wdata, unsigned wdata_len, |
352 | u8 * rdata, unsigned rdata_len) | 356 | u8 * rdata, unsigned rdata_len, |
357 | int force_poll) | ||
353 | { | 358 | { |
354 | if (!first_ec) | 359 | if (!first_ec) |
355 | return -ENODEV; | 360 | return -ENODEV; |
356 | 361 | ||
357 | return acpi_ec_transaction(first_ec, command, wdata, | 362 | return acpi_ec_transaction(first_ec, command, wdata, |
358 | wdata_len, rdata, rdata_len); | 363 | wdata_len, rdata, rdata_len, |
364 | force_poll); | ||
359 | } | 365 | } |
360 | 366 | ||
361 | EXPORT_SYMBOL(ec_transaction); | 367 | EXPORT_SYMBOL(ec_transaction); |
@@ -374,7 +380,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | |||
374 | * bit to be cleared (and thus clearing the interrupt source). | 380 | * bit to be cleared (and thus clearing the interrupt source). |
375 | */ | 381 | */ |
376 | 382 | ||
377 | result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1); | 383 | result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1, 0); |
378 | if (result) | 384 | if (result) |
379 | return result; | 385 | return result; |
380 | 386 | ||
@@ -410,6 +416,7 @@ static u32 acpi_ec_gpe_handler(void *data) | |||
410 | acpi_status status = AE_OK; | 416 | acpi_status status = AE_OK; |
411 | u8 value; | 417 | u8 value; |
412 | struct acpi_ec *ec = data; | 418 | struct acpi_ec *ec = data; |
419 | |||
413 | atomic_inc(&ec->event_count); | 420 | atomic_inc(&ec->event_count); |
414 | 421 | ||
415 | if (acpi_ec_mode == EC_INTR) { | 422 | if (acpi_ec_mode == EC_INTR) { |
diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index 68c4b58525ba..41e901f53e7c 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c | |||
@@ -85,7 +85,7 @@ static int set_lcd_level(int level) | |||
85 | buf[0] = 0x80; | 85 | buf[0] = 0x80; |
86 | buf[1] = (u8) (level*31); | 86 | buf[1] = (u8) (level*31); |
87 | 87 | ||
88 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0); | 88 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0, 1); |
89 | } | 89 | } |
90 | 90 | ||
91 | static int get_lcd_level(void) | 91 | static int get_lcd_level(void) |
@@ -93,7 +93,7 @@ static int get_lcd_level(void) | |||
93 | u8 wdata = 0, rdata; | 93 | u8 wdata = 0, rdata; |
94 | int result; | 94 | int result; |
95 | 95 | ||
96 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1); | 96 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); |
97 | if (result < 0) | 97 | if (result < 0) |
98 | return result; | 98 | return result; |
99 | 99 | ||
@@ -105,7 +105,7 @@ static int get_auto_brightness(void) | |||
105 | u8 wdata = 4, rdata; | 105 | u8 wdata = 4, rdata; |
106 | int result; | 106 | int result; |
107 | 107 | ||
108 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1); | 108 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1, 1); |
109 | if (result < 0) | 109 | if (result < 0) |
110 | return result; | 110 | return result; |
111 | 111 | ||
@@ -119,14 +119,14 @@ static int set_auto_brightness(int enable) | |||
119 | 119 | ||
120 | wdata[0] = 4; | 120 | wdata[0] = 4; |
121 | 121 | ||
122 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1); | 122 | result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1, 1); |
123 | if (result < 0) | 123 | if (result < 0) |
124 | return result; | 124 | return result; |
125 | 125 | ||
126 | wdata[0] = 0x84; | 126 | wdata[0] = 0x84; |
127 | wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); | 127 | wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0); |
128 | 128 | ||
129 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0); | 129 | return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0, 1); |
130 | } | 130 | } |
131 | 131 | ||
132 | static int get_wireless_state(int *wlan, int *bluetooth) | 132 | static int get_wireless_state(int *wlan, int *bluetooth) |
@@ -134,7 +134,7 @@ static int get_wireless_state(int *wlan, int *bluetooth) | |||
134 | u8 wdata = 0, rdata; | 134 | u8 wdata = 0, rdata; |
135 | int result; | 135 | int result; |
136 | 136 | ||
137 | result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1); | 137 | result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1, 1); |
138 | if (result < 0) | 138 | if (result < 0) |
139 | return -1; | 139 | return -1; |
140 | 140 | ||