/*
* Blackfin Infra-red Driver
*
* Copyright 2006-2009 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*
*/
#include "bfin_sir.h"
#ifdef CONFIG_SIR_BFIN_DMA
#define DMA_SIR_RX_XCNT 10
#define DMA_SIR_RX_YCNT (PAGE_SIZE / DMA_SIR_RX_XCNT)
#define DMA_SIR_RX_FLUSH_JIFS (HZ * 4 / 250)
#endif
#if ANOMALY_05000447
static int max_rate = 57600;
#else
static int max_rate = 115200;
#endif
static void turnaround_delay(int mtt)
{
long ticks;
mtt = mtt < 10000 ? 10000 : mtt;
ticks = 1 + mtt / (USEC_PER_SEC / HZ);
schedule_timeout_uninterruptible(ticks);
}
static void bfin_sir_init_ports(struct bfin_sir_port *sp, struct platform_device *pdev)
{
int i;
struct resource *res;
for (i = 0; i < pdev->num_resources; i++) {
res = &pdev->resource[i];
switch (res->flags) {
case IORESOURCE_MEM:
sp->membase = (void __iomem *)res->start;
break;
case IORESOURCE_IRQ:
sp->irq = res->start;
break;
case IORESOURCE_DMA:
sp->rx_dma_channel = res->start;
sp->tx_dma_channel = res->end;
break;
default:
break;
}
}
sp->clk = get_sclk();
#ifdef CONFIG_SIR_BFIN_DMA
sp->tx_done = 1;
init_timer(&(sp->rx_dma_timer));
#endif
}
static void bfin_sir_stop_tx(struct bfin_sir_port *port)
{
#ifdef CONFIG_SIR_BFIN_DMA
disable_dma(port->tx_dma_channel);
#endif
while (!(UART_GET_LSR(port) & THRE)) {
cpu_relax();
continue;
}
UART_CLEAR_IER(port, ETBEI);
}
static void bfin_sir_enable_tx(struct bfin_sir_port *port)
{
UART_SET_IER(port, ETBEI);
}
static void bfin_sir_stop_rx(struct bfin_sir_port *port)
{
UART_CLEAR_IER(port, ERBFI);
}
static void bfin_sir_enable_rx(struct bfin_sir_port *port)
{
UART_SET_IER(port, ERBFI);
}
static int bfin_sir_set_speed(struct bfin_sir_port *port, int speed)
{
int ret = -EINVAL;
unsigned int quot;
unsigned short val, lsr, lcr;
static int utime;
int count = 10;
lcr = WLS(8);
switch (speed) {
case 9600:
case 19200:
case 38400:
case 57600:
case 115200:
/*
* IRDA is not affected by anomaly 05000230, so there is no
* need to tweak the divisor like he UART driver (which will
* slightly speed up the baud rate on us).
*/
quot = (port->clk + (8 * speed)) / (16 * speed);
do {
udelay(utime);
lsr = UART_GET_LSR(port);
} while (!(lsr & TEMT) && count--);
/* The useconds for 1 bits to transmit */
utime = 1000000 / speed + 1;
/* Clear UCEN bit to reset the UART state machine
* and control registers
*/
val = UART_GET_GCTL(port);
val &= ~UCEN;
UART_PUT_GCTL(port, val);
/* Set DLAB in LCR to Access THR RBR IER */
UART_SET_DLAB(port);
SSYNC();
UART_PUT_DLL(port, quot & 0xFF);
UART_PUT_DLH(port, (quot >> 8) & 0xFF);
SSYNC();
/* Clear DLAB in LCR */
UART_CLEAR_DLAB(port);
SSYNC();
UART_PUT_LCR(port, lcr);
val = UART_GET_GCTL(port);
val |= UCEN;
UART_PUT_GCTL(port, val);
ret = 0;
break;
default:
printk(KERN_WARNING "bfin_sir: Invalid speed %d\n", speed);
break;
}
val = UART_GET_GCTL(port);
/* If not add the 'RPOLC', we can't catch the receive interrupt.
* It's related with the HW layout and the IR transiver.
*/
val |= UMOD_IRDA | RPOLC;
UART_PUT_GCTL(port, val);
return ret;
}
static int bfin_sir_is_receiving(struct net_device *dev)
{
struct bfin_sir_self *self = netdev_priv(dev);
struct bfin_sir_port *port = self->sir_port;
if (!(UART_GET_IER(port) & ERBFI))
return 0;
return self->rx_buff.state != OUTSIDE_FRAME;
}
#ifdef CONFIG_SIR_BFIN_PIO
static void bfin_sir_tx_chars(struct net_device *dev)
{
unsigned int chr;
struct bfin_sir_self *self = netdev_priv(dev);
struct bfin_sir_port *port = self->sir_port;
if (self->tx_buff.len != 0) {
chr = *(self->tx_buff.data);
UART_PUT_CHAR(port, chr);
self->tx_buff.data++;
self->tx_buff.len--;
} else {
self->stats.tx_packets++;
self->stats.tx_bytes += self->tx_buff.data - self->tx_buff.head;
if (self->newspeed) {
bfin_sir_set_speed(port, self->newspeed);
self->speed = self->newspeed;
self->newspeed = 0;
}