/*
* driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX)
*
* Copyright (C) 2010 Maxim Levitsky <maximlevitsky@gmail.com>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pnp.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <media/ir-core.h>
#include <media/ir-common.h>
#include "ene_ir.h"
static int sample_period = -1;
static int enable_idle = 1;
static int input = 1;
static int debug;
static int txsim;
static int ene_irq_status(struct ene_device *dev);
/* read a hardware register */
static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
{
u8 retval;
outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
retval = inb(dev->hw_io + ENE_IO);
ene_dbg_verbose("reg %04x == %02x", reg, retval);
return retval;
}
/* write a hardware register */
static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
{
outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
outb(value, dev->hw_io + ENE_IO);
ene_dbg_verbose("reg %04x <- %02x", reg, value);
}
/* change specific bits in hardware register */
static void ene_hw_write_reg_mask(struct ene_device *dev,
u16 reg, u8 value, u8 mask)
{
u8 regvalue;
outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
regvalue |= (value & mask);
outb(regvalue, dev->hw_io + ENE_IO);
ene_dbg_verbose("reg %04x <- %02x (mask=%02x)", reg, value, mask);
}
/* detect hardware features */
static int ene_hw_detect(struct ene_device *dev)
{
u8 chip_major, chip_minor;
u8 hw_revision, old_ver;
u8 tmp;
u8 fw_capabilities;
int pll_freq;
tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
pll_freq = (ene_hw_read_reg(dev, ENE_PLLFRH) << 4) +
(ene_hw_read_reg(dev, ENE_PLLFRL) >> 4);
if (pll_freq != 1000)
dev->rx_period_adjust = 4;
else
dev->rx_period_adjust = 2;
ene_printk(KERN_NOTICE, "PLL freq = %d\n", pll_freq);
if (hw_revision == 0xFF) {
ene_printk(KERN_WARNING, "device seems to be disabled\n");
ene_printk(KERN_WARNING,
"send a mail to lirc-list@lists.sourceforge.net\n");
ene_printk(KERN_WARNING, "please attach output of acpidump\n");
return -ENODEV;
}
if (chip_major == 0x33) {
ene_printk(KERN_WARNING, "chips 0x33xx aren't supported\n");
return -ENODEV;
}
if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_C;
} else if (old_ver == 0x24 && hw_revision == 0xC0) {
dev->hw_revision = ENE_HW_B;
ene_printk(KERN_NOTICE, "KB3926B detected\n"