2010-12-17 11:26:59

by Par-Gunnar HJALMDAHL

[permalink] [raw]
Subject: [PATCH 08/11] Bluetooth: Add support for CG2900 UART

This patch adds support for the ST-Ericsson CG2900
UART transport. The CG2900 is chip supporting GPS, Bluetooth,
and FM radio.
This file registers towards the N_HCI line discipline driver
and when a transport is opened, i.e. a UART is opened, it
registers as a transport towards the CG2900 framework.
This driver also handles baud rate changing and power control
towards the CG2900 chip.

Signed-off-by: Par-Gunnar Hjalmdahl <[email protected]>
---
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/cg2900_uart.c | 1849 +++++++++++++++++++++++++++++++++++++++
drivers/mfd/Kconfig | 9 +
3 files changed, 1859 insertions(+), 0 deletions(-)
create mode 100644 drivers/bluetooth/cg2900_uart.c

diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 71bdf13..5ec417d 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
+obj-$(CONFIG_MFD_CG2900_UART) += cg2900_uart.o

btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
diff --git a/drivers/bluetooth/cg2900_uart.c b/drivers/bluetooth/cg2900_uart.c
new file mode 100644
index 0000000..5e29d2d
--- /dev/null
+++ b/drivers/bluetooth/cg2900_uart.c
@@ -0,0 +1,1849 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Authors:
+ * Par-Gunnar Hjalmdahl ([email protected]) for ST-Ericsson.
+ * Henrik Possung ([email protected]) for ST-Ericsson.
+ * Josef Kindberg ([email protected]) for ST-Ericsson.
+ * Dariusz Szymszak ([email protected]) for ST-Ericsson.
+ * Kjell Andersson ([email protected]) for ST-Ericsson.
+ * Lukasz Rymanowski ([email protected]) for ST-Ericsson.
+ * License terms: GNU General Public License (GPL), version 2
+ *
+ * Linux Bluetooth UART Driver for ST-Ericsson CG2900 connectivity controller.
+ */
+#define NAME "cg2900_uart"
+#define pr_fmt(fmt) NAME ": " fmt "\n"
+
+#include <asm/byteorder.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/tty.h>
+#include <linux/tty_ldisc.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/cg2900.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+
+#include "hci_uart.h"
+
+#define MAIN_DEV (uart_info->dev)
+
+/* Workqueues' names */
+#define UART_WQ_NAME "cg2900_uart_wq"
+#define UART_NAME "cg2900_uart"
+
+/*
+ * A BT command complete event without any parameters is the defined size plus
+ * 1 byte extra for the status field which is always present in a
+ * command complete event.
+ */
+#define HCI_BT_CMD_COMPLETE_LEN (sizeof(struct hci_ev_cmd_complete) + 1)
+
+/* Timers used in milliseconds */
+#define UART_TX_TIMEOUT 100
+#define UART_RESP_TIMEOUT 1000
+
+/* Number of bytes to reserve at start of sk_buffer when receiving packet */
+#define RX_SKB_RESERVE 8
+/* Max size of received packet (not including reserved bytes) */
+#define RX_SKB_MAX_SIZE 1024
+
+/* Size of the header in the different packets */
+#define HCI_BT_EVT_HDR_SIZE 2
+#define HCI_BT_ACL_HDR_SIZE 4
+#define HCI_FM_RADIO_HDR_SIZE 1
+#define HCI_GNSS_HDR_SIZE 3
+
+/* Position of length field in the different packets */
+#define HCI_EVT_LEN_POS 2
+#define HCI_ACL_LEN_POS 3
+#define FM_RADIO_LEN_POS 1
+#define GNSS_LEN_POS 2
+
+/* Baud rate defines */
+#define ZERO_BAUD_RATE 0
+#define DEFAULT_BAUD_RATE 115200
+#define HIGH_BAUD_RATE 3000000
+
+#define BT_SIZE_OF_HDR (sizeof(__le16) + sizeof(__u8))
+#define BT_PARAM_LEN(__pkt_len) (__pkt_len - BT_SIZE_OF_HDR)
+
+/* Standardized Bluetooth H:4 channels */
+#define HCI_BT_CMD_H4_CHANNEL 0x01
+#define HCI_BT_ACL_H4_CHANNEL 0x02
+#define HCI_BT_SCO_H4_CHANNEL 0x03
+#define HCI_BT_EVT_H4_CHANNEL 0x04
+
+#define BT_BDADDR_SIZE 6
+
+/* Reserve 1 byte for the HCI H:4 header */
+#define HCI_H4_SIZE 1
+#define CG2900_SKB_RESERVE HCI_H4_SIZE
+
+/* Default H4 channels which may change depending on connected controller */
+#define HCI_FM_RADIO_H4_CHANNEL 0x08
+#define HCI_GNSS_H4_CHANNEL 0x09
+
+/* Bluetooth error codes */
+#define HCI_BT_ERROR_NO_ERROR 0x00
+
+/* Bytes in the command Hci_Cmd_ST_Set_Uart_Baud_Rate */
+#define CG2900_BAUD_RATE_57600 0x03
+#define CG2900_BAUD_RATE_115200 0x02
+#define CG2900_BAUD_RATE_230400 0x01
+#define CG2900_BAUD_RATE_460800 0x00
+#define CG2900_BAUD_RATE_921600 0x20
+#define CG2900_BAUD_RATE_2000000 0x25
+#define CG2900_BAUD_RATE_3000000 0x27
+#define CG2900_BAUD_RATE_4000000 0x2B
+
+/* GNSS */
+struct gnss_hci_hdr {
+ __u8 op_code;
+ __le16 plen;
+} __attribute__((packed));
+
+/* FM legacy command packet */
+struct fm_leg_cmd {
+ __u8 length;
+ __u8 opcode;
+ __u8 read_write;
+ __u8 fm_function;
+ union { /* Payload varies with function */
+ __le16 irqmask;
+ struct fm_leg_fm_cmd {
+ __le16 head;
+ __le16 data[];
+ } fm_cmd;
+ };
+} __attribute__((packed));
+
+/* FM legacy command complete packet */
+struct fm_leg_cmd_cmpl {
+ __u8 param_length;
+ __u8 status;
+ __u8 opcode;
+ __u8 read_write;
+ __u8 cmd_status;
+ __u8 fm_function;
+ __le16 response_head;
+ __le16 data[];
+} __attribute__((packed));
+
+/* FM legacy interrupt packet, PG2 style */
+struct fm_leg_irq_v2 {
+ __u8 param_length;
+ __u8 status;
+ __u8 opcode;
+ __u8 event_type;
+ __u8 event_id;
+ __le16 irq;
+} __attribute__((packed));
+
+/* FM legacy interrupt packet, PG1 style */
+struct fm_leg_irq_v1 {
+ __u8 param_length;
+ __u8 opcode;
+ __u8 event_id;
+ __le16 irq;
+} __attribute__((packed));
+
+union fm_leg_evt_or_irq {
+ __u8 param_length;
+ struct fm_leg_cmd_cmpl evt;
+ struct fm_leg_irq_v2 irq_v2;
+ struct fm_leg_irq_v1 irq_v1;
+} __attribute__((packed));
+
+/* BT VS SetBaudRate command */
+#define CG2900_BT_OP_VS_SET_BAUD_RATE 0xFC09
+struct bt_vs_set_baud_rate_cmd {
+ __le16 opcode;
+ __u8 plen;
+ __u8 baud_rate;
+} __attribute__((packed));
+
+/**
+ * enum uart_rx_state - UART RX-state for UART.
+ * @W4_PACKET_TYPE: Waiting for packet type.
+ * @W4_EVENT_HDR: Waiting for BT event header.
+ * @W4_ACL_HDR: Waiting for BT ACL header.
+ * @W4_FM_RADIO_HDR: Waiting for FM header.
+ * @W4_GNSS_HDR: Waiting for GNSS header.
+ * @W4_DATA: Waiting for data in rest of the packet (after header).
+ */
+enum uart_rx_state {
+ W4_PACKET_TYPE,
+ W4_EVENT_HDR,
+ W4_ACL_HDR,
+ W4_FM_RADIO_HDR,
+ W4_GNSS_HDR,
+ W4_DATA
+};
+
+/**
+ * enum sleep_state - Sleep-state for UART.
+ * @CHIP_AWAKE: Chip is awake.
+ * @CHIP_FALLING_ASLEEP: Chip is falling asleep.
+ * @CHIP_ASLEEP: Chip is asleep.
+ * @CHIP_SUSPENDED: Chip in suspend state.
+ * @CHIP_RESUMING: Chip is going back from suspend state.
+ * @CHIP_POWERED_DOWN: Chip is off.
+ */
+enum sleep_state {
+ CHIP_AWAKE,
+ CHIP_FALLING_ASLEEP,
+ CHIP_ASLEEP,
+ CHIP_SUSPENDED,
+ CHIP_RESUMING,
+ CHIP_POWERED_DOWN
+};
+
+/**
+ * enum baud_rate_change_state - Baud rate-state for UART.
+ * @BAUD_IDLE: No baud rate change is ongoing.
+ * @BAUD_SENDING_RESET: HCI reset has been sent. Waiting for command complete
+ * event.
+ * @BAUD_START: Set baud rate cmd scheduled for sending.
+ * @BAUD_SENDING: Set baud rate cmd sending in progress.
+ * @BAUD_WAITING: Set baud rate cmd sent, waiting for command complete
+ * event.
+ * @BAUD_SUCCESS: Baud rate change has succeeded.
+ * @BAUD_FAIL: Baud rate change has failed.
+ */
+enum baud_rate_change_state {
+ BAUD_IDLE,
+ BAUD_SENDING_RESET,
+ BAUD_START,
+ BAUD_SENDING,
+ BAUD_WAITING,
+ BAUD_SUCCESS,
+ BAUD_FAIL
+};
+
+/**
+ * struct uart_work_struct - Work structure for UART module.
+ * @work: Work structure.
+ * @data: Pointer to private data.
+ *
+ * This structure is used to pack work for work queue.
+ */
+struct uart_work_struct{
+ struct work_struct work;
+ void *data;
+};
+/**
+ * struct uart_info - Main UART info structure.
+ * @rx_state: Current RX state.
+ * @rx_count: Number of bytes left to receive.
+ * @rx_skb: SK_buffer to store the received data into.
+ * @tx_queue: TX queue for sending data to chip.
+ * @hu: Hci uart structure.
+ * @wq: UART work queue.
+ * @baud_rate_state: UART baud rate change state.
+ * @baud_rate: Current baud rate setting.
+ * @sleep_state: UART sleep state.
+ * @timer: UART timer (for chip sleep).
+ * @sleep_state_lock: Used to protect chip state.
+ * @sleep_allowed: Indicate if tty has functions needed for sleep mode.
+ * @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.
+ */
+struct uart_info {
+ enum uart_rx_state rx_state;
+ unsigned long rx_count;
+ struct sk_buff *rx_skb;
+ struct sk_buff_head tx_queue;
+
+ struct hci_uart *hu;
+
+ struct workqueue_struct *wq;
+ enum baud_rate_change_state baud_rate_state;
+ int baud_rate;
+ enum sleep_state sleep_state;
+ struct timer_list timer;
+ struct mutex sleep_state_lock;
+ bool sleep_allowed;
+ struct regulator *regulator;
+ bool regulator_enabled;
+ struct device *dev;
+ struct cg2900_chip_dev chip_dev;
+ int cts_irq;
+};
+
+/* Module parameters */
+static int uart_default_baud = DEFAULT_BAUD_RATE;
+static int uart_high_baud = HIGH_BAUD_RATE;
+static int uart_debug;
+
+static DECLARE_WAIT_QUEUE_HEAD(uart_wait_queue);
+
+static void update_timer(struct uart_info *uart_info);
+
+/**
+ * is_chip_flow_off() - Check if chip has set flow off.
+ * @tty: Pointer to tty.
+ *
+ * Returns:
+ * true - chip flows off.
+ * false - chip flows on.
+ */
+static bool is_chip_flow_off(struct uart_info *uart_info)
+{
+ int lines;
+
+ lines = hci_uart_tiocmget(uart_info->hu);
+
+ if (lines & TIOCM_CTS)
+ return false;
+ else
+ return true;
+}
+
+/**
+ * create_work_item() - Create work item and add it to the work queue.
+ * @uart_info: Main Uart structure.
+ * @work_func: Work function.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EBUSY if not possible to queue work.
+ * -ENOMEM if allocation fails.
+ */
+static int create_work_item(struct uart_info *uart_info,
+ work_func_t work_func)
+{
+ struct uart_work_struct *new_work;
+ int res;
+
+ new_work = kmalloc(sizeof(*new_work), GFP_ATOMIC);
+ if (!new_work) {
+ dev_err(MAIN_DEV,
+ "Failed to alloc memory for uart_work_struct\n");
+ return -ENOMEM;
+ }
+
+ new_work->data = uart_info;
+ INIT_WORK(&new_work->work, work_func);
+
+ res = queue_work(uart_info->wq, &new_work->work);
+ if (!res) {
+ dev_err(MAIN_DEV,
+ "Failed to queue work_struct because it's already "
+ "in the queue\n");
+ kfree(new_work);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/**
+ * handle_cts_irq() - Called to handle CTS interrupt in work context.
+ * @work: work which needs to be done.
+ *
+ * The handle_cts_irq() function is a work handler called if interrupt on CTS
+ * occurred. It updates the sleep timer which will wake up the transport.
+ */
+static void handle_cts_irq(struct work_struct *work)
+{
+ struct uart_work_struct *current_work =
+ container_of(work, struct uart_work_struct, work);
+
+ /* Restart timer and disable interrupt. */
+ update_timer((struct uart_info *)current_work->data);
+ kfree(current_work);
+}
+
+/**
+ * cts_interrupt() - Called to handle CTS interrupt.
+ * @irq: Interrupt that occurred.
+ * @dev_id: Device ID where interrupt occurred.
+ *
+ * The handle_cts_irq() function is called if interrupt on CTS occurred.
+ * It disables the interrupt and starts a new work thread to handle
+ * the interrupt.
+ */
+static irqreturn_t cts_interrupt(int irq, void *dev_id)
+{
+ struct uart_info *uart_info = dev_get_drvdata(dev_id);
+#ifdef CONFIG_PM
+ disable_irq_wake(irq);
+#endif
+ disable_irq_nosync(irq);
+
+ /* If chip is suspended, resume callback will be called. */
+ if (CHIP_SUSPENDED != uart_info->sleep_state)
+ /* Create work and leave IRQ context. */
+ (void)create_work_item(uart_info, handle_cts_irq);
+ else {
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_RESUMING\n");
+ uart_info->sleep_state = CHIP_RESUMING;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * set_cts_irq() - Enable interrupt on CTS.
+ * @uart_info: Main Uart structure.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Error codes from request_irq and disable_uart.
+ */
+static int set_cts_irq(struct uart_info *uart_info)
+{
+ int err;
+ struct cg2900_platform_data *pf_data;
+
+ pf_data = dev_get_platdata(uart_info->dev);
+
+ /* First disable the UART so we can use IRQ on the GPIOs */
+ if (pf_data->uart.disable_uart) {
+ err = pf_data->uart.disable_uart(&uart_info->chip_dev);
+ if (err) {
+ dev_err(MAIN_DEV, "Could not disable UART (%d)\n", err);
+ goto error;
+ }
+ }
+
+ /* Set IRQ on CTS. */
+ err = request_irq(uart_info->cts_irq,
+ cts_interrupt,
+ IRQF_TRIGGER_FALLING,
+ UART_NAME,
+ uart_info->dev);
+ if (err) {
+ dev_err(MAIN_DEV, "Could not request CTS IRQ (%d)\n", err);
+ goto error;
+ }
+
+#ifdef CONFIG_PM
+ enable_irq_wake(uart_info->cts_irq);
+#endif
+ return 0;
+
+error:
+ if (pf_data->uart.enable_uart)
+ (void)pf_data->uart.enable_uart(&uart_info->chip_dev);
+ return err;
+}
+
+/**
+ * unset_cts_irq() - Disable interrupt on CTS.
+ * @uart_info: Main Uart structure.
+ */
+static void unset_cts_irq(struct uart_info *uart_info)
+{
+ int err = 0;
+ struct cg2900_platform_data *pf_data;
+
+ pf_data = dev_get_platdata(uart_info->dev);
+
+ /* Free CTS interrupt and restore UART settings. */
+ free_irq(uart_info->cts_irq, uart_info->dev);
+
+ if (pf_data->uart.enable_uart) {
+ err = pf_data->uart.enable_uart(&uart_info->chip_dev);
+ if (err)
+ dev_err(MAIN_DEV,
+ "Unable to enable UART Hardware (%d)\n", err);
+ }
+}
+
+/**
+ * get_sleep_timeout() - Get sleep timeout.
+ * @uart_info: Main Uart structure.
+ *
+ * Check all conditions for sleep and return sleep timeout.
+ * Return:
+ * 0: sleep not allowed.
+ * other: Timeout value in ms.
+ */
+static unsigned long get_sleep_timeout(struct uart_info *uart_info)
+{
+ unsigned long timeout_jiffies = cg2900_get_sleep_timeout();
+
+ if (timeout_jiffies &&
+ uart_info->hu &&
+ uart_info->hu->fd &&
+ uart_info->sleep_allowed)
+ return timeout_jiffies;
+
+ return 0;
+}
+
+/**
+ * update_timer() - Updates or starts the sleep timer.
+ * @uart_info: Main Uart structure.
+ *
+ * Updates or starts the sleep timer used to detect when there are no current
+ * data transmissions.
+ */
+static void update_timer(struct uart_info *uart_info)
+{
+ unsigned long timeout_jiffies = get_sleep_timeout(uart_info);
+
+ del_timer(&uart_info->timer);
+
+ /* Resuming state is special. Need to get back chip to awake state. */
+ if (!timeout_jiffies && uart_info->sleep_state != CHIP_RESUMING)
+ return;
+
+ mutex_lock(&(uart_info->sleep_state_lock));
+ /*
+ * This function indicates data is transmitted.
+ * Therefore see to that the chip is awake.
+ */
+ if (CHIP_AWAKE == uart_info->sleep_state)
+ goto finished;
+
+
+ if (CHIP_ASLEEP == uart_info->sleep_state ||
+ CHIP_RESUMING == uart_info->sleep_state) {
+ /* Disable IRQ only when it was enabled. */
+ unset_cts_irq(uart_info);
+ (void)hci_uart_set_baudrate(uart_info->hu,
+ uart_info->baud_rate);
+ }
+ /* Set FLOW on. */
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_ON);
+
+ /* Unset BREAK. */
+ dev_dbg(MAIN_DEV, "update_timer: Clear break\n");
+ hci_uart_set_break(uart_info->hu, BREAK_OFF);
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_AWAKE\n");
+ uart_info->sleep_state = CHIP_AWAKE;
+
+finished:
+ mutex_unlock(&(uart_info->sleep_state_lock));
+ /*
+ * If timer is running restart it. If not, start it.
+ * All this is handled by mod_timer().
+ */
+ mod_timer(&(uart_info->timer), jiffies + timeout_jiffies);
+}
+
+/**
+ * sleep_timer_expired() - Called when sleep timer expires.
+ * @data: Value supplied when starting the timer.
+ *
+ * The sleep_timer_expired() function is called if there are no ongoing data
+ * transmissions. It tries to put the chip in sleep mode.
+ *
+ */
+static void sleep_timer_expired(unsigned long data)
+{
+ struct uart_info *uart_info = (struct uart_info *)data;
+ unsigned long timeout_jiffies = get_sleep_timeout(uart_info);
+ int err = 0;
+
+ if (!timeout_jiffies)
+ return;
+
+ mutex_lock(&(uart_info->sleep_state_lock));
+
+ switch (uart_info->sleep_state) {
+ case CHIP_FALLING_ASLEEP:
+ if (!is_chip_flow_off(uart_info))
+ goto run_timer;
+
+ /* Flow OFF. */
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_OFF);
+
+ /*
+ * Set baud zero.
+ * This cause shut off UART clock as well.
+ */
+ (void)hci_uart_set_baudrate(uart_info->hu,
+ ZERO_BAUD_RATE);
+ err = set_cts_irq(uart_info);
+ if (err < 0) {
+ dev_err(MAIN_DEV, "Can not set interrupt on CTS, "
+ "err:%d\n", err);
+ (void)hci_uart_set_baudrate(uart_info->hu,
+ uart_info->baud_rate);
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_ON);
+ hci_uart_set_break(uart_info->hu, BREAK_OFF);
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_AWAKE\n");
+ uart_info->sleep_state = CHIP_AWAKE;
+
+ if (err == -ECANCELED)
+ goto run_timer;
+ else
+ goto error;
+ }
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_ASLEEP\n");
+ uart_info->sleep_state = CHIP_ASLEEP;
+ break;
+ case CHIP_AWAKE:
+
+ dev_dbg(MAIN_DEV, "sleep_timer_expired: Set break\n");
+ hci_uart_set_break(uart_info->hu, BREAK_ON);
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_FALLING_ASLEEP\n");
+ uart_info->sleep_state = CHIP_FALLING_ASLEEP;
+ goto run_timer;
+
+ case CHIP_POWERED_DOWN:
+ case CHIP_SUSPENDED:
+ case CHIP_ASLEEP: /* Fallthrough. */
+ default:
+ dev_dbg(MAIN_DEV,
+ "Chip sleeps, is suspended or powered down\n");
+ break;
+ }
+
+ mutex_unlock(&(uart_info->sleep_state_lock));
+
+ return;
+
+run_timer:
+ mutex_unlock(&(uart_info->sleep_state_lock));
+ mod_timer(&(uart_info->timer), jiffies + timeout_jiffies);
+ return;
+error:
+ /* Disable sleep mode.*/
+ dev_err(MAIN_DEV, "Disable sleep mode\n");
+ uart_info->sleep_allowed = false;
+ mutex_unlock(&(uart_info->sleep_state_lock));
+}
+
+#ifdef CONFIG_PM
+/**
+ * cg2900_uart_suspend() - Called by Linux PM to put the device in a low power mode.
+ * @pdev: Pointer to platform device.
+ * @state: New state.
+ *
+ * In UART case, CG2900 driver does nothing on suspend.
+ *
+ * Returns:
+ * 0 - Success.
+ */
+static int cg2900_uart_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct uart_info *uart_info = dev_get_drvdata(&pdev->dev);
+
+ if (uart_info->sleep_state == CHIP_POWERED_DOWN)
+ return 0;
+
+ if (uart_info->sleep_state != CHIP_ASLEEP)
+ return -EBUSY;
+
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_SUSPENDED\n");
+ uart_info->sleep_state = CHIP_SUSPENDED;
+ return 0;
+}
+
+/**
+ * cg2900_uart_resume() - Called to bring a device back from a low power state.
+ * @pdev: Pointer to platform device.
+ *
+ * In UART case, CG2900 driver does nothing on resume.
+ *
+ * Returns:
+ * 0 - Success.
+ */
+static int cg2900_uart_resume(struct platform_device *pdev)
+{
+ struct uart_info *uart_info = dev_get_drvdata(&pdev->dev);
+
+ if (uart_info->sleep_state == CHIP_POWERED_DOWN)
+ return 0;
+
+ if (uart_info->sleep_state == CHIP_RESUMING)
+ /* System resume because of trafic on UART. Lets wakeup.*/
+ update_timer(uart_info);
+ else {
+ /* No need to wakeup chip. Go back to Asleep state.*/
+ dev_dbg(MAIN_DEV, "New sleep_state: CHIP_ASLEEP\n");
+ uart_info->sleep_state = CHIP_ASLEEP;
+ }
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+/**
+ * cg2900_enable_regulator() - Enable regulator.
+ * @uart_info: Main Uart structure.
+ *
+ * Returns:
+ * 0 - Success.
+ * Error from regulator_get, regulator_enable.
+ */
+static int cg2900_enable_regulator(struct uart_info *uart_info)
+{
+#ifdef CONFIG_REGULATOR
+ int err;
+
+ /* Get and enable regulator. */
+ uart_info->regulator = regulator_get(uart_info->dev, "gbf_1v8");
+ if (IS_ERR(uart_info->regulator)) {
+ dev_err(MAIN_DEV, "Not able to find regulator\n");
+ err = PTR_ERR(uart_info->regulator);
+ } else {
+ err = regulator_enable(uart_info->regulator);
+ if (err)
+ dev_err(MAIN_DEV, "Not able to enable regulator\n");
+ else
+ uart_info->regulator_enabled = true;
+ }
+ return err;
+#else
+ return 0;
+#endif
+}
+
+/**
+ * cg2900_disable_regulator() - Disable regulator.
+ * @uart_info: Main Uart structure.
+ *
+ */
+static void cg2900_disable_regulator(struct uart_info *uart_info)
+{
+#ifdef CONFIG_REGULATOR
+ /* Disable and put regulator. */
+ if (uart_info->regulator && uart_info->regulator_enabled) {
+ regulator_disable(uart_info->regulator);
+ uart_info->regulator_enabled = false;
+ }
+ regulator_put(uart_info->regulator);
+ uart_info->regulator = NULL;
+#endif
+}
+
+/**
+ * is_set_baud_rate_cmd() - Checks if data contains set baud rate hci cmd.
+ * @data: Pointer to data array to check.
+ *
+ * Returns:
+ * true - if cmd found;
+ * false - otherwise.
+ */
+static bool is_set_baud_rate_cmd(const char *data)
+{
+ struct hci_command_hdr *cmd;
+
+ if (data[0] != HCI_BT_CMD_H4_CHANNEL)
+ return false;
+
+ cmd = (struct hci_command_hdr *)&data[1];
+ if (le16_to_cpu(cmd->opcode) == CG2900_BT_OP_VS_SET_BAUD_RATE &&
+ cmd->plen == BT_PARAM_LEN(sizeof(struct bt_vs_set_baud_rate_cmd)))
+ return true;
+
+ return false;
+}
+
+/**
+ * is_bt_cmd_complete_no_param() - Checks if data contains command complete event for a certain command.
+ * @skb: sk_buffer containing the data including H:4 header.
+ * @opcode: Command op code.
+ * @status: Command status.
+ *
+ * Returns:
+ * true - If this is the command complete we were looking for;
+ * false - otherwise.
+ */
+static bool is_bt_cmd_complete_no_param(struct sk_buff *skb, u16 opcode,
+ u8 *status)
+{
+ struct hci_event_hdr *event;
+ struct hci_ev_cmd_complete *complete;
+ u8 *data = &(skb->data[0]);
+
+ if (HCI_BT_EVT_H4_CHANNEL != *data)
+ return false;
+
+ data += HCI_H4_SIZE;
+ event = (struct hci_event_hdr *)data;
+ if (HCI_EV_CMD_COMPLETE != event->evt ||
+ HCI_BT_CMD_COMPLETE_LEN != event->plen)
+ return false;
+
+ data += sizeof(*event);
+ complete = (struct hci_ev_cmd_complete *)data;
+ if (opcode != le16_to_cpu(complete->opcode))
+ return false;
+
+ if (status) {
+ /*
+ * All command complete have the status field at first byte of
+ * packet data.
+ */
+ data += sizeof(*complete);
+ *status = *data;
+ }
+ return true;
+}
+
+/**
+ * alloc_rx_skb() - Alloc an sk_buff structure for receiving data from controller.
+ * @size: Size in number of octets.
+ * @priority: Allocation priority, e.g. GFP_KERNEL.
+ *
+ * Returns:
+ * Pointer to sk_buff structure.
+ */
+static struct sk_buff *alloc_rx_skb(unsigned int size, gfp_t priority)
+{
+ struct sk_buff *skb;
+
+ /* Allocate the SKB and reserve space for the header */
+ skb = alloc_skb(size + RX_SKB_RESERVE, priority);
+ if (skb)
+ skb_reserve(skb, RX_SKB_RESERVE);
+
+ return skb;
+}
+
+/**
+ * finish_setting_baud_rate() - Handles sending the ste baud rate hci cmd.
+ * @hu: Pointer to associated Hci uart structure.
+ *
+ * finish_setting_baud_rate() makes sure that the set baud rate cmd has
+ * been really sent out on the wire and then switches the tty driver to new
+ * baud rate.
+ */
+static void finish_setting_baud_rate(struct hci_uart *hu)
+{
+ struct uart_info *uart_info =
+ (struct uart_info *)dev_get_drvdata(hu->proto->dev);
+ /*
+ * Give the tty driver time to send data and proceed. If it hasn't
+ * been sent we can't do much about it anyway.
+ */
+ schedule_timeout_interruptible(msecs_to_jiffies(UART_TX_TIMEOUT));
+
+ /*
+ * Now set the termios struct to the new baudrate. Start by storing
+ * the old termios.
+ */
+ if (hci_uart_set_baudrate(hu, uart_info->baud_rate) < 0) {
+ /* Something went wrong.*/
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+ } else {
+ dev_dbg(MAIN_DEV, "Setting termios to new baud rate\n");
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_WAITING\n");
+ uart_info->baud_rate_state = BAUD_WAITING;
+ }
+
+ hci_uart_flow_ctrl(hu, FLOW_ON);
+}
+
+/**
+ * alloc_set_baud_rate_cmd() - Allocates new sk_buff and fills in the change baud rate hci cmd.
+ * @uart_info: Main Uart structure.
+ * @baud: (in/out) Requested new baud rate. Updated to default baud rate
+ * upon invalid value.
+ *
+ * Returns:
+ * Pointer to allocated sk_buff if successful;
+ * NULL otherwise.
+ */
+static struct sk_buff *alloc_set_baud_rate_cmd(struct uart_info *uart_info,
+ int *baud)
+{
+ struct sk_buff *skb;
+ u8 *h4;
+ struct bt_vs_set_baud_rate_cmd *cmd;
+
+ skb = alloc_skb(sizeof(*cmd) + CG2900_SKB_RESERVE, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(MAIN_DEV,
+ "alloc_set_baud_rate_cmd: Failed to alloc skb\n");
+ return NULL;
+ }
+ skb_reserve(skb, CG2900_SKB_RESERVE);
+
+ cmd = (struct bt_vs_set_baud_rate_cmd *)skb_put(skb, sizeof(cmd));
+
+ /* Create the Hci_Cmd_ST_Set_Uart_Baud_Rate packet */
+ cmd->opcode = cpu_to_le16(CG2900_BT_OP_VS_SET_BAUD_RATE);
+ cmd->plen = BT_PARAM_LEN(sizeof(cmd));
+
+ switch (*baud) {
+ case 57600:
+ cmd->baud_rate = CG2900_BAUD_RATE_57600;
+ break;
+ case 115200:
+ cmd->baud_rate = CG2900_BAUD_RATE_115200;
+ break;
+ case 230400:
+ cmd->baud_rate = CG2900_BAUD_RATE_230400;
+ break;
+ case 460800:
+ cmd->baud_rate = CG2900_BAUD_RATE_460800;
+ break;
+ case 921600:
+ cmd->baud_rate = CG2900_BAUD_RATE_921600;
+ break;
+ case 2000000:
+ cmd->baud_rate = CG2900_BAUD_RATE_2000000;
+ break;
+ case 3000000:
+ cmd->baud_rate = CG2900_BAUD_RATE_3000000;
+ break;
+ case 4000000:
+ cmd->baud_rate = CG2900_BAUD_RATE_4000000;
+ break;
+ default:
+ dev_err(MAIN_DEV,
+ "Invalid speed requested (%d), using 115200 bps "
+ "instead\n", *baud);
+ cmd->baud_rate = CG2900_BAUD_RATE_115200;
+ *baud = 115200;
+ break;
+ };
+
+ h4 = skb_push(skb, HCI_H4_SIZE);
+ *h4 = HCI_BT_CMD_H4_CHANNEL;
+
+ return skb;
+}
+
+/**
+ * work_do_transmit() - Transmit data packet to connectivity controller over UART.
+ * @work: Pointer to work info structure. Contains uart_info structure
+ * pointer.
+ */
+static void work_do_transmit(struct work_struct *work)
+{
+ struct uart_work_struct *current_work;
+ struct uart_info *uart_info;
+
+ current_work = container_of(work, struct uart_work_struct, work);
+ uart_info = (struct uart_info *)current_work->data;
+
+ kfree(current_work);
+
+ /* Restart timer. */
+ update_timer(uart_info);
+
+ (void)hci_uart_tx_wakeup(uart_info->hu);
+}
+
+/**
+ * work_hw_deregistered() - Handle HW deregistered.
+ * @work: Reference to work data.
+ */
+static void work_hw_deregistered(struct work_struct *work)
+{
+ struct uart_work_struct *current_work;
+ struct uart_info *uart_info;
+ int err;
+ current_work = container_of(work, struct uart_work_struct, work);
+ uart_info = (struct uart_info *)current_work->data;
+
+ err = cg2900_deregister_trans_driver(&uart_info->chip_dev);
+ if (err)
+ dev_err(MAIN_DEV, "Could not deregister UART from Core (%d)\n",
+ err);
+
+ kfree(current_work);
+}
+
+/**
+ * set_baud_rate() - Sets new baud rate for the UART.
+ * @hu: Pointer to hci_uart structure.
+ * @baud: New baud rate.
+ *
+ * This function first sends the HCI command
+ * Hci_Cmd_ST_Set_Uart_Baud_Rate. It then changes the baud rate in HW, and
+ * finally it waits for the Command Complete event for the
+ * Hci_Cmd_ST_Set_Uart_Baud_Rate command.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * -EALREADY if baud rate change is already in progress.
+ * -EFAULT if one or more of the UART related structs is not allocated.
+ * -ENOMEM if skb allocation has failed.
+ * -EPERM if setting the new baud rate has failed.
+ * Errors from create_work_item.
+ */
+static int set_baud_rate(struct hci_uart *hu, int baud)
+{
+ int err = 0;
+ struct sk_buff *skb;
+ int old_baud_rate;
+ struct uart_info *uart_info =
+ (struct uart_info *)dev_get_drvdata(hu->proto->dev);
+
+ dev_dbg(MAIN_DEV, "set_baud_rate (%d baud)\n", baud);
+
+ if (uart_info->baud_rate_state != BAUD_IDLE) {
+ dev_err(MAIN_DEV,
+ "Trying to set new baud rate before old setting "
+ "is finished\n");
+ return -EALREADY;
+ }
+
+ hci_uart_flow_ctrl(uart_info->hu, FLOW_OFF);
+
+ /*
+ * Store old baud rate so that we can restore it if something goes
+ * wrong.
+ */
+ old_baud_rate = uart_info->baud_rate;
+
+ skb = alloc_set_baud_rate_cmd(uart_info, &baud);
+ if (!skb) {
+ dev_err(MAIN_DEV, "alloc_set_baud_rate_cmd failed\n");
+ return -ENOMEM;
+ }
+
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_START\n");
+ uart_info->baud_rate_state = BAUD_START;
+ uart_info->baud_rate = baud;
+
+ /* Queue the sk_buffer... */
+ skb_queue_tail(&uart_info->tx_queue, skb);
+
+ /* ... and call the common UART TX function */
+ err = create_work_item(uart_info, work_do_transmit);
+ if (err) {
+ dev_err(MAIN_DEV,
+ "Failed to send change baud rate cmd, freeing skb\n");
+ skb = skb_dequeue_tail(&uart_info->tx_queue);
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+ uart_info->baud_rate = old_baud_rate;
+ kfree_skb(skb);
+ return err;
+ }
+
+ dev_dbg(MAIN_DEV, "Set baud rate cmd scheduled for sending\n");
+
+ /*
+ * Now wait for the command complete.
+ * It will come at the new baudrate.
+ */
+ wait_event_interruptible_timeout(uart_wait_queue,
+ ((BAUD_SUCCESS == uart_info->baud_rate_state) ||
+ (BAUD_FAIL == uart_info->baud_rate_state)),
+ msecs_to_jiffies(UART_RESP_TIMEOUT));
+ if (BAUD_SUCCESS == uart_info->baud_rate_state)
+ dev_info(MAIN_DEV, "Baud rate changed to %d baud\n", baud);
+ else {
+ dev_err(MAIN_DEV, "Failed to set new baud rate (%d)\n",
+ uart_info->baud_rate_state);
+ err = -EPERM;
+ }
+
+ /* Finally flush the TTY so we are sure that is no bad data there */
+ hci_uart_flush_buffer(hu);
+ dev_dbg(MAIN_DEV, "Flushing TTY after baud rate change\n");
+ /* Finished. Set state to IDLE */
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+
+ return err;
+}
+
+/**
+ * uart_write() - Transmit data to CG2900 over UART.
+ * @dev: Transport device information.
+ * @skb: SK buffer to transmit.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Errors from create_work_item.
+ */
+static int uart_write(struct cg2900_chip_dev *dev, struct sk_buff *skb)
+{
+ int err;
+ struct uart_info *uart_info = dev_get_drvdata(dev->dev);
+
+ /* Delete sleep timer. */
+ (void)del_timer(&uart_info->timer);
+
+ if (uart_debug)
+ dev_dbg(MAIN_DEV, "uart_write: data len = %d\n", skb->len);
+
+ /* Queue the sk_buffer... */
+ skb_queue_tail(&uart_info->tx_queue, skb);
+
+ /* ...and start TX operation */
+
+ err = create_work_item(uart_info, work_do_transmit);
+ if (err)
+ dev_err(MAIN_DEV,
+ "Failed to create work item (%d) uart_tty_wakeup\n",
+ err);
+
+ return err;
+}
+
+/**
+ * uart_open() - Open the CG2900 UART for data transfers.
+ * @dev: Transport device information.
+ *
+ * Returns:
+ * 0 if there is no error,
+ * -EACCES if write to transport failed,
+ * -EIO if chip did not answer to commands.
+ * Errors from set_baud_rate.
+ */
+static int uart_open(struct cg2900_chip_dev *dev)
+{
+ u8 *h4;
+ struct sk_buff *skb;
+ struct hci_command_hdr *cmd;
+ struct uart_info *uart_info = dev_get_drvdata(dev->dev);
+ /*
+ * Chip has just been started up. It has a system to autodetect
+ * exact baud rate and transport to use. There are only a few commands
+ * it will recognize and HCI Reset is one of them.
+ * We therefore start with sending that before actually changing
+ * baud rate.
+ *
+ * Create the Hci_Reset packet
+ */
+
+ skb = alloc_skb(sizeof(*cmd) + HCI_H4_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ dev_err(MAIN_DEV, "Couldn't allocate sk_buff with length %d",
+ sizeof(*cmd));
+ return -EACCES;
+ }
+ skb_reserve(skb, HCI_H4_SIZE);
+ cmd = (struct hci_command_hdr *)skb_put(skb, sizeof(*cmd));
+ cmd->opcode = cpu_to_le16(HCI_OP_RESET);
+ cmd->plen = 0; /* No parameters for HCI reset */
+
+ h4 = skb_push(skb, HCI_H4_SIZE);
+ *h4 = HCI_BT_CMD_H4_CHANNEL;
+
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_SENDING_RESET\n");
+ uart_info->baud_rate_state = BAUD_SENDING_RESET;
+ dev_dbg(MAIN_DEV, "Sending HCI reset before baud rate change\n");
+
+
+ /* Queue the sk_buffer... */
+ skb_queue_tail(&uart_info->tx_queue, skb);
+
+ (void)hci_uart_tx_wakeup(uart_info->hu);
+
+ /*
+ * Wait for command complete. If error, exit without changing
+ * baud rate.
+ */
+ wait_event_interruptible_timeout(uart_wait_queue,
+ BAUD_IDLE == uart_info->baud_rate_state,
+ msecs_to_jiffies(UART_RESP_TIMEOUT));
+ if (BAUD_IDLE != uart_info->baud_rate_state) {
+ dev_err(MAIN_DEV, "Failed to send HCI Reset\n");
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+ return -EIO;
+ }
+
+ /* Just return if there will be no change of baud rate */
+ if (uart_default_baud != uart_high_baud)
+ return set_baud_rate(uart_info->hu, uart_high_baud);
+ else
+ return 0;
+}
+
+/**
+ * uart_set_chip_power() - Enable or disable the CG2900.
+ * @chip_on: true if chip shall be enabled, false otherwise.
+ */
+static void uart_set_chip_power(struct cg2900_chip_dev *dev, bool chip_on)
+{
+ int uart_baudrate = uart_default_baud;
+ struct cg2900_platform_data *pf_data;
+ struct uart_info *uart_info;
+
+ pf_data = dev_get_platdata(dev->dev);
+ uart_info = dev_get_drvdata(dev->dev);
+
+ dev_info(MAIN_DEV, "Set chip power: %s\n",
+ (chip_on ? "ENABLE" : "DISABLE"));
+
+ if (!uart_info->hu) {
+ dev_err(MAIN_DEV, "Hci uart struct is not allocated!");
+ return;
+ }
+
+ if (chip_on) {
+ if (uart_info->sleep_state != CHIP_POWERED_DOWN) {
+ dev_err(MAIN_DEV, "Chip is already powered up");
+ return;
+ }
+
+ if (cg2900_enable_regulator(uart_info))
+ return;
+
+ 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:
+ break;
+ case CHIP_FALLING_ASLEEP:
+ del_timer(&uart_info->timer);
+ 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;
+ }
+
+ 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);
+}
+
+/**
+ * uart_chip_startup_finished() - CG2900 startup finished.
+ */
+static void uart_chip_startup_finished(struct cg2900_chip_dev *dev)
+{
+ struct uart_info *uart_info = dev_get_drvdata(dev->dev);
+ /* Run the timer. */
+ update_timer(uart_info);
+}
+/**
+ * uart_close() - Close the CG2900 UART for data transfers.
+ * @dev: Transport device information.
+ *
+ * Returns:
+ * 0 if there is no error.
+ */
+static int uart_close(struct cg2900_chip_dev *dev)
+{
+ /* The chip is already shut down. Power off the chip. */
+ uart_set_chip_power(dev, false);
+ return 0;
+}
+
+/**
+ * send_skb_to_core() - Sends packet received from UART to CG2900 Core.
+ * @skb: Received data packet.
+ *
+ * This function checks if UART is waiting for Command complete event,
+ * see set_baud_rate.
+ * If it is waiting it checks if it is the expected packet and the status.
+ * If not is passes the packet to CG2900 Core.
+ */
+static void send_skb_to_core(struct uart_info *uart_info, struct sk_buff *skb)
+{
+ u8 status;
+
+ if (!skb) {
+ dev_err(MAIN_DEV, "send_skb_to_core: Received NULL as skb\n");
+ return;
+ }
+
+ if (BAUD_WAITING == uart_info->baud_rate_state) {
+ /*
+ * Should only really be one packet received now:
+ * the CmdComplete for the SetBaudrate command
+ * Let's see if this is the packet we are waiting for.
+ */
+ if (!is_bt_cmd_complete_no_param(skb,
+ CG2900_BT_OP_VS_SET_BAUD_RATE, &status)) {
+ /*
+ * Received other event. Should not really happen,
+ * but pass the data to CG2900 Core anyway.
+ */
+ dev_dbg(MAIN_DEV, "Sending packet to CG2900 Core while "
+ "waiting for BaudRate CmdComplete\n");
+ uart_info->chip_dev.c_cb.data_from_chip
+ (&uart_info->chip_dev, skb);
+ return;
+ }
+
+ /*
+ * We have received complete event for our baud rate
+ * change command
+ */
+ if (HCI_BT_ERROR_NO_ERROR == status) {
+ dev_dbg(MAIN_DEV, "Received baud rate change complete "
+ "event OK\n");
+ dev_dbg(MAIN_DEV,
+ "New baud_rate_state: BAUD_SUCCESS\n");
+ uart_info->baud_rate_state = BAUD_SUCCESS;
+ } else {
+ dev_err(MAIN_DEV,
+ "Received baud rate change complete event "
+ "with status 0x%X\n", status);
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_FAIL\n");
+ uart_info->baud_rate_state = BAUD_FAIL;
+ }
+ wake_up_interruptible(&uart_wait_queue);
+ kfree_skb(skb);
+ } else if (BAUD_SENDING_RESET == uart_info->baud_rate_state) {
+ /*
+ * Should only really be one packet received now:
+ * the CmdComplete for the Reset command
+ * Let's see if this is the packet we are waiting for.
+ */
+ if (!is_bt_cmd_complete_no_param(skb, HCI_OP_RESET, &status)) {
+ /*
+ * Received other event. Should not really happen,
+ * but pass the data to CG2900 Core anyway.
+ */
+ dev_dbg(MAIN_DEV, "Sending packet to CG2900 Core while "
+ "waiting for Reset CmdComplete\n");
+ uart_info->chip_dev.c_cb.data_from_chip
+ (&uart_info->chip_dev, skb);
+ return;
+ }
+
+ /*
+ * We have received complete event for our baud rate
+ * change command
+ */
+ if (HCI_BT_ERROR_NO_ERROR == status) {
+ dev_dbg(MAIN_DEV,
+ "Received HCI reset complete event OK\n");
+ /*
+ * Go back to BAUD_IDLE since this was not really
+ * baud rate change but just a preparation of the chip
+ * to be ready to receive commands.
+ */
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_IDLE\n");
+ uart_info->baud_rate_state = BAUD_IDLE;
+ } else {
+ dev_err(MAIN_DEV,
+ "Received HCI reset complete event with "
+ "status 0x%X", status);
+ dev_dbg(MAIN_DEV, "New baud_rate_state: BAUD_FAIL\n");
+ uart_info->baud_rate_state = BAUD_FAIL;
+ }
+ wake_up_interruptible(&uart_wait_queue);
+ kfree_skb(skb);
+ } else {
+ /* Just pass data to CG2900 Core */
+ uart_info->chip_dev.c_cb.data_from_chip
+ (&uart_info->chip_dev, skb);
+ }
+}
+
+/**
+ * check_data_len() - Check number of bytes to receive.
+ * @len: Number of bytes left to receive.
+ */
+static void check_data_len(struct uart_info *uart_info, int len)
+{
+ /* First get number of bytes left in the sk_buffer */
+ register int room = skb_tailroom(uart_info->rx_skb);
+
+ if (!len) {
+ /* No data left to receive. Transmit to CG2900 Core */
+ send_skb_to_core(uart_info, uart_info->rx_skb);
+ } else if (len > room) {
+ dev_err(MAIN_DEV, "Data length is too large (%d > %d)\n",
+ len, room);
+ kfree_skb(uart_info->rx_skb);
+ } else {
+ /*
+ * "Normal" case. Switch to data receiving state and store
+ * data length.
+ */
+ uart_info->rx_state = W4_DATA;
+ uart_info->rx_count = len;
+ return;
+ }
+
+ uart_info->rx_state = W4_PACKET_TYPE;
+ uart_info->rx_skb = NULL;
+ uart_info->rx_count = 0;
+}
+
+/**
+ * cg2900_hu_receive() - Handles received UART data.
+ * @data: Data received
+ * @count: Number of bytes received
+ *
+ * The cg2900_hu_receive() function handles received UART data and puts it
+ * together to one complete packet.
+ *
+ * Returns:
+ * Number of bytes not handled, i.e. 0 = no error.
+ */
+static int cg2900_hu_receive(struct hci_uart *hu,
+ void *data, int count)
+{
+ const u8 *r_ptr;
+ u8 *w_ptr;
+ int len;
+ struct hci_event_hdr *evt;
+ struct hci_acl_hdr *acl;
+ union fm_leg_evt_or_irq *fm;
+ struct gnss_hci_hdr *gnss;
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+ u8 *tmp;
+
+ r_ptr = (const u8 *)data;
+
+ update_timer(uart_info);
+
+ if (uart_debug)
+ print_hex_dump_bytes(NAME " RX:\t", DUMP_PREFIX_NONE,
+ data, count);
+
+ /* Continue while there is data left to handle */
+ while (count) {
+ /*
+ * If we have already received a packet we know how many bytes
+ * there are left.
+ */
+ if (!uart_info->rx_count)
+ goto check_h4_header;
+
+ /* First copy received data into the skb_rx */
+ len = min_t(unsigned int, uart_info->rx_count, count);
+ memcpy(skb_put(uart_info->rx_skb, len), r_ptr, len);
+ /* Update counters from the length and step the data pointer */
+ uart_info->rx_count -= len;
+ count -= len;
+ r_ptr += len;
+
+ if (uart_info->rx_count)
+ /*
+ * More data to receive to current packet. Break and
+ * wait for next data on the UART.
+ */
+ break;
+
+ /* Handle the different states */
+ tmp = uart_info->rx_skb->data + CG2900_SKB_RESERVE;
+ switch (uart_info->rx_state) {
+ case W4_DATA:
+ /*
+ * Whole data packet has been received.
+ * Transmit it to CG2900 Core.
+ */
+ send_skb_to_core(uart_info, uart_info->rx_skb);
+
+ uart_info->rx_state = W4_PACKET_TYPE;
+ uart_info->rx_skb = NULL;
+ continue;
+
+ case W4_EVENT_HDR:
+ evt = (struct hci_event_hdr *)tmp;
+ check_data_len(uart_info, evt->plen);
+ /* Header read. Continue with next bytes */
+ continue;
+
+ case W4_ACL_HDR:
+ acl = (struct hci_acl_hdr *)tmp;
+ check_data_len(uart_info, le16_to_cpu(acl->dlen));
+ /* Header read. Continue with next bytes */
+ continue;
+
+ case W4_FM_RADIO_HDR:
+ fm = (union fm_leg_evt_or_irq *)tmp;
+ check_data_len(uart_info, fm->param_length);
+ /* Header read. Continue with next bytes */
+ continue;
+
+ case W4_GNSS_HDR:
+ gnss = (struct gnss_hci_hdr *)tmp;
+ check_data_len(uart_info, le16_to_cpu(gnss->plen));
+ /* Header read. Continue with next bytes */
+ continue;
+
+ default:
+ dev_err(MAIN_DEV,
+ "Bad state indicating memory overwrite "
+ "(0x%X)\n", (u8)(uart_info->rx_state));
+ break;
+ }
+
+check_h4_header:
+ /* Check which H:4 packet this is and update RX states */
+ if (*r_ptr == HCI_BT_EVT_H4_CHANNEL) {
+ uart_info->rx_state = W4_EVENT_HDR;
+ uart_info->rx_count = HCI_BT_EVT_HDR_SIZE;
+ } else if (*r_ptr == HCI_BT_ACL_H4_CHANNEL) {
+ uart_info->rx_state = W4_ACL_HDR;
+ uart_info->rx_count = HCI_BT_ACL_HDR_SIZE;
+ } else if (*r_ptr == HCI_FM_RADIO_H4_CHANNEL) {
+ uart_info->rx_state = W4_FM_RADIO_HDR;
+ uart_info->rx_count = HCI_FM_RADIO_HDR_SIZE;
+ } else if (*r_ptr == HCI_GNSS_H4_CHANNEL) {
+ uart_info->rx_state = W4_GNSS_HDR;
+ uart_info->rx_count = HCI_GNSS_HDR_SIZE;
+ } else {
+ dev_err(MAIN_DEV, "Unknown HCI packet type 0x%X\n",
+ (u8)*r_ptr);
+ r_ptr++;
+ count--;
+ continue;
+ }
+
+ /*
+ * Allocate packet. We do not yet know the size and therefore
+ * allocate max size.
+ */
+ uart_info->rx_skb = alloc_rx_skb(RX_SKB_MAX_SIZE, GFP_ATOMIC);
+ if (!uart_info->rx_skb) {
+ dev_err(MAIN_DEV,
+ "Can't allocate memory for new packet\n");
+ uart_info->rx_state = W4_PACKET_TYPE;
+ uart_info->rx_count = 0;
+ return 0;
+ }
+
+ /* Write the H:4 header first in the sk_buffer */
+ w_ptr = skb_put(uart_info->rx_skb, 1);
+ *w_ptr = *r_ptr;
+
+ /* First byte (H4 header) read. Goto next byte */
+ r_ptr++;
+ count--;
+ }
+
+ return count;
+}
+
+/**
+ * cg2900_hu_open() - Called when UART line discipline changed to N_HCI.
+ * @hu: Pointer to associated Hci uart structure.
+ *
+ * Returns:
+ * 0 if there is no error.
+ * Errors from cg2900_register_trans_driver.
+ */
+static int cg2900_hu_open(struct hci_uart *hu)
+{
+ int err;
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+
+ if (!uart_info)
+ return -EACCES;
+
+ dev_info(MAIN_DEV, "UART opened\n");
+
+ skb_queue_head_init(&uart_info->tx_queue);
+
+ uart_info->hu = hu;
+
+ /* Tell CG2900 Core that UART is connected */
+ err = cg2900_register_trans_driver(&uart_info->chip_dev);
+ if (err)
+ dev_err(MAIN_DEV, "Could not register transport driver (%d)\n",
+ err);
+
+ if (hu->tty->ops->tiocmget && hu->tty->ops->break_ctl)
+ uart_info->sleep_allowed = true;
+ else {
+ dev_err(MAIN_DEV, "Sleep mode not available\n");
+ uart_info->sleep_allowed = false;
+ }
+
+ return err;
+
+}
+
+/**
+ * cg2900_hu_close() - Close UART tty.
+ * @hu: Pointer to associated hci_uart structure.
+ *
+ * The uart_tty_close() function is called when the line discipline is changed
+ * to something else, the TTY is closed, or the TTY detects a hangup.
+ */
+static int cg2900_hu_close(struct hci_uart *hu)
+{
+ int err;
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+
+
+ BUG_ON(!uart_info);
+ BUG_ON(!uart_info->wq);
+
+ /* Purge any stored sk_buffers */
+ skb_queue_purge(&uart_info->tx_queue);
+ if (uart_info->rx_skb) {
+ kfree_skb(uart_info->rx_skb);
+ uart_info->rx_skb = NULL;
+ }
+
+ dev_info(MAIN_DEV, "UART closed\n");
+ err = create_work_item(uart_info, work_hw_deregistered);
+ if (err)
+ dev_err(MAIN_DEV, "Failed to create work item (%d) "
+ "work_hw_deregistered\n", err);
+
+ uart_info->hu = NULL;
+
+ return 0;
+}
+
+/**
+ * cg2900_hu_dequeue() - Get new skbuff.
+ * @hu: Pointer to associated hci_uart structure.
+ *
+ * The uart_tty_close() function is called when the line discipline is changed
+ * to something else, the TTY is closed, or the TTY detects a hangup.
+ */
+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);
+ skb = skb_dequeue(&uart_info->tx_queue);
+
+ 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
+ * sending is finished inform the tty driver about the new
+ * baud rate.
+ */
+ if ((BAUD_START == uart_info->baud_rate_state) &&
+ skb && (is_set_baud_rate_cmd(skb->data))) {
+ dev_dbg(MAIN_DEV, "UART set baud rate cmd found.");
+ uart_info->baud_rate_state = BAUD_SENDING;
+ }
+
+ if (uart_debug && skb)
+ print_hex_dump_bytes(NAME " TX:\t", DUMP_PREFIX_NONE,
+ skb->data, skb->len);
+
+ return skb;
+}
+
+/**
+ * cg2900_hu_flush() - Flush buffers.
+ * @hu: Pointer to associated hci_uart structure.
+ *
+ */
+static int cg2900_hu_flush(struct hci_uart *hu)
+{
+ struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
+
+ dev_dbg(MAIN_DEV, "ui %p", uart_info);
+ skb_queue_purge(&uart_info->tx_queue);
+ return 0;
+}
+
+/**
+ * cg2900_uart_probe() - Initialize CG2900 UART resources.
+ * @pdev: Platform device.
+ *
+ * This function initializes the module and registers to the UART framework.
+ *
+ * Returns:
+ * 0 if success.
+ * -ENOMEM for failed alloc or structure creation.
+ * -ECHILD for failed work queue creation.
+ * Error codes generated by tty_register_ldisc.
+ */
+static int __devinit cg2900_uart_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct uart_info *uart_info;
+ struct hci_uart_proto *p;
+ struct resource *resource;
+
+ pr_debug("cg2900_uart_probe");
+
+ uart_info = kzalloc(sizeof(*uart_info), GFP_KERNEL);
+ if (!uart_info) {
+ pr_err("Couldn't allocate uart_info");
+ return -ENOMEM;
+ }
+
+ uart_info->sleep_state = CHIP_POWERED_DOWN;
+ mutex_init(&(uart_info->sleep_state_lock));
+
+ uart_info->chip_dev.t_cb.open = uart_open;
+ uart_info->chip_dev.t_cb.close = uart_close;
+ uart_info->chip_dev.t_cb.write = uart_write;
+ uart_info->chip_dev.t_cb.set_chip_power = uart_set_chip_power;
+ uart_info->chip_dev.t_cb.chip_startup_finished =
+ uart_chip_startup_finished;
+ uart_info->chip_dev.pdev = pdev;
+ uart_info->chip_dev.dev = &pdev->dev;
+ uart_info->chip_dev.t_data = uart_info;
+
+ resource = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!resource) {
+ dev_err(&pdev->dev, "CTS IRQ does not exist\n");
+ err = -EINVAL;
+ goto error_handling_free;
+ }
+ uart_info->cts_irq = resource->start;
+
+ /* Init UART TX work queue */
+ uart_info->wq = create_singlethread_workqueue(UART_WQ_NAME);
+ if (!uart_info->wq) {
+ dev_err(MAIN_DEV, "Could not create workqueue\n");
+ err = -ECHILD; /* No child processes */
+ goto error_handling_free;
+ }
+ init_timer(&uart_info->timer);
+ uart_info->timer.function = sleep_timer_expired;
+ uart_info->timer.expires = jiffies + cg2900_get_sleep_timeout();
+ uart_info->timer.data = (unsigned long)uart_info;
+
+ uart_info->dev = &pdev->dev;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ dev_err(MAIN_DEV, "Could not allocate p.");
+ goto error_handling_wq;
+ }
+
+ p->dev = uart_info->dev;
+ p->id = HCI_UART_STE;
+ p->open = &cg2900_hu_open;
+ p->close = &cg2900_hu_close;
+ p->recv = &cg2900_hu_receive;
+ p->dequeue = &cg2900_hu_dequeue;
+ p->flush = &cg2900_hu_flush;
+
+ dev_set_drvdata(uart_info->dev, (void *)uart_info);
+
+ err = hci_uart_register_proto(p);
+ if (err) {
+ dev_err(MAIN_DEV, "Can not register protocol.");
+ kfree(p);
+ goto error_handling_wq;
+ }
+
+ goto finished;
+
+error_handling_wq:
+ destroy_workqueue(uart_info->wq);
+error_handling_free:
+ kfree(uart_info);
+ uart_info = NULL;
+finished:
+ return err;
+}
+
+/**
+ * cg2900_uart_remove() - Release CG2900 UART resources.
+ * @pdev: Platform device.
+ *
+ * Returns:
+ * 0 if success.
+ * Error codes generated by tty_unregister_ldisc.
+ */
+static int __devexit cg2900_uart_remove(struct platform_device *pdev)
+{
+ struct uart_info *uart_info = dev_get_drvdata(&pdev->dev);
+
+ pr_debug("cg2900_uart_remove");
+
+ if (!uart_info)
+ return -ECHILD;
+
+ if (uart_info->hu)
+ hci_uart_unregister_proto(uart_info->hu->proto);
+
+ destroy_workqueue(uart_info->wq);
+
+ dev_info(MAIN_DEV, "CG2900 UART removed\n");
+ kfree(uart_info);
+ uart_info = NULL;
+ return 0;
+}
+
+static struct platform_driver cg2900_uart_driver = {
+ .driver = {
+ .name = "cg2900-uart",
+ .owner = THIS_MODULE,
+ },
+ .probe = cg2900_uart_probe,
+ .remove = __devexit_p(cg2900_uart_remove),
+#ifdef CONFIG_PM
+ .suspend = cg2900_uart_suspend,
+ .resume = cg2900_uart_resume
+#endif
+};
+
+
+/**
+ * cg2900_uart_init() - Initialize module.
+ *
+ * Registers platform driver.
+ */
+static int __init cg2900_uart_init(void)
+{
+ pr_debug("cg2900_uart_init");
+ return platform_driver_register(&cg2900_uart_driver);
+}
+
+/**
+ * cg2900_uart_exit() - Remove module.
+ *
+ * Unregisters platform driver.
+ */
+static void __exit cg2900_uart_exit(void)
+{
+ pr_debug("cg2900_uart_exit");
+ platform_driver_unregister(&cg2900_uart_driver);
+}
+
+module_init(cg2900_uart_init);
+module_exit(cg2900_uart_exit);
+
+module_param(uart_default_baud, int, S_IRUGO);
+MODULE_PARM_DESC(uart_default_baud,
+ "Default UART baud rate, e.g. 115200. If not set 115200 will "
+ "be used.");
+
+module_param(uart_high_baud, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(uart_high_baud,
+ "High speed UART baud rate, e.g. 4000000. If not set 3000000 "
+ "will be used.");
+
+module_param(uart_debug, int, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(uart_debug, "Enable/Disable debug. 0 means Debug disabled.");
+MODULE_AUTHOR("Par-Gunnar Hjalmdahl ST-Ericsson");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ST-Ericsson CG2900 UART Driver");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4c2b5cc..d5c57bc 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -290,6 +290,15 @@ config MFD_STLC2690_CHIP
STLC2690 support Bluetooth and FM radio, however FM is not supported
over H:4 interface.

+config MFD_CG2900_UART
+ tristate "Support CG2900 UART transport"
+ depends on MFD_CG2900
+ select BT
+ select BT_HCIUART
+ help
+ Support for UART as transport for ST-Ericsson CG2900 Connectivity
+ Controller. Registers against Bluetooth N_HCI TTY line discipline.
+
config MFD_CG2900_AUDIO
tristate "Support CG2900 audio interface"
depends on MFD_CG2900
--
1.7.3.2


2010-12-17 12:02:54

by Vitaly Wool

[permalink] [raw]
Subject: Re: [PATCH 08/11] Bluetooth: Add support for CG2900 UART

Hi Par,

so on the top level: this is yet another H4 implementation plus
channel-based packet routing, right?

Could you please also elaborate

More comments on the code are inlined.

> +#define MAIN_DEV ? ? ? ? ? ? ? (uart_info->dev)

What is that for?

> + * cg2900_uart_suspend() - Called by Linux PM to put the device in a low power mode.
> + * @pdev: ? ? ?Pointer to platform device.
> + * @state: ? ? New state.
> + *
> + * In UART case, CG2900 driver does nothing on suspend.
> + *
> + * Returns:
> + * ? 0 - Success.
> + */
> +static int cg2900_uart_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> + ? ? ? struct uart_info *uart_info = dev_get_drvdata(&pdev->dev);
> +
> + ? ? ? if (uart_info->sleep_state == CHIP_POWERED_DOWN)
> + ? ? ? ? ? ? ? return 0;
> +
> + ? ? ? if (uart_info->sleep_state != CHIP_ASLEEP)
> + ? ? ? ? ? ? ? return -EBUSY;
> +
> + ? ? ? dev_dbg(MAIN_DEV, "New sleep_state: CHIP_SUSPENDED\n");
> + ? ? ? uart_info->sleep_state = CHIP_SUSPENDED;
> + ? ? ? return 0;
> +}

I don't think this is safe wrt work queue. What if it gets scheduled
when drivers are suspended?

Thanks,
Vitaly

2010-12-17 12:30:28

by Par-Gunnar HJALMDAHL

[permalink] [raw]
Subject: RE: [PATCH 08/11] Bluetooth: Add support for CG2900 UART

Hi Vitaly,

> -----Original Message-----
> From: Vitaly Wool [mailto:[email protected]]
> Sent: den 17 december 2010 13:03
> To: Par-Gunnar HJALMDAHL
> Cc: Pavan Savoy; Alan Cox; Arnd Bergmann; Samuel Ortiz; Marcel
> Holtmann; [email protected]; linux-
> [email protected]; Lukasz Rymanowski; Linus WALLEIJ; Par-Gunnar
> Hjalmdahl
> Subject: Re: [PATCH 08/11] Bluetooth: Add support for CG2900 UART
>
> Hi Par,
>
> so on the top level: this is yet another H4 implementation plus
> channel-based packet routing, right?
>
> Could you please also elaborate
>

Yes, the low-level basis is similar to e.g. hci_h4.c, where we register to N_HCI line discipline and then use the first byte to separate between different channels.
While hci_h4.c supports only the standardized Bluetooth H4 channels, the cg2900_uart targets also the CG2900 specific H4 channels.

> More comments on the code are inlined.
>
> > +#define MAIN_DEV ? ? ? ? ? ? ? (uart_info->dev)
>
> What is that for?
>

This is just a simplification when using for example dev_err(). It's just to shorten the text (not much in this case but there are other files where it looks better).
No big problem to fix if you want that.

> > + * cg2900_uart_suspend() - Called by Linux PM to put the device in a
> low power mode.
> > + * @pdev: ? ? ?Pointer to platform device.
> > + * @state: ? ? New state.
> > + *
> > + * In UART case, CG2900 driver does nothing on suspend.
> > + *
> > + * Returns:
> > + * ? 0 - Success.
> > + */
> > +static int cg2900_uart_suspend(struct platform_device *pdev,
> pm_message_t state)
> > +{
> > + ? ? ? struct uart_info *uart_info = dev_get_drvdata(&pdev->dev);
> > +
> > + ? ? ? if (uart_info->sleep_state == CHIP_POWERED_DOWN)
> > + ? ? ? ? ? ? ? return 0;
> > +
> > + ? ? ? if (uart_info->sleep_state != CHIP_ASLEEP)
> > + ? ? ? ? ? ? ? return -EBUSY;
> > +
> > + ? ? ? dev_dbg(MAIN_DEV, "New sleep_state: CHIP_SUSPENDED\n");
> > + ? ? ? uart_info->sleep_state = CHIP_SUSPENDED;
> > + ? ? ? return 0;
> > +}
>
> I don't think this is safe wrt work queue. What if it gets scheduled
> when drivers are suspended?
>
> Thanks,
> Vitaly

I'm not 100% sure what you mean since I don't think sleep_state should ever be in CHIP_ASLEEP if we have a work ongoing or in the queue, but I will ask our low power expert to look into this.

/P-G