Commit d882cbce authored by Komal Shah's avatar Komal Shah Committed by Tony Lindgren

[PATCH] ARM: OMAP: 24xx Irda update

Attached the updated IrDA patch (integrated workqueue) with 24xx
support.
Signed-off-by: default avatarKomal Shah <komal_shah802003@yahoo.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 2ef51b0a
......@@ -36,6 +36,7 @@
#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
#include <asm/arch/tc.h>
#include <asm/arch/irda.h>
#include <asm/arch/usb.h>
#include <asm/arch/common.h>
......@@ -171,10 +172,44 @@ static struct platform_device h2_smc91x_device = {
.resource = h2_smc91x_resources,
};
#define H2_IRDA_FIRSEL_GPIO_PIN 17
static int h2_transceiver_mode(struct device *dev, int state)
{
if (state & IR_SIRMODE)
omap_set_gpio_dataout(H2_IRDA_FIRSEL_GPIO_PIN, 0);
else /* MIR/FIR */
omap_set_gpio_dataout(H2_IRDA_FIRSEL_GPIO_PIN, 1);
return 0;
}
static struct omap_irda_config h2_irda_data = {
.transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE,
};
static struct resource h2_irda_resources[] = {
[0] = {
.start = INT_UART3,
.end = INT_UART3,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device h2_irda_device = {
.name = "omapirda",
.id = 0,
.dev = {
.platform_data = &h2_irda_data,
},
.num_resources = ARRAY_SIZE(h2_irda_resources),
.resource = h2_irda_resources,
};
static struct platform_device *h2_devices[] __initdata = {
&h2_nor_device,
&h2_nand_device,
&h2_smc91x_device,
&h2_irda_device,
};
static void __init h2_init_smc91x(void)
......@@ -267,6 +302,15 @@ static void __init h2_init(void)
// omap_cfg_reg(U19_ARMIO1); /* CD */
omap_cfg_reg(BALLOUT_V8_ARMIO3); /* WP */
/* Irda */
#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE)
omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7, FUNC_MUX_CTRL_A);
if (!(omap_request_gpio(H2_IRDA_FIRSEL_GPIO_PIN))) {
omap_set_gpio_direction(H2_IRDA_FIRSEL_GPIO_PIN, 0);
h2_irda_data.transceiver_mode = h2_transceiver_mode;
}
#endif
platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices));
omap_board_config = h2_config;
omap_board_config_size = ARRAY_SIZE(h2_config);
......
......@@ -21,6 +21,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/workqueue.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
......@@ -34,9 +35,11 @@
#include <asm/mach/map.h>
#include <asm/arch/gpio.h>
#include <asm/arch/gpioexpander.h>
#include <asm/arch/irqs.h>
#include <asm/arch/mux.h>
#include <asm/arch/tc.h>
#include <asm/arch/irda.h>
#include <asm/arch/usb.h>
#include <asm/arch/common.h>
......@@ -193,11 +196,96 @@ static struct platform_device intlat_device = {
.resource = intlat_resources,
};
/* Select between the IrDA and aGPS module
*/
static int h3_select_irda(struct device *dev, int state)
{
unsigned char expa;
int err = 0;
if ((err = read_gpio_expa(&expa, 0x26))) {
printk(KERN_ERR "Error reading from I/O EXPANDER \n");
return err;
}
/* 'P6' enable/disable IRDA_TX and IRDA_RX */
if (state & IR_SEL) { /* IrDA */
if ((err = write_gpio_expa(expa | 0x40, 0x26))) {
printk(KERN_ERR "Error writing to I/O EXPANDER \n");
return err;
}
} else {
if ((err = write_gpio_expa(expa & ~0x40, 0x26))) {
printk(KERN_ERR "Error writing to I/O EXPANDER \n");
return err;
}
}
return err;
}
static void set_trans_mode(void *data)
{
int *mode = data;
unsigned char expa;
int err = 0;
if ((err = read_gpio_expa(&expa, 0x27)) != 0) {
printk(KERN_ERR "Error reading from I/O expander\n");
}
expa &= ~0x03;
if (*mode & IR_SIRMODE) {
expa |= 0x01;
} else { /* MIR/FIR */
expa |= 0x03;
}
if ((err = write_gpio_expa(expa, 0x27)) != 0) {
printk(KERN_ERR "Error writing to I/O expander\n");
}
}
static int h3_transceiver_mode(struct device *dev, int mode)
{
struct omap_irda_config *irda_config = dev->platform_data;
cancel_delayed_work(irda_config->gpio_expa);
PREPARE_WORK(irda_config->gpio_expa, set_trans_mode, &mode);
schedule_work(irda_config->gpio_expa);
return 0;
}
static struct omap_irda_config h3_irda_data = {
.transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE,
.transceiver_mode = h3_transceiver_mode,
.select_irda = h3_select_irda,
};
static struct resource h3_irda_resources[] = {
[0] = {
.start = INT_UART3,
.end = INT_UART3,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device h3_irda_device = {
.name = "omapirda",
.id = 0,
.dev = {
.platform_data = &h3_irda_data,
},
.num_resources = ARRAY_SIZE(h3_irda_resources),
.resource = h3_irda_resources,
};
static struct platform_device *devices[] __initdata = {
&nor_device,
&nand_device,
&smc91x_device,
&intlat_device,
&h3_irda_device,
};
static struct omap_usb_config h3_usb_config __initdata = {
......
......@@ -17,6 +17,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <asm/hardware.h>
#include <asm/mach-types.h>
......@@ -25,8 +26,10 @@
#include <asm/mach/flash.h>
#include <asm/arch/gpio.h>
#include <asm/arch/gpioexpander.h>
#include <asm/arch/mux.h>
#include <asm/arch/usb.h>
#include <asm/arch/irda.h>
#include <asm/arch/board.h>
#include <asm/arch/common.h>
#include "prcm-regs.h"
......@@ -108,9 +111,93 @@ static struct platform_device h4_smc91x_device = {
.resource = h4_smc91x_resources,
};
/* Select between the IrDA and aGPS module
*/
static int h4_select_irda(struct device *dev, int state)
{
unsigned char expa;
int err = 0;
if ((err = read_gpio_expa(&expa, 0x21))) {
printk(KERN_ERR "Error reading from I/O expander\n");
return err;
}
/* 'P6' enable/disable IRDA_TX and IRDA_RX */
if (state & IR_SEL) { /* IrDa */
if ((err = write_gpio_expa(expa | 0x01, 0x21))) {
printk(KERN_ERR "Error writing to I/O expander\n");
return err;
}
} else {
if ((err = write_gpio_expa(expa & ~0x01, 0x21))) {
printk(KERN_ERR "Error writing to I/O expander\n");
return err;
}
}
return err;
}
static void set_trans_mode(void *data)
{
int *mode = data;
unsigned char expa;
int err = 0;
if ((err = read_gpio_expa(&expa, 0x20)) != 0) {
printk(KERN_ERR "Error reading from I/O expander\n");
}
expa &= ~0x01;
if (!(*mode & IR_SIRMODE)) { /* MIR/FIR */
expa |= 0x01;
}
if ((err = write_gpio_expa(expa, 0x20)) != 0) {
printk(KERN_ERR "Error writing to I/O expander\n");
}
}
static int h4_transceiver_mode(struct device *dev, int mode)
{
struct omap_irda_config *irda_config = dev->platform_data;
cancel_delayed_work(&irda_config->gpio_expa);
PREPARE_WORK(&irda_config->gpio_expa, set_trans_mode, &mode);
schedule_work(&irda_config->gpio_expa);
return 0;
}
static struct omap_irda_config h4_irda_data = {
.transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE,
.transceiver_mode = h4_transceiver_mode,
.select_irda = h4_select_irda,
};
static struct resource h4_irda_resources[] = {
[0] = {
.start = INT_24XX_UART3_IRQ,
.end = INT_24XX_UART3_IRQ,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device h4_irda_device = {
.name = "omapirda",
.id = -1,
.dev = {
.platform_data = &h4_irda_data,
},
.num_resources = 1,
.resource = h4_irda_resources,
};
static struct platform_device *h4_devices[] __initdata = {
&h4_smc91x_device,
&h4_flash_device,
&h4_irda_device,
};
static inline void __init h4_init_smc91x(void)
......@@ -174,6 +261,10 @@ static void __init omap_h4_init(void)
* You have to mux them off in device drivers later on
* if not needed.
*/
#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE)
omap_cfg_reg(K15_24XX_UART3_TX);
omap_cfg_reg(K14_24XX_UART3_RX);
#endif
platform_add_devices(h4_devices, ARRAY_SIZE(h4_devices));
omap_board_config = h4_config;
omap_board_config_size = ARRAY_SIZE(h4_config);
......
......@@ -54,6 +54,10 @@ MUX_CFG_24XX("W19_24XX_SYS_NIRQ", 0x12c, 0, 1, 1, 1)
MUX_CFG_24XX("Y20_24XX_GPIO60", 0x12c, 3, 0, 0, 1)
MUX_CFG_24XX("M15_24XX_GPIO92", 0x10a, 3, 0, 0, 1)
/* UART3 */
MUX_CFG_24XX("K15_24XX_UART3_TX", 0x118, 0, 0, 0, 1)
MUX_CFG_24XX("K14_24XX_UART3_RX", 0x119, 0, 0, 0, 1)
};
int __init omap2_mux_init(void)
......
......@@ -341,12 +341,12 @@ config TOSHIBA_FIR
To compile it as a module, choose M here: the module will be called
donauboe.
config OMAP1610_IR
tristate "OMAP1610 IrDA(SIR/MIR/FIR)"
depends on IRDA && ARCH_OMAP
select GPIOEXPANDER_OMAP if MACH_OMAP_H3
config OMAP_IR
tristate "OMAP IrDA(SIR/MIR/FIR)"
depends on IRDA && (ARCH_OMAP1 || ARCH_OMAP2)
select GPIOEXPANDER_OMAP if (MACH_OMAP_H3 || MACH_OMAP_H4)
help
Say Y here if you want to build support for the Omap1610 IR.
Say Y here if you want to build support for the OMAP IR.
config AU1000_FIR
tristate "Alchemy Au1000 SIR/FIR"
......
......@@ -43,7 +43,7 @@ obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin-sir.o
obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o
obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o
obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o
obj-$(CONFIG_OMAP1610_IR) += omap1610-ir.o
obj-$(CONFIG_OMAP_IR) += omap-ir.o
# The SIR helper module
......
/*
* BRIEF MODULE DESCRIPTION
*
* Infra-red driver for the OMAP1610-H2 and OMAP1710-H3 Platforms
* (SIR/MIR/FIR modes)
* (based on omap-sir.c)
*
* Copyright 2003 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* source@mvista.com
*
* Copyright 2004 Texas Instruments.
*
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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.
*
Modifications:
Feb 2004, Texas Instruments
- Ported to 2.6 kernel (Feb 2004).
*
Apr 2004, Texas Instruments
- Added support for H3 (Apr 2004).
Nov 2004, Texas Instruments
- Added support for Power Management.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/rtnetlink.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h>
#include <net/irda/wrapper.h>
#include <net/irda/irda_device.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/serial.h>
#include <asm/mach-types.h>
#include <asm/dma.h>
#include <asm/arch/mux.h>
#include <asm/arch/gpio.h>
#include <asm/arch/irda.h>
#define UART3_EFR_EN (1 << 4)
#define UART3_MCR_EN_TCR_TLR (1 << 6)
#define UART3_LCR_WL_8 (3 << 0)
#define UART3_LCR_SP2 (1 << 2)
#define UART3_LCR_DIVEN (1 << 7)
#define UART3_FCR_FIFO_EN (1 << 0)
#define UART3_FCR_FIFO_RX (1 << 1)
#define UART3_FCR_FIFO_TX (1 << 2)
#define UART3_FCR_FIFO_DMA1 (1 << 3)
#define UART3_FCR_FIFO_TX_TRIG16 (1 << 4)
#define UART3_FCR_FIFO_RX_TRIG16 (1 << 6)
#define UART3_FCR_CONFIG UART3_FCR_FIFO_EN | UART3_FCR_FIFO_RX | \
UART3_FCR_FIFO_TX | UART3_FCR_FIFO_DMA1 | \
UART3_FCR_FIFO_TX_TRIG16 | \
UART3_FCR_FIFO_RX_TRIG16
#define UART3_SCR_TX_TRIG1 (1 << 6)
#define UART3_SCR_RX_TRIG1 (1 << 7)
#define UART3_MDR1_RESET (0x07)
#define UART3_MDR1_SIR (1 << 0)
#define UART3_MDR1_MIR (4 << 0)
#define UART3_MDR1_FIR (5 << 0)
#define UART3_MDR1_SIP_AUTO (1 << 6)
#define UART3_MDR2_TRIG1 (0 << 1)
#define UART3_MDR2_IRTX_UNDERRUN (1 << 0)
#define UART3_ACERG_TX_UNDERRUN_DIS (1 << 4)
#define UART3_ACERG_SD_MODE_LOW (1 << 6)
#define UART3_ACERG_DIS_IR_RX (1 << 5)
#define UART3_IER_EOF (1 << 5)
#define UART3_IER_CTS (1 << 7)
#define UART3_IIR_TX_STATUS (1 << 5)
#define UART3_IIR_EOF (0x80)
#define IS_FIR(si) ((si)->speed >= 4000000)
#define IRDA_FRAME_SIZE_LIMIT 4096
static int rx_state = 0; /* RX state for IOCTL */
struct omap_irda {
unsigned char open;
int speed; /* Current IrDA speed */
int newspeed;
struct net_device_stats stats;
struct irlap_cb *irlap;
struct qos_info qos;
int rx_dma_channel;
int tx_dma_channel;
dma_addr_t rx_buf_dma_phys; /* Physical adress of RX DMA buffer */
dma_addr_t tx_buf_dma_phys; /* Physical adress of TX DMA buffer */
void *rx_buf_dma_virt; /* Virtual adress of RX DMA buffer */
void *tx_buf_dma_virt; /* Virtual adress of TX DMA buffer */
struct device *dev;
struct omap_irda_config *pdata;
};
#define OMAP_IRDA_DEBUG 0
#if (OMAP_IRDA_DEBUG > 0)
#define DBG(format, args...) printk(KERN_ERR "%s(): " format, __FUNCTION__, ## args);
#define DBG_IRQ(format, args...) printk(KERN_ERR "%s(): " format, __FUNCTION__, ## args);
#else
#define DBG(format, args...)
#define DBG_IRQ(format, args...)
#endif
#if (OMAP_IRDA_DEBUG > 1)
#define __ECHO_IN printk(KERN_ERR "%s: enter\n",__FUNCTION__);
#define __ECHO_OUT printk(KERN_ERR "%s: exit\n",__FUNCTION__);
#else
#define __ECHO_IN
#define __ECHO_OUT
#endif
#ifdef OMAP1610_IR_HARDWARE_DEBUG_ENABLE
#define HDBG_DELAY 200
void hard_debug1(u16 i)
{
for (; i; i--) {
omap_writew(0x2000,
OMAP1610_GPIO1_BASE + OMAP1610_GPIO_CLEAR_DATAOUT);
udelay(HDBG_DELAY);
omap_writew(0x2000,
OMAP1610_GPIO1_BASE + OMAP1610_GPIO_SET_DATAOUT);
udelay(HDBG_DELAY);
}
}
void hard_debug2(u16 i)
{
for (; i; i--) {
omap_writew(0x8000,
OMAP1610_GPIO1_BASE + OMAP1610_GPIO_CLEAR_DATAOUT);
udelay(HDBG_DELAY);
omap_writew(0x8000,
OMAP1610_GPIO1_BASE + OMAP1610_GPIO_SET_DATAOUT);
udelay(HDBG_DELAY);
}
}
#define HDBG1(i) hard_debug1(i)
#define HDBG2(i) hard_debug2(i)
#else
#define HDBG1(i)
#define HDBG2(i)
#endif
static void inline uart_reg_out(int idx, u8 val)
{
omap_writeb(val, idx);
}
static u8 inline uart_reg_in(int idx)
{
u8 b = omap_readb(idx);
return b;
}
/* forward declarations */
extern void irda_device_setup(struct net_device *dev);
extern void omap_stop_dma(int lch);
static int omap_irda_set_speed(struct net_device *dev, int speed);
static void omap_irda_start_rx_dma(struct omap_irda *si)
{
/* default for h2/h3 */
unsigned long src_start = 0xfffb9800;
unsigned int trigger = 0;
if (machine_is_omap_h2() || machine_is_omap_h3()) {
src_start = UART3_RHR;
trigger = 0;
}
if (machine_is_omap_h4()) {
src_start = OMAP_UART3_BASE;
trigger = OMAP24XX_DMA_UART3_RX;
}
/* Configure DMA */
omap_set_dma_src_params(si->rx_dma_channel, 0x3, 0x0, src_start,
0, 0);
omap_enable_dma_irq(si->rx_dma_channel, 0x01);
omap_set_dma_dest_params(si->rx_dma_channel, 0x0, 0x1,
si->rx_buf_dma_phys,
0, 0);
omap_set_dma_transfer_params(si->rx_dma_channel, 0x0,
IRDA_FRAME_SIZE_LIMIT, 0x1,
0x0, trigger, 0);
omap_start_dma(si->rx_dma_channel);
}
static void omap_start_tx_dma(struct omap_irda *si, int size)
{
/* default for h2/h3 */
unsigned long dest_start = 0xfffb9800;
unsigned int trigger = 0;
if (machine_is_omap_h2() || machine_is_omap_h3()) {
dest_start = UART3_THR;
trigger = 0;
}
if (machine_is_omap_h4()) {
dest_start = OMAP_UART3_BASE;
trigger = OMAP24XX_DMA_UART3_TX;
}
__ECHO_IN;
/* Configure DMA */
omap_set_dma_dest_params(si->tx_dma_channel, 0x03, 0x0,
dest_start, 0, 0);
omap_enable_dma_irq(si->tx_dma_channel, 0x01);
omap_set_dma_src_params(si->tx_dma_channel, 0x0, 0x1,
si->tx_buf_dma_phys,
0, 0);
omap_set_dma_transfer_params(si->tx_dma_channel, 0x0, size, 0x1,
0x0, trigger, 0);
HDBG1(1);
/* Start DMA */
omap_start_dma(si->tx_dma_channel);
HDBG1(1);
__ECHO_OUT;
}
/* DMA RX callback - normally, we should not go here,
it calls only if something is going wrong
*/
static void omap_irda_rx_dma_callback(int lch, u16 ch_status, void *data)
{
struct net_device *dev = data;
struct omap_irda *si = dev->priv;
printk(KERN_ERR "RX Transfer error or very big frame\n");
/* Clear interrupts */
uart_reg_in(UART3_IIR);
si->stats.rx_frame_errors++;
uart_reg_in(UART3_RESUME);
/* Re-init RX DMA */
omap_irda_start_rx_dma(si);
}
/* DMA TX callback - calling when frame transfer has been finished */
static void omap_irda_tx_dma_callback(int lch, u16 ch_status, void *data)
{
struct net_device *dev = data;
struct omap_irda *si = dev->priv;
__ECHO_IN;
/*Stop DMA controller */
omap_stop_dma(si->tx_dma_channel);
__ECHO_OUT;
}
/*
* Set the IrDA communications speed.
* Interrupt have to be disabled here.
*/
static int omap_irda_startup(struct net_device *dev)
{
struct omap_irda *si = dev->priv;
__ECHO_IN;
/* FIXME: use clk_* apis for UART3 clock*/
/* Enable UART3 clock and set UART3 to IrDA mode */
if (machine_is_omap_h2() || machine_is_omap_h3())
omap_writel(omap_readl(MOD_CONF_CTRL_0) | (1 << 31) | (1 << 15),
MOD_CONF_CTRL_0);
/* Only for H2?
*/
if (si->pdata->transceiver_mode && machine_is_omap_h2()) {
/* Is it select_irda on H2 ? */
omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7,
FUNC_MUX_CTRL_A);
si->pdata->transceiver_mode(si->dev, IR_SIRMODE);
}
uart_reg_out(UART3_MDR1, UART3_MDR1_RESET); /* Reset mode */
/* Clear DLH and DLL */
uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
uart_reg_out(UART3_DLL, 0);
uart_reg_out(UART3_DLH, 0);
uart_reg_out(UART3_LCR, 0xbf); /* FIXME: Add #define */
uart_reg_out(UART3_EFR, UART3_EFR_EN);
uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
/* Enable access to UART3_TLR and UART3_TCR registers */
uart_reg_out(UART3_MCR, UART3_MCR_EN_TCR_TLR);
uart_reg_out(UART3_SCR, 0);
/* Set Rx trigger to 1 and Tx trigger to 1 */
uart_reg_out(UART3_TLR, 0);
/* Set LCR to 8 bits and 1 stop bit */
uart_reg_out(UART3_LCR, 0x03);
/* Clear RX and TX FIFO and enable FIFO */
/* Use DMA Req for transfers */
uart_reg_out(UART3_FCR, UART3_FCR_CONFIG);
uart_reg_out(UART3_MCR, 0);
uart_reg_out(UART3_SCR, UART3_SCR_TX_TRIG1 |
UART3_SCR_RX_TRIG1);
/* Enable UART3 SIR Mode,(Frame-length method to end frames) */
uart_reg_out(UART3_MDR1, UART3_MDR1_SIR);
/* Set Status FIFO trig to 1 */
uart_reg_out(UART3_MDR2, 0);
/* Enables RXIR input */
/* and disable TX underrun */
/* SEND_SIP pulse */
uart_reg_out(UART3_ACREG, UART3_ACERG_SD_MODE_LOW |
UART3_ACERG_TX_UNDERRUN_DIS);
/* Enable EOF Interrupt only */
uart_reg_out(UART3_IER, UART3_IER_CTS | UART3_IER_EOF);
/* Set Maximum Received Frame size to 2048 bytes */
uart_reg_out(UART3_RXFLL, 0x00);
uart_reg_out(UART3_RXFLH, 0x08);
uart_reg_in(UART3_RESUME);
__ECHO_OUT;
return 0;
}
static int omap_irda_shutdown(struct omap_irda *si)
{
unsigned long flags;
local_irq_save(flags);
/* Disable all UART3 Interrupts */
uart_reg_out(UART3_IER, 0);
/* Disable UART3 and disable baud rate generator */
uart_reg_out(UART3_MDR1, UART3_MDR1_RESET);
/* set SD_MODE pin to high and Disable RX IR */
uart_reg_out(UART3_ACREG, (UART3_ACERG_DIS_IR_RX |
~(UART3_ACERG_SD_MODE_LOW)));
/* Clear DLH and DLL */
uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
uart_reg_out(UART3_DLL, 0);
uart_reg_out(UART3_DLH, 0);
local_irq_restore(flags);
return 0;
}
static irqreturn_t
omap_irda_irq(int irq, void *dev_id, struct pt_regs *hw_regs)
{
struct net_device *dev = dev_id;
struct omap_irda *si = dev->priv;
struct sk_buff *skb;
u8 status;
int w = 0;
__ECHO_IN;
/* Clear EOF interrupt */
status = uart_reg_in(UART3_IIR);
if (status & UART3_IIR_TX_STATUS) {
u8 mdr2 = uart_reg_in(UART3_MDR2);
HDBG1(2);
if (mdr2 & UART3_MDR2_IRTX_UNDERRUN)
printk(KERN_ERR "IrDA Buffer underrun error\n");
si->stats.tx_packets++;
if (si->newspeed) {
omap_irda_set_speed(dev, si->newspeed);
si->newspeed = 0;
}
netif_wake_queue(dev);
if (!(status & UART3_IIR_EOF))
return IRQ_HANDLED;
}
/* Stop DMA and if there are no errors, send frame to upper layer */
omap_stop_dma(si->rx_dma_channel);
status = uart_reg_in(UART3_SFLSR); /* Take a frame status */
if (status != 0) { /* Bad frame? */
si->stats.rx_frame_errors++;
uart_reg_in(UART3_RESUME);
} else {
/* We got a frame! */
skb = alloc_skb(IRDA_FRAME_SIZE_LIMIT, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "omap_sir: out of memory for RX SKB\n");
return IRQ_HANDLED;
}
/*
* Align any IP headers that may be contained
* within the frame.
*/
skb_reserve(skb, 1);
w = OMAP_DMA_CDAC_REG(si->rx_dma_channel);
if (cpu_is_omap16xx())
w -= OMAP1_DMA_CDSA_L_REG(si->rx_dma_channel);
if (cpu_is_omap24xx())
w -= OMAP2_DMA_CDSA_REG(si->rx_dma_channel);
if (!IS_FIR(si)) {
/* Copy DMA buffer to skb */
memcpy(skb_put(skb, w - 2), si->rx_buf_dma_virt, w - 2);
} else {
/* Copy DMA buffer to skb */
memcpy(skb_put(skb, w - 4), si->rx_buf_dma_virt, w - 4);
}
skb->dev = dev;
skb->mac.raw = skb->data;
skb->protocol = htons(ETH_P_IRDA);
si->stats.rx_packets++;
si->stats.rx_bytes += skb->len;
netif_receive_skb(skb); /* Send data to upper level */
}
/* Re-init RX DMA */
omap_irda_start_rx_dma(si);
dev->last_rx = jiffies;
__ECHO_OUT;
return IRQ_HANDLED;
}
static int omap_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct omap_irda *si = dev->priv;
int speed = irda_get_next_speed(skb);
int mtt = irda_get_mtt(skb);
int xbofs = irda_get_next_xbofs(skb);
__ECHO_IN;
/*
* Does this packet contain a request to change the interface
* speed? If so, remember it until we complete the transmission
* of this frame.
*/
if (speed != si->speed && speed != -1)
si->newspeed = speed;
if (xbofs) {
/* Set number of addtional BOFS */
uart_reg_out(UART3_EBLR, xbofs + 1);
}
/*
* If this is an empty frame, we can bypass a lot.
*/
if (skb->len == 0) {
if (si->newspeed) {
si->newspeed = 0;
omap_irda_set_speed(dev, speed);
}
dev_kfree_skb(skb);
return 0;
}
netif_stop_queue(dev);
/* Copy skb data to DMA buffer */
memcpy(si->tx_buf_dma_virt, skb->data, skb->len);
/* Copy skb data to DMA buffer */
si->stats.tx_bytes += skb->len;
/* Set frame length */
uart_reg_out(UART3_TXFLL, (skb->len & 0xff));
uart_reg_out(UART3_TXFLH, (skb->len >> 8));
if (mtt > 1000)
mdelay(mtt / 1000);
else
udelay(mtt);
/* Start TX DMA transfer */
omap_start_tx_dma(si, skb->len);
/* We can free skb now because it's already in DMA buffer */
dev_kfree_skb(skb);
dev->trans_start = jiffies;
__ECHO_OUT;
return 0;
}
static int
omap_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
{
struct if_irda_req *rq = (struct if_irda_req *)ifreq;
struct omap_irda *si = dev->priv;
int ret = -EOPNOTSUPP;
__ECHO_IN;
switch (cmd) {
case SIOCSBANDWIDTH:
if (capable(CAP_NET_ADMIN)) {
/*
* We are unable to set the speed if the
* device is not running.
*/
if (si->open) {
ret =
omap_irda_set_speed(dev, rq->ifr_baudrate);
} else {
printk(KERN_ERR "omap_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n");
ret = 0;
}
}
break;
case SIOCSMEDIABUSY:
ret = -EPERM;
if (capable(CAP_NET_ADMIN)) {
irda_device_set_media_busy(dev, TRUE);
ret = 0;
}
break;
case SIOCGRECEIVING:
rq->ifr_receiving = rx_state;
break;
default:
break;
}
__ECHO_OUT;
return ret;
}
static struct net_device_stats *omap_irda_stats(struct net_device *dev)
{
struct omap_irda *si = dev->priv;
return &si->stats;
}
static int omap_irda_start(struct net_device *dev)
{
struct omap_irda *si = dev->priv;
int err;
int rx_channel = OMAP_DMA_NO_DEVICE;
int tx_channel = OMAP_DMA_NO_DEVICE;
__ECHO_IN;
si->speed = 9600;
err = request_irq(dev->irq, omap_irda_irq, 0, dev->name, dev);
if (err)
goto err_irq;
/*
* The interrupt must remain disabled for now.
*/
disable_irq(dev->irq);
/* FIXME: These info can come from board-* files, if no one
* objects
*/
if (machine_is_omap_h2() || machine_is_omap_h3()) {
rx_channel = OMAP_DMA_UART3_RX;
tx_channel = OMAP_DMA_UART3_TX;
}
if (machine_is_omap_h4()) {
rx_channel = OMAP24XX_DMA_UART3_RX;
tx_channel = OMAP24XX_DMA_UART3_TX;
}
/* Request DMA channels for IrDA hardware */
if (omap_request_dma(rx_channel, "IrDA Rx DMA",
(void *)omap_irda_rx_dma_callback,
dev, &(si->rx_dma_channel))) {
printk(KERN_ERR "Failed to request IrDA Rx DMA\n");
goto err_irq;
}
if (omap_request_dma(tx_channel, "IrDA Tx DMA",
(void *)omap_irda_tx_dma_callback,
dev, &(si->tx_dma_channel))) {
printk(KERN_ERR "Failed to request IrDA Tx DMA\n");
goto err_irq;
}
/* Allocate TX and RX buffers for DMA channels */
si->rx_buf_dma_virt =
dma_alloc_coherent(NULL, IRDA_FRAME_SIZE_LIMIT, &(si->rx_buf_dma_phys),
GFP_KERNEL);
si->tx_buf_dma_virt =
dma_alloc_coherent(NULL, IRDA_FRAME_SIZE_LIMIT, &(si->tx_buf_dma_phys),
GFP_KERNEL);
/*
* Setup the serial port for the specified config.
*/
if (si->pdata->select_irda)
si->pdata->select_irda(si->dev, IR_SEL);
err = omap_irda_startup(dev);
if (err)
goto err_startup;
omap_irda_set_speed(dev, si->speed = 9600);
/*
* Open a new IrLAP layer instance.
*/
si->irlap = irlap_open(dev, &si->qos, "omap_sir");
err = -ENOMEM;
if (!si->irlap)
goto err_irlap;
/* Now enable the interrupt and start the queue */
si->open = 1;
/* Start RX DMA */
omap_irda_start_rx_dma(si);
enable_irq(dev->irq);
netif_start_queue(dev);
__ECHO_OUT;
return 0;
err_irlap:
si->open = 0;
omap_irda_shutdown(si);
err_startup:
err_irq:
free_irq(dev->irq, dev);
return err;
}
static int omap_irda_stop(struct net_device *dev)
{
struct omap_irda *si = dev->priv;
__ECHO_IN;
disable_irq(dev->irq);
netif_stop_queue(dev);
omap_free_dma(si->rx_dma_channel);
omap_free_dma(si->tx_dma_channel);
if (si->rx_buf_dma_virt)
dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT,
si->rx_buf_dma_virt, si->rx_buf_dma_phys);
if (si->tx_buf_dma_virt)
dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT,
si->tx_buf_dma_virt, si->tx_buf_dma_phys);
omap_irda_shutdown(si);
/* Stop IrLAP */
if (si->irlap) {
irlap_close(si->irlap);
si->irlap = NULL;
}
si->open = 0;
/*
* Free resources
*/
free_irq(dev->irq, dev);
__ECHO_OUT;
return 0;
}
static int omap_irda_set_speed(struct net_device *dev, int speed)
{
struct omap_irda *si = dev->priv;
int divisor;
unsigned long flags;
__ECHO_IN;
/* Set IrDA speed */
if (speed <= 115200) {
local_irq_save(flags);
/* SIR mode */
if (si->pdata->transceiver_mode)
si->pdata->transceiver_mode(si->dev, IR_SIRMODE);
/* Set SIR mode */
uart_reg_out(UART3_MDR1, 1);
uart_reg_out(UART3_EBLR, 1);
divisor = 48000000 / (16 * speed); /* Base clock 48 MHz */
HDBG2(1);
uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
uart_reg_out(UART3_DLL, (divisor & 0xff));
uart_reg_out(UART3_DLH, (divisor >> 8));
uart_reg_out(UART3_LCR, 0x03);
uart_reg_out(UART3_MCR, 0);
HDBG2(1);
local_irq_restore(flags);
} else if (speed <= 1152000) {
local_irq_save(flags);
/* Set MIR mode, auto SIP */
uart_reg_out(UART3_MDR1, UART3_MDR1_MIR |
UART3_MDR1_SIP_AUTO);
uart_reg_out(UART3_EBLR, 2);
divisor = 48000000 / (41 * speed); /* Base clock 48 MHz */
uart_reg_out(UART3_LCR, UART3_LCR_DIVEN);
uart_reg_out(UART3_DLL, (divisor & 0xff));
uart_reg_out(UART3_DLH, (divisor >> 8));
uart_reg_out(UART3_LCR, 0x03);
if (si->pdata->transceiver_mode)
si->pdata->transceiver_mode(si->dev, IR_MIRMODE);
local_irq_restore(flags);
} else {
local_irq_save(flags);
/* FIR mode */
uart_reg_out(UART3_MDR1, UART3_MDR1_FIR |
UART3_MDR1_SIP_AUTO);
if (si->pdata->transceiver_mode)
si->pdata->transceiver_mode(si->dev, IR_FIRMODE);
local_irq_restore(flags);
}
si->speed = speed;
__ECHO_OUT;
return 0;
}
#ifdef CONFIG_PM
/*
* Suspend the IrDA interface.
*/
static int omap_irda_suspend(struct platform_device *pdev, pm_message_t state)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct omap_irda *si = dev->priv;
if (!dev)
return 0;
if (si->open) {
/*
* Stop the transmit queue
*/
netif_device_detach(dev);
disable_irq(dev->irq);
omap_irda_shutdown(si);
}
return 0;
}
/*
* Resume the IrDA interface.
*/
static int omap_irda_resume(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct omap_irda *si= dev->priv;
if (!dev)
return 0;
if (si->open) {
/*
* If we missed a speed change, initialise at the new speed
* directly. It is debatable whether this is actually
* required, but in the interests of continuing from where
* we left off it is desireable. The converse argument is
* that we should re-negotiate at 9600 baud again.
*/
if (si->newspeed) {
si->speed = si->newspeed;
si->newspeed = 0;
}
omap_irda_startup(dev);
omap_irda_set_speed(dev, si->speed);
enable_irq(dev->irq);
/*
* This automatically wakes up the queue
*/
netif_device_attach(dev);
}
return 0;
}
#else
#define omap_irda_suspend NULL
#define omap_irda_resume NULL
#endif
static int omap_irda_probe(struct platform_device *pdev)
{
struct net_device *dev;
struct omap_irda *si;
unsigned int baudrate_mask;
int err = 0;
int irq = NO_IRQ;
if (!pdev->dev.platform_data) {
printk(KERN_ERR "IrDA Platform data not supplied\n");
return -ENOENT;
}
dev = alloc_irdadev(sizeof(struct omap_irda));
if (!dev)
goto err_mem_1;
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
printk(KERN_WARNING "no irq for IrDA\n");
return -ENOENT;
}
si = dev->priv;
si->dev = &pdev->dev;
si->pdata = pdev->dev.platform_data;
dev->hard_start_xmit = omap_irda_hard_xmit;
dev->open = omap_irda_start;
dev->stop = omap_irda_stop;
dev->do_ioctl = omap_irda_ioctl;
dev->get_stats = omap_irda_stats;
dev->irq = irq;
irda_init_max_qos_capabilies(&si->qos);
baudrate_mask = 0;
if (si->pdata->transceiver_cap & IR_SIRMODE)
baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
if (si->pdata->transceiver_cap & IR_MIRMODE)
baudrate_mask |= IR_57600 | IR_1152000;
if (si->pdata->transceiver_cap & IR_FIRMODE)
baudrate_mask |= IR_4000000 << 8;
si->qos.baud_rate.bits &= baudrate_mask;
si->qos.min_turn_time.bits = 7;
irda_qos_bits_to_value(&si->qos);
/* Any better way to avoid this? No. */
if (machine_is_omap_h3() || machine_is_omap_h4()) {
INIT_WORK(&si->pdata->gpio_expa, NULL, NULL);
}
err = register_netdev(dev);
if (!err)
platform_set_drvdata(pdev, dev);
else
free_netdev(dev);
err_mem_1:
return err;
}
static int omap_irda_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
if (pdev) {
unregister_netdev(dev);
free_netdev(dev);
}
return 0;
}
static struct platform_driver omapir_driver = {
.probe = omap_irda_probe,
.remove = omap_irda_remove,
.suspend = omap_irda_suspend,
.resume = omap_irda_resume,
.driver = {
.name = "omapirda",
},
};
static char __initdata banner[] = "OMAP IrDA driver\n";
static int __init omap_irda_init(void)
{
printk(banner);
return platform_driver_register(&omapir_driver);
}
static void __exit omap_irda_exit(void)
{
platform_driver_unregister(&omapir_driver);
}
module_init(omap_irda_init);
module_exit(omap_irda_exit);
MODULE_AUTHOR("MontaVista");
MODULE_DESCRIPTION("OMAP IrDA Driver");
MODULE_LICENSE("GPL");
/*
* BRIEF MODULE DESCRIPTION
*
* Infra-red driver for the OMAP1610-H2 and OMAP1710-H3 Platforms
* (SIR/MIR/FIR modes)
* (based on omap-sir.c)
*
* Copyright 2003 MontaVista Software Inc.
* Author: MontaVista Software, Inc.
* source@mvista.com
*
* Copyright 2004 Texas Instruments.
*
* 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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.
*
Modifications:
Feb 2004, Texas Instruments
- Ported to 2.6 kernel (Feb 2004).
*
Apr 2004, Texas Instruments
- Added support for H3 (Apr 2004).
Nov 2004, Texas Instruments
- Added support for Power Management.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/rtnetlink.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <net/irda/irda.h>
#include <net/irda/irmod.h>
#include <net/irda/wrapper.h>
#include <net/irda/irda_device.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/serial.h>
#include <asm/mach-types.h>
#include <asm/dma.h>
#include <asm/arch/mux.h>
#include <asm/arch/gpio.h>
#include <linux/i2c.h>
#ifdef CONFIG_MACH_OMAP_H3
#include <asm/arch/gpioexpander.h>
#endif
#define SIR_MODE 0
#define MIR_MODE 1
#define FIR_MODE 2
#define OMAP1610_H2_FIRSEL_GPIO 17
static int rx_state = 0; /* RX state for IOCTL */
struct omap1610_irda {
unsigned char open;
int speed; /* Current IrDA speed */
int newspeed;
struct net_device_stats stats;
struct irlap_cb *irlap;
struct qos_info qos;
int rx_dma_channel;
int tx_dma_channel;
dma_addr_t rx_buf_dma_phys; /* Physical adress of RX DMA buffer */
dma_addr_t tx_buf_dma_phys; /* Physical adress of TX DMA buffer */
void *rx_buf_dma_virt; /* Virtual adress of RX DMA buffer */
void *tx_buf_dma_virt; /* Virtual adress of TX DMA buffer */
struct device *dev;
};
#define OMAP_IRDA_DEBUG 0
#if (OMAP_IRDA_DEBUG > 0)
#define DBG(format, args...) printk(KERN_ERR "%s(): " format, __FUNCTION__, ## args);
#define DBG_IRQ(format, args...) printk(KERN_ERR "%s(): " format, __FUNCTION__, ## args);
#else
#define DBG(format, args...)
#define DBG_IRQ(format, args...)
#endif
#if (OMAP_IRDA_DEBUG > 1)
#define __ECHO_IN printk(KERN_ERR "%s: enter\n",__FUNCTION__);
#define __ECHO_OUT printk(KERN_ERR "%s: exit\n",__FUNCTION__);
#else
#define __ECHO_IN
#define __ECHO_OUT
#endif
#ifdef OMAP1610_IR_HARDWARE_DEBUG_ENABLE
#define HDBG_DELAY 200
void hard_debug1(u16 i)
{
for (; i; i--) {
omap_writew(0x2000,
OMAP1610_GPIO1_BASE + OMAP1610_GPIO_CLEAR_DATAOUT);
udelay(HDBG_DELAY);
omap_writew(0x2000,
OMAP1610_GPIO1_BASE + OMAP1610_GPIO_SET_DATAOUT);
udelay(HDBG_DELAY);
}
}
void hard_debug2(u16 i)
{
for (; i; i--) {
omap_writew(0x8000,
OMAP1610_GPIO1_BASE + OMAP1610_GPIO_CLEAR_DATAOUT);
udelay(HDBG_DELAY);
omap_writew(0x8000,
OMAP1610_GPIO1_BASE + OMAP1610_GPIO_SET_DATAOUT);
udelay(HDBG_DELAY);
}
}
#define HDBG1(i) hard_debug1(i)
#define HDBG2(i) hard_debug2(i)
#else
#define HDBG1(i)
#define HDBG2(i)
#endif
/* forward declarations */
extern void irda_device_setup(struct net_device *dev);
extern void omap_stop_dma(int lch);
static int omap1610_irda_set_speed(struct net_device *dev, int speed);
static void omap1610_irda_start_rx_dma(struct omap1610_irda *si)
{
/* Configure DMA */
omap_set_dma_src_params(si->rx_dma_channel, 0x3, 0x0, (unsigned long)UART3_RHR,
0, 0);
omap_enable_dma_irq(si->rx_dma_channel, 0x01);
omap_set_dma_dest_params(si->rx_dma_channel, 0x0, 0x1,
si->rx_buf_dma_phys,
0, 0);
omap_set_dma_transfer_params(si->rx_dma_channel, 0x0, 4096, 0x1, 0x0, 0, 0);
omap_start_dma(si->rx_dma_channel);
}
static void omap1610_start_tx_dma(struct omap1610_irda *si, int size)
{
__ECHO_IN;
/* Configure DMA */
omap_set_dma_dest_params(si->tx_dma_channel, 0x03, 0x0, (unsigned long)UART3_THR,
0, 0);
omap_enable_dma_irq(si->tx_dma_channel, 0x01);
omap_set_dma_src_params(si->tx_dma_channel, 0x0, 0x1,
si->tx_buf_dma_phys,
0, 0);
omap_set_dma_transfer_params(si->tx_dma_channel, 0x0, size, 0x1, 0x0, 0, 0);
HDBG1(1);
/* Start DMA */
omap_start_dma(si->tx_dma_channel);
HDBG1(1);
__ECHO_OUT;
}
/* DMA RX callback - normally, we should not go here,
it calls only if something is going wrong
*/
static void omap1610_irda_rx_dma_callback(int lch, u16 ch_status, void *data)
{
struct net_device *dev = data;
struct omap1610_irda *si = dev->priv;
printk(KERN_ERR "RX Transfer error or very big frame \n");
/* Clear interrupts */
omap_readb(UART3_IIR);
si->stats.rx_frame_errors++;
omap_readb(UART3_RESUME);
/* Re-init RX DMA */
omap1610_irda_start_rx_dma(si);
}
/* DMA TX callback - calling when frame transfer has been finished */
static void omap1610_irda_tx_dma_callback(int lch, u16 ch_status, void *data)
{
struct net_device *dev = data;
struct omap1610_irda *si = dev->priv;
__ECHO_IN;
/*Stop DMA controller */
omap_stop_dma(si->tx_dma_channel);
__ECHO_OUT;
}
/*
* Set the IrDA communications speed.
* Interrupt have to be disabled here.
*/
static int omap1610_irda_startup(struct net_device *dev)
{
__ECHO_IN;
/* Enable UART3 clock and set UART3 to IrDA mode */
omap_writel(omap_readl(MOD_CONF_CTRL_0) | (1 << 31) | (1 << 15),
MOD_CONF_CTRL_0);
if (machine_is_omap_h2()) {
// omap_cfg_reg(Y15_1610_GPIO17);
omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7, FUNC_MUX_CTRL_A);
omap_set_gpio_direction(OMAP1610_H2_FIRSEL_GPIO, 0);
omap_set_gpio_dataout(OMAP1610_H2_FIRSEL_GPIO, 0);
}
omap_writeb(0x07, UART3_MDR1); /* Put UART3 in reset mode */
/* Clear DLH and DLL */
omap_writeb(1 << 7, UART3_LCR);
omap_writeb(0, UART3_DLL);
omap_writeb(0, UART3_DLH);
omap_writeb(0xbf, UART3_LCR);
omap_writeb(1 << 4, UART3_EFR);
omap_writeb(1 << 7, UART3_LCR);
/* Enable access to UART3_TLR and UART3_TCR registers */
omap_writeb(1 << 6, UART3_MCR);
omap_writeb(0, UART3_SCR);
/* Set Rx trigger to 1 and Tx trigger to 1 */
omap_writeb(0, UART3_TLR);
/* Set LCR to 8 bits and 1 stop bit */
omap_writeb(0x03, UART3_LCR);
/* Clear RX and TX FIFO and enable FIFO */
/* Use DMA Req for transfers */
omap_writeb((1 << 2) | (1 << 1) | (1 << 3) | (1 << 4) | (1 << 6) | 1,
UART3_FCR);
omap_writeb(0, UART3_MCR);
omap_writeb((1 << 7) | (1 << 6), UART3_SCR);
/* Enable UART3 SIR Mode,(Frame-length method to end frames) */
omap_writeb(1, UART3_MDR1);
/* Set Status FIFO trig to 1 */
omap_writeb(0, UART3_MDR2);
/* Enables RXIR input */
/* and disable TX underrun */
/* SEND_SIP pulse */
// omap_writeb((1 << 7) | (1 << 6) | (1 << 4), UART3_ACREG);
omap_writeb((1 << 6) | (1 << 4), UART3_ACREG);
/* Enable EOF Interrupt only */
omap_writeb((1 << 7) | (1 << 5), UART3_IER);
/* Set Maximum Received Frame size to 2048 bytes */
omap_writeb(0x00, UART3_RXFLL);
omap_writeb(0x08, UART3_RXFLH);
omap_readb(UART3_RESUME);
__ECHO_OUT;
return 0;
}
static int omap1610_irda_shutdown(struct omap1610_irda *si)
{
/* Disable all UART3 Interrupts */
omap_writeb(0, UART3_IER);
/* Disable UART3 and disable baud rate generator */
omap_writeb(0x07, UART3_MDR1); /* Put UART3 in reset mode */
omap_writeb((1 << 5), UART3_ACREG); /* set SD_MODE pin to high and Disable RX IR */
/* Clear DLH and DLL */
omap_writeb(1 << 7, UART3_LCR);
omap_writeb(0, UART3_DLL);
omap_writeb(0, UART3_DLH);
return 0;
}
static irqreturn_t
omap1610_irda_irq(int irq, void *dev_id, struct pt_regs *hw_regs)
{
struct net_device *dev = dev_id;
struct omap1610_irda *si = dev->priv;
struct sk_buff *skb;
u8 status;
int w = 0;
__ECHO_IN;
/* Clear EOF interrupt */
status = omap_readb(UART3_IIR);
if (status & (1 << 5)) {
u8 mdr2 = omap_readb(UART3_MDR2);
HDBG1(2);
if (mdr2 & 1)
printk(KERN_ERR "IRDA Buffer underrun error");
si->stats.tx_packets++;
if (si->newspeed) {
omap1610_irda_set_speed(dev, si->newspeed);
si->newspeed = 0;
}
netif_wake_queue(dev);
if (!(status & 0x80))
return IRQ_HANDLED;
}
/* Stop DMA and if there are no errors, send frame to upper layer */
omap_stop_dma(si->rx_dma_channel);
status = omap_readb(UART3_SFLSR); /* Take a frame status */
if (status != 0) { /* Bad frame? */
si->stats.rx_frame_errors++;
omap_readb(UART3_RESUME);
} else {
/* We got a frame! */
skb = alloc_skb(4096, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "omap_sir: out of memory for RX SKB\n");
return IRQ_HANDLED;
}
/*
* Align any IP headers that may be contained
* within the frame.
*/
skb_reserve(skb, 1);
w = OMAP_DMA_CDAC_REG(si->rx_dma_channel);
w -= OMAP1_DMA_CDSA_L_REG(si->rx_dma_channel);
if (si->speed != 4000000) {
memcpy(skb_put(skb, w - 2), si->rx_buf_dma_virt, w - 2); /* Copy DMA buffer to skb */
} else {
memcpy(skb_put(skb, w - 4), si->rx_buf_dma_virt, w - 4); /* Copy DMA buffer to skb */
}
skb->dev = dev;
skb->mac.raw = skb->data;
skb->protocol = htons(ETH_P_IRDA);
si->stats.rx_packets++;
si->stats.rx_bytes += skb->len;
netif_receive_skb(skb); /* Send data to upper level */
}
/* Re-init RX DMA */
omap1610_irda_start_rx_dma(si);
dev->last_rx = jiffies;
__ECHO_OUT;
return IRQ_HANDLED;
}
static int omap1610_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct omap1610_irda *si = dev->priv;
int speed = irda_get_next_speed(skb);
int mtt = irda_get_mtt(skb);
int xbofs = irda_get_next_xbofs(skb);
__ECHO_IN;
/*
* Does this packet contain a request to change the interface
* speed? If so, remember it until we complete the transmission
* of this frame.
*/
if (speed != si->speed && speed != -1)
si->newspeed = speed;
if (xbofs) {
/* Set number of addtional BOFS */
omap_writeb(xbofs + 1, UART3_EBLR);
}
/*
* If this is an empty frame, we can bypass a lot.
*/
if (skb->len == 0) {
if (si->newspeed) {
si->newspeed = 0;
omap1610_irda_set_speed(dev, speed);
}
dev_kfree_skb(skb);
return 0;
}
netif_stop_queue(dev);
/* Copy skb data to DMA buffer */
memcpy(si->tx_buf_dma_virt, skb->data, skb->len);
si->stats.tx_bytes += skb->len;
/* Set frame length */
omap_writeb((skb->len & 0xff), UART3_TXFLL);
omap_writeb((skb->len >> 8), UART3_TXFLH);
if (mtt > 1000)
mdelay(mtt / 1000);
else
udelay(mtt);
/* Start TX DMA transfer */
omap1610_start_tx_dma(si, skb->len);
/* We can free skb now because it's already in DMA buffer */
dev_kfree_skb(skb);
dev->trans_start = jiffies;
__ECHO_OUT;
return 0;
}
static int
omap1610_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd)
{
struct if_irda_req *rq = (struct if_irda_req *)ifreq;
struct omap1610_irda *si = dev->priv;
int ret = -EOPNOTSUPP;
__ECHO_IN;
switch (cmd) {
case SIOCSBANDWIDTH:
if (capable(CAP_NET_ADMIN)) {
/*
* We are unable to set the speed if the
* device is not running.
*/
if (si->open) {
ret =
omap1610_irda_set_speed(dev,
rq->ifr_baudrate);
} else {
printk
(KERN_ERR
"omap_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n");
ret = 0;
}
}
break;
case SIOCSMEDIABUSY:
ret = -EPERM;
if (capable(CAP_NET_ADMIN)) {
irda_device_set_media_busy(dev, TRUE);
ret = 0;
}
break;
case SIOCGRECEIVING:
rq->ifr_receiving = rx_state;
break;
default:
break;
}
__ECHO_OUT;
return ret;
}
static struct net_device_stats *omap1610_irda_stats(struct net_device *dev)
{
struct omap1610_irda *si = dev->priv;
return &si->stats;
}
static int omap1610_irda_start(struct net_device *dev)
{
struct omap1610_irda *si = dev->priv;
int err;
unsigned long flags = 0;
#ifdef CONFIG_MACH_OMAP_H3
u8 ioExpanderVal = 0;
#endif
__ECHO_IN;
si->speed = 9600;
err = request_irq(dev->irq, omap1610_irda_irq, 0, dev->name, dev);
if (err)
goto err_irq;
/*
* The interrupt must remain disabled for now.
*/
disable_irq(dev->irq);
/* Request DMA channels for IrDA hardware */
if (omap_request_dma(OMAP_DMA_UART3_RX, "IrDA Rx DMA",
(void *)omap1610_irda_rx_dma_callback,
dev, &(si->rx_dma_channel))) {
printk(KERN_ERR "Failed to request IrDA Rx DMA \n");
goto err_irq;
}
if (omap_request_dma(OMAP_DMA_UART3_TX, "IrDA Tx DMA",
(void *)omap1610_irda_tx_dma_callback,
dev, &(si->tx_dma_channel))) {
printk(KERN_ERR "Failed to request IrDA Tx DMA \n");
goto err_irq;
}
/* Allocate TX and RX buffers for DMA channels */
si->rx_buf_dma_virt =
dma_alloc_coherent(NULL, 4096, &(si->rx_buf_dma_phys), flags);
si->tx_buf_dma_virt =
dma_alloc_coherent(NULL, 4096, &(si->tx_buf_dma_phys), flags);
/*
* Setup the serial port for the specified config.
*/
#ifdef CONFIG_MACH_OMAP_H3
if ((err = read_gpio_expa(&ioExpanderVal, 0x26))) {
printk(KERN_ERR "Error reading from I/O EXPANDER \n");
return err;
}
ioExpanderVal |= 0x40; /* 'P6' Enable IRDA_TX and IRDA_RX */
if ((err = write_gpio_expa(ioExpanderVal, 0x26))) {
printk(KERN_ERR "Error writing to I/O EXPANDER \n");
return err;
}
#endif
err = omap1610_irda_startup(dev);
if (err)
goto err_startup;
omap1610_irda_set_speed(dev, si->speed = 9600);
/*
* Open a new IrLAP layer instance.
*/
si->irlap = irlap_open(dev, &si->qos, "omap_sir");
err = -ENOMEM;
if (!si->irlap)
goto err_irlap;
/* Now enable the interrupt and start the queue */
si->open = 1;
/* Start RX DMA */
omap1610_irda_start_rx_dma(si);
enable_irq(dev->irq);
netif_start_queue(dev);
__ECHO_OUT;
return 0;
err_irlap:
si->open = 0;
omap1610_irda_shutdown(si);
err_startup:
err_irq:
free_irq(dev->irq, dev);
return err;
}
static int omap1610_irda_stop(struct net_device *dev)
{
struct omap1610_irda *si = dev->priv;
__ECHO_IN;
disable_irq(dev->irq);
netif_stop_queue(dev);
omap_free_dma(si->rx_dma_channel);
omap_free_dma(si->tx_dma_channel);
dma_free_coherent(NULL, 4096, si->rx_buf_dma_virt, si->rx_buf_dma_phys);
dma_free_coherent(NULL, 4096, si->tx_buf_dma_virt, si->tx_buf_dma_phys);
omap1610_irda_shutdown(si);
/* Stop IrLAP */
if (si->irlap) {
irlap_close(si->irlap);
si->irlap = NULL;
}
si->open = 0;
/*
* Free resources
*/
free_irq(dev->irq, dev);
__ECHO_OUT;
return 0;
}
#ifdef CONFIG_MACH_OMAP_H3
static void set_h3_gpio_expa(u8 FIR_SEL, u8 IrDA_INVSEL)
{
u8 ioExpanderVal = 0;
if (read_gpio_expa(&ioExpanderVal, 0x27) != 0) {
printk(KERN_ERR "Error reading from I/O EXPANDER \n");
return;
}
ioExpanderVal &= ~0x03;
ioExpanderVal |= FIR_SEL << 1;
ioExpanderVal |= IrDA_INVSEL << 0;
if (write_gpio_expa(ioExpanderVal, 0x27) != 0) {
printk(KERN_ERR "Error writing to I/O EXPANDER \n");
return;
}
if (read_gpio_expa(&ioExpanderVal, 0x27) != 0) {
printk(KERN_ERR "Error reading from I/O EXPANDER \n");
return;
}
}
int which_speed;
static void set_h3_gpio_expa_handler(void *data)
{
int *mode = data;
if (*mode == SIR_MODE)
set_h3_gpio_expa(0, 1);
else if (*mode == MIR_MODE)
set_h3_gpio_expa(1, 1);
else if (*mode == FIR_MODE)
set_h3_gpio_expa(1, 1);
}
DECLARE_WORK(set_h3_gpio_expa_work, &set_h3_gpio_expa_handler, &which_speed);
static inline void set_h3_irda_mode(int mode)
{
cancel_delayed_work(&set_h3_gpio_expa_work);
which_speed = mode;
schedule_work(&set_h3_gpio_expa_work);
}
#else
#define set_h3_irda_mode(x)
#endif
static int omap1610_irda_set_speed(struct net_device *dev, int speed)
{
struct omap1610_irda *si = dev->priv;
int divisor;
__ECHO_IN;
/* Set IrDA speed */
if (speed <= 115200) {
/* SIR mode */
if (machine_is_omap_h2()) {
omap_set_gpio_dataout(OMAP1610_H2_FIRSEL_GPIO, 0);
}
if (machine_is_omap_h3())
set_h3_irda_mode(SIR_MODE);
printk("Set SIR Mode! Speed: %d\n", speed);
omap_writeb(1, UART3_MDR1); /* Set SIR mode */
omap_writeb(1, UART3_EBLR);
divisor = 48000000 / (16 * speed); /* Base clock 48 MHz */
HDBG2(1);
omap_writeb(1 << 7, UART3_LCR);
omap_writeb((divisor & 0xFF), UART3_DLL);
omap_writeb((divisor >> 8), UART3_DLH);
omap_writeb(0x03, UART3_LCR);
omap_writeb(0, UART3_MCR);
HDBG2(1);
} else if (speed <= 1152000) {
/* MIR mode */
printk("Set MIR Mode! Speed: %d\n", speed);
omap_writeb((1 << 2) | (1 << 6), UART3_MDR1); /* Set MIR mode with
SIP after each frame */
omap_writeb(2, UART3_EBLR);
divisor = 48000000 / (41 * speed); /* Base clock 48 MHz */
omap_writeb(1 << 7, UART3_LCR);
omap_writeb((divisor & 0xFF), UART3_DLL);
omap_writeb((divisor >> 8), UART3_DLH);
omap_writeb(0x03, UART3_LCR);
if (machine_is_omap_h2())
omap_set_gpio_dataout(OMAP1610_H2_FIRSEL_GPIO, 1);
if (machine_is_omap_h3())
set_h3_irda_mode(MIR_MODE);
} else {
/* FIR mode */
printk("Set FIR Mode! Speed: %d\n", speed);
omap_writeb((1 << 2) | (1 << 6) | 1, UART3_MDR1); /* Set FIR mode
with SIP after each frame */
if (machine_is_omap_h2())
omap_set_gpio_dataout(OMAP1610_H2_FIRSEL_GPIO, 1);
if (machine_is_omap_h3())
set_h3_irda_mode(FIR_MODE);
}
si->speed = speed;
__ECHO_OUT;
return 0;
}
#ifdef CONFIG_PM
/*
* Suspend the IrDA interface.
*/
static int omap1610_irda_suspend(struct platform_device *pdev, pm_message_t state)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct omap1610_irda *si = dev->priv;
if (!dev)
return 0;
if (si->open) {
/*
* Stop the transmit queue
*/
netif_device_detach(dev);
disable_irq(dev->irq);
omap1610_irda_shutdown(si);
}
return 0;
}
/*
* Resume the IrDA interface.
*/
static int omap1610_irda_resume(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
struct omap1610_irda *si= dev->priv;
if (!dev)
return 0;
if (si->open) {
/*
* If we missed a speed change, initialise at the new speed
* directly. It is debatable whether this is actually
* required, but in the interests of continuing from where
* we left off it is desireable. The converse argument is
* that we should re-negotiate at 9600 baud again.
*/
if (si->newspeed) {
si->speed = si->newspeed;
si->newspeed = 0;
}
omap1610_irda_startup(dev);
omap1610_irda_set_speed(dev, si->speed);
enable_irq(dev->irq);
/*
* This automatically wakes up the queue
*/
netif_device_attach(dev);
}
return 0;
}
#else
#define omap1610_irda_suspend NULL
#define omap1610_irda_resume NULL
#endif
static int omap1610_irda_probe(struct platform_device *pdev)
{
struct net_device *dev;
struct omap1610_irda *si;
unsigned int baudrate_mask;
int err = 0;
dev = alloc_irdadev(sizeof(struct omap1610_irda));
if (!dev)
goto err_mem_1;
si = dev->priv;
si->dev = &pdev->dev;
dev->hard_start_xmit = omap1610_irda_hard_xmit;
dev->open = omap1610_irda_start;
dev->stop = omap1610_irda_stop;
dev->do_ioctl = omap1610_irda_ioctl;
dev->get_stats = omap1610_irda_stats;
dev->irq = INT_UART3;
irda_init_max_qos_capabilies(&si->qos);
/*
* OMAP1610 supports SIR, MIR, FIR modes,
* but actualy supported modes depend on hardware implementation.
* OMAP1610 Innovator supports only SIR and
* OMAP1610 H2 supports both SIR and FIR
*/
baudrate_mask =
IR_9600 | IR_19200 | IR_38400 | IR_57600 | IR_115200 | IR_576000 |
IR_1152000;
if (machine_is_omap_h2() || machine_is_omap_h3()) {
baudrate_mask |= (IR_4000000 << 8);
}
si->qos.baud_rate.bits &= baudrate_mask;
si->qos.min_turn_time.bits = 7;
irda_qos_bits_to_value(&si->qos);
err = register_netdev(dev);
if (!err)
platform_set_drvdata(pdev, dev);
else
free_netdev(dev);
err_mem_1:
return err;
}
static int omap1610_irda_remove(struct platform_device *pdev)
{
struct net_device *dev = platform_get_drvdata(pdev);
#ifdef CONFIG_MACH_OMAP_H3
if (machine_is_omap_h3())
cancel_delayed_work(&set_h3_gpio_expa_work);
#endif
if (pdev) {
unregister_netdev(dev);
free_netdev(dev);
}
return 0;
}
static struct platform_driver omap1610ir_driver = {
.probe = omap1610_irda_probe,
.remove = omap1610_irda_remove,
.suspend = omap1610_irda_suspend,
.resume = omap1610_irda_resume,
.driver = {
.name = "omap1610-ir",
},
};
static char __initdata banner[] = "OMAP1610 IrDA driver\n";
static int __init omap1610_irda_init(void)
{
printk(banner);
return platform_driver_register(&omap1610ir_driver);
}
static void __exit omap1610_irda_exit(void)
{
platform_driver_unregister(&omap1610ir_driver);
}
module_init(omap1610_irda_init);
module_exit(omap1610_irda_exit);
MODULE_AUTHOR("MontaVista");
MODULE_DESCRIPTION("OMAP IrDA Driver");
MODULE_LICENSE("GPL");
/*
* linux/include/asm-arm/arch-omap/irda.h
*
* Copyright (C) 2005 Komal Shah <komal_shah802003@yahoo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ASMARM_ARCH_IRDA_H
#define ASMARM_ARCH_IRDA_H
/* board specific transceiver capabilities */
#define IR_SEL 1 /* Selects IrDA */
#define IR_SIRMODE 2
#define IR_FIRMODE 4
#define IR_MIRMODE 8
struct omap_irda_config {
int transceiver_cap;
int (*transceiver_mode)(struct device *dev, int mode);
int (*select_irda)(struct device *dev, int state);
/* Very specific to the needs of some platforms (h3,h4)
* having calls which can sleep in irda_set_speed.
*/
struct work_struct gpio_expa;
};
#endif
......@@ -242,6 +242,7 @@
#define INT_24XX_GPIO_BANK2 30
#define INT_24XX_GPIO_BANK3 31
#define INT_24XX_GPIO_BANK4 32
#define INT_24XX_UART3_IRQ 74
/* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730) and
* 16 MPUIO lines */
......
......@@ -410,6 +410,11 @@ enum omap24xx_index {
/* 24xx GPIO */
Y20_24XX_GPIO60,
M15_24XX_GPIO92,
/* UART3 */
K15_24XX_UART3_TX,
K14_24XX_UART3_RX,
};
#ifdef CONFIG_OMAP_MUX
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment