diff options
-rw-r--r-- | drivers/bluetooth/cg2900_uart.c | 174 |
1 files changed, 94 insertions, 80 deletions
diff --git a/drivers/bluetooth/cg2900_uart.c b/drivers/bluetooth/cg2900_uart.c index 2695711c505..f9943e775b2 100644 --- a/drivers/bluetooth/cg2900_uart.c +++ b/drivers/bluetooth/cg2900_uart.c @@ -31,6 +31,7 @@ #include <linux/tty.h> #include <linux/tty_ldisc.h> #include <linux/types.h> +#include <linux/wakelock.h> #include <linux/workqueue.h> #include <linux/mfd/cg2900.h> #include <net/bluetooth/bluetooth.h> @@ -273,15 +274,16 @@ struct uart_delayed_work_struct{ * @sleep_work: Delayed sleep work struct. * @wakeup_work: Wake-up work struct. * @sleep_state_lock: Used to protect chip state. - * @sleep_allowed: Indicates if tty has functions needed for sleep mode. - * @tx_in_progress: Indicates data sending in progress. - * @rx_in_progress: Indicates data receiving in progress. + * @sleep_allowed: Indicate if tty has functions needed for sleep mode. + * @transfer_counter: Variable to keep track of ongoing transfers. * @regulator: Regulator. * @regulator_enabled: True if regulator is enabled. * @dev: Pointer to CG2900 uart device. * @chip_dev: Chip device for current UART transport. * @cts_irq: CTS interrupt for this UART. * @cts_gpio: CTS GPIO for this UART. + * @wake_lock: Wake lock for keeping user space awake (for Android). + * @suspend_blocked: True if suspend operation is blocked in the framework. */ struct uart_info { enum uart_rx_state rx_state; @@ -299,14 +301,15 @@ struct uart_info { struct uart_work_struct wakeup_work; struct mutex sleep_state_lock; bool sleep_allowed; - bool tx_in_progress; - bool rx_in_progress; + atomic_t transfer_counter; struct regulator *regulator; bool regulator_enabled; struct device *dev; struct cg2900_chip_dev chip_dev; int cts_irq; int cts_gpio; + struct wake_lock wake_lock; + bool suspend_blocked; }; /* Module parameters */ @@ -419,6 +422,10 @@ static irqreturn_t cts_interrupt(int irq, void *dev_id) disable_irq_wake(irq); #endif disable_irq_nosync(irq); + if (!uart_info->suspend_blocked) { + wake_lock(&uart_info->wake_lock); + uart_info->suspend_blocked = true; + } /* Create work and leave IRQ context. */ (void)create_work_item(uart_info, handle_cts_irq); @@ -567,6 +574,11 @@ static void wake_up_chip(struct uart_info *uart_info) if (CHIP_POWERED_DOWN == uart_info->sleep_state) goto finished; + if (!uart_info->suspend_blocked) { + wake_lock(&uart_info->wake_lock); + uart_info->suspend_blocked = true; + } + /* * This function indicates data is transmitted. * Therefore see to that the chip is awake. @@ -632,12 +644,6 @@ static void set_chip_sleep_mode(struct work_struct *work) switch (uart_info->sleep_state) { case CHIP_FALLING_ASLEEP: - if (uart_info->tx_in_progress) { - dev_dbg(MAIN_DEV, "tx is still in progress, " - "not going to sleep\n"); - break; - } - if (!is_chip_flow_off(uart_info)) { dev_dbg(MAIN_DEV, "Chip flow is on, it's not ready to" "sleep yet\n"); @@ -674,13 +680,12 @@ static void set_chip_sleep_mode(struct work_struct *work) dev_dbg(MAIN_DEV, "New sleep_state: CHIP_ASLEEP\n"); uart_info->sleep_state = CHIP_ASLEEP; + if (uart_info->suspend_blocked) { + wake_unlock(&uart_info->wake_lock); + uart_info->suspend_blocked = false; + } break; case CHIP_AWAKE: - if (uart_info->tx_in_progress) { - dev_dbg(MAIN_DEV, "tx is still in progress, " - "not going to sleep\n"); - break; - } dev_dbg(MAIN_DEV, "sleep_timer_expired: Set break\n"); hci_uart_set_break(uart_info->hu, BREAK_ON); @@ -1031,19 +1036,29 @@ static void work_do_transmit(struct work_struct *work) struct uart_work_struct *current_work = container_of(work, struct uart_work_struct, work); struct uart_info *uart_info = (struct uart_info *)current_work->data; + unsigned long timeout_jiffies = get_sleep_timeout(uart_info); kfree(current_work); /* Mark that there is an ongoing transfer. */ - uart_info->tx_in_progress = true; + atomic_inc(&(uart_info->transfer_counter)); /* Cancel pending sleep work if there is any. */ - cancel_delayed_work_sync(&uart_info->sleep_work.work); + cancel_delayed_work(&uart_info->sleep_work.work); /* Wake up the chip and transport. */ wake_up_chip(uart_info); (void)hci_uart_tx_wakeup(uart_info->hu); + + /* + * If there are no ongoing transfers schedule the sleep work. + */ + if (atomic_dec_and_test(&uart_info->transfer_counter) && + timeout_jiffies) + queue_delayed_work(uart_info->wq, + &uart_info->sleep_work.work, + timeout_jiffies); } /** @@ -1287,73 +1302,78 @@ static void uart_set_chip_power(struct cg2900_chip_dev *dev, bool chip_on) dev_info(MAIN_DEV, "Set chip power: %s\n", (chip_on ? "ENABLE" : "DISABLE")); - cancel_delayed_work_sync(&uart_info->sleep_work.work); + /* Cancel any ongoing works.*/ + cancel_work_sync(&uart_info->wakeup_work.work); + + mutex_lock(&uart_info->sleep_state_lock); + if (!uart_info->hu) { dev_err(MAIN_DEV, "Hci uart struct is not allocated\n"); - return; + goto unlock; } if (chip_on) { + if (!uart_info->suspend_blocked) { + wake_lock(&uart_info->wake_lock); + uart_info->suspend_blocked = true; + } if (uart_info->sleep_state != CHIP_POWERED_DOWN) { dev_err(MAIN_DEV, "Chip is already powered up (%d)\n", uart_info->sleep_state); - return; + goto unlock; } if (cg2900_enable_regulator(uart_info)) - return; + goto unlock; if (pf_data->enable_chip) { pf_data->enable_chip(dev); dev_dbg(MAIN_DEV, "New sleep_state: CHIP_AWAKE\n"); uart_info->sleep_state = CHIP_AWAKE; } - goto finished; - } - /* Turn off the chip.*/ - switch (uart_info->sleep_state) { - case CHIP_AWAKE: - cancel_delayed_work(&uart_info->sleep_work.work); - break; - case CHIP_FALLING_ASLEEP: - cancel_delayed_work(&uart_info->sleep_work.work); - hci_uart_set_break(uart_info->hu, BREAK_OFF); - break; - case CHIP_SUSPENDED: - case CHIP_ASLEEP: - unset_cts_irq(uart_info); - hci_uart_flow_ctrl(uart_info->hu, FLOW_ON); - hci_uart_set_break(uart_info->hu, BREAK_OFF); - break; - default: - break; - } + (void)hci_uart_set_baudrate(uart_info->hu, uart_baudrate); - /* Cancel any ongoing works since chip is shutting down */ - cancel_work_sync(&uart_info->wakeup_work.work); + } else { + /* Turn off the chip.*/ + switch (uart_info->sleep_state) { + case CHIP_AWAKE: + cancel_delayed_work(&uart_info->sleep_work.work); + break; + case CHIP_FALLING_ASLEEP: + cancel_delayed_work(&uart_info->sleep_work.work); + hci_uart_set_break(uart_info->hu, BREAK_OFF); + break; + case CHIP_SUSPENDED: + case CHIP_ASLEEP: + unset_cts_irq(uart_info); + hci_uart_flow_ctrl(uart_info->hu, FLOW_ON); + hci_uart_set_break(uart_info->hu, BREAK_OFF); + break; + default: + break; + } - if (pf_data->disable_chip) { - pf_data->disable_chip(dev); - dev_dbg(MAIN_DEV, - "New sleep_state: CHIP_POWERED_DOWN\n"); - uart_info->sleep_state = CHIP_POWERED_DOWN; + if (uart_info->suspend_blocked) { + wake_unlock(&uart_info->wake_lock); + uart_info->suspend_blocked = false; + } + if (pf_data->disable_chip) { + pf_data->disable_chip(dev); + dev_dbg(MAIN_DEV, + "New sleep_state: CHIP_POWERED_DOWN\n"); + uart_info->sleep_state = CHIP_POWERED_DOWN; + } + cg2900_disable_regulator(uart_info); + /* + * Setting baud rate to 0 will tell UART driver to shut off its + * clocks. + */ + (void)hci_uart_set_baudrate(uart_info->hu, ZERO_BAUD_RATE); } - cg2900_disable_regulator(uart_info); - /* - * Setting baud rate to 0 will tell UART driver to shut off its - * clocks. - */ - uart_baudrate = ZERO_BAUD_RATE; - -finished: - /* - * Now we have to set the digital baseband UART - * to default baudrate if chip is ON or to zero baudrate if - * chip is turning OFF. - */ - (void)hci_uart_set_baudrate(uart_info->hu, uart_baudrate); +unlock: + mutex_unlock(&(uart_info->sleep_state_lock)); } /** @@ -1547,7 +1567,7 @@ static int cg2900_hu_receive(struct hci_uart *hu, r_ptr = (const u8 *)data; /* Mark that there is an ongoing transfer. */ - uart_info->rx_in_progress = true; + atomic_inc(&(uart_info->transfer_counter)); /* Cancel pending sleep work if there is any. */ cancel_delayed_work(&uart_info->sleep_work.work); @@ -1657,7 +1677,6 @@ check_h4_header: "Can't allocate memory for new packet\n"); uart_info->rx_state = W4_PACKET_TYPE; uart_info->rx_count = 0; - uart_info->rx_in_progress = false; return 0; } @@ -1673,12 +1692,11 @@ check_h4_header: /* Schedule a work to wake-up the chip */ (void)queue_work(uart_info->wq, &uart_info->wakeup_work.work); - uart_info->rx_in_progress = false; - /* * If there are no ongoing transfers schedule the sleep work. */ - if (!(uart_info->tx_in_progress) && timeout_jiffies) + if (atomic_dec_and_test(&uart_info->transfer_counter) && + timeout_jiffies) queue_delayed_work(uart_info->wq, &uart_info->sleep_work.work, timeout_jiffies); @@ -1770,10 +1788,9 @@ static struct sk_buff *cg2900_hu_dequeue(struct hci_uart *hu) { struct sk_buff *skb; struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev); - unsigned long timeout_jiffies = get_sleep_timeout(uart_info); skb = skb_dequeue(&uart_info->tx_queue); - if (BAUD_SENDING == uart_info->baud_rate_state && !skb) + if (BAUD_SENDING == uart_info->baud_rate_state) finish_setting_baud_rate(hu); /* * If it's set baud rate cmd set correct baud state and after @@ -1790,16 +1807,6 @@ static struct sk_buff *cg2900_hu_dequeue(struct hci_uart *hu) print_hex_dump_bytes(NAME " TX:\t", DUMP_PREFIX_NONE, skb->data, skb->len); - if (!skb) - uart_info->tx_in_progress = false; - /* - * If there are no ongoing transfers schedule the sleep work. - */ - if (!(uart_info->rx_in_progress) && timeout_jiffies && !skb) - queue_delayed_work(uart_info->wq, - &uart_info->sleep_work.work, - timeout_jiffies); - return skb; } @@ -1856,6 +1863,8 @@ static int __devinit cg2900_uart_probe(struct platform_device *pdev) uart_info->chip_dev.pdev = pdev; uart_info->chip_dev.dev = &pdev->dev; uart_info->chip_dev.t_data = uart_info; + wake_lock_init(&uart_info->wake_lock, WAKE_LOCK_SUSPEND, NAME); + uart_info->suspend_blocked = false; resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!resource) { @@ -1875,6 +1884,10 @@ static int __devinit cg2900_uart_probe(struct platform_device *pdev) goto error_handling_free; } + /* Initialize the atom variable to 0 (means no ongoing + * transmit/receive). */ + atomic_set(&(uart_info->transfer_counter), 0); + /* Initialize sleep work data */ uart_info->sleep_work.data = uart_info; INIT_DELAYED_WORK(&uart_info->sleep_work.work, set_chip_sleep_mode); @@ -1940,6 +1953,7 @@ static int __devexit cg2900_uart_remove(struct platform_device *pdev) if (uart_info->hu) hci_uart_unregister_proto(uart_info->hu->proto); + wake_lock_destroy(&uart_info->wake_lock); destroy_workqueue(uart_info->wq); dev_info(MAIN_DEV, "CG2900 UART removed\n"); |