/* * 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 #include #include #include #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)); }