~ubuntu-branches/ubuntu/quantal/linux-lowlatency/quantal

« back to all changes in this revision

Viewing changes to ubuntu/omnibook/nbsmi.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-04kado7d1u2er2rl
Tags: 3.2.0-16.25
Add new lowlatency kernel flavour

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * nbsmi.c -- Toshiba SMI low-level acces code
 
3
 * 
 
4
 * This program is free software; you can redistribute it and/or modify it
 
5
 * under the terms of the GNU General Public License as published by the
 
6
 * Free Software Foundation; either version 2, or (at your option) any
 
7
 * later version.
 
8
 * 
 
9
 * This program is distributed in the hope that it will be useful, but
 
10
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 
12
 * General Public License for more details.
 
13
 *
 
14
 * Written by Mathieu Bérard <mathieu.berard@crans.org>, 2006
 
15
 *
 
16
 * Sources of inspirations for this code were:
 
17
 * -Toshiba via provided hardware specification
 
18
 * -Thorsten Zachmann with the 's1bl' project
 
19
 * -Frederico Munoz with the 'tecra_acpi' project
 
20
 * Thanks to them
 
21
 */
 
22
 
 
23
#include "omnibook.h"
 
24
#include "hardware.h"
 
25
#include <linux/preempt.h>
 
26
#include <linux/pci.h>
 
27
#include <linux/kref.h>
 
28
#include <asm/io.h>
 
29
#include <asm/mc146818rtc.h>
 
30
#include <linux/workqueue.h>
 
31
#include <linux/delay.h>
 
32
 
 
33
/* copied from drivers/input/serio/i8042-io.h */
 
34
#define I8042_KBD_PHYS_DESC "isa0060/serio0"
 
35
 
 
36
/*
 
37
 * ATI's IXP PCI-LPC bridge
 
38
 */
 
39
#define INTEL_PMBASE    0x40
 
40
#define INTEL_GPE0_EN   0x2c
 
41
 
 
42
#define BUFFER_SIZE     0x20
 
43
#define INTEL_OFFSET    0x60
 
44
#define INTEL_SMI_PORT  0xb2    /* APM_CNT port in INTEL ICH specs */
 
45
 
 
46
/*
 
47
 * Toshiba Specs state 0xef here but:
 
48
 * -this would overflow (ef + 19 > ff)
 
49
 * -code from Toshiba use e0, which make much more sense
 
50
 */
 
51
 
 
52
#define ATI_OFFSET      0xe0
 
53
#define ATI_SMI_PORT    0xb0
 
54
 
 
55
#define EC_INDEX_PORT   0x300
 
56
#define EC_DATA_PORT    0x301
 
57
 
 
58
/* Masks decode for GetAeral */
 
59
#define WLEX_MASK       0x4
 
60
#define WLAT_MASK       0x8
 
61
#define BTEX_MASK       0x1
 
62
#define BTAT_MASK       0x2
 
63
 
 
64
/*
 
65
 * Private data of this backend
 
66
 */
 
67
struct nbsmi_backend_data {
 
68
        struct pci_dev *lpc_bridge;     /* Southbridge chip ISA bridge/LPC interface PCI device */
 
69
        u8 start_offset;                /* Start offset in CMOS memory */
 
70
        struct input_dev *nbsmi_input_dev;
 
71
        struct work_struct fnkey_work;
 
72
};
 
73
 
 
74
/*
 
75
 * Possible list of supported southbridges
 
76
 * Here mostly to implement a more or less clean PCI probing
 
77
 * Works only because of previous DMI probing.
 
78
 * It's in compal.c
 
79
 */
 
80
extern const struct pci_device_id lpc_bridge_table[];
 
81
 
 
82
/*
 
83
 * Since we are going to trigger an SMI, all registers (I assume this does not
 
84
 * include esp and maybe ebp) and eflags may be mangled in the
 
85
 * process. 
 
86
 * We also disable preemtion and IRQs upon SMI call.
 
87
 */
 
88
static inline u32 ati_do_smi_call(u16 function)
 
89
{
 
90
        unsigned long flags;
 
91
        u32 retval = 0;
 
92
 
 
93
        local_irq_save(flags);
 
94
        preempt_disable();
 
95
        
 
96
/*
 
97
 * eflags, eax, ebx, ecx, edx, esi and edi are clobbered upon writing to SMI_PORT
 
98
 * thus the clobber list.
 
99
 *
 
100
 * Equivalent pseudocode:
 
101
 *
 
102
 * eax = function; [non null]
 
103
 * outw(eax, ATI_SMI_PORT); <- This Trigger an SMI
 
104
 * if( eax == 0 ) [success if eax has been cleared]
 
105
 *      goto out;
 
106
 * if( inb(ATI_SMI_PORT + 1) == 0) [if not in eax, success maybe be stored here]
 
107
 *      goto out;
 
108
 * retval = -EIO; [too bad]
 
109
 * out:
 
110
 */
 
111
        __asm__ __volatile__("outw  %%ax, %2;   \
 
112
                              orw %%ax, %%ax;   \
 
113
                              jz 1f;            \
 
114
                              inw %3, %%ax;     \
 
115
                              orw %%ax, %%ax;   \
 
116
                              jz 1f;            \
 
117
                              movl %4, %0;      \
 
118
                              1:;"
 
119
                             : "=m" (retval)
 
120
                             : "a"(function), "N"(ATI_SMI_PORT), "N"(ATI_SMI_PORT+1), "i"(-EIO)
 
121
                             : "memory", "ebx", "ecx", "edx", "esi", "edi", "cc");
 
122
 
 
123
        local_irq_restore(flags);
 
124
        preempt_enable_no_resched();
 
125
        return retval;
 
126
}
 
