AlanZhang

Modification on i.MX53 mxc_uart.c driver to support high speed comm to BCM4330 bluetooth -blog archive

Discussion created by AlanZhang Employee on May 27, 2012

In general application scenes of bluetooth in i.MX53 system, it seems that no exception occurs for most usecases. What I want to describe here is the usecase involving bluetooth module to wake up i.MX53 and then continue to perform the operation interrupted by the last time system suspend.

Usecase: Use bluetooth headset to listen mp3 playing back, in the middle of playing time, press "Power" key to force system entry suspend mode, then wait several seconds (about 2 ~ 3 seconds), bluetooth headset will try to recover the former communication channel, so the on-board BCM4330 will alert i.MX53 via "BT_HOST_WAKEUP" signal to wake i.MX53 from suspend, meanwhile BCM4330 will initiate HCI packets to i.MX53, if successful, BCM4330 will entry SNIFF mode for future communication. The communication failure occurs at the "resume" period time when BCM4330 initiates some HCI traffic to i.MX53 for SNIFF mode. From the log captured by ll_recv, some wrong bytes are captured in 3M baud rate per second. If lower the baud rate to 115200, the wakeup process is ok. The resolution steps are the following:

1. The UART port to BCM4330 works in DCE mode, that means CTS is drived by i.MX53, low active, high inactive;

2. However UART supports async. wake-up machanism, this machanism cannot be applied for debug port usage. Async. Wake-up is not appropriate (i.e. you cannot type in serial terminal under suspend status to activate system to resume);

3. The origina mxc_uart.c support CTS in hardware flow control mode, so the only condition to trigger CTS is the threshold of RxFIFO, if the threshold is meeted, CTS is always active;

4. Because CTS is the gating signal to allow BCM4330 send data out, so we need to explictly inactivate it in software when entrying suspend, then recover it to be hardware flow contorl in resume;

5. The concrete modification is the following:

static unsigned int stCR2;
static int mxcuart_suspend(struct platform_device *pdev, pm_message_t state)
{
    unsigned int crNum;
    uart_mxc_port *umxc = platform_get_drvdata(pdev);

    if (umxc == NULL)
        return 0;    /* skip disabled ports */

    if (umxc && umxc->port.flags & ASYNC_INITIALIZED)
        uart_suspend_port(&mxc_reg, &umxc->port);

    if (umxc && umxc->port.flags & ASYNC_SUSPENDED)
        umxc->port.state->port.tty->hw_stopped = 1;

    if (pdev->id == 2) {
        // UART #3 connects to BCM4330
        stCR2 = readl(umxc->port.membase + MXC_UARTUCR2);
        crNum = (stCR2 & (~ MXC_UARTUCR2_CTSC)) | MXC_UARTUCR2_CTS;
        writel(crNum, umxc->port.membase + MXC_UARTUCR2);
    }

    return 0;
}

/*!
 * This function is called to bring the UART back from a low power state. Refer
 * to the document driver-model/driver.txt in the kernel source tree for more
 * information.
 *
 * @param   pdev  the device structure used to give information on which UART
 *                to resume
 *
 * @return  The function returns 0 on success and -1 on failure
 */
static int mxcuart_resume(struct platform_device *pdev)
{
    uart_mxc_port *umxc = platform_get_drvdata(pdev);

    if (umxc == NULL)
        return 0;    /* skip disabled ports */

    if (umxc && umxc->port.flags & ASYNC_SUSPENDED) {
        umxc->port.state->port.tty->hw_stopped = 0;
        uart_resume_port(&mxc_reg, &umxc->port);
    }

    if (pdev->id == 2) {
        // UART #3 connects to BCM4330
        writel(stCR2, umxc->port.membase + MXC_UARTUCR2);
    }

    return 0;
}

Outcomes