/*
* driver for ENE KB3926 B/C/D/E/F 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
*
* Special thanks to:
* Sami R. <maesesami@gmail.com> for lot of help in debugging and therefore
* bringing to life support for transmission & learning mode.
*
* Charlie Andrews <charliethepilot@googlemail.com> for lots of help in
* bringing up the support of new firmware buffer that is popular
* on latest notebooks
*
* ENE for partial device documentation
*
*/
#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 <media/rc-core.h>
#include "ene_ir.h"
static int sample_period;
static bool learning_mode_force;
static int debug;
static bool txsim;
static void ene_set_reg_addr(struct ene_device *dev, u16 reg)
{
outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
}
/* read a hardware register */
static u8 ene_read_reg(struct ene_device *dev, u16 reg)
{
u8 retval;
ene_set_reg_addr(dev, reg);
retval = inb(dev->hw_io + ENE_IO);
dbg_regs("reg %04x == %02x", reg, retval);
return retval;
}
/* write a hardware register */
static void ene_write_reg(struct ene_device *dev, u16 reg, u8 value)
{
dbg_regs("reg %04x <- %02x", reg, value);
ene_set_reg_addr(dev, reg);
outb(value, dev->hw_io + ENE_IO);
}
/* Set bits in hardware register */
static void ene_set_reg_mask(struct ene_device *dev, u16 reg, u8 mask)
{
dbg_regs("reg %04x |= %02x", reg, mask);
ene_set_reg_addr(dev, reg);
outb(inb(dev->hw_io + ENE_IO) | mask, dev->hw_io + ENE_IO);
}
/* Clear bits in hardware register */
static void ene_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask)
{
dbg_regs("reg %04x &= ~%02x ", reg, mask);
ene_set_reg_addr(dev, reg);
outb(inb(dev->hw_io + ENE_IO) & ~mask, dev->hw_io + ENE_IO);
}
/* A helper to set/clear a bit in register according to boolean variable */
static void ene_set_clear_reg_mask(struct ene_device *dev, u16 reg, u8 mask,
bool set)
{
if (set)
ene_set_reg_mask(dev, reg, mask);
else
ene_clear_reg_mask(dev, reg, mask);
}
/* detect hardware features */
static int ene_hw_detect(struct ene_device *dev)
{
u8 chip_major, chip_minor;
u8 hw_revision, old_ver;
u8 fw_reg2, fw_reg1;
ene_clear_reg_mask(dev, ENE_ECSTS, ENE_ECSTS_RSRVD);
chip_major = ene_read_reg