127
 
 
128
static inline u32 intel_do_smi_call(u16 function, struct pci_dev *lpc_bridge)
 
129
{
 
130
        u32 state;
 
131
        unsigned long flags;
 
132
        u32 retval = 0;
 
133
        u32 sci_en = 0;
 
134
 
 
135
        local_irq_save(flags);
 
136
        preempt_disable();
 
137
 
 
138
/* 
 
139
 * We get the PMBASE offset ( bits 15:7 at 0x40 offset of PCI config space )
 
140
 * And we access offset 2c (GPE0_EN), save the state, disable all SCI
 
141
 * and restore the state after the SMI call
 
142
 */                     
 
143
        pci_read_config_dword(lpc_bridge, INTEL_PMBASE, &sci_en);
 
144
        sci_en = sci_en & 0xff80; /* Keep bits 15:7 */
 
145
        sci_en += INTEL_GPE0_EN;  /* GPEO_EN offset */
 
146
        state = inl(sci_en);
 
147
        outl(0, sci_en);
 
148
 
 
149
/*
 
150
 * eflags, eax, ebx, ecx, edx, esi and edi are clobbered upon writing to SMI_PORT
 
151
 * thus the clobber list.
 
152
 *
 
153
 * Equivalent pseudocode:
 
154
 *
 
155
 * eax = function; [non null]
 
156
 * outw(eax, INTEL_SMI_PORT); <- This Trigger an SMI
 
157
 * if( eax == 0 ) [success if eax has been cleared]
 
158
 *      goto out; 
 
159
 * retval = -EIO; [too bad]
 
160
 * out:
 
161
 */
 
162
        __asm__ __volatile__("outw %%ax, %2;    \
 
163
                              orw %%ax, %%ax;   \
 
164
                              jz 1f;            \
 
165
                              movl %3, %0;      \
 
166
                              1:;"
 
167
                             : "=m" (retval)
 
168
                             : "a"(function), "N"(INTEL_SMI_PORT), "i"(-EIO)
 
169
                             : "memory", "ebx", "ecx", "edx", "esi", "edi", "cc");
 
170
 
 
171
        outl(state, sci_en);
 
172
        local_irq_restore(flags);
 
173
        preempt_enable_no_resched();
 
174
        return retval;
 
175
}
 
176
 
 
177
static int nbsmi_smi_command(u16 function, 
 
178
                             const u8 * inputbuffer,
 
179
                             u8 * outputbuffer,
 
180
                             const struct nbsmi_backend_data *priv_data)
 
181
{
 
182
        int count;
 
183
        u32 retval = 0;
 
184
        
 
185
 
 
186
        for (count = 0; count < BUFFER_SIZE; count++) {
 
187
                outb(count + priv_data->start_offset, RTC_PORT(2));
 
188
                outb(*(inputbuffer + count), RTC_PORT(3));
 
189
        }
 
190
 
 
191
/* 
 
192
 * We have to write 0xe4XX to smi_port
 
193
 * where XX is the SMI function code
 
194
 */
 
195
        function = (function & 0xff) << 8;
 
196
        function |= 0xe4;
 
197
 
 
198
        switch (priv_data->lpc_bridge->vendor) {
 
199
        case PCI_VENDOR_ID_INTEL:
 
200
                retval = intel_do_smi_call(function, priv_data->lpc_bridge);
 
201
                break;
 
202
        case PCI_VENDOR_ID_ATI:
 
203
                retval = ati_do_smi_call(function);
 
204
                break;
 
205
        default:
 
206
                BUG();
 
207
        }
 
208
 
 
209
        if (retval)
 
210
                printk(O_ERR "smi_command failed with error %u.\n", retval);
 
211
 
 
212
        for (count = 0; count < BUFFER_SIZE; count++) {
 
213
                outb(count + priv_data->start_offset, RTC_PORT(2));
 
214
                *(outputbuffer + count) = inb(RTC_PORT(3));
 
215
        }
 
216
 
 
217
        return retval;
 
218
}
 
219
 
 
220
static int nbsmi_smi_read_command(const struct omnibook_operation *io_op, u8 * data)
 
221
{
 
222
        int retval;
 
223
        u8 *inputbuffer;
 
224
        u8 *outputbuffer;
 
225
        struct nbsmi_backend_data *priv_data = io_op->backend->data;
 
226
 
 
227
        if (!priv_data)
 
228
                return -ENODEV;
 
229
 
 
230
        inputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL);
 
231
        if (!inputbuffer) {
 
232
                retval = -ENOMEM;
 
233
                goto error1;
 
234
        }
 
235
 
 
236
        outputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL);
 
