| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  |  * driver for ENE KB3926 B/C/D CIR (pnp id: ENE0XXX) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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>
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <linux/input.h>
 | 
					
						
							|  |  |  | #include <media/ir-core.h>
 | 
					
						
							|  |  |  | #include <media/ir-common.h>
 | 
					
						
							|  |  |  | #include "ene_ir.h"
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	int pll_freq; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	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"); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		dev->hw_revision = ENE_HW_D; | 
					
						
							|  |  |  | 		ene_printk(KERN_WARNING, | 
					
						
							|  |  |  | 			"unknown ENE chip detected, assuming KB3926D\n"); | 
					
						
							|  |  |  | 		ene_printk(KERN_WARNING, | 
					
						
							|  |  |  | 			"driver support might be not complete"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_printk(KERN_DEBUG, | 
					
						
							|  |  |  | 		"chip is 0x%02x%02x - kbver = 0x%02x, rev = 0x%02x\n", | 
					
						
							|  |  |  | 			chip_major, chip_minor, old_ver, hw_revision); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* detect features hardware supports */ | 
					
						
							|  |  |  | 	if (dev->hw_revision < ENE_HW_C) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fw_capabilities = ene_hw_read_reg(dev, ENE_FW2); | 
					
						
							|  |  |  | 	ene_dbg("Firmware capabilities: %02x", fw_capabilities); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN; | 
					
						
							|  |  |  | 	dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable && | 
					
						
							|  |  |  | 	    (fw_capabilities & ENE_FW2_FAN_AS_NRML_IN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_printk(KERN_NOTICE, "hardware features:\n"); | 
					
						
							|  |  |  | 	ene_printk(KERN_NOTICE, | 
					
						
							|  |  |  | 		"learning and transmit %s, gpio40_learn %s, fan_in %s\n", | 
					
						
							|  |  |  | 	       dev->hw_learning_and_tx_capable ? "on" : "off", | 
					
						
							|  |  |  | 	       dev->hw_gpio40_learning ? "on" : "off", | 
					
						
							|  |  |  | 	       dev->hw_fan_as_normal_input ? "on" : "off"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->hw_learning_and_tx_capable) { | 
					
						
							|  |  |  | 		ene_printk(KERN_WARNING, | 
					
						
							|  |  |  | 		"Device supports transmitting, but that support is\n"); | 
					
						
							|  |  |  | 		ene_printk(KERN_WARNING, | 
					
						
							|  |  |  | 		"lightly tested. Please test it and mail\n"); | 
					
						
							|  |  |  | 		ene_printk(KERN_WARNING, | 
					
						
							|  |  |  | 		"lirc-list@lists.sourceforge.net\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* this enables/disables IR input via gpio40*/ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | static void ene_enable_gpio40_receive(struct ene_device *dev, int enable) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, enable ? | 
					
						
							|  |  |  | 			      0 : ENE_CIR_CONF2_GPIO40DIS, | 
					
						
							|  |  |  | 			      ENE_CIR_CONF2_GPIO40DIS); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* this enables/disables IR via standard input */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | static void ene_enable_normal_receive(struct ene_device *dev, int enable) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_RX_ON : 0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* this enables/disables IR input via unused fan tachtometer input */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | static void ene_enable_fan_receive(struct ene_device *dev, int enable) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (!enable) | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0); | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN); | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	dev->rx_fan_input_inuse = enable; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | /* Sense current received carrier */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | static int ene_rx_sense_carrier(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int period = ene_hw_read_reg(dev, ENE_RX_CARRIER); | 
					
						
							|  |  |  | 	int carrier; | 
					
						
							|  |  |  | 	ene_dbg("RX: hardware carrier period = %02x", period); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(period & ENE_RX_CARRIER_VALID)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	period &= ~ENE_RX_CARRIER_VALID; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!period) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	carrier = 2000000 / period; | 
					
						
							|  |  |  | 	ene_dbg("RX: sensed carrier = %d Hz", carrier); | 
					
						
							|  |  |  | 	return carrier; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* determine which input to use*/ | 
					
						
							|  |  |  | static void ene_rx_set_inputs(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	int learning_mode = dev->learning_enabled; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ene_dbg("RX: setup receiver, learning mode = %d", learning_mode); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ene_enable_normal_receive(dev, 1); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* old hardware doesn't support learning mode for sure */ | 
					
						
							|  |  |  | 	if (dev->hw_revision <= ENE_HW_B) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	/* receiver not learning capable, still set gpio40 correctly */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	if (!dev->hw_learning_and_tx_capable) { | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		ene_enable_gpio40_receive(dev, !dev->hw_gpio40_learning); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* enable learning mode */ | 
					
						
							|  |  |  | 	if (learning_mode) { | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		ene_enable_gpio40_receive(dev, dev->hw_gpio40_learning); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* fan input is not used for learning */ | 
					
						
							|  |  |  | 		if (dev->hw_fan_as_normal_input) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 			ene_enable_fan_receive(dev, 0); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* disable learning mode */ | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if (dev->hw_fan_as_normal_input) { | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 			ene_enable_fan_receive(dev, 1); | 
					
						
							|  |  |  | 			ene_enable_normal_receive(dev, 0); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 		} else | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 			ene_enable_gpio40_receive(dev, | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 					!dev->hw_gpio40_learning); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set few additional settings for this mode */ | 
					
						
							|  |  |  | 	ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_mode ? | 
					
						
							|  |  |  | 			      ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_mode ? | 
					
						
							|  |  |  | 			      ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->rx_fan_input_inuse) { | 
					
						
							|  |  |  | 		dev->props->rx_resolution = ENE_SAMPLE_PERIOD_FAN * 1000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		dev->props->timeout = | 
					
						
							|  |  |  | 			ENE_FAN_VALUE_MASK * ENE_SAMPLE_PERIOD_FAN * 1000; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		dev->props->rx_resolution = sample_period * 1000; | 
					
						
							|  |  |  | 		dev->props->timeout = ENE_MAXGAP * 1000; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Enable the device for receive */ | 
					
						
							|  |  |  | static void ene_rx_enable(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 reg_value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->hw_revision < ENE_HW_C) { | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1); | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0; | 
					
						
							|  |  |  | 		reg_value |= ENEC_IRQ_UNK_EN; | 
					
						
							|  |  |  | 		reg_value &= ~ENEC_IRQ_STATUS; | 
					
						
							|  |  |  | 		reg_value |= (dev->irq & ENEC_IRQ_MASK); | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENEC_IRQ, reg_value); | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00); | 
					
						
							|  |  |  | 	ene_rx_set_inputs(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set sampling period */ | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ack any pending irqs - just in case */ | 
					
						
							|  |  |  | 	ene_irq_status(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* enable firmware bits */ | 
					
						
							|  |  |  | 	ene_hw_write_reg_mask(dev, ENE_FW1, | 
					
						
							|  |  |  | 			      ENE_FW1_ENABLE | ENE_FW1_IRQ, | 
					
						
							|  |  |  | 			      ENE_FW1_ENABLE | ENE_FW1_IRQ); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* enter idle mode */ | 
					
						
							|  |  |  | 	ir_raw_event_set_idle(dev->idev, 1); | 
					
						
							|  |  |  | 	ir_raw_event_reset(dev->idev); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | /* Disable the device receiver */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | static void ene_rx_disable(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* disable inputs */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ene_enable_normal_receive(dev, 0); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->hw_fan_as_normal_input) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		ene_enable_fan_receive(dev, 0); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* disable hardware IRQ and firmware flag */ | 
					
						
							|  |  |  | 	ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ir_raw_event_set_idle(dev->idev, 1); | 
					
						
							|  |  |  | 	ir_raw_event_reset(dev->idev); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* prepare transmission */ | 
					
						
							|  |  |  | static void ene_tx_prepare(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 conf1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	conf1 = ene_hw_read_reg(dev, ENE_CIR_CONF1); | 
					
						
							|  |  |  | 	dev->saved_conf1 = conf1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->hw_revision == ENE_HW_C) | 
					
						
							|  |  |  | 		conf1 &= ~ENE_CIR_CONF1_TX_CLEAR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Enable TX engine */ | 
					
						
							|  |  |  | 	conf1 |= ENE_CIR_CONF1_TX_ON; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set carrier */ | 
					
						
							|  |  |  | 	if (dev->tx_period) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		/* NOTE: duty cycle handling is just a guess, it might
 | 
					
						
							|  |  |  | 			not be aviable. Default values were tested */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 		int tx_period_in500ns = dev->tx_period * 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		int tx_pulse_width_in_500ns = | 
					
						
							|  |  |  | 			tx_period_in500ns / (100 / dev->tx_duty_cycle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!tx_pulse_width_in_500ns) | 
					
						
							|  |  |  | 			tx_pulse_width_in_500ns = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ene_dbg("TX: pulse distance = %d * 500 ns", tx_period_in500ns); | 
					
						
							|  |  |  | 		ene_dbg("TX: pulse width = %d * 500 ns", | 
					
						
							|  |  |  | 						tx_pulse_width_in_500ns); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENE_TX_PERIOD, ENE_TX_PERIOD_UNKBIT | | 
					
						
							|  |  |  | 					tx_period_in500ns); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENE_TX_PERIOD_PULSE, | 
					
						
							|  |  |  | 					tx_pulse_width_in_500ns); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		conf1 |= ENE_CIR_CONF1_TX_CARR; | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		conf1 &= ~ENE_CIR_CONF1_TX_CARR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENE_CIR_CONF1, conf1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* end transmission */ | 
					
						
							|  |  |  | static void ene_tx_complete(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENE_CIR_CONF1, dev->saved_conf1); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	dev->tx_buffer = NULL; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* set transmit mask */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | static void ene_tx_hw_set_transmiter_mask(struct ene_device *dev) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	u8 txport1 = ene_hw_read_reg(dev, ENE_TX_PORT1) & ~ENE_TX_PORT1_EN; | 
					
						
							|  |  |  | 	u8 txport2 = ene_hw_read_reg(dev, ENE_TX_PORT2) & ~ENE_TX_PORT2_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->transmitter_mask & 0x01) | 
					
						
							|  |  |  | 		txport1 |= ENE_TX_PORT1_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->transmitter_mask & 0x02) | 
					
						
							|  |  |  | 		txport2 |= ENE_TX_PORT2_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENE_TX_PORT1, txport1); | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENE_TX_PORT2, txport2); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* TX one sample - must be called with dev->hw_lock*/ | 
					
						
							|  |  |  | static void ene_tx_sample(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 raw_tx; | 
					
						
							|  |  |  | 	u32 sample; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	if (!dev->tx_buffer) { | 
					
						
							|  |  |  | 		ene_dbg("TX: attempt to transmit NULL buffer"); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Grab next TX sample */ | 
					
						
							|  |  |  | 	if (!dev->tx_sample) { | 
					
						
							|  |  |  | again: | 
					
						
							|  |  |  | 		if (dev->tx_pos == dev->tx_len + 1) { | 
					
						
							|  |  |  | 			if (!dev->tx_done) { | 
					
						
							|  |  |  | 				ene_dbg("TX: no more data to send"); | 
					
						
							|  |  |  | 				dev->tx_done = 1; | 
					
						
							|  |  |  | 				goto exit; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				ene_dbg("TX: last sample sent by hardware"); | 
					
						
							|  |  |  | 				ene_tx_complete(dev); | 
					
						
							|  |  |  | 				complete(&dev->tx_complete); | 
					
						
							|  |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		sample = dev->tx_buffer[dev->tx_pos++]; | 
					
						
							|  |  |  | 		dev->tx_sample_pulse = !dev->tx_sample_pulse; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ene_dbg("TX: sample %8d (%s)", sample, dev->tx_sample_pulse ? | 
					
						
							|  |  |  | 							"pulse" : "space"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		dev->tx_sample = DIV_ROUND_CLOSEST(sample, ENE_TX_SMPL_PERIOD); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* guard against too short samples */ | 
					
						
							|  |  |  | 		if (!dev->tx_sample) | 
					
						
							|  |  |  | 			goto again; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	raw_tx = min(dev->tx_sample , (unsigned int)ENE_TX_SMLP_MASK); | 
					
						
							|  |  |  | 	dev->tx_sample -= raw_tx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->tx_sample_pulse) | 
					
						
							|  |  |  | 		raw_tx |= ENE_TX_PULSE_MASK; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENE_TX_INPUT1 + dev->tx_reg, raw_tx); | 
					
						
							|  |  |  | 	dev->tx_reg = !dev->tx_reg; | 
					
						
							|  |  |  | exit: | 
					
						
							|  |  |  | 	/* simulate TX done interrupt */ | 
					
						
							|  |  |  | 	if (txsim) | 
					
						
							|  |  |  | 		mod_timer(&dev->tx_sim_timer, jiffies + HZ / 500); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* timer to simulate tx done interrupt */ | 
					
						
							|  |  |  | static void ene_tx_irqsim(unsigned long data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	ene_tx_sample(dev); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* read irq status and ack it */ | 
					
						
							|  |  |  | static int ene_irq_status(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 irq_status; | 
					
						
							|  |  |  | 	u8 fw_flags1, fw_flags2; | 
					
						
							|  |  |  | 	int cur_rx_pointer; | 
					
						
							|  |  |  | 	int retval = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fw_flags2 = ene_hw_read_reg(dev, ENE_FW2); | 
					
						
							|  |  |  | 	cur_rx_pointer = !!(fw_flags2 & ENE_FW2_BUF_HIGH); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->hw_revision < ENE_HW_C) { | 
					
						
							|  |  |  | 		irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!(irq_status & ENEB_IRQ_STATUS_IR)) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENEB_IRQ_STATUS, | 
					
						
							|  |  |  | 				 irq_status & ~ENEB_IRQ_STATUS_IR); | 
					
						
							|  |  |  | 		dev->rx_pointer = cur_rx_pointer; | 
					
						
							|  |  |  | 		return ENE_IRQ_RX; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	irq_status = ene_hw_read_reg(dev, ENEC_IRQ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(irq_status & ENEC_IRQ_STATUS)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* original driver does that twice - a workaround ? */ | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS); | 
					
						
							|  |  |  | 	ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* clear unknown flag in F8F9 */ | 
					
						
							|  |  |  | 	if (fw_flags2 & ENE_FW2_IRQ_CLR) | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* check if this is a TX interrupt */ | 
					
						
							|  |  |  | 	fw_flags1 = ene_hw_read_reg(dev, ENE_FW1); | 
					
						
							|  |  |  | 	if (fw_flags1 & ENE_FW1_TXIRQ) { | 
					
						
							|  |  |  | 		ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ); | 
					
						
							|  |  |  | 		retval |= ENE_IRQ_TX; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check if this is RX interrupt */ | 
					
						
							|  |  |  | 	if (dev->rx_pointer != cur_rx_pointer) { | 
					
						
							|  |  |  | 		retval |= ENE_IRQ_RX; | 
					
						
							|  |  |  | 		dev->rx_pointer = cur_rx_pointer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} else if (!(retval & ENE_IRQ_TX)) { | 
					
						
							|  |  |  | 		ene_dbg("RX: interrupt without change in RX pointer(%d)", | 
					
						
							|  |  |  | 			dev->rx_pointer); | 
					
						
							|  |  |  | 		retval |= ENE_IRQ_RX; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((retval & ENE_IRQ_RX) && (retval & ENE_IRQ_TX)) | 
					
						
							|  |  |  | 		ene_dbg("both RX and TX interrupt at same time"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return retval; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* interrupt handler */ | 
					
						
							|  |  |  | static irqreturn_t ene_isr(int irq, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u16 hw_value; | 
					
						
							|  |  |  | 	int i, hw_sample; | 
					
						
							|  |  |  | 	int pulse; | 
					
						
							|  |  |  | 	int irq_status; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	int carrier = 0; | 
					
						
							|  |  |  | 	irqreturn_t retval = IRQ_NONE; | 
					
						
							|  |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	struct ir_raw_event ev; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	irq_status = ene_irq_status(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!irq_status) | 
					
						
							|  |  |  | 		goto unlock; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	retval = IRQ_HANDLED; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (irq_status & ENE_IRQ_TX) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (!dev->hw_learning_and_tx_capable) { | 
					
						
							|  |  |  | 			ene_dbg("TX interrupt on unsupported device!"); | 
					
						
							|  |  |  | 			goto unlock; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		ene_tx_sample(dev); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(irq_status & ENE_IRQ_RX)) | 
					
						
							|  |  |  | 		goto unlock; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	if (dev->carrier_detect_enabled || debug) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 		carrier = ene_rx_sense_carrier(dev); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | #if 0
 | 
					
						
							|  |  |  | 	/* TODO */ | 
					
						
							|  |  |  | 	if (dev->carrier_detect_enabled && carrier) | 
					
						
							|  |  |  | 		ir_raw_event_report_frequency(dev->idev, carrier); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ENE_SAMPLES_SIZE; i++) { | 
					
						
							|  |  |  | 		hw_value = ene_hw_read_reg(dev, | 
					
						
							|  |  |  | 				ENE_SAMPLE_BUFFER + dev->rx_pointer * 4 + i); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (dev->rx_fan_input_inuse) { | 
					
						
							|  |  |  | 			/* read high part of the sample */ | 
					
						
							|  |  |  | 			hw_value |= ene_hw_read_reg(dev, | 
					
						
							|  |  |  | 			    ENE_SAMPLE_BUFFER_FAN + | 
					
						
							|  |  |  | 					dev->rx_pointer * 4 + i) << 8; | 
					
						
							|  |  |  | 			pulse = hw_value & ENE_FAN_SMPL_PULS_MSK; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* clear space bit, and other unused bits */ | 
					
						
							|  |  |  | 			hw_value &= ENE_FAN_VALUE_MASK; | 
					
						
							|  |  |  | 			hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			pulse = !(hw_value & ENE_SAMPLE_SPC_MASK); | 
					
						
							|  |  |  | 			hw_value &= ENE_SAMPLE_VALUE_MASK; | 
					
						
							|  |  |  | 			hw_sample = hw_value * sample_period; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (dev->rx_period_adjust) { | 
					
						
							|  |  |  | 				hw_sample *= (100 - dev->rx_period_adjust); | 
					
						
							|  |  |  | 				hw_sample /= 100; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		/* no more data */ | 
					
						
							|  |  |  | 		if (!(hw_value)) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		ene_dbg("RX: %d (%s)", hw_sample, pulse ? "pulse" : "space"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ev.duration = hw_sample * 1000; | 
					
						
							|  |  |  | 		ev.pulse = pulse; | 
					
						
							|  |  |  | 		ir_raw_event_store_with_filter(dev->idev, &ev); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ir_raw_event_handle(dev->idev); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | unlock: | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	return retval; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Initialize default settings */ | 
					
						
							|  |  |  | static void ene_setup_settings(struct ene_device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	dev->tx_period = 32; | 
					
						
							|  |  |  | 	dev->tx_duty_cycle = 25; /*%*/ | 
					
						
							|  |  |  | 	dev->transmitter_mask = 3; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Force learning mode if (input == 2), otherwise
 | 
					
						
							|  |  |  | 		let user set it with LIRC_SET_REC_CARRIER */ | 
					
						
							|  |  |  | 	dev->learning_enabled = | 
					
						
							|  |  |  | 		(input == 2 && dev->hw_learning_and_tx_capable); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->rx_pointer = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* outside interface: called on first open*/ | 
					
						
							|  |  |  | static int ene_open(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	dev->in_use = 1; | 
					
						
							|  |  |  | 	ene_setup_settings(dev); | 
					
						
							|  |  |  | 	ene_rx_enable(dev); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* outside interface: called on device close*/ | 
					
						
							|  |  |  | static void ene_close(void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_rx_disable(dev); | 
					
						
							|  |  |  | 	dev->in_use = 0; | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | /* outside interface: set transmitter mask */ | 
					
						
							|  |  |  | static int ene_set_tx_mask(void *data, u32 tx_mask) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ene_dbg("TX: attempt to set transmitter mask %02x", tx_mask); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* invalid txmask */ | 
					
						
							|  |  |  | 	if (!tx_mask || tx_mask & ~0x3) { | 
					
						
							|  |  |  | 		ene_dbg("TX: invalid mask"); | 
					
						
							|  |  |  | 		/* return count of transmitters */ | 
					
						
							|  |  |  | 		return 2; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	dev->transmitter_mask = tx_mask; | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | /* outside interface : set tx carrier */ | 
					
						
							|  |  |  | static int ene_set_tx_carrier(void *data, u32 carrier) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	u32 period = 1000000 / carrier; /* (1 / freq) (* # usec in 1 sec) */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ene_dbg("TX: attempt to set tx carrier to %d kHz", carrier); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	if (period && (period > ENE_TX_PERIOD_MAX || | 
					
						
							|  |  |  | 			period < ENE_TX_PERIOD_MIN)) { | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		ene_dbg("TX: out of range %d-%d carrier, " | 
					
						
							|  |  |  | 			"falling back to 32 kHz", | 
					
						
							|  |  |  | 			1000 / ENE_TX_PERIOD_MIN, | 
					
						
							|  |  |  | 			1000 / ENE_TX_PERIOD_MAX); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		period = 32; /* this is just a coincidence!!! */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ene_dbg("TX: set carrier to %d kHz", carrier); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	dev->tx_period = period; | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* outside interface: enable learning mode */ | 
					
						
							|  |  |  | static int ene_set_learning_mode(void *data, int enable) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	unsigned long flags; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	if (enable == dev->learning_enabled) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	dev->learning_enabled = enable; | 
					
						
							|  |  |  | 	ene_rx_set_inputs(dev); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | /* outside interface: set rec carrier */ | 
					
						
							|  |  |  | static int ene_set_rec_carrier(void *data, u32 min, u32 max) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							|  |  |  | 	ene_set_learning_mode(dev, | 
					
						
							|  |  |  | 		max > ENE_NORMAL_RX_HI || min < ENE_NORMAL_RX_LOW); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | /* outside interface: enable or disable idle mode */ | 
					
						
							|  |  |  | static void ene_rx_set_idle(void *data, int idle) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							|  |  |  | 	ene_dbg("%sabling idle mode", idle ? "en" : "dis"); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD, | 
					
						
							|  |  |  | 		(enable_idle && idle) ? 0 : ENE_CIR_SAMPLE_OVERFLOW, | 
					
						
							|  |  |  | 			ENE_CIR_SAMPLE_OVERFLOW); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | /* outside interface: transmit */ | 
					
						
							|  |  |  | static int ene_transmit(void *data, int *buf, u32 n) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = (struct ene_device *)data; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dev->tx_buffer = buf; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	dev->tx_len = n / sizeof(int); | 
					
						
							|  |  |  | 	dev->tx_pos = 0; | 
					
						
							|  |  |  | 	dev->tx_reg = 0; | 
					
						
							|  |  |  | 	dev->tx_done = 0; | 
					
						
							|  |  |  | 	dev->tx_sample = 0; | 
					
						
							|  |  |  | 	dev->tx_sample_pulse = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_dbg("TX: %d samples", dev->tx_len); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ene_tx_hw_set_transmiter_mask(dev); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	ene_tx_prepare(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Transmit first two samples */ | 
					
						
							|  |  |  | 	ene_tx_sample(dev); | 
					
						
							|  |  |  | 	ene_tx_sample(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (wait_for_completion_timeout(&dev->tx_complete, 2 * HZ) == 0) { | 
					
						
							|  |  |  | 		ene_dbg("TX: timeout"); | 
					
						
							|  |  |  | 		spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 		ene_tx_complete(dev); | 
					
						
							|  |  |  | 		spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | 		ene_dbg("TX: done"); | 
					
						
							|  |  |  | 	return n; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | /* probe entry */ | 
					
						
							|  |  |  | static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | { | 
					
						
							|  |  |  | 	int error = -ENOMEM; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	struct ir_dev_props *ir_props; | 
					
						
							|  |  |  | 	struct input_dev *input_dev; | 
					
						
							|  |  |  | 	struct ene_device *dev; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	/* allocate memory */ | 
					
						
							|  |  |  | 	input_dev = input_allocate_device(); | 
					
						
							|  |  |  | 	ir_props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	if (!input_dev || !ir_props || !dev) | 
					
						
							|  |  |  | 		goto error; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* validate resources */ | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	error = -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	if (!pnp_port_valid(pnp_dev, 0) || | 
					
						
							|  |  |  | 	    pnp_port_len(pnp_dev, 0) < ENE_MAX_IO) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		goto error; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!pnp_irq_valid(pnp_dev, 0)) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		goto error; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	dev->hw_io = pnp_port_start(pnp_dev, 0); | 
					
						
							|  |  |  | 	dev->irq = pnp_irq(pnp_dev, 0); | 
					
						
							|  |  |  | 	spin_lock_init(&dev->hw_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* claim the resources */ | 
					
						
							|  |  |  | 	error = -EBUSY; | 
					
						
							|  |  |  | 	if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME)) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		goto error; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (request_irq(dev->irq, ene_isr, | 
					
						
							|  |  |  | 			IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev)) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		goto error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pnp_set_drvdata(pnp_dev, dev); | 
					
						
							|  |  |  | 	dev->pnp_dev = pnp_dev; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* detect hardware version and features */ | 
					
						
							|  |  |  | 	error = ene_hw_detect(dev); | 
					
						
							|  |  |  | 	if (error) | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 		goto error; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ene_setup_settings(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!dev->hw_learning_and_tx_capable && txsim) { | 
					
						
							|  |  |  | 		dev->hw_learning_and_tx_capable = 1; | 
					
						
							|  |  |  | 		setup_timer(&dev->tx_sim_timer, ene_tx_irqsim, | 
					
						
							|  |  |  | 						(long unsigned int)dev); | 
					
						
							|  |  |  | 		ene_printk(KERN_WARNING, | 
					
						
							|  |  |  | 			"Simulation of TX activated\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ir_props->driver_type = RC_DRIVER_IR_RAW; | 
					
						
							|  |  |  | 	ir_props->allowed_protos = IR_TYPE_ALL; | 
					
						
							|  |  |  | 	ir_props->priv = dev; | 
					
						
							|  |  |  | 	ir_props->open = ene_open; | 
					
						
							|  |  |  | 	ir_props->close = ene_close; | 
					
						
							|  |  |  | 	ir_props->min_timeout = ENE_MINGAP * 1000; | 
					
						
							|  |  |  | 	ir_props->max_timeout = ENE_MAXGAP * 1000; | 
					
						
							|  |  |  | 	ir_props->timeout = ENE_MAXGAP * 1000; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	if (dev->hw_revision == ENE_HW_B) | 
					
						
							|  |  |  | 		ir_props->s_idle = ene_rx_set_idle; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	dev->props = ir_props; | 
					
						
							|  |  |  | 	dev->idev = input_dev; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* don't allow too short/long sample periods */ | 
					
						
							|  |  |  | 	if (sample_period < 5 || sample_period > 0x7F) | 
					
						
							|  |  |  | 		sample_period = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* choose default sample period */ | 
					
						
							|  |  |  | 	if (sample_period == -1) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		sample_period = 50; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* on revB, hardware idle mode eats first sample
 | 
					
						
							|  |  |  | 		  if we set too low sample period */ | 
					
						
							|  |  |  | 		if (dev->hw_revision == ENE_HW_B && enable_idle) | 
					
						
							|  |  |  | 			sample_period = 75; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ir_props->rx_resolution = sample_period * 1000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (dev->hw_learning_and_tx_capable) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ir_props->s_learning_mode = ene_set_learning_mode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (input == 0) | 
					
						
							|  |  |  | 			ir_props->s_rx_carrier_range = ene_set_rec_carrier; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		init_completion(&dev->tx_complete); | 
					
						
							|  |  |  | 		ir_props->tx_ir = ene_transmit; | 
					
						
							|  |  |  | 		ir_props->s_tx_mask = ene_set_tx_mask; | 
					
						
							|  |  |  | 		ir_props->s_tx_carrier = ene_set_tx_carrier; | 
					
						
							|  |  |  | 		ir_props->tx_resolution = ENE_TX_SMPL_PERIOD * 1000; | 
					
						
							|  |  |  | 		/* ir_props->s_carrier_report = ene_set_carrier_report; */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	device_set_wakeup_capable(&pnp_dev->dev, 1); | 
					
						
							|  |  |  | 	device_set_wakeup_enable(&pnp_dev->dev, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	if (dev->hw_learning_and_tx_capable) | 
					
						
							|  |  |  | 		input_dev->name = "ENE eHome Infrared Remote Transceiver"; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		input_dev->name = "ENE eHome Infrared Remote Receiver"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	error = -ENODEV; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	if (ir_input_register(input_dev, RC_MAP_RC6_MCE, ir_props, | 
					
						
							|  |  |  | 							ENE_DRIVER_NAME)) | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n"); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | error: | 
					
						
							|  |  |  | 	if (dev->irq) | 
					
						
							|  |  |  | 		free_irq(dev->irq, dev); | 
					
						
							|  |  |  | 	if (dev->hw_io) | 
					
						
							|  |  |  | 		release_region(dev->hw_io, ENE_MAX_IO); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input_free_device(input_dev); | 
					
						
							|  |  |  | 	kfree(ir_props); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	kfree(dev); | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* main unload function */ | 
					
						
							|  |  |  | static void ene_remove(struct pnp_dev *pnp_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = pnp_get_drvdata(pnp_dev); | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 	ene_rx_disable(dev); | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&dev->hw_lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	free_irq(dev->irq, dev); | 
					
						
							|  |  |  | 	release_region(dev->hw_io, ENE_MAX_IO); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	ir_input_unregister(dev->idev); | 
					
						
							|  |  |  | 	kfree(dev->props); | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	kfree(dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* enable wake on IR (wakes on specific button on original remote) */ | 
					
						
							|  |  |  | static void ene_enable_wake(struct ene_device *dev, int enable) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	enable = enable && device_may_wakeup(&dev->pnp_dev->dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_dbg("wake on IR %s", enable ? "enabled" : "disabled"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_hw_write_reg_mask(dev, ENE_FW1, enable ? | 
					
						
							|  |  |  | 		ENE_FW1_WAKE : 0, ENE_FW1_WAKE); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_PM
 | 
					
						
							|  |  |  | static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = pnp_get_drvdata(pnp_dev); | 
					
						
							|  |  |  | 	ene_enable_wake(dev, 1); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ene_resume(struct pnp_dev *pnp_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = pnp_get_drvdata(pnp_dev); | 
					
						
							|  |  |  | 	if (dev->in_use) | 
					
						
							|  |  |  | 		ene_rx_enable(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ene_enable_wake(dev, 0); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ene_shutdown(struct pnp_dev *pnp_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ene_device *dev = pnp_get_drvdata(pnp_dev); | 
					
						
							|  |  |  | 	ene_enable_wake(dev, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct pnp_device_id ene_ids[] = { | 
					
						
							|  |  |  | 	{.id = "ENE0100",}, | 
					
						
							|  |  |  | 	{.id = "ENE0200",}, | 
					
						
							|  |  |  | 	{.id = "ENE0201",}, | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	{.id = "ENE0202",}, | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	{}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct pnp_driver ene_driver = { | 
					
						
							|  |  |  | 	.name = ENE_DRIVER_NAME, | 
					
						
							|  |  |  | 	.id_table = ene_ids, | 
					
						
							|  |  |  | 	.flags = PNP_DRIVER_RES_DO_NOT_CHANGE, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	.probe = ene_probe, | 
					
						
							|  |  |  | 	.remove = __devexit_p(ene_remove), | 
					
						
							|  |  |  | #ifdef CONFIG_PM
 | 
					
						
							|  |  |  | 	.suspend = ene_suspend, | 
					
						
							|  |  |  | 	.resume = ene_resume, | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	.shutdown = ene_shutdown, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init ene_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return pnp_register_driver(&ene_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ene_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pnp_unregister_driver(&ene_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_param(sample_period, int, S_IRUGO); | 
					
						
							|  |  |  | MODULE_PARM_DESC(sample_period, "Hardware sample period (50 us default)"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_param(enable_idle, bool, S_IRUGO | S_IWUSR); | 
					
						
							|  |  |  | MODULE_PARM_DESC(enable_idle, | 
					
						
							|  |  |  | 	"Enables turning off signal sampling after long inactivity time; " | 
					
						
							|  |  |  | 	"if disabled might help detecting input signal (default: enabled)" | 
					
						
							|  |  |  | 	" (KB3926B only)"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_param(input, bool, S_IRUGO); | 
					
						
							|  |  |  | MODULE_PARM_DESC(input, "select which input to use " | 
					
						
							|  |  |  | 	"0 - auto, 1 - standard, 2 - wideband(KB3926C+)"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_param(debug, int, S_IRUGO | S_IWUSR); | 
					
						
							|  |  |  | MODULE_PARM_DESC(debug, "Enable debug (debug=2 verbose debug output)"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_param(txsim, bool, S_IRUGO); | 
					
						
							|  |  |  | MODULE_PARM_DESC(txsim, | 
					
						
							|  |  |  | 	"Simulate TX features on unsupported hardware (dangerous)"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(pnp, ene_ids); | 
					
						
							|  |  |  | MODULE_DESCRIPTION | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:26 -03:00
										 |  |  | 	("Infrared input driver for KB3926B/KB3926C/KB3926D " | 
					
						
							| 
									
										
										
										
											2010-07-31 11:59:25 -03:00
										 |  |  | 	"(aka ENE0100/ENE0200/ENE0201) CIR port"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Maxim Levitsky"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_init(ene_init); | 
					
						
							|  |  |  | module_exit(ene_exit); |