1
/* $Id: lirc_wpc8769l.c,v 1.8 2009/03/15 09:34:01 lirc Exp $ */
3
/****************************************************************************
4
** lirc_wpc8769l.c ****************************************************
5
****************************************************************************
7
* lirc_wpc8769l - Device driver for the integrated CIR receiver found in
8
* Acer Aspire 6530G (and probably other models), based on
9
* the Winbond 8769L embedded controller.
10
* (Written using the lirc_serial driver as a guide).
12
* Copyright (C) 2008, 2009 Juan J. Garcia de Soria <skandalfo@gmail.com>
13
* This program is free software; you can redistribute it and/or modify
14
* it under the terms of the GNU General Public License as published by
15
* the Free Software Foundation; either version 2 of the License, or
16
* (at your option) any later version.
18
* This program is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU General Public License for more details.
23
* You should have received a copy of the GNU General Public License
24
* along with this program; if not, write to the Free Software
25
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33
#include <linux/version.h>
34
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 2, 18)
35
#error "**********************************************************"
36
#error " Sorry, this driver needs kernel version 2.2.18 or higher "
37
#error "**********************************************************"
40
#include <linux/autoconf.h>
42
#include <linux/module.h>
43
#include <linux/errno.h>
45
#include <linux/interrupt.h>
46
#include <linux/ioport.h>
47
#include <linux/time.h>
48
#include <linux/timer.h>
49
#include <linux/types.h>
50
#include <linux/poll.h>
52
#include <linux/bitops.h>
54
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
59
#include <linux/irq.h>
61
#include <linux/acpi.h>
63
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
64
#include <linux/platform_device.h>
68
#include "../kcompat.h"
69
#include "../lirc_dev/lirc_dev.h"
71
#include "lirc_wpc8769l.h"
73
/* Name of the lirc device. */
74
#define LIRC_DRIVER_NAME "lirc_wpc8769l"
76
#define dprintk(fmt, args...) \
79
printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
83
#define wprintk(fmt, args...) \
85
printk(KERN_WARN LIRC_DRIVER_NAME ": " \
89
#define eprintk(fmt, args...) \
91
printk(KERN_ERR LIRC_DRIVER_NAME ": " \
95
#define iprintk(fmt, args...) \
97
printk(KERN_INFO LIRC_DRIVER_NAME ": " \
101
/* Number of driver->lirc-dev buffer elements. */
104
/* Number of 0xff bytes received in a row. */
105
static unsigned int wpc8769l_ff_bytes_in_a_row;
107
/* Hardware resource parameters. */
108
static unsigned int baseport1;
109
static unsigned int baseport2;
110
static unsigned int irq;
112
/* Debugging flag. */
115
/* If true, we skip ACPI autodetection and use the parameter-supplied I/O and
117
static int skip_probe;
119
/* Whether the device is open or not. */
120
static int lirc_wpc8769l_is_open;
122
/* Code disabled since it didn't seem to work with the test hardware. */
123
/*#define LIRC_WPC8769L_WAKEUP*/
124
#ifdef LIRC_WPC8769L_WAKEUP
125
/* These parameters are taken from the driver for MS Windows Vista.
126
* The specific values used for your hardware may be found at this registry
129
* HKEY_LOCAL_MACHINE/CurrentControlSet/Services/Winbond CIR/PowerKey
131
static int protocol_select = 2;
132
static int max_info_bits = 24;
133
static unsigned int rc_wakeup_code = 0x7ffffbf3;
134
static unsigned int rc_wakeup_mask = 0xff000fff;
137
/* Resource allocation pointers. */
138
static struct resource *wpc8769l_portblock1_resource;
139
static struct resource *wpc8769l_portblock2_resource;
141
/* Hardware related spinlock. */
142
static DEFINE_SPINLOCK(wpc8769l_hw_spinlock);
144
/* The buffer for ISR to bottom half data transfer. */
145
static struct lirc_buffer rbuf;
147
/* Bit-to-MODE2 coalescing helper variables. */
148
static int last_was_pulse;
149
static lirc_t last_counter;
151
/* Microseconds after a timeout-triggered pulse. */
154
/* Microseconds when the timer was started. */
155
static s64 timerstartus;
157
/* Put another pulse/space to the queue, checking for overruns. */
158
static void put_item(lirc_t data)
160
if (lirc_buffer_full(&rbuf)) {
161
if (printk_ratelimit())
162
eprintk("RX buffer overrun.\n");
165
lirc_buffer_write(&rbuf, (void *) &data);
168
/* Put any accumulated pulse/space to userspace. */
169
static void put_span(void)
173
/* Take the usecs length. */
176
/* Mark pulse or space. */
180
/* Put the span to the buffer. */
183
/* Reset counter, in order to avoid emitting duplicate data. */
188
/* Aggregate pulse time. */
189
static void put_pulse_bit(lirc_t n)
191
if (last_was_pulse) {
193
if (last_counter > PULSE_MASK)
194
last_counter = PULSE_MASK;
199
if (last_counter > PULSE_MASK)
200
last_counter = PULSE_MASK;
204
/* Aggregate space time. */
205
static void put_space_bit(lirc_t n)
207
if (!last_was_pulse) {
209
if (last_counter > PULSE_MASK)
210
last_counter = PULSE_MASK;
215
if (last_counter > PULSE_MASK)
216
last_counter = PULSE_MASK;
220
/* Timeout function for last pulse part. */
221
static void wpc8769l_last_timeout(unsigned long l)
223
struct timeval currenttv;
226
spin_lock_irqsave(&wpc8769l_hw_spinlock, flags);
228
/* Mark the time at which we inserted the timeout span. */
229
do_gettimeofday(¤ttv);
230
lastus = ((s64) currenttv.tv_sec) * 1000000ll + currenttv.tv_usec;
232
/* Emit the timeout as a space. */
233
put_space_bit(lastus - timerstartus);
235
/* Signal the bottom half wait queue
236
* that there's data available. */
237
wake_up_interruptible(&rbuf.wait_poll);
239
spin_unlock_irqrestore(&wpc8769l_hw_spinlock, flags);
242
/* Timer for end-of-code pulse timeout. */
243
static struct timer_list last_span_timer =
244
TIMER_INITIALIZER(wpc8769l_last_timeout, 0, 0);
246
/* Interrupt handler, doing the bit sample to mode2 conversion.
247
* Perhaps this work should be taken outside of the ISR... */
248
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
249
static irqreturn_t irq_handler(int irqno, void *blah)
251
static irqreturn_t irq_handler(int irqno, void *blah, struct pt_regs *regs)
257
struct timeval currenttv;
260
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
261
unsigned char data_buf[WPC8769L_BYTE_BUFFER_SIZE];
262
unsigned char *data_ptr;
263
unsigned long *ldata;
264
unsigned int next_one, next_zero, size;
270
spin_lock_irqsave(&wpc8769l_hw_spinlock, flags);
272
/* Check whether there's any data available. */
273
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
274
data = inb(baseport1 + WPC8769L_DATA_STATUS_REG);
276
if (data & WPC8769L_DATA_READY_MASK) {
277
/* Get current timestamp. */
278
do_gettimeofday(¤ttv);
279
currentus = ((s64) currenttv.tv_sec) * 1000000ll +
282
/* If we had a timeout before we might need to fill
283
* in additional space time. */
285
/* Calculate the difference, compensating
286
* the time for the data successfully
287
* received (estimated to be
288
* WPC8769L_BYTES_PER_BURST bytes). */
289
span = currentus - lastus
290
- WPC8769L_BYTES_PER_BURST
291
* WPC8769L_USECS_PER_BYTE;
293
/* Only insert positive spans. */
295
/* Emit the extended gap as a space. */
299
/* Mark that we had the last timeout into account. */
306
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
310
/* Read the next byte of data. */
311
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
312
data = inb(baseport1 + WPC8769L_DATA_REG);
313
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
316
for (mask = 0x01 ; mask < 0x100; mask <<= 1) {
318
put_space_bit(WPC8769L_USECS_PER_BIT);
320
put_pulse_bit(WPC8769L_USECS_PER_BIT);
324
/* Check for 0xff in a row. */
326
wpc8769l_ff_bytes_in_a_row++;
328
wpc8769l_ff_bytes_in_a_row = 0;
330
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
331
data = inb(baseport1 + WPC8769L_DATA_ACK_REG);
332
if (data & WPC8769L_DATA_ACK_REG) {
333
outb(WPC8769L_BANK_E0,
334
baseport1 + WPC8769L_SELECT_REG);
335
data = inb(baseport1 +
336
WPC8769L_REMAINING_RX_DATA_REG);
343
} while (more && count < WPC8769L_BYTES_PER_BURST);
345
if (wpc8769l_ff_bytes_in_a_row
346
>= WPC8769L_FF_BYTES_BEFORE_RESET) {
348
/* Put in another 0xff byte. */
349
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
353
put_space_bit(8 * WPC8769L_USECS_PER_BIT);
356
/* Reset the hardware in the case of too many
357
* 0xff bytes in a row. */
358
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
359
outb(WPC8769L_TIMEOUT_RESET_MASK,
360
baseport1 + WPC8769L_TIMEOUT_RESET_REG);
363
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
367
ldata = (unsigned long *) data_buf;
368
next_one = generic_find_next_le_bit(ldata, size, 0);
371
put_pulse_bit(next_one
372
* WPC8769L_USECS_PER_BIT);
374
while (next_one < size) {
375
next_zero = generic_find_next_zero_le_bit(ldata,
379
(next_zero - next_one)
380
* WPC8769L_USECS_PER_BIT);
382
if (next_zero < size) {
383
next_one = generic_find_next_le_bit(ldata,
384
size, next_zero + 1);
387
(next_one - next_zero)
388
* WPC8769L_USECS_PER_BIT);
395
/* Mark the IRQ as handled. */
398
/* Signal the bottom half wait queue
399
* that there's data available. */
400
wake_up_interruptible(&rbuf.wait_poll);
402
/* Set up timeout handling. */
403
mod_timer(&last_span_timer,
404
jiffies + WPC8769L_LAST_TIMEOUT_JIFFIES);
406
/* Set up last timer us mark. */
407
timerstartus = currentus;
410
spin_unlock_irqrestore(&wpc8769l_hw_spinlock, flags);
411
return IRQ_RETVAL(handled);
414
/* Prepare the hardware on module load. */
415
static void wpc8769l_prepare_hardware(void)
418
spin_lock_irqsave(&wpc8769l_hw_spinlock, flags);
420
/* I don't know why this needs reading. */
421
outb(WPC8769L_BANK_E4, baseport1 + WPC8769L_SELECT_REG);
422
inb(baseport1 + WPC8769L_READ_ON_STARTUP_REG);
424
spin_unlock_irqrestore(&wpc8769l_hw_spinlock, flags);
428
/* Wake up device from power down and check whether it was the
429
* device that woke us up.
431
static int wpc8769l_power_up_and_check_if_we_woke_us_up(void)
437
spin_lock_irqsave(&wpc8769l_hw_spinlock, flags);
440
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
441
data &= ~WPC8769L_CLOCK_OFF_MASK;
442
data |= WPC8769L_CLOCK_ON_MASK;
443
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
445
res = inb(baseport2 + WPC8769L_WAKEUP_STATUS_REG)
446
& WPC8769L_WAKEUP_WOKE_UP_MASK;
448
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
449
data &= ~WPC8769L_CLOCK_OFF_MASK;
450
data |= WPC8769L_CLOCK_ON_MASK;
451
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
453
outb(WPC8769L_WAKEUP_WOKE_UP_MASK,
454
baseport2 + WPC8769L_WAKEUP_STATUS_REG);
456
outb(WPC8769L_WAKEUP_ACK_MASK,
457
baseport2 + WPC8769L_WAKEUP_ACK_REG);
459
outb(WPC8769L_BANK_F0, baseport1 + WPC8769L_SELECT_REG);
460
res = (inb(baseport1 + WPC8769L_WAKEUP_STATUS_LEG_REG)
461
& WPC8769L_WAKEUP_STATUS_LEG_MASK) ? 1 : 0;
464
spin_unlock_irqrestore(&wpc8769l_hw_spinlock, flags);
469
/* Disable interrupts from device. */
470
static void wpc8769l_disable_interrupts(void)
473
spin_lock_irqsave(&wpc8769l_hw_spinlock, flags);
475
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
476
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
477
outb(inb(baseport1 + WPC8769L_INTERRUPT_REG)
478
& ~WPC8769L_INTERRUPT_1_MASK,
479
baseport1 + WPC8769L_INTERRUPT_REG);
480
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
481
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
482
outb(inb(baseport1 + WPC8769L_INTERRUPT_REG)
483
& ~WPC8769L_INTERRUPT_1_MASK,
484
baseport1 + WPC8769L_INTERRUPT_REG);
486
spin_unlock_irqrestore(&wpc8769l_hw_spinlock, flags);
489
#ifdef LIRC_WPC8769L_WAKEUP
490
/* Expand value nibble for configuration of wake up parameters.
491
* This seems to manchester-encode a nibble into a byte. */
492
static unsigned int wpc8769l_expand_value_nibble(unsigned int nibble)
495
unsigned int tmp, tmp2, res;
499
for (i = 0; i < 4; i += 2) {
500
tmp = (nibble >> i) & 0x3;
518
res |= ((tmp2 << i) << i);
524
/* Expand mask nibble for configuration of wake up parameters. */
525
static unsigned int wpc8769l_expand_mask_nibble(unsigned int nibble)
528
unsigned int tmp, tmp2, res;
532
for (i = 0; i < 4; i += 2) {
533
tmp = (nibble >> i) & 0x3;
551
res |= ((tmp2 << i) << i);
557
/* Configure wake up triggers for the hardware that supports it.
558
* THE CALLER MUST HAVE ACQUIRED wpc8769l_hw_spinlock BEFORE CALLING.
560
static void wpc8769l_configure_wakeup_triggers(void)
563
unsigned int data, data2;
567
x = inb(baseport2 + WPC8769L_WAKEUP_ENABLE_REG)
568
& WPC8769L_WAKEUP_ENABLE_MASK;
569
outb(inb(baseport2 + WPC8769L_WAKEUP_ENABLE_REG)
570
& ~WPC8769L_WAKEUP_ENABLE_MASK,
571
baseport2 + WPC8769L_WAKEUP_ENABLE_REG);
573
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
574
data &= ~WPC8769L_CLOCK_OFF_MASK;
575
data |= WPC8769L_CLOCK_ON_MASK;
576
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
578
outb(WPC8769L_WAKEUP_CONFIGURING_MASK,
579
baseport2 + WPC8769L_WAKEUP_STATUS_REG);
580
outb(WPC8769L_WAKEUP_ACK_MASK,
581
baseport2 + WPC8769L_WAKEUP_ACK_REG);
583
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
584
data &= ~WPC8769L_CLOCK_OFF_MASK;
585
data |= WPC8769L_CLOCK_ON_MASK;
586
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
588
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
589
data &= ~WPC8769L_CLOCK_OFF_MASK;
590
data |= WPC8769L_CLOCK_ON_MASK;
591
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
593
data = inb(baseport2 + WPC8769L_WAKEUP_CONFIG_REG);
594
data &= WPC8769L_WAKEUP_CONFIG_PRE_MASK;
595
data |= (max_info_bits + WPC8769L_MAX_INFO_BITS_BIAS)
596
<< WPC8769L_MAX_INFO_BITS_SHIFT;
597
outb(data, baseport2 + WPC8769L_WAKEUP_CONFIG_REG);
601
/* Program values. */
602
while (j < WPC8769L_WAKEUP_DATA_BITS) {
603
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
604
data &= ~WPC8769L_CLOCK_OFF_MASK;
605
data |= WPC8769L_CLOCK_ON_MASK;
606
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
608
outb(i + WPC8769L_WAKEUP_DATA_BASE,
609
baseport2 + WPC8769L_WAKEUP_DATA_PTR_REG);
611
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
612
data &= ~WPC8769L_CLOCK_OFF_MASK;
613
data |= WPC8769L_CLOCK_ON_MASK;
614
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
616
data = (rc_wakeup_code >> j) & 0x0f;
617
data = wpc8769l_expand_value_nibble(data);
618
outb(data, baseport2 + WPC8769L_WAKEUP_DATA_REG);
625
while (j < WPC8769L_WAKEUP_DATA_BITS) {
626
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
627
data &= ~WPC8769L_CLOCK_OFF_MASK;
628
data |= WPC8769L_CLOCK_ON_MASK;
629
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
631
outb(i + WPC8769L_WAKEUP_MASK_BASE,
632
baseport2 + WPC8769L_WAKEUP_DATA_PTR_REG);
634
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
635
data &= ~WPC8769L_CLOCK_OFF_MASK;
636
data |= WPC8769L_CLOCK_ON_MASK;
637
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
639
data = (rc_wakeup_mask >> j) & 0x0f;
640
data = wpc8769l_expand_mask_nibble(data);
641
outb(data, baseport2 + WPC8769L_WAKEUP_DATA_REG);
647
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
648
data &= ~WPC8769L_CLOCK_OFF_MASK;
649
data |= WPC8769L_CLOCK_ON_MASK;
650
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
652
data2 = inb(baseport2 + WPC8769L_WAKEUP_CONFIG2_REG);
653
data2 &= WPC8769L_WAKEUP_CONFIG2_AND_MASK;
654
data2 |= WPC8769L_WAKEUP_CONFIG2_OR_MASK;
656
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
657
data &= ~WPC8769L_CLOCK_OFF_MASK;
658
data |= WPC8769L_CLOCK_ON_MASK;
659
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
661
outb(data2, baseport2 + WPC8769L_WAKEUP_CONFIG2_REG);
663
if (x != WPC8769L_WAKEUP_ENABLE_MASK)
664
outb(inb(baseport2 + WPC8769L_WAKEUP_ENABLE_REG)
665
| WPC8769L_WAKEUP_ENABLE_MASK,
666
baseport2 + WPC8769L_WAKEUP_ENABLE_REG);
670
/* Enable interrupts from device. */
671
static void wpc8769l_enable_interrupts(void)
673
unsigned int data, data2, data_save;
678
spin_lock_irqsave(&wpc8769l_hw_spinlock, flags);
680
outb(WPC8769L_BANK_F0, baseport1 + WPC8769L_SELECT_REG);
681
data_save = inb(baseport1 + WPC8769L_WAKEUP_STATUS_LEG_REG);
683
outb(WPC8769L_BANK_E0, baseport1 + WPC8769L_SELECT_REG);
684
outb(0, baseport1 + WPC8769L_HARDWARE_ENABLE1_REG);
686
outb(WPC8769L_BANK_E0, baseport1 + WPC8769L_SELECT_REG);
687
outb(WPC8769L_BANK_E0, baseport1 + WPC8769L_SELECT_REG);
688
outb(inb(baseport1 + WPC8769L_HARDWARE_ENABLE1_REG)
689
| WPC8769L_HARDWARE_ENABLE1_MASK,
690
baseport1 + WPC8769L_HARDWARE_ENABLE1_REG);
692
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
693
outb(0, baseport1 + WPC8769L_CONFIG_REG);
695
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
696
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
697
data = inb(baseport1 + WPC8769L_CONFIG_REG);
698
data &= ~WPC8769L_CONFIG_OFF_MASK;
699
data |= WPC8769L_CONFIG_ON_MASK;
700
outb(data, baseport1 + WPC8769L_CONFIG_REG);
702
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
703
outb(WPC8769L_DATA_STATUS_MASK_1, baseport1 + WPC8769L_DATA_STATUS_REG);
705
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
706
outb(WPC8769L_DATA_STATUS_MASK_2, baseport1 + WPC8769L_DATA_STATUS_REG);
708
outb(WPC8769L_BANK_F4, baseport1 + WPC8769L_SELECT_REG);
709
outb(WPC8769L_BANK_F4, baseport1 + WPC8769L_SELECT_REG);
710
outb(inb(baseport1 + WPC8769L_CONFIG2_REG)
711
& ~WPC8769L_CONFIG2_OFF_MASK,
712
baseport1 + WPC8769L_CONFIG2_REG);
714
outb(WPC8769L_BANK_EC, baseport1 + WPC8769L_SELECT_REG);
715
outb(WPC8769L_BANK_EC, baseport1 + WPC8769L_SELECT_REG);
716
outb(inb(baseport1 + WPC8769L_CONFIG3_REG)
717
| WPC8769L_CONFIG3_ON_MASK,
718
baseport1 + WPC8769L_CONFIG3_REG);
720
outb(WPC8769L_BANK_F4, baseport1 + WPC8769L_SELECT_REG);
721
data = inb(baseport1 + WPC8769L_CONFIG4_REG);
722
data &= WPC8769L_CONFIG4_AND_MASK;
723
data |= WPC8769L_CONFIG4_ON_MASK;
725
outb(WPC8769L_BANK_F4, baseport1 + WPC8769L_SELECT_REG);
726
outb(data, baseport1 + WPC8769L_CONFIG4_REG);
728
outb(WPC8769L_BANK_E0, baseport1 + WPC8769L_SELECT_REG);
729
outb(WPC8769L_BANK_E0, baseport1 + WPC8769L_SELECT_REG);
730
outb(inb(baseport1 + WPC8769L_CONFIG5_REG)
731
| WPC8769L_CONFIG5_ON_MASK,
732
baseport1 + WPC8769L_CONFIG5_REG);
734
outb(WPC8769L_BANK_E0, baseport1 + WPC8769L_SELECT_REG);
735
outb(WPC8769L_CONFIG6_MASK, baseport1 + WPC8769L_CONFIG6_REG);
737
outb(WPC8769L_BANK_E0, baseport1 + WPC8769L_SELECT_REG);
738
outb(0, baseport1 + WPC8769L_CONFIG7_REG);
742
* This has to do with wake-up support, which is
743
* disabled when the second I/O range doesn't
746
/* -- internal subroutine -- */
747
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
748
data &= ~WPC8769L_CLOCK_OFF_MASK;
749
data |= WPC8769L_CLOCK_ON_MASK;
750
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
752
data2 = inb(baseport2 + WPC8769L_WAKEUP_CONFIG3_REG);
753
a = (data2 >> WPC8769L_WAKEUP_CONFIG3_A_SHIFT)
754
& WPC8769L_WAKEUP_CONFIG3_A_MASK;
755
b = (data2 >> WPC8769L_WAKEUP_CONFIG3_B_SHIFT)
756
& WPC8769L_WAKEUP_CONFIG3_B_MASK;
758
data = inb(baseport2 + WPC8769L_BANK2_CLOCK_REG);
759
data &= ~WPC8769L_CLOCK_OFF_MASK;
760
data |= WPC8769L_CLOCK_ON_MASK;
761
outb(data, baseport2 + WPC8769L_BANK2_CLOCK_REG);
763
data2 &= ~WPC8769L_WAKEUP_CONFIG3_OFF_MASK;
764
data2 |= WPC8769L_WAKEUP_CONFIG3_ON_MASK;
765
outb(data2, baseport2 + WPC8769L_WAKEUP_CONFIG3_REG);
766
/* -- end internal subroutine -- */
768
#ifdef LIRC_WPC8769L_WAKEUP
769
/* Call for setting wake up filters */
770
wpc8769l_configure_wakeup_triggers();
773
/* No second port range. Take these defaults. */
774
a = (data_save & WPC8769L_WAKEUP_STATUS_LEG_MASK_A)
776
b = (data_save & WPC8769L_WAKEUP_STATUS_LEG_MASK_B)
780
outb(WPC8769L_BANK_EC, baseport1 + WPC8769L_SELECT_REG);
781
outb(WPC8769L_BANK_EC, baseport1 + WPC8769L_SELECT_REG);
783
data = inb(baseport1 + WPC8769L_CONFIG3_REG);
785
? (data & ~WPC8769L_CONFIG3_MASK_1)
786
: (data | WPC8769L_CONFIG3_MASK_1);
787
outb(data, baseport1 + WPC8769L_CONFIG3_REG);
789
outb(WPC8769L_BANK_F4, baseport1 + WPC8769L_SELECT_REG);
790
outb(WPC8769L_BANK_F4, baseport1 + WPC8769L_SELECT_REG);
792
data = inb(baseport1 + WPC8769L_CONFIG2_REG);
794
? (data & ~WPC8769L_CONFIG2_MASK_1)
795
: (data | WPC8769L_CONFIG2_MASK_1);
796
outb(data, baseport1 + WPC8769L_CONFIG2_REG);
798
outb(0, baseport1 + WPC8769L_CONFIG8_REG);
800
outb(0, baseport1 + WPC8769L_CONFIG9_REG);
802
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
803
outb(WPC8769L_BANK_00, baseport1 + WPC8769L_SELECT_REG);
804
outb(inb(baseport1 + WPC8769L_INTERRUPT_REG)
805
| WPC8769L_INTERRUPT_1_MASK,
806
baseport1 + WPC8769L_INTERRUPT_REG);
808
spin_unlock_irqrestore(&wpc8769l_hw_spinlock, flags);
811
/* Called when the device is opened. */
812
static int set_use_inc(void *data)
816
/* Reset pulse values. */
820
/* Reset last timeout value. */
823
/* Init the read buffer. */
824
if (lirc_buffer_init(&rbuf, sizeof(lirc_t), RBUF_LEN) < 0)
827
/* Acquire the IRQ. */
828
result = request_irq(irq, irq_handler,
829
IRQF_DISABLED | IRQF_SHARED,
830
LIRC_DRIVER_NAME, THIS_MODULE);
834
eprintk("IRQ %d busy\n", irq);
835
lirc_buffer_free(&rbuf);
838
eprintk("Bad irq number or handler\n");
839
lirc_buffer_free(&rbuf);
842
dprintk("IRQ %d obtained.\n", irq);
846
/* Mark the device as open. */
847
lirc_wpc8769l_is_open = 1;
849
/* Enable hardware interrupts. */
850
wpc8769l_enable_interrupts();
856
/* Called when the device is released. */
857
static void set_use_dec(void *data)
859
/* Mark the device as closed. */
860
lirc_wpc8769l_is_open = 0;
862
/* Cancel the timeout if pending. */
863
del_timer_sync(&last_span_timer);
865
/* Disable the hardware interrupts. */
866
wpc8769l_disable_interrupts();
869
free_irq(irq, THIS_MODULE);
870
dprintk("Freed IRQ %d\n", irq);
872
/* Free the RX buffer. */
873
lirc_buffer_free(&rbuf);
878
static struct lirc_driver driver = {
879
.name = LIRC_DRIVER_NAME,
887
.set_use_inc = set_use_inc,
888
.set_use_dec = set_use_dec,
891
.owner = THIS_MODULE,
894
static acpi_status wec_parse_resources(struct acpi_resource *resource,
897
if (resource->type == ACPI_RESOURCE_TYPE_IO) {
898
/* Read the two I/O ranges. */
900
baseport1 = resource->data.io.minimum;
902
baseport2 = resource->data.io.minimum;
903
} else if (resource->type == ACPI_RESOURCE_TYPE_IRQ) {
904
/* Read the rx IRQ number. */
906
irq = resource->data.irq.interrupts[0];
911
static acpi_status wec_parse_device(acpi_handle handle, u32 level,
912
void *context, void **return_value)
915
iprintk("Found %s device via ACPI.\n", WPC8769L_ACPI_HID);
917
status = acpi_walk_resources(handle, METHOD_NAME__CRS,
918
wec_parse_resources, NULL);
919
if (ACPI_FAILURE(status))
925
/* Find the device I/O ranges and IRQ number by searching for the
927
static int wpc8769l_acpi_detect(void)
930
status = acpi_get_devices(WPC8769L_ACPI_HID, wec_parse_device, NULL,
932
if (ACPI_FAILURE(status))
939
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
940
static struct platform_device *lirc_wpc8769l_platform_dev;
942
static int __devinit lirc_wpc8769l_probe(struct platform_device *dev)
947
static int __devexit lirc_wpc8769l_remove(struct platform_device *dev)
952
static int lirc_wpc8769l_suspend(struct platform_device *dev,
955
if (lirc_wpc8769l_is_open)
956
/* Disable all interrupts. */
957
wpc8769l_disable_interrupts();
961
static int lirc_wpc8769l_resume(struct platform_device *dev)
963
if (lirc_wpc8769l_is_open) {
964
/* Check if we caused resuming; we still do nothing about it. */
965
wpc8769l_power_up_and_check_if_we_woke_us_up();
967
/* Enable interrupts again. */
968
wpc8769l_enable_interrupts();
973
static struct platform_driver lirc_wpc8769l_platform_driver = {
974
.probe = lirc_wpc8769l_probe,
975
.remove = __devexit_p(lirc_wpc8769l_remove),
976
.suspend = lirc_wpc8769l_suspend,
977
.resume = lirc_wpc8769l_resume,
979
.name = LIRC_DRIVER_NAME,
980
.owner = THIS_MODULE,
984
static int __init lirc_wpc8769l_platform_init(void)
988
result = platform_driver_register(&lirc_wpc8769l_platform_driver);
990
eprintk("Platform driver register returned %d.\n", result);
994
lirc_wpc8769l_platform_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0);
995
if (!lirc_wpc8769l_platform_dev) {
997
goto exit_driver_unregister;
1000
result = platform_device_add(lirc_wpc8769l_platform_dev);
1002
goto exit_device_put;
1007
platform_device_put(lirc_wpc8769l_platform_dev);
1009
exit_driver_unregister:
1010
platform_driver_unregister(&lirc_wpc8769l_platform_driver);
1014
static void __exit lirc_wpc8769l_platform_exit(void)
1016
platform_device_unregister(lirc_wpc8769l_platform_dev);
1017
platform_driver_unregister(&lirc_wpc8769l_platform_driver);
1021
static int __init lirc_wpc8769l_module_init(void)
1025
/* If needed, read the resource information for the ACPI device
1028
rc = wpc8769l_acpi_detect();
1030
eprintk("Error when looking for %s ACPI device.\n",
1036
/* Check that we got some resource info to work with. */
1037
if (!baseport1 || !irq) {
1039
eprintk("Not all required resources found for %s device.\n",
1044
dprintk("%s device found to use 0x%04x, 0x%04x I/O bases, IRQ #%d.\n",
1045
LIRC_DRIVER_NAME, baseport1, baseport2, irq);
1047
/* Request the two I/O regions. */
1048
wpc8769l_portblock1_resource = request_region(baseport1,
1049
WPC8769L_IO_REGION_1_SIZE, LIRC_DRIVER_NAME);
1050
if (!wpc8769l_portblock1_resource) {
1052
eprintk("Could not allocate I/O range at 0x%04x", baseport1);
1056
wpc8769l_portblock2_resource = request_region(baseport2,
1057
WPC8769L_IO_REGION_2_SIZE, LIRC_DRIVER_NAME);
1058
if (!wpc8769l_portblock2_resource) {
1060
printk(KERN_ERR "Could not allocate I/O range "
1063
goto exit_release_region_1;
1067
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
1068
/* Register the platform driver and device. */
1069
rc = lirc_wpc8769l_platform_init();
1071
goto exit_release_region_2;
1074
/* Prepare the hardware. */
1075
wpc8769l_prepare_hardware();
1077
/* Do load-time checks. */
1078
wpc8769l_power_up_and_check_if_we_woke_us_up();
1080
/* Configure the driver hooks. */
1081
driver.features = LIRC_CAN_REC_MODE2;
1082
driver.minor = lirc_register_driver(&driver);
1083
if (driver.minor < 0) {
1084
eprintk("lirc_register_driver failed!\n");
1086
goto exit_platform_exit;
1089
iprintk("Driver loaded.\n");
1091
return 0; /* Everything OK. */
1094
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
1095
lirc_wpc8769l_platform_exit();
1097
exit_release_region_2:
1100
release_region(baseport2, WPC8769L_IO_REGION_2_SIZE);
1102
exit_release_region_1:
1103
release_region(baseport1, WPC8769L_IO_REGION_1_SIZE);
1108
module_init(lirc_wpc8769l_module_init);
1110
static void __exit lirc_wpc8769l_module_exit(void)
1112
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
1113
/* Unregister the platform driver and device. */
1114
lirc_wpc8769l_platform_exit();
1117
/* Unregister the LIRC driver. */
1118
lirc_unregister_driver(driver.minor);
1120
/* Release the second range. */
1122
release_region(baseport2, WPC8769L_IO_REGION_2_SIZE);
1124
/* Release the first range. */
1125
release_region(baseport1, WPC8769L_IO_REGION_1_SIZE);
1127
iprintk("Driver unloaded.\n");
1130
module_exit(lirc_wpc8769l_module_exit);
1132
MODULE_LICENSE("GPL");
1133
MODULE_AUTHOR("Juan J. Garcia de Soria");
1134
MODULE_DESCRIPTION("Driver for the integrated Winbond WPC8769L-based IR\
1135
receiver found in Acer laptops.");
1136
MODULE_VERSION("0.0");
1138
module_param(debug, bool, S_IRUGO | S_IWUSR);
1139
MODULE_PARM_DESC(debug, "Enable debugging messages");
1141
module_param(baseport1, uint, S_IRUGO);
1142
MODULE_PARM_DESC(baseport1,
1143
"First I/O range base address (default: ACPI autodetect).");
1145
module_param(baseport2, uint, S_IRUGO);
1146
MODULE_PARM_DESC(baseport2,
1147
"Second I/O range base address (default: ACPI autodetect).");
1149
module_param(irq, uint, S_IRUGO);
1150
MODULE_PARM_DESC(irq, "IRQ number (default: ACPI autodetect).");
1152
module_param(skip_probe, bool, S_IRUGO);
1153
MODULE_PARM_DESC(skip_probe,
1154
"Skip ACPI-based device detection \
1155
(default: false for ACPI autodetect).");
1157
#ifdef LIRC_WPC8769L_WAKEUP
1158
module_param(protocol_select, int, S_IRUGO);
1159
MODULE_PARM_DESC(protocol_select,
1160
"Define the protocol for wake up functions (default: 2).");
1162
module_param(max_info_bits, int, S_IRUGO);
1163
MODULE_PARM_DESC(max_info_bits,
1164
"Define the maximum info bits for wake up functions (default: 24).");
1166
module_param(rc_wakeup_code, uint, S_IRUGO);
1167
MODULE_PARM_DESC(rc_wakeup_code,
1168
"Define the RC code value for wake up functions\
1169
(default: 0x7ffffbf3).");
1171
module_param(rc_wakeup_mask, uint, S_IRUGO);
1172
MODULE_PARM_DESC(rc_wakeup_mask,
1173
"Define the RC code mask for wake up functions (default: 0xff000fff).");