237
        if (!outputbuffer) {
 
238
                retval = -ENOMEM;
 
239
                goto error2;
 
240
        }
 
241
 
 
242
        retval = nbsmi_smi_command((u16) io_op->read_addr, inputbuffer, outputbuffer, priv_data);
 
243
        if (retval)
 
244
                goto out;
 
245
 
 
246
        *data = outputbuffer[0];
 
247
 
 
248
        if (io_op->read_mask)
 
249
                *data &= io_op->read_mask;
 
250
 
 
251
      out:
 
252
        kfree(outputbuffer);
 
253
      error2:
 
254
        kfree(inputbuffer);
 
255
      error1:
 
256
        return retval;
 
257
}
 
258
 
 
259
static int nbsmi_smi_write_command(const struct omnibook_operation *io_op, u8 data)
 
260
{
 
261
        int retval;
 
262
        u8 *inputbuffer;
 
263
        u8 *outputbuffer;
 
264
        struct nbsmi_backend_data *priv_data = io_op->backend->data;
 
265
 
 
266
        if (!priv_data)
 
267
                return -ENODEV;
 
268
 
 
269
        inputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL);
 
270
        if (!inputbuffer) {
 
271
                retval = -ENOMEM;
 
272
                goto error1;
 
273
        }
 
274
 
 
275
        outputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL);
 
276
        if (!outputbuffer) {
 
277
                retval = -ENOMEM;
 
278
                goto error2;
 
279
        }
 
280
 
 
281
        inputbuffer[0] = data;
 
282
 
 
283
        retval = nbsmi_smi_command((u16) io_op->write_addr, inputbuffer, outputbuffer, priv_data);
 
284
 
 
285
        kfree(outputbuffer);
 
286
      error2:
 
287
        kfree(inputbuffer);
 
288
      error1:
 
289
        return retval;
 
290
}
 
291
 
 
292
/*
 
293
 * Read/Write to INDEX/DATA interface at port 0x300 (SMSC Mailbox registers)
 
294
 */
 
295
static inline void nbsmi_ec_read_command(u8 index, u8 * data)
 
296
{
 
297
        outb(index, EC_INDEX_PORT);
 
298
        *data = inb(EC_DATA_PORT);
 
299
}
 
300
 
 
301
#if 0
 
302
static inline void nbsmi_ec_write_command(u8 index, u8 data)
 
303
{
 
304
        outb(index, EC_INDEX_PORT);
 
305
        outb(data, EC_DATA_PORT);
 
306
}
 
307
#endif
 
308
 
 
309
 
 
310
/*
 
311
 * Hotkeys workflow:
 
312
 * 1. Fn+Foo pressed
 
313
 * 2. Scancode 0x6d generated by kbd controller
 
314
 * 3. Scancode 0x6d caught by omnibook input handler
 
315
 * 4. SMI Call issued -> Got keycode of last actually pressed Fn key
 
316
 * 5. nbsmi_scan_table used to associate a detected keycode with a generated one
 
317
 * 6. Generated keycode issued using the omnibook input device
 
318
 */
 
319
 
 
320
/*
 
321
 * The input handler should only bind with the standard AT keyboard.
 
322
 * XXX: Scancode 0x6d won't be detected if the keyboard has already been
 
323
 * grabbed (the Xorg event input driver do that)
 
324
 */
 
325
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
 
326
static int hook_connect(struct input_handler *handler,
 
327
                                         struct input_dev *dev,
 
328
                                         const struct input_device_id *id)
 
329
#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
 
330
static struct input_handle *hook_connect(struct input_handler *handler,
 
331
                                         struct input_dev *dev,
 
332
                                         const struct input_device_id *id)
 
333
#else
 
334
static struct input_handle *hook_connect(struct input_handler *handler,
 
335
                                         struct input_dev *dev,
 
336
                                         struct input_device_id *id)
 
337
#endif
 
338
{
 
339
        struct input_handle *handle;
 
340
        int error;
 
341
 
 
342
        /* the 0x0001 vendor magic number is found in atkbd.c */
 
343
        if(!(dev->id.bustype == BUS_I8042 && dev->id.vendor == 0x0001))
 
344
                goto out_nobind;
 
345
 
 
346
        if(!strstr(dev->phys, I8042_KBD_PHYS_DESC))
 
347
                goto out_nobind;
 
348
 
 
349
        dprintk("hook_connect for device %s.\n", dev->name);
 
350
 
 
351
        if(dev->grab)
 
352
                printk(O_WARN "Input device is grabbed by %s, Fn hotkeys won't work.\n",
 
353
                        dev->grab->name);
 
354
 
 
355
        handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
 
356
        if (!handle)
 
357
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
 
358
                return -ENOMEM;
 
359
#else
 
360
                return NULL;
 
361
#endif
 
362
 
 
363
        handle->dev = dev;
 
364
        handle->handler = handler;
 
365
        handle->name = "omnibook_scancode_hook";
 
366
        handle->private = handler->private;
 
367
 
 
368
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
 
369
        error = input_register_handle(handle);
 
370
        if (error) {
 
371
                dprintk("register_handle failed\n");
 
372
                goto out_nobind_free;
 
373
        } 
 
374
        error = input_open_device(handle);
 
375
        if (error) {
 
376
                dprintk("register_handle failed\n");
 
377
                input_unregister_handle(handle);
 
378
                goto out_nobind_free;
 
379
        } 
 
380
        
 
381
#else
 
382
        error = input_open_device(handle);
 
383
        if (error==0) dprintk("Input device opened\n");
 
384
        else { 
 
385
                dprintk("opening input device failed\n");
 
386
                goto out_nobind_free;
 
387
        }
 
388
#endif
 
389
 
 
390
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
 
391
        return 0;
 
392
out_nobind_free:
 
393
        kfree(handle);
 
394
out_nobind:
 
395
        return -ENODEV;
 
396
#else
 
397
        return handle;
 
398
out_nobind_free:
 
399
        kfree(handle);
 
400
out_nobind:
 
401
        return NULL;
 
402
#endif  
 
403
}
 
