blob: 0fb57de4b75034fa3d2606822a779e12277b3a53 [file] [log] [blame]
/*
* This file is part of Nokia H4P bluetooth driver
*
* Copyright (C) 2005, 2006 Nokia Corporation.
*
* 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.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/serial_reg.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/io.h>
#include "hci_h4p.h"
inline void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val)
{
__raw_writeb(val, info->uart_base + (offset << 2));
}
inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
{
return __raw_readb(info->uart_base + (offset << 2));
}
void hci_h4p_set_rts(struct hci_h4p_info *info, int active)
{
u8 b;
b = hci_h4p_inb(info, UART_MCR);
if (active)
b |= UART_MCR_RTS;
else
b &= ~UART_MCR_RTS;
hci_h4p_outb(info, UART_MCR, b);
}
int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active,
int timeout_ms)
{
unsigned long timeout;
int state;
timeout = jiffies + msecs_to_jiffies(timeout_ms);
for (;;) {
state = hci_h4p_inb(info, UART_MSR) & UART_MSR_CTS;
if (active) {
if (state)
return 0;
} else {
if (!state)
return 0;
}
if (time_after(jiffies, timeout))
return -ETIMEDOUT;
msleep(1);
}
}
void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
{
u8 lcr, b;
lcr = hci_h4p_inb(info, UART_LCR);
hci_h4p_outb(info, UART_LCR, 0xbf);
b = hci_h4p_inb(info, UART_EFR);
if (on)
b |= which;
else
b &= ~which;
hci_h4p_outb(info, UART_EFR, b);
hci_h4p_outb(info, UART_LCR, lcr);
}
void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
{
unsigned long flags;
spin_lock_irqsave(&info->lock, flags);
__hci_h4p_set_auto_ctsrts(info, on, which);
spin_unlock_irqrestore(&info->lock, flags);
}
void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed)
{
unsigned int divisor;
u8 lcr, mdr1;
BT_DBG("Setting speed %lu", speed);
if (speed >= 460800) {
divisor = UART_CLOCK / 13 / speed;
mdr1 = 3;
} else {
divisor = UART_CLOCK / 16 / speed;
mdr1 = 0;
}
/* Make sure UART mode is disabled */
hci_h4p_outb(info, UART_OMAP_MDR1, 7);
lcr = hci_h4p_inb(info, UART_LCR);
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB); /* Set DLAB */
hci_h4p_outb(info, UART_DLL, divisor & 0xff); /* Set speed */
hci_h4p_outb(info, UART_DLM, divisor >> 8);
hci_h4p_outb(info, UART_LCR, lcr);
/* Make sure UART mode is enabled */
hci_h4p_outb(info, UART_OMAP_MDR1, mdr1);
}
int hci_h4p_reset_uart(struct hci_h4p_info *info)
{
int count = 0;
/* Reset the UART */
hci_h4p_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET);
while (!(hci_h4p_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) {
if (count++ > 100) {
dev_err(info->dev, "hci_h4p: UART reset timeout\n");
return -ENODEV;
}
udelay(1);
}
return 0;
}
void hci_h4p_store_regs(struct hci_h4p_info *info)
{
u16 lcr = 0;
lcr = hci_h4p_inb(info, UART_LCR);
hci_h4p_outb(info, UART_LCR, 0xBF);
info->dll = hci_h4p_inb(info, UART_DLL);
info->dlh = hci_h4p_inb(info, UART_DLM);
info->efr = hci_h4p_inb(info, UART_EFR);
hci_h4p_outb(info, UART_LCR, lcr);
info->mdr1 = hci_h4p_inb(info, UART_OMAP_MDR1);
info->ier = hci_h4p_inb(info, UART_IER);
}
void hci_h4p_restore_regs(struct hci_h4p_info *info)
{
u16 lcr = 0;
hci_h4p_init_uart(info);
hci_h4p_outb(info, UART_OMAP_MDR1, 7);
lcr = hci_h4p_inb(info, UART_LCR);
hci_h4p_outb(info, UART_LCR, 0xBF);
hci_h4p_outb(info, UART_DLL, info->dll); /* Set speed */
hci_h4p_outb(info, UART_DLM, info->dlh);
hci_h4p_outb(info, UART_EFR, info->efr);
hci_h4p_outb(info, UART_LCR, lcr);
hci_h4p_outb(info, UART_OMAP_MDR1, info->mdr1);
hci_h4p_outb(info, UART_IER, info->ier);
}
void hci_h4p_init_uart(struct hci_h4p_info *info)
{
u8 mcr, efr;
/* Enable and setup FIFO */
hci_h4p_outb(info, UART_OMAP_MDR1, 0x00);
hci_h4p_outb(info, UART_LCR, 0xbf);
efr = hci_h4p_inb(info, UART_EFR);
hci_h4p_outb(info, UART_EFR, UART_EFR_ECB);
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
mcr = hci_h4p_inb(info, UART_MCR);
hci_h4p_outb(info, UART_MCR, UART_MCR_TCRTLR);
hci_h4p_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
(3 << 6) | (0 << 4));
hci_h4p_outb(info, UART_LCR, 0xbf);
hci_h4p_outb(info, UART_TI752_TLR, 0xed);
hci_h4p_outb(info, UART_TI752_TCR, 0xef);
hci_h4p_outb(info, UART_EFR, efr);
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
hci_h4p_outb(info, UART_MCR, 0x00);
hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8);
hci_h4p_outb(info, UART_IER, UART_IER_RDI);
hci_h4p_outb(info, UART_OMAP_SYSC, (1 << 0) | (1 << 2) | (2 << 3));
}