/*
* winbond-cir.c - Driver for the Consumer IR functionality of Winbond
* SuperI/O chips.
*
* Currently supports the Winbond WPCD376i chip (PNP id WEC1022), but
* could probably support others (Winbond WEC102X, NatSemi, etc)
* with minor modifications.
*
* Original Author: David Härdeman <david@hardeman.nu>
* Copyright (C) 2012 Sean Young <sean@mess.org>
* Copyright (C) 2009 - 2011 David Härdeman <david@hardeman.nu>
*
* Dedicated to my daughter Matilda, without whose loving attention this
* driver would have been finished in half the time and with a fraction
* of the bugs.
*
* Written using:
* o Winbond WPCD376I datasheet helpfully provided by Jesse Barnes at Intel
* o NatSemi PC87338/PC97338 datasheet (for the serial port stuff)
* o DSDT dumps
*
* Supported features:
* o IR Receive
* o IR Transmit
* o Wake-On-CIR functionality
* o Carrier detection
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/pnp.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/leds.h>
#include <linux/spinlock.h>
#include <linux/pci_ids.h>
#include <linux/io.h>
#include <linux/bitrev.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <media/rc-core.h>
#define DRVNAME "winbond-cir"
/* CEIR Wake-Up Registers, relative to data->wbase */
#define WBCIR_REG_WCEIR_CTL 0x03 /* CEIR Receiver Control */
#define WBCIR_REG_WCEIR_STS 0x04 /* CEIR Receiver Status */
#define WBCIR_REG_WCEIR_EV_EN 0x05 /* CEIR Receiver Event Enable */
#define WBCIR_REG_WCEIR_CNTL 0x06 /* CEIR Receiver Counter Low */
#define WBCIR_REG_WCEIR_CNTH 0x07 /* CEIR Receiver Counter High */
#define WBCIR_REG_WCEIR_INDEX 0x08 /* CEIR Receiver Index */
#define WBCIR_REG_WCEIR_DATA 0x09 /* CEIR Receiver Data */
#define WBCIR_REG_WCEIR_CSL 0x0A /* CEIR Re. Compare Strlen */
#define WBCIR_REG_WCEIR_CFG1 0x0B /* CEIR Re. Configuration 1 */
#define WBCIR_REG_WCEIR_CFG2 0x0C /* CEIR Re. Configuration 2 */
/* CEIR Enhanced Functionality Registers, relative to data->ebase */
#define WBCIR_REG_ECEIR_CTS 0x00 /* Enhanced IR Control Status */
#define WBCIR_REG_ECEIR_CCTL 0x01 /* Infrared Counter Control */
#define WBCIR_REG_ECEIR_CNT_LO 0x02 /* Infrared Counter LSB */
#define WBCIR_REG_ECEIR_CNT_HI 0x03 /* Infrared Counter MSB */
#define WBCIR_REG_ECEIR_IREM 0x04 /* Infrared Emitter Status */
/* SP3 Banked Registers, relative to data->sbase */
#define WBCIR_REG_SP3_BSR 0x03 /* Bank Select, all banks */
/* Bank 0 */
#define WBCIR_REG_SP3_RXDATA 0x00 /* FIFO RX data (r) */
#define WBCIR_REG_SP3_TXDATA 0x00 /* FIFO TX data (w) */
#define WBCIR_REG_SP3_IER 0x01 /* Interrupt Enable */
#define WBCIR_REG_SP3_EIR 0x02 /* Event Identification (r) */
#define WBCIR_REG_SP3_FCR 0x02 /* FIFO Control (w) */
#define WBCIR_REG_SP3_MCR 0x04 /* Mode Control */
#define WBCIR_REG_SP3_LSR 0x05 /* Link Status */
#define WBCIR_REG_SP3_MSR 0x06 /* Modem Status */
#define WBCIR_REG_SP3_ASCR 0x07 /* Aux Status and Control */
/* Bank 2 */
#define WBCIR_REG_SP3_BGDL 0x00 /* Baud Divisor LSB */
#define WBCIR_REG_SP3_BGDH 0x01 /* Baud Divisor MSB */
#define WBCIR_REG_SP3_EXCR1 0x02 /* Extended Control 1 */
#define WBCIR_REG_SP3_EXCR2 0x04 /* Extended Control 2 */
#define WBCIR_REG_SP3_TXFLV 0x06 /* TX FIFO Level */
#define WBCIR_REG_SP3_RXFLV 0x07 /* RX FIFO Level */
/* Bank 3 */
#define WBCIR_REG_SP3_MRID 0x00 /* Module Identification */
#define WBCIR_REG_SP3_SH_LCR 0x01 /* LCR Shadow */
#define WBCIR_REG_SP3_SH_FCR 0x02 /* FCR Shadow */
/* Bank 4 */
#define WBCIR_REG_SP3_IRCR1 0x02 /* Infrared Control 1 */
/* Bank 5 */
#define WBCIR_REG_SP3_IRCR2 0x04 /* Infrared Control 2 */
/* Bank 6 */
#define WBCIR_REG_SP3_IRCR3 0x00 /* Infrared Control 3 */
#define WBCIR_REG_SP3_SIR_PW 0x02 /* SIR Pulse Width */
/* Bank 7 */
#define WBCIR_REG_SP3_IRRXDC 0x00 /* IR RX Demod Control */
#define WBCIR_REG_SP3_IRTXMC 0x01 /* IR TX Mod Control */
#define WBCIR_REG_SP3_RCCFG 0x02 /* CEIR Config */
#define WBCIR_REG_SP3_IRCFG1 0x04 /* Infrared Config 1 */
#define WBCIR_REG_SP3_IRCFG4 0x07 /* Infrared Config 4 */
/*
* Magic values follow
*/
/* No interrupts for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_NONE 0x00
/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_RX 0x01
/* TX data low bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_TX_LOW 0x02
/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_ERR 0x04
/* TX data empty bit for WBCEIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_TX_EMPTY 0x20
/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
#define WBCIR_LED_ENABLE 0x80
/* RX data available bit for WBCIR_REG_SP3_LSR */
#define WBCIR_RX_AVAIL 0x01
/* RX data overrun error bit for WBCIR_REG_SP3_LSR */
#define WBCIR_RX_OVERRUN 0x02
/* TX End-Of-Transmission bit for WBCIR_REG_SP3_ASCR */
#define WBCIR_TX_EOT 0x04
/* RX disable bit for WBCIR_REG_SP3_ASCR */
#define WBCIR_RX_DISABLE 0x20
/* TX data underrun error bit for WBCIR_REG_SP3_ASCR */
#define WBCIR_TX_UNDERRUN 0x40
/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
#define WBCIR_EXT_ENABLE 0x01
/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
#define WBCIR_REGSEL_COMPARE 0x10
/* Select mask register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
#define WBCIR_REGSEL_MASK 0x20
/* Starting address of selected register in WBCIR_REG_WCEIR_INDEX */
#define WBCIR_REG_ADDR0 0x00
/* Enable carrier counter */
#define WBCIR_CNTR_EN 0x01
/* Reset carrier counter */
#define WBCIR_CNTR_R 0x02
/* Invert TX */
#define WBCIR_IRTX_INV 0x04
/* Receiver oversampling */
#define WBCIR_RX_T_OV 0x40
/* Valid banks for the SP3 UART */
enum wbcir_bank {
WBCIR_BANK_0 = 0x00,
WBCIR_BANK_1 = 0x80,
WBCIR_BANK_2 = 0xE0,
WBCIR_BANK_3 = 0xE4,
WBCIR_BANK_4 = 0xE8,
WBCIR_BANK_5 = 0xEC,
WBCIR_BANK_6 = 0xF0,
WBCIR_BANK_7 = 0xF4