404
 
 
405
static void hook_disconnect(struct input_handle *handle)
 
406
{
 
407
        dprintk("hook_disconnect.\n");
 
408
        input_close_device(handle);
 
409
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
 
410
        input_unregister_handle(handle);
 
411
#endif
 
412
        kfree(handle);
 
413
}
 
414
 
 
415
/*
 
416
 * Hook for scancode 0x6d. Actual handling is done in a workqueue as 
 
417
 * the nbsmi backend might sleep.
 
418
 */
 
419
 
 
420
static void hook_event(struct input_handle *handle, unsigned int event_type,
 
421
                      unsigned int event_code, int value)
 
422
{
 
423
        if (event_type == EV_MSC && event_code == MSC_SCAN && value == SMI_FN_SCAN)
 
424
                schedule_work(&((struct nbsmi_backend_data *)handle->private)->fnkey_work);
 
425
}
 
426
 
 
427
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
 
428
static const struct input_device_id hook_ids[] = {
 
429
#else
 
430
static struct input_device_id hook_ids[] = {
 
431
#endif
 
432
        {
 
433
                .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
 
434
                .evbit = { BIT(EV_KEY) },
 
435
        },
 
436
        { },    /* Terminating entry */
 
437
};
 
438
 
 
439
static struct input_handler hook_handler = {
 
440
        .event          = hook_event,
 
441
        .connect        = hook_connect,
 
442
        .disconnect     = hook_disconnect,
 
443
        .name           = OMNIBOOK_MODULE_NAME,
 
444
        .id_table       = hook_ids,
 
445
};
 
446
 
 
447
/*
 
448
 * Define some KEY_ that may be missing in input.h for some kernel versions
 
449
 */
 
450
#ifndef KEY_WLAN
 
451
#define KEY_WLAN 238
 
452
#endif 
 
453
 
 
454
/*
 
455
 * Detected scancode to keycode table
 
456
 */
 
457
static const struct {
 
458
        unsigned int scancode;
 
459
        unsigned int keycode;
 
460
} nbsmi_scan_table[] = {
 
461
        { KEY_ESC, KEY_MUTE},
 
462
        { KEY_F1, KEY_FN_F1},
 
463
        { KEY_F2, KEY_PROG1},
 
464
        { KEY_F3, KEY_SLEEP},
 
465
        { KEY_F4, KEY_SUSPEND},
 
466
        { KEY_F5, KEY_SWITCHVIDEOMODE},
 
467
        { KEY_F6, KEY_BRIGHTNESSDOWN},
 
468
        { KEY_F7, KEY_BRIGHTNESSUP},
 
469
        { KEY_F8, KEY_WLAN},
 
470
        { KEY_F9, KEY_FN_F9},
 
471
        { KEY_SPACE, KEY_ZOOM},
 
472
        { 0,0},
 
473
};
 
474
 
 
475
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
 
476
static void omnibook_handle_fnkey(struct work_struct *work);
 
477
#else
 
478
static void omnibook_handle_fnkey(void* data);
 
479
#endif
 
480
 
 
481
/*
 
482
 * Register the input handler and the input device in the input subsystem
 
483
 */
 
484
static int register_input_subsystem(struct nbsmi_backend_data *priv_data)
 
485
{
 
486
        int i, retval = 0;
 
487
        struct input_dev *nbsmi_input_dev;
 
488
 
 
489
        nbsmi_input_dev = input_allocate_device();
 
490
        if (!nbsmi_input_dev) {
 
491
                retval = -ENOMEM;
 
492
                goto out;
 
493
        }
 
494
 
 
495
        nbsmi_input_dev->name = "Omnibook NbSMI scancode generator";
 
496
        nbsmi_input_dev->phys = "omnibook/input0";
 
497
        nbsmi_input_dev->id.bustype = BUS_HOST;
 
498
        
 
499
        set_bit(EV_KEY, nbsmi_input_dev->evbit);
 
500
        
 
501
        for(i=0 ; i < ARRAY_SIZE(nbsmi_scan_table); i++)
 
502
                set_bit(nbsmi_scan_table[i].keycode, nbsmi_input_dev->keybit);
 
503
 
 
504
        retval = input_register_device(nbsmi_input_dev);
 
505
        if(retval) {
 
506
                input_free_device(nbsmi_input_dev);
 
507
                goto out;
 
508
        }
 
509
 
 
510
        priv_data->nbsmi_input_dev = nbsmi_input_dev;
 
511
 
 
512
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
 
513
        INIT_WORK(&priv_data->fnkey_work, *omnibook_handle_fnkey);
 
514
#else
 
515
        INIT_WORK(&priv_data->fnkey_work, *omnibook_handle_fnkey, priv_data);
 
516
#endif
 
517
 
 
518
 
 
519
        hook_handler.private = priv_data;
 
520
 
 
521
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
 
522
        retval = input_register_handler(&hook_handler); 
 
523
#else
 
524
        input_register_handler(&hook_handler);
 
525
#endif
 
526
 
 
527
        out:    
 
528
        return retval;
 
529
}
 
530
 
 
531
/*
 
532
 * Try to init the backend
 
533
 * This function can be called blindly as it use a kref
 
534
 * to check if the init sequence was already done.
 
535
 */
 
536
static int omnibook_nbsmi_init(const struct omnibook_operation *io_op)
 
537
{
 
538
        int retval = 0;
 
539
        int i;
 
540
        u8 ec_data;
 
541
        u32 smi_port = 0;
 
542
        struct nbsmi_backend_data *priv_data;
 
543
 
 
544
        /* ectypes other than TSM40 have no business with this backend */
 
545
        if (!(omnibook_ectype & TSM40))
 
546
                return -ENODEV;
 
547
 
 
548
        if (io_op->backend->already_failed) {
 
549
                dprintk("NbSmi backend init already failed, skipping.\n");
 
550
                return -ENODEV;
 
551
        }
 
552
 
 
553
        if (!io_op->backend->data) {
 
554
                /* Fist use of the backend */
 
555
                dprintk("Try to init NbSmi\n");
 
556
                mutex_init(&io_op->backend->mutex);
 
557
                mutex_lock(&io_op->backend->mutex);
 
558
                kref_init(&io_op->backend->kref);
 
559
 
 
560
                priv_data = kzalloc(sizeof(struct nbsmi_backend_data), GFP_KERNEL);
 
561
                if (!priv_data) {
 
562
                        retval = -ENOMEM;
 
563
                        goto error0;
 
564
                }
 
565
 
 
566
                /* PCI probing: find the LPC Super I/O bridge PCI device */
 
567
                for (i = 0; !priv_data->lpc_bridge && lpc_bridge_table[i].vendor; ++i)
 
568
                        priv_data->lpc_bridge =
 
569
                            pci_get_device(lpc_bridge_table[i].vendor, lpc_bridge_table[i].device,
 
570
                                           NULL);
 
571
 
 
572
                if (!priv_data->lpc_bridge) {
 
573
                        printk(O_ERR "Fail to find a supported LPC I/O bridge, please report\n");
 
574
                        retval = -ENODEV;
 
575
                        goto error1;
 
576
                }
 
577
 
 
578
                if ((retval = pci_enable_device(priv_data->lpc_bridge))) {
 
579
                        printk(O_ERR "Unable to enable PCI device.\n");
 
580
                        goto error2;
 
581
                }
 
582
 
 
583
                switch (priv_data->lpc_bridge->vendor) {
 
584
                case PCI_VENDOR_ID_INTEL:
 
585
                        priv_data->start_offset = INTEL_OFFSET;
 
586
                        smi_port = INTEL_SMI_PORT;
 
587
                        break;
 
588
                case PCI_VENDOR_ID_ATI:
 
589
                        priv_data->start_offset = ATI_OFFSET;
 
590
                        smi_port = ATI_SMI_PORT;
 
591
                        break;
 
592
                default:
 
593
                        BUG();
 
594
                }
 
595
 
 
596
                if (!request_region(smi_port, 2, OMNIBOOK_MODULE_NAME)) {
 
597
                        printk(O_ERR "Request SMI I/O region error\n");
 
598
                        retval = -ENODEV;
 
599
                        goto error2;
 
600
                }
 
601
 
 
602
                if (!request_region(EC_INDEX_PORT, 2, OMNIBOOK_MODULE_NAME)) {
 
603
                        printk(O_ERR "Request EC I/O region error\n");
 
604
                        retval = -ENODEV;
 
605
                        goto error3;
 
606
                }
 
607
 
 
608
                /*
 
609
                 * Try some heuristic tests to avoid enabling this interface on unsuported laptops:
 
610
                 * See what a port 300h read index 8f gives. Guess there is nothing if read 0xff
 
611
                 */
 
612
 
 
613
                nbsmi_ec_read_command(SMI_FN_PRESSED, &ec_data);
 
614
                dprintk("NbSmi test probe read: %x\n", ec_data);
 
615
                if (ec_data == 0xff) {
 
616
                        printk(O_ERR "Probing at SMSC Mailbox registers failed, disabling NbSmi\n");
 
617
                        retval = -ENODEV;
 
618
                        goto error4;
 
619
                }
 
620
 
 
621
                retval = register_input_subsystem(priv_data);
 
622
                if(retval)
 
623
                        goto error4;
 
624
 
 
625
                io_op->backend->data = priv_data;
 
626
 
 
627
                dprintk("NbSmi init ok\n");
 
628
                mutex_unlock(&io_op->backend->mutex);
 
629
                return 0;
 
630
        } else {
 
631
                dprintk("NbSmi has already been initialized\n");
 
632
                kref_get(&io_op->backend->kref);
 
633
                return 0;
 
634
        }
 
635
      error4:
 
636
        release_region(EC_INDEX_PORT, 2);
 
637
      error3:
 
638
        release_region(smi_port, 2);
 
639
      error2:
 
640
        pci_dev_put(priv_data->lpc_bridge);
 
641
      error1:
 
642
        kfree(priv_data);
 
643
        io_op->backend->data = NULL;
 
644
      error0:
 
645
        io_op->backend->already_failed = 1;
 
646
        mutex_unlock(&io_op->backend->mutex);
 
647
        mutex_destroy(&io_op->backend->mutex);
 
648
        return retval;
 
649
}
 
650
 
 
651
/*
 
652
 * Free all allocated stuff and unregister from the input subsystem
 
653
 */
 
654
static void nbsmi_free(struct kref *ref)
 
655
{
 
656
        u32 smi_port = 0;
 
657
        struct omnibook_backend *backend;
 
658
        struct nbsmi_backend_data *priv_data;
 
659
 
 
660
        dprintk("NbSmi not used anymore: disposing\n");
 
661
 
 
662
        backend = container_of(ref, struct omnibook_backend, kref);
 
663
        priv_data = backend->data;
 
664
 
 
665
        flush_scheduled_work();
 
666
        input_unregister_handler(&hook_handler);
 
667
        input_unregister_device(priv_data->nbsmi_input_dev);
 
668
 
 
669
        mutex_lock(&backend->mutex);
 
670
 
 
671
        switch (priv_data->lpc_bridge->vendor) {
 
672
        case PCI_VENDOR_ID_INTEL:
 
673
                smi_port = INTEL_SMI_PORT;
 
674
                break;
 
675
        case PCI_VENDOR_ID_ATI:
 
676
                smi_port = ATI_SMI_PORT;
 
677
                break;
 
678
        default:
 
679
                BUG();
 
680
        }
 
681
 
 
682
        pci_dev_put(priv_data->lpc_bridge);
 
683
        release_region(smi_port, 2);
 
684
        release_region(EC_INDEX_PORT, 2);
 
685
        kfree(priv_data);
 
686
        backend->data = NULL;
 
687
        mutex_unlock(&backend->mutex);
 
688
        mutex_destroy(&backend->mutex);
 
689
}
 
690
 
 
691
static void omnibook_nbsmi_exit(const struct omnibook_operation *io_op)
 
692
{
 
693
        /* ectypes other than TSM40 have no business with this backend */
 
694
        BUG_ON(!(omnibook_ectype & TSM40));
 
695
        dprintk("Trying to dispose NbSmi\n");
 
696
        kref_put(&io_op->backend->kref, nbsmi_free);
 
697
}
 
698
 
 
699
/*
 
700
 * Adjust the lcd backlight level by delta.
 
701
 * Used for Fn+F6/F7 keypress
 
702
 */
 
703
static int adjust_brighness(int delta)
 
704
{
 
705
        struct omnibook_feature *lcd_feature = omnibook_find_feature("lcd");
 
706
        struct omnibook_operation *io_op;
 
707
        int retval = 0;
 
708
        u8 brgt;
 
709
 
 
710
        if(!lcd_feature)
 
711
                return -ENODEV;
 
712
 
 
713
        io_op = lcd_feature->io_op;
 
714
 
 
715
        mutex_lock(&io_op->backend->mutex);
 
716
 
 
717
        if(( retval = __backend_byte_read(io_op, &brgt)))
 
718
                goto out;       
 
719
 
 
720
        dprintk("FnF6/F7 pressed: adjusting britghtnes.\n");
 
721
 
 
722
        if (((int) brgt + delta) < 0)
 
723
                brgt = 0;
 
724
        else if ((brgt + delta) > omnibook_max_brightness)
 
725
                brgt = omnibook_max_brightness;
 
726
        else
 
727
                brgt += delta;
 
728
 
 
729
        retval = __backend_byte_write(io_op, brgt);
 
730
 
 
731
        out:
 
732
        mutex_unlock(&io_op->backend->mutex);
 
733
        return retval;
 
734
}
 
735
 
 
736
static const struct omnibook_operation last_scan_op = SIMPLE_BYTE(SMI,SMI_GET_FN_LAST_SCAN,0);
 
737
 
 
738
/*
 
739
 * Workqueue handler for Fn hotkeys
 
740
 */
 
741
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
 
742
static void omnibook_handle_fnkey(struct work_struct *work)
 
743
#else
 
744
static void omnibook_handle_fnkey(void* data)
 
745
#endif
 
746
{
 
747
        int i;
 
748
        u8 gen_scan;
 
749
        struct input_dev *input_dev;
 
750
 
 
751
        if(backend_byte_read(&last_scan_op, &gen_scan))
 
752
                return;
 
753
 
 
754
        dprintk("detected scancode %x.\n", gen_scan);
 
755
        switch(gen_scan) {
 
756
        case KEY_F6:
 
757
                adjust_brighness(-1);
 
758
                break;
 
759
        case KEY_F7:
 
760
                adjust_brighness(+1);
 
761
                break;
 
762
        }
 
763
 
 
764
        for(i = 0 ; i < ARRAY_SIZE(nbsmi_scan_table); i++) {
 
765
                if( gen_scan == nbsmi_scan_table[i].scancode) {
 
766
                        dprintk("generating keycode %i.\n", nbsmi_scan_table[i].keycode);
 
767
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
 
768
                        input_dev = container_of(work, struct nbsmi_backend_data, fnkey_work)->nbsmi_input_dev;
 
769
#else
 
770
                        input_dev = ((struct nbsmi_backend_data *) data)->nbsmi_input_dev;
 
771
#endif
 
772
                        omnibook_report_key(input_dev, nbsmi_scan_table[i].keycode);
 
773
                        break;
 
774
                }
 
775
        }
 
776
}
 
777
 
 
778
static int omnibook_nbsmi_get_wireless(const struct omnibook_operation *io_op, unsigned int *state)
 
779
{
 
780
        int retval = 0;
 
781
        struct omnibook_operation aerial_op = SIMPLE_BYTE(SMI, SMI_GET_KILL_SWITCH, 0);
 
782
        u8 data;
 
783
 
 
784
        if ((retval = nbsmi_smi_read_command(&aerial_op, &data)))
 
785
                goto out;
 
786
 
 
787
        dprintk("get_wireless (kill switch) raw_state: %x\n", data);
 
788
 
 
789
        *state = data ? KILLSWITCH : 0;
 
790
 
 
791
        aerial_op.read_addr = SMI_GET_AERIAL;
 
792
 
 
793
        if ((retval = nbsmi_smi_read_command(&aerial_op, &data)))
 
794
                goto out;
 
795
 
 
796
        dprintk("get_wireless (aerial) raw_state: %x\n", data);
 
797
 
 
798
        *state |= (data & WLEX_MASK) ? WIFI_EX : 0;
 
799
        *state |= (data & WLAT_MASK) ? WIFI_STA : 0;
 
800
        *state |= (data & BTEX_MASK) ? BT_EX : 0;
 
801
        *state |= (data & BTAT_MASK) ? BT_STA : 0;
 
802
 
 
803
      out:
 
804
        return retval;
 
805
}
 
806
 
 
807
static int omnibook_nbsmi_set_wireless(const struct omnibook_operation *io_op, unsigned int state)
 
808
{
 
809
        int retval = 0;
 
810
        u8 data;
 
811
        struct omnibook_operation aerial_op = SIMPLE_BYTE(SMI, SMI_SET_AERIAL, 0);
 
812
 
 
813
        data = !!(state & BT_STA);
 
814
        data |= !!(state & WIFI_STA) << 0x1;
 
815
 
 
816
        dprintk("set_wireless raw_state: %x\n", data);
 
817
 
 
818
        retval = nbsmi_smi_write_command(&aerial_op, data);
 
819
 
 
820
        return retval;
 
821
}
 
822
 
 
823
static int omnibook_nbmsi_hotkeys_get(const struct omnibook_operation *io_op, unsigned int *state)
 
824
{
 
825
        int retval;
 
826
        u8 data = 0;
 
827
        struct omnibook_operation hotkeys_op = SIMPLE_BYTE(SMI, SMI_GET_FN_INTERFACE, 0);
 
828
 
 
829
        retval = nbsmi_smi_read_command(&hotkeys_op, &data);
 
830
        if (retval < 0)
 
831
                return retval;
 
832
 
 
833
        dprintk("get_hotkeys raw_state: %x\n", data);
 
834
 
 
835
        *state = (data & SMI_FN_KEYS_MASK) ? HKEY_FN : 0;
 
836
        *state |= (data & SMI_STICK_KEYS_MASK) ? HKEY_STICK : 0;
 
837
        *state |= (data & SMI_FN_TWICE_LOCK_MASK) ? HKEY_TWICE_LOCK : 0;
 
838
        *state |= (data & SMI_FN_DOCK_MASK) ? HKEY_DOCK : 0;
 
839
 
 
840
        return 0;
 
841
}
 
842
 
 
843
 
 
844
static int omnibook_nbmsi_hotkeys_set(const struct omnibook_operation *io_op, unsigned int state)
 
845
{
 
846
        int i, retval;
 
847
        u8 data, rdata;
 
848
        struct omnibook_operation hotkeys_op = SIMPLE_BYTE(SMI, SMI_SET_FN_F5_INTERFACE, 0);    
 
849
        u8* data_array;
 
850
 
 
851
        data = !!(state & HKEY_FNF5);
 
852
 
 
853
        dprintk("set_hotkeys (Fn F5) raw_state: %x\n", data);
 
854
 
 
855
        retval = nbsmi_smi_write_command(&hotkeys_op, data);
 
856
        if (retval < 0)
 
857
                return retval;
 
858
 
 
859
        hotkeys_op.write_addr = SMI_SET_FN_INTERFACE;
 
860
        hotkeys_op.read_addr =  SMI_GET_FN_INTERFACE;
 
861
 
 
862
        data = (state & HKEY_FN) ? SMI_FN_KEYS_MASK : 0;
 
863
        data |= (state & HKEY_STICK) ? SMI_STICK_KEYS_MASK : 0;
 
864
        data |= (state & HKEY_TWICE_LOCK) ? SMI_FN_TWICE_LOCK_MASK : 0;
 
865
        data |= (state & HKEY_DOCK) ? SMI_FN_DOCK_MASK : 0;
 
866
 
 
867
        dprintk("set_hotkeys (Fn interface) raw_state: %x\n", data);
 
868
 
 
869
        /*
 
870
         * Hardware seems to be quite stubborn and multiple retries may be
 
871
         * required. The criteria here is simple: retry until probed state match
 
872
         * the requested one (with timeout).
 
873
         */
 
874
 
 
875
        data_array = kcalloc(250, sizeof(u8), GFP_KERNEL);
 
876
        if(!data_array)
 
877
                return -ENODEV;
 
878
 
 
879
        for (i = 0; i < 250; i++) {
 
880
                retval = nbsmi_smi_write_command(&hotkeys_op, data);
 
881
                if (retval)
 
882
                        goto out;
 
883
                mdelay(1);
 
884
                retval = nbsmi_smi_read_command(&hotkeys_op, &rdata);
 
885
                if(retval)
 
886
                        goto out;
 
887
                data_array[i] = rdata;
 
888
                if(rdata == data) {
 
889
                        dprintk("check loop ok after %i iters\n.",i);
 
890
                        retval = 0;
 
891
                        goto out;
 
892
                }
 
893
        }
 
894
        dprintk("error or check loop timeout !!\n");
 
895
        dprintk("forensics datas: ");
 
896
        for (i = 0; i < 250; i++)
 
897
                dprintk_simple("%x ", data_array[i]);
 
898
        dprintk_simple("\n");
 
899
out:
 
900
        kfree(data_array);
 
901
        return retval;
 
902
}
 
903
 
 
904
static const unsigned int nbsmi_display_mode_list[] = {
 
905
        DISPLAY_LCD_ON,
 
906
        DISPLAY_LCD_ON | DISPLAY_CRT_ON,
 
907
        DISPLAY_CRT_ON,
 
908
        DISPLAY_LCD_ON | DISPLAY_TVO_ON,
 
909
        DISPLAY_TVO_ON,
 
910
};
 
911
 
 
912
static int omnibook_nbmsi_display_get(const struct omnibook_operation *io_op, unsigned int *state)
 
913
{
 
914
        int retval = 0;
 
915
        u8 data;
 
916
 
 
917
        retval = nbsmi_smi_read_command(io_op, &data);
 
918
        if (retval < 0)
 
919
                return retval;
 
920
 
 
921
        if (data > (ARRAY_SIZE(nbsmi_display_mode_list) - 1))
 
922
                return -EIO;
 
923
 
 
924
        *state = nbsmi_display_mode_list[data];
 
925
 
 
926
        return DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON;
 
927
}
 
928
 
 
929
static int omnibook_nbmsi_display_set(const struct omnibook_operation *io_op, unsigned int state)
 
930
{
 
931
        int retval;
 
932
        int i;
 
933
        u8 matched = 255;
 
934
 
 
935
        for (i = 0; i < ARRAY_SIZE(nbsmi_display_mode_list); i++) {
 
936
                if (nbsmi_display_mode_list[i] == state) {
 
937
                        matched = i;
 
938
                        break;
 
939
                }
 
940
        }
 
941
 
 
942
        if(matched == 255) {
 
943
                printk(O_ERR "Display mode %x is unsupported.\n", state);
 
944
                return -EINVAL;
 
945
        }
 
946
 
 
947
        retval = nbsmi_smi_write_command(io_op, matched);
 
948
        if (retval < 0)
 
949
                return retval;
 
950
 
 
951
        return DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON;
 
952
}
 
953
 
 
954
struct omnibook_backend nbsmi_backend = {
 
955
        .name = "nbsmi",
 
956
        .hotkeys_read_cap = HKEY_FN | HKEY_STICK | HKEY_TWICE_LOCK | HKEY_DOCK,
 
957
        .hotkeys_write_cap = HKEY_FN | HKEY_STICK | HKEY_TWICE_LOCK | HKEY_DOCK | HKEY_FNF5,
 
958
        .init = omnibook_nbsmi_init,
 
959
        .exit = omnibook_nbsmi_exit,
 
960
        .byte_read = nbsmi_smi_read_command,
 
961
        .byte_write = nbsmi_smi_write_command,
 
962
        .aerial_get = omnibook_nbsmi_get_wireless,
 
963
        .aerial_set = omnibook_nbsmi_set_wireless,
 
964
        .hotkeys_get = omnibook_nbmsi_hotkeys_get,
 
965
        .hotkeys_set = omnibook_nbmsi_hotkeys_set,
 
966
        .display_get = omnibook_nbmsi_display_get,
 
967
        .display_set = omnibook_nbmsi_display_set,
 
968
};