summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHemant Gupta <hemant.gupta@stericsson.com>2011-10-11 19:45:44 +0530
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:05:58 +0200
commit99b39f8e3dfb5800dac9c836b3cb9dc4840ca42b (patch)
treeb124f783545062ee33a3bc78138241213d2c504d
parentb231eb1acabe59c01e7cf57e56b62048ff147f09 (diff)
cg2900: Support new connectivity chips
This patch adds support for new connectivity combo chips. Support is added for CG2905 and CG2910 chips firmware downloading. CG2910 supports NFC, therefore a new HCI H4 channel is added. ST-Ericsson Linux next: Not Tested, ER 373024 ST-Ericsson ID: 373024 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10019 Change-Id: I18158e26e6e651c0ba7e6b98b872a20d5fcaa423 Signed-off-by: Hemant Gupta <hemant.gupta@stericsson.com> Signed-off-by: Nitin Dhingra <nitin.dhingra@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33716 Reviewed-by: QATOOLS
-rw-r--r--Documentation/DocBook/cg2900.tmpl23
-rw-r--r--drivers/staging/cg2900/bluetooth/btcg2900.c12
-rw-r--r--drivers/staging/cg2900/bluetooth/cg2900_uart.c32
-rw-r--r--drivers/staging/cg2900/devices-cg2900.c9
-rw-r--r--drivers/staging/cg2900/include/cg2900.h26
-rw-r--r--drivers/staging/cg2900/mfd/cg2900_audio.c76
-rw-r--r--drivers/staging/cg2900/mfd/cg2900_chip.c153
-rw-r--r--drivers/staging/cg2900/mfd/cg2900_chip.h22
-rw-r--r--drivers/staging/cg2900/mfd/cg2900_core.h1
9 files changed, 275 insertions, 79 deletions
diff --git a/Documentation/DocBook/cg2900.tmpl b/Documentation/DocBook/cg2900.tmpl
index 34667f1b1d9..4cefb9c1427 100644
--- a/Documentation/DocBook/cg2900.tmpl
+++ b/Documentation/DocBook/cg2900.tmpl
@@ -263,7 +263,7 @@
<term>Writing to a channel</term>
<listitem>
<para>
- When a stack (Bluetooth, FM, or GNSS) wants to send a packet it shall perform a write operation.
+ When a stack (Bluetooth, FM, GNSS or NFC) wants to send a packet it shall perform a write operation.
The packet shall not contain the H:4 header since this is added by the CG2900 driver.
All other data in the packet shall however exist in the packet in the format correct for that HCI channel.
The CG2900 users need to perform flow control over channels so any ticket handling
@@ -320,7 +320,7 @@
<term>Reading from a channel</term>
<listitem>
<para>
- When a stack (Bluetooth, FM, or GNSS) wants to receive a packet it shall perform a receive operation.
+ When a stack (Bluetooth, FM, GNSS or NFC) wants to receive a packet it shall perform a receive operation.
The packet returned does not contain the H:4 header since this is removed by the CG2900 driver.
All other data in the packet in the packet is in the format correct for that HCI channel.
The CG2900 driver does not perform any flow control over the H:4 channel so any ticket handling
@@ -542,6 +542,7 @@
<listitem>
<para>
This example will open the GNSS channel, write a packet, read a packet and then close the channel.
+ The same example is applicable for all other user space access for eg. NFC driver.
In this example all functions are performed in the same thread.
It is however adviced to perform <function>read</function> and <function>ioctl</function> read through a separate thread,
preferrably using <function>poll</function>.
@@ -1286,6 +1287,24 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>NFC</term>
+ <listitem>
+ <variablelist>
+ <varlistentry>
+ <term>File</term>
+ <listitem><para><filename>/dev/cg2900_nfc</filename></para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Description</term>
+ <listitem>
+ <para>The cg2900_nfc is the device for the HCI NFC channel.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</section>
diff --git a/drivers/staging/cg2900/bluetooth/btcg2900.c b/drivers/staging/cg2900/bluetooth/btcg2900.c
index 07aae9a32ca..cfeeb4a23c9 100644
--- a/drivers/staging/cg2900/bluetooth/btcg2900.c
+++ b/drivers/staging/cg2900/bluetooth/btcg2900.c
@@ -35,11 +35,6 @@
#define BT_HEADER_LENGTH 0x03
-#define STLC2690_HCI_REV 0x0600
-#define CG2900_PG1_HCI_REV 0x0101
-#define CG2900_PG2_HCI_REV 0x0200
-#define CG2900_PG1_SPECIAL_HCI_REV 0x0700
-
#define NAME "BTCG2900 "
/* Wait for 5 seconds for a response to our requests */
@@ -204,10 +199,11 @@ static struct sk_buff *get_bt_enable_cmd(struct btcg2900_info *info,
}
/* If connected chip does not support the command return NULL */
- if (CG2900_PG1_SPECIAL_HCI_REV != rev_data.revision &&
- CG2900_PG1_HCI_REV != rev_data.revision &&
- CG2900_PG2_HCI_REV != rev_data.revision)
+ if (!check_chip_revision_support(rev_data.revision)) {
+ BT_ERR(NAME "Unsupported Chip revision:0x%x\n",
+ rev_data.revision);
return NULL;
+ }
/* CG2900 used */
skb = pf_data->alloc_skb(sizeof(*cmd), GFP_KERNEL);
diff --git a/drivers/staging/cg2900/bluetooth/cg2900_uart.c b/drivers/staging/cg2900/bluetooth/cg2900_uart.c
index 3c88ed6d18f..6b577514ae6 100644
--- a/drivers/staging/cg2900/bluetooth/cg2900_uart.c
+++ b/drivers/staging/cg2900/bluetooth/cg2900_uart.c
@@ -7,6 +7,7 @@
* Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
* Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
* Lukasz Rymanowski (lukasz.rymanowski@tieto.com) for ST-Ericsson.
+ * Hemant Gupta (hemant.gupta@stericsson.com) for ST-Ericsson.
* License terms: GNU General Public License (GPL), version 2
*
* Linux Bluetooth UART Driver for ST-Ericsson CG2900 connectivity controller.
@@ -79,6 +80,7 @@
/* Size of the header in the different packets */
#define HCI_BT_EVT_HDR_SIZE 2
#define HCI_BT_ACL_HDR_SIZE 4
+#define HCI_NFC_HDR_SIZE 3
#define HCI_FM_RADIO_HDR_SIZE 1
#define HCI_GNSS_HDR_SIZE 3
@@ -109,6 +111,7 @@
#define CG2900_SKB_RESERVE HCI_H4_SIZE
/* Default H4 channels which may change depending on connected controller */
+#define HCI_NFC_H4_CHANNEL 0x05
#define HCI_FM_RADIO_H4_CHANNEL 0x08
#define HCI_GNSS_H4_CHANNEL 0x09
@@ -126,6 +129,15 @@
#define CG2900_BAUD_RATE_3250000 0x28
#define CG2900_BAUD_RATE_4000000 0x2B
+/* NFC: 1byte extra is recieved for checksum */
+#define NFC_CHECKSUM_DATA_LEN 0x01
+
+/* NFC */
+struct nfc_hci_hdr {
+ __u8 op_code;
+ __le16 plen;
+} __packed;
+
/**
* enum sleep_allowed_bits - bits indicating if sleep is allowed.
* @SLEEP_TTY_READY: Bit for checking if break can be applied using tty.
@@ -207,6 +219,7 @@ struct bt_vs_set_baud_rate_cmd {
* @W4_PACKET_TYPE: Waiting for packet type.
* @W4_EVENT_HDR: Waiting for BT event header.
* @W4_ACL_HDR: Waiting for BT ACL header.
+ * @W4_NFC_HDR: Waiting for NFC 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).
@@ -215,6 +228,7 @@ enum uart_rx_state {
W4_PACKET_TYPE,
W4_EVENT_HDR,
W4_ACL_HDR,
+ W4_NFC_HDR,
W4_FM_RADIO_HDR,
W4_GNSS_HDR,
W4_DATA
@@ -1719,6 +1733,7 @@ static int cg2900_hu_receive(struct hci_uart *hu,
int len;
struct hci_event_hdr *evt;
struct hci_acl_hdr *acl;
+ struct nfc_hci_hdr *nfc;
union fm_leg_evt_or_irq *fm;
struct gnss_hci_hdr *gnss;
struct uart_info *uart_info = dev_get_drvdata(hu->proto->dev);
@@ -1790,6 +1805,20 @@ static int cg2900_hu_receive(struct hci_uart *hu,
/* Header read. Continue with next bytes */
continue;
+ case W4_NFC_HDR:
+ nfc = (struct nfc_hci_hdr *)tmp;
+ /*
+ * NFC Packet(s) have 1 extra byte for checksum.
+ * This is not indicated by the byte length determined
+ * from the received NFC Packet over HCI. So
+ * length of received NFC packet should be updated
+ * accordingly.
+ */
+ check_data_len(uart_info, le16_to_cpu(nfc->plen) +
+ NFC_CHECKSUM_DATA_LEN);
+ /* 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);
@@ -1817,6 +1846,9 @@ check_h4_header:
} 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_NFC_H4_CHANNEL) {
+ uart_info->rx_state = W4_NFC_HDR;
+ uart_info->rx_count = HCI_NFC_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;
diff --git a/drivers/staging/cg2900/devices-cg2900.c b/drivers/staging/cg2900/devices-cg2900.c
index e6703c96aa9..4c5d4a22a77 100644
--- a/drivers/staging/cg2900/devices-cg2900.c
+++ b/drivers/staging/cg2900/devices-cg2900.c
@@ -40,11 +40,6 @@
#define H4_HEADER_LENGTH 0x01
#define BT_HEADER_LENGTH 0x03
-#define STLC2690_HCI_REV 0x0600
-#define CG2900_PG1_HCI_REV 0x0101
-#define CG2900_PG2_HCI_REV 0x0200
-#define CG2900_PG1_SPECIAL_HCI_REV 0x0700
-
struct vs_power_sw_off_cmd {
__le16 op_code;
u8 len;
@@ -65,9 +60,7 @@ static struct sk_buff *dcg2900_get_power_switch_off_cmd
int i;
/* If connected chip does not support the command return NULL */
- if (CG2900_PG1_SPECIAL_HCI_REV != dev->chip.hci_revision &&
- CG2900_PG1_HCI_REV != dev->chip.hci_revision &&
- CG2900_PG2_HCI_REV != dev->chip.hci_revision)
+ if (!check_chip_revision_support(dev->chip.hci_revision))
return NULL;
dev_dbg(dev->dev, "Generating PowerSwitchOff command\n");
diff --git a/drivers/staging/cg2900/include/cg2900.h b/drivers/staging/cg2900/include/cg2900.h
index 99ac90da58f..85b5e03eb3c 100644
--- a/drivers/staging/cg2900/include/cg2900.h
+++ b/drivers/staging/cg2900/include/cg2900.h
@@ -28,6 +28,15 @@
#define CG2900_CHAR_DEV_IOCTL_EVENT_IDLE 0
#define CG2900_CHAR_DEV_IOCTL_EVENT_RESET 1
+/* Specific chip version data */
+#define STLC2690_REV 0x0600
+#define CG2900_PG1_REV 0x0101
+#define CG2900_PG2_REV 0x0200
+#define CG2900_PG1_SPECIAL_REV 0x0700
+#define CG2905_PG1_1_REV 0x1805
+#define CG2910_PG1_REV 0x1004
+#define CG2910_PG2_REV 0x1008
+
/**
* struct cg2900_rev_data - Contains revision data for the local controller.
* @revision: Revision of the controller, e.g. to indicate that it is
@@ -39,8 +48,8 @@
* the manufacturer.
*/
struct cg2900_rev_data {
- int revision;
- int sub_version;
+ u16 revision;
+ u16 sub_version;
};
#ifdef __KERNEL__
@@ -273,6 +282,19 @@ static inline void cg2900_set_prv(struct cg2900_user_data *dev, void *data)
dev->private_data = data;
}
+static inline bool check_chip_revision_support(u16 hci_revision)
+{
+ if (hci_revision != CG2900_PG1_SPECIAL_REV &&
+ hci_revision != CG2900_PG1_REV &&
+ hci_revision != CG2900_PG2_REV &&
+ hci_revision != CG2905_PG1_1_REV &&
+ hci_revision != CG2910_PG1_REV &&
+ hci_revision != CG2910_PG2_REV)
+ return false;
+
+ return true;
+}
+
extern int cg2900_register_chip_driver(struct cg2900_id_callbacks *cb);
extern void cg2900_deregister_chip_driver(struct cg2900_id_callbacks *cb);
extern int cg2900_register_trans_driver(struct cg2900_chip_dev *dev);
diff --git a/drivers/staging/cg2900/mfd/cg2900_audio.c b/drivers/staging/cg2900/mfd/cg2900_audio.c
index 2f00d79ed1e..267e4bd8a02 100644
--- a/drivers/staging/cg2900/mfd/cg2900_audio.c
+++ b/drivers/staging/cg2900/mfd/cg2900_audio.c
@@ -3,6 +3,7 @@
* Authors:
* Par-Gunnar Hjalmdahl (par-gunnar.p.hjalmdahl@stericsson.com) for ST-Ericsson.
* Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * Hemant Gupta (hemant.gupta@stericsson.com) for ST-Ericsson.
* License terms: GNU General Public License (GPL), version 2
*
* Linux Bluetooth Audio Driver for ST-Ericsson CG2900 controller.
@@ -50,8 +51,11 @@
/* Used to select proper API, ignoring subrevisions etc */
enum chip_revision {
- CHIP_REV_PG1,
- CHIP_REV_PG2
+ CG2900_CHIP_REV_PG1,
+ CG2900_CHIP_REV_PG2,
+ CG2905_CHIP_REV_PG1_1,
+ CG2910_CHIP_REV_PG1,
+ CG2910_CHIP_REV_PG2
};
/**
@@ -130,7 +134,7 @@ struct endpoint_config_node {
* @false otherwise.
* @endpoints: List containing the endpoint configurations.
* @stream_ids: Bitmask for in-use stream ids (only used with
- * PG2 chip API).
+ * CG2900 PG2 onwards chip API).
*/
struct audio_info {
struct list_head list;
@@ -880,7 +884,7 @@ static int send_vs_delete_stream(struct audio_user *audio_user,
struct audio_cb_info *cb_info = cg2900_get_usr(pf_data);
/* Now delete the stream - format command... */
- if (info->revision == CHIP_REV_PG1) {
+ if (info->revision == CG2900_CHIP_REV_PG1) {
struct bt_vs_reset_session_cfg_cmd *cmd;
dev_dbg(BT_DEV, "BT: HCI_VS_Reset_Session_Configuration\n");
@@ -933,12 +937,13 @@ static int send_vs_delete_stream(struct audio_user *audio_user,
}
/* wait for response */
- if (info->revision == CHIP_REV_PG1) {
+ if (info->revision == CG2900_CHIP_REV_PG1) {
err = receive_bt_cmd_complete(audio_user, opcode, NULL, 0);
} else {
u8 vs_err;
- /* All commands in PG2 API returns one byte extra status */
+ /* All commands on CG2900 PG2 onwards
+ * API returns one byte extra status */
err = receive_bt_cmd_complete(audio_user, opcode,
&vs_err, sizeof(vs_err));
@@ -1250,7 +1255,8 @@ static int send_vs_stream_ctrl(struct audio_user *user, u8 stream, u8 command)
goto finished;
}
- /* All commands in PG2 API returns one byte with extra status */
+ /* All commands on CG2900 PG2 onwards
+ * API returns one byte extra status */
err = receive_bt_cmd_complete(user,
CG2900_MC_VS_STREAM_CONTROL,
&vs_err, sizeof(vs_err));
@@ -1338,7 +1344,8 @@ static int send_vs_create_stream(struct audio_user *user, u8 inport,
goto finished_release_id;
}
- /* All commands in PG2 API returns one byte with extra status */
+ /* All commands on CG2900 PG2 onwards
+ * API returns one byte extra status */
err = receive_bt_cmd_complete(user,
CG2900_MC_VS_CREATE_STREAM,
&vs_err, sizeof(vs_err));
@@ -1423,7 +1430,8 @@ static int send_vs_port_cfg(struct audio_user *user, u8 port,
goto finished;
}
- /* All commands in PG2 API returns one byte with extra status */
+ /* All commands on CG2900 PG2 onwards
+ * API returns one byte extra status */
err = receive_bt_cmd_complete(user, CG2900_MC_VS_PORT_CONFIG,
&vs_err, sizeof(vs_err));
if (err)
@@ -1516,8 +1524,8 @@ static int set_dai_config_pg1(struct audio_user *audio_user,
i2s_pcm = &config->conf.i2s_pcm;
/*
- * PG1 chips don't support I2S over the PCM/I2S bus,
- * and PG2 chips don't use this command
+ * CG2900 PG1 chips don't support I2S over the PCM/I2S bus,
+ * and CG2900 PG2 onwards chips don't use this command
*/
if (i2s_pcm->protocol != PORT_PROTOCOL_PCM) {
dev_err(BT_DEV,
@@ -1583,13 +1591,15 @@ finished_unlock_mutex:
}
/**
- * set_dai_config_pg2() - Internal implementation of @cg2900_audio_set_dai_config for PG2 hardware.
+ * set_dai_config_pg2() - Internal implementation of
+ * cg2900_audio_set_dai_config for CG2900 PG2 onwards hardware.
* @audio_user: Pointer to audio user struct.
* @config: Pointer to the configuration to set.
*
- * Sets the Digital Audio Interface (DAI) configuration for PG2
- * hardware. This is an internal function and basic
- * argument-verification should have been done by the caller.
+ * Sets the Digital Audio Interface (DAI) configuration for
+ * CG2900 PG2 onwards hardware. This is an internal function
+ * and basic argument-verification should have been
+ * done by the caller.
*
* Returns:
* 0 if there is no error.
@@ -1831,7 +1841,7 @@ static int conn_start_i2s_to_fm_rx(struct audio_user *audio_user,
goto finished_unlock_mutex;
/* Set up the stream */
- if (info->revision == CHIP_REV_PG1) {
+ if (info->revision == CG2900_CHIP_REV_PG1) {
struct i2s_fm_stream_config_priv stream_priv;
/* Now send HCI_VS_Set_Session_Configuration command */
@@ -1868,7 +1878,7 @@ static int conn_start_i2s_to_fm_rx(struct audio_user *audio_user,
dev_dbg(FM_DEV, "stream_handle set to %d\n", *stream_handle);
/* Now start the stream */
- if (info->revision == CHIP_REV_PG1)
+ if (info->revision == CG2900_CHIP_REV_PG1)
err = send_vs_session_ctrl(audio_user, *stream_handle,
CG2900_BT_SESSION_START);
else
@@ -1964,7 +1974,7 @@ static int conn_start_i2s_to_fm_tx(struct audio_user *audio_user,
goto finished_unlock_mutex;
/* Set up the stream */
- if (info->revision == CHIP_REV_PG1) {
+ if (info->revision == CG2900_CHIP_REV_PG1) {
struct i2s_fm_stream_config_priv stream_priv;
/* Now send HCI_VS_Set_Session_Configuration command */
@@ -2001,7 +2011,7 @@ static int conn_start_i2s_to_fm_tx(struct audio_user *audio_user,
dev_dbg(FM_DEV, "stream_handle set to %d\n", *stream_handle);
/* Now start the stream */
- if (info->revision == CHIP_REV_PG1)
+ if (info->revision == CG2900_CHIP_REV_PG1)
err = send_vs_session_ctrl(audio_user, *stream_handle,
CG2900_BT_SESSION_START);
else
@@ -2112,7 +2122,7 @@ static int conn_start_pcm_to_sco(struct audio_user *audio_user,
mutex_lock(&info->bt_mutex);
/* Set up the stream */
- if (info->revision == CHIP_REV_PG1) {
+ if (info->revision == CG2900_CHIP_REV_PG1) {
err = send_vs_session_config(audio_user, config_pcm_sco_stream,
&bt_config->sco);
} else {
@@ -2143,7 +2153,7 @@ static int conn_start_pcm_to_sco(struct audio_user *audio_user,
dev_dbg(BT_DEV, "stream_handle set to %d\n", *stream_handle);
/* Now start the stream */
- if (info->revision == CHIP_REV_PG1)
+ if (info->revision == CG2900_CHIP_REV_PG1)
err = send_vs_session_ctrl(audio_user, *stream_handle,
CG2900_BT_SESSION_START);
else
@@ -2193,7 +2203,7 @@ static int conn_stop_stream(struct audio_user *audio_user,
mutex_lock(&info->bt_mutex);
/* Now stop the stream */
- if (info->revision == CHIP_REV_PG1)
+ if (info->revision == CG2900_CHIP_REV_PG1)
err = send_vs_session_ctrl(audio_user, stream_handle,
CG2900_BT_SESSION_STOP);
else
@@ -2357,11 +2367,23 @@ int cg2900_audio_open(unsigned int *session, struct device *parent)
switch (rev_data.revision) {
case CG2900_PG1_REV:
case CG2900_PG1_SPECIAL_REV:
- info->revision = CHIP_REV_PG1;
+ info->revision = CG2900_CHIP_REV_PG1;
break;
case CG2900_PG2_REV:
- info->revision = CHIP_REV_PG2;
+ info->revision = CG2900_CHIP_REV_PG2;
+ break;
+
+ case CG2905_PG1_1_REV:
+ info->revision = CG2905_CHIP_REV_PG1_1;
+ break;
+
+ case CG2910_PG1_REV:
+ info->revision = CG2910_CHIP_REV_PG1;
+ break;
+
+ case CG2910_PG2_REV:
+ info->revision = CG2910_CHIP_REV_PG2;
break;
default:
@@ -2501,10 +2523,10 @@ int cg2900_audio_set_dai_config(unsigned int session,
return -EIO;
}
- /* Different commands are used for PG1 and PG2 */
- if (info->revision == CHIP_REV_PG1)
+ /* Different command is used for CG2900 PG1 */
+ if (info->revision == CG2900_CHIP_REV_PG1)
err = set_dai_config_pg1(audio_user, config);
- else if (info->revision == CHIP_REV_PG2)
+ else
err = set_dai_config_pg2(audio_user, config);
return err;
diff --git a/drivers/staging/cg2900/mfd/cg2900_chip.c b/drivers/staging/cg2900/mfd/cg2900_chip.c
index 3ff07714508..3c8ea1e08e5 100644
--- a/drivers/staging/cg2900/mfd/cg2900_chip.c
+++ b/drivers/staging/cg2900/mfd/cg2900_chip.c
@@ -6,6 +6,7 @@
* Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
* Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
* Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * Hemant Gupta (hemant.gupta@stericsson.com) for ST-Ericsson.
* License terms: GNU General Public License (GPL), version 2
*
* Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
@@ -81,6 +82,11 @@
*/
#define CHANNEL_BT_EVT 0x04
+/** CHANNEL_NFC - Bluetooth HCI H:4 channel
+ * for NFC in the ST-Ericsson connectivity controller.
+ */
+#define CHANNEL_NFC 0x05
+
/** CHANNEL_FM_RADIO - Bluetooth HCI H:4 channel
* for FM radio in the ST-Ericsson connectivity controller.
*/
@@ -128,6 +134,10 @@
*/
#define CG2900_BT_EVT "cg2900_bt_evt"
+/** CG2900_NFC - Bluetooth HCI H4 channel for NFC.
+ */
+#define CG2900_NFC "cg2900_nfc"
+
/** CG2900_FM_RADIO - Bluetooth HCI H4 channel for FM radio.
*/
#define CG2900_FM_RADIO "cg2900_fm_radio"
@@ -386,7 +396,9 @@ struct cg2900_chip_info {
int nbr_of_polls;
bool startup;
int mfd_size;
+ int mfd_extra_size;
int mfd_char_size;
+ int mfd_extra_char_size;
};
/**
@@ -417,6 +429,8 @@ static DECLARE_WAIT_QUEUE_HEAD(clk_user_wait_queue);
static struct mfd_cell cg2900_devs[];
static struct mfd_cell cg2900_char_devs[];
+static struct mfd_cell cg2910_extra_devs[];
+static struct mfd_cell cg2910_extra_char_devs[];
static void chip_startup_finished(struct cg2900_chip_info *info, int err);
static void chip_shutdown(struct cg2900_user_data *user);
@@ -1031,7 +1045,7 @@ static void send_patch_file(struct cg2900_chip_dev *dev)
int err;
int bytes_sent;
struct cg2900_chip_info *info = dev->c_data;
- int file_name_size = strlen("CG2900_XXXX_XXXX_settings.fw");
+ int file_name_size = strlen("CG29XX_XXXX_XXXX_settings.fw");
bytes_sent = cg2900_read_and_send_file_part(info->user_in_charge,
info->logger,
@@ -1059,7 +1073,7 @@ static void send_patch_file(struct cg2900_chip_dev *dev)
* so add 1.
*/
err = snprintf(info->settings_file_name, file_name_size + 1,
- "CG2900_%04X_%04X_settings.fw", dev->chip.hci_revision,
+ "CG29XX_%04X_%04X_settings.fw", dev->chip.hci_revision,
dev->chip.hci_sub_version);
if (err == file_name_size) {
dev_dbg(BOOT_DEV, "Downloading settings file %s\n",
@@ -1178,16 +1192,16 @@ shut_down_chip:
int err;
err = mfd_add_devices(dev->dev, main_info->cell_base_id,
- cg2900_devs, info->mfd_size, NULL, 0);
+ cg2900_devs, info->mfd_size, NULL, 0);
if (err) {
dev_err(dev->dev, "Failed to add cg2900_devs (%d)\n",
- err);
+ err);
goto finished;
}
err = mfd_add_devices(dev->dev, main_info->cell_base_id,
- cg2900_char_devs, info->mfd_char_size,
- NULL, 0);
+ cg2900_char_devs, info->mfd_char_size,
+ NULL, 0);
if (err) {
dev_err(dev->dev, "Failed to add cg2900_char_devs (%d)"
"\n", err);
@@ -1199,8 +1213,39 @@ shut_down_chip:
* Increase base ID so next connected transport will not get the
* same device IDs.
*/
- main_info->cell_base_id += MAX(info->mfd_size,
- info->mfd_char_size);
+ main_info->cell_base_id +=
+ MAX(info->mfd_size, info->mfd_char_size);
+
+ if (dev->chip.hci_revision == CG2910_PG1_REV ||
+ dev->chip.hci_revision == CG2910_PG2_REV) {
+ err = mfd_add_devices(dev->dev, main_info->cell_base_id,
+ cg2910_extra_devs,
+ info->mfd_extra_size, NULL, 0);
+ if (err) {
+ dev_err(dev->dev, "Failed to add cg2910_extra_devs "
+ "(%d)\n", err);
+ goto finished;
+ }
+
+ err = mfd_add_devices(dev->dev, main_info->cell_base_id,
+ cg2910_extra_char_devs,
+ info->mfd_extra_char_size, NULL, 0);
+ if (err) {
+ dev_err(dev->dev, "Failed to add cg2910_extra_char_devs "
+ "(%d)\n", err);
+ mfd_remove_devices(dev->dev);
+ goto finished;
+ }
+
+ /*
+ * Increase base ID so next connected transport
+ * will not get the same device IDs.
+ */
+ main_info->cell_base_id +=
+ MAX(info->mfd_extra_size,
+ info->mfd_extra_char_size);
+ }
+
info->startup = false;
}
@@ -1266,7 +1311,7 @@ static void work_load_patch_and_settings(struct work_struct *work)
struct cg2900_work *my_work;
struct cg2900_chip_dev *dev;
struct cg2900_chip_info *info;
- int file_name_size = strlen("CG2900_XXXX_XXXX_patch.fw");
+ int file_name_size = strlen("CG29XX_XXXX_XXXX_patch.fw");
if (!work) {
dev_err(MAIN_DEV,
@@ -1288,7 +1333,7 @@ static void work_load_patch_and_settings(struct work_struct *work)
* so add 1.
*/
err = snprintf(info->patch_file_name, file_name_size + 1,
- "CG2900_%04X_%04X_patch.fw", dev->chip.hci_revision,
+ "CG29XX_%04X_%04X_patch.fw", dev->chip.hci_revision,
dev->chip.hci_sub_version);
if (err == file_name_size) {
dev_dbg(BOOT_DEV, "Downloading patch file %s\n",
@@ -1602,7 +1647,10 @@ static bool handle_vs_system_reset_cmd_complete(struct cg2900_chip_dev *dev,
status);
if (HCI_BT_ERROR_NO_ERROR == status) {
- if (dev->chip.hci_revision == CG2900_PG2_REV) {
+ if (dev->chip.hci_revision == CG2900_PG2_REV &&
+ dev->chip.hci_revision == CG2905_PG1_1_REV &&
+ dev->chip.hci_revision == CG2910_PG1_REV &&
+ dev->chip.hci_revision == CG2910_PG2_REV) {
/*
* We must now wait for the selftest results. They will
* take a certain amount of time to finish so start a
@@ -1859,6 +1907,29 @@ static bool handle_rx_data_bt_evt(struct cg2900_chip_dev *dev,
else if (op_code == CG2900_BT_OP_VS_BT_ENABLE)
pkt_handled = handle_vs_bt_enable_cmd_status
(dev, cmd_status->status);
+ } else if (HCI_EV_VENDOR_SPECIFIC == evt->evt) {
+ /*
+ * In the new versions of CG29xx i.e. after CG2900 we
+ * will recieve HCI_Event_VS_Write_File_Block_Complete
+ * instead of Command Complete while downloading
+ * the patches/settings file
+ */
+ struct bt_vs_evt *cmd_evt;
+ cmd_evt = (struct bt_vs_evt *)data;
+
+ if (cmd_evt->evt_id == CG2900_EV_VS_WRITE_FILE_BLOCK_COMPLETE) {
+ struct bt_vs_write_file_block_evt *write_block_evt;
+ write_block_evt =
+ (struct bt_vs_write_file_block_evt *)
+ cmd_evt->data;
+ dev_dbg(dev->dev, "Received VS write file block complete\n");
+ pkt_handled = handle_vs_write_file_block_cmd_complete(
+ dev, &write_block_evt->status);
+ } else {
+ dev_err(dev->dev, "Vendor Specific Event 0x%x"
+ "Not Supported\n", cmd_evt->evt_id);
+ return false;
+ }
} else if (HCI_EV_HW_ERROR == evt->evt) {
struct hci_ev_hw_error *hw_error;
@@ -3128,6 +3199,9 @@ static struct cg2900_user_data btacl_data = {
static struct cg2900_user_data btevt_data = {
.h4_channel = CHANNEL_BT_EVT,
};
+static struct cg2900_user_data nfc_data = {
+ .h4_channel = CHANNEL_NFC,
+};
static struct cg2900_user_data fm_data = {
.h4_channel = CHANNEL_FM_RADIO,
};
@@ -3232,6 +3306,14 @@ static struct mfd_cell cg2900_devs[] = {
},
};
+static struct mfd_cell cg2910_extra_devs[] = {
+ {
+ .name = "cg2900-nfc",
+ .platform_data = &nfc_data,
+ .pdata_size = sizeof(nfc_data)
+ }
+};
+
static struct cg2900_user_data char_btcmd_data = {
.channel_data = {
.char_dev_name = CG2900_BT_CMD,
@@ -3250,6 +3332,12 @@ static struct cg2900_user_data char_btevt_data = {
},
.h4_channel = CHANNEL_BT_EVT,
};
+static struct cg2900_user_data char_nfc_data = {
+ .channel_data = {
+ .char_dev_name = CG2900_NFC,
+ },
+ .h4_channel = CHANNEL_NFC,
+};
static struct cg2900_user_data char_fm_data = {
.channel_data = {
.char_dev_name = CG2900_FM_RADIO,
@@ -3390,6 +3478,15 @@ static struct mfd_cell cg2900_char_devs[] = {
},
};
+static struct mfd_cell cg2910_extra_char_devs[] = {
+ {
+ .name = "cg2900-chardev",
+ .id = 12,
+ .platform_data = &char_nfc_data,
+ .pdata_size = sizeof(char_nfc_data)
+ }
+};
+
/**
* set_plat_data() - Initializes data for an MFD cell.
* @cell: MFD cell.
@@ -3438,20 +3535,14 @@ static bool check_chip_support(struct cg2900_chip_dev *dev)
dev_dbg(dev->dev, "check_chip_support\n");
/*
- * Check if this is a CG2900 revision.
+ * Check if this is a CG29XX revision.
* We do not care about the sub-version at the moment. Change this if
* necessary.
*/
- if ((dev->chip.manufacturer != CG2900_SUPP_MANUFACTURER) ||
- (dev->chip.hci_revision != CG2900_PG1_SPECIAL_REV &&
- (dev->chip.hci_revision < CG2900_SUPP_REVISION_MIN ||
- dev->chip.hci_revision > CG2900_SUPP_REVISION_MAX))) {
- dev_dbg(dev->dev, "Chip not supported by CG2900 driver\n"
- "\tMan: 0x%02X\n"
- "\tRev: 0x%04X\n"
- "\tSub: 0x%04X\n",
- dev->chip.manufacturer, dev->chip.hci_revision,
- dev->chip.hci_sub_version);
+ if (dev->chip.manufacturer != CG2900_SUPP_MANUFACTURER
+ || !(check_chip_revision_support(dev->chip.hci_revision))) {
+ dev_err(dev->dev, "Unsupported Chip revision:0x%x\n",
+ dev->chip.hci_revision);
return false;
}
@@ -3516,15 +3607,27 @@ static bool check_chip_support(struct cg2900_chip_dev *dev)
btacl_data.channel_data.bt_bus = pf_data->bus;
btevt_data.channel_data.bt_bus = pf_data->bus;
+ /* Set the Platform data based on supported chip */
for (i = 0; i < ARRAY_SIZE(cg2900_devs); i++)
set_plat_data(&cg2900_devs[i], dev);
for (i = 0; i < ARRAY_SIZE(cg2900_char_devs); i++)
set_plat_data(&cg2900_char_devs[i], dev);
-
- info->startup = true;
info->mfd_size = ARRAY_SIZE(cg2900_devs);
info->mfd_char_size = ARRAY_SIZE(cg2900_char_devs);
+ if (dev->chip.hci_revision == CG2910_PG1_REV ||
+ dev->chip.hci_revision == CG2910_PG2_REV) {
+ for (i = 0; i < ARRAY_SIZE(cg2910_extra_devs); i++)
+ set_plat_data(&cg2910_extra_devs[i], dev);
+ for (i = 0; i < ARRAY_SIZE(cg2910_extra_char_devs); i++)
+ set_plat_data(&cg2910_extra_char_devs[i], dev);
+ info->mfd_extra_size = ARRAY_SIZE(cg2910_extra_devs);
+ info->mfd_extra_char_size = ARRAY_SIZE(cg2910_extra_char_devs);
+ }
+
+
+ info->startup = true;
+
/*
* The devices will be registered when chip has been powered down, i.e.
* when the system startup is ready.
@@ -3532,7 +3635,7 @@ static bool check_chip_support(struct cg2900_chip_dev *dev)
mutex_unlock(&main_info->man_mutex);
- dev_info(dev->dev, "Chip supported by the CG2900 chip driver\n");
+ dev_info(dev->dev, "Chip supported by the CG2900 driver\n");
/* Finish by turning off the chip */
cg2900_create_work_item(info->wq, work_power_off_chip, dev);
diff --git a/drivers/staging/cg2900/mfd/cg2900_chip.h b/drivers/staging/cg2900/mfd/cg2900_chip.h
index b3fd556b7d7..5db655903a8 100644
--- a/drivers/staging/cg2900/mfd/cg2900_chip.h
+++ b/drivers/staging/cg2900/mfd/cg2900_chip.h
@@ -6,6 +6,7 @@
* Josef Kindberg (josef.kindberg@stericsson.com) for ST-Ericsson.
* Dariusz Szymszak (dariusz.xd.szymczak@stericsson.com) for ST-Ericsson.
* Kjell Andersson (kjell.k.andersson@stericsson.com) for ST-Ericsson.
+ * Hemant Gupta (hemant.gupta@stericsson.com) for ST-Ericsson.
* License terms: GNU General Public License (GPL), version 2
*
* Linux Bluetooth HCI H:4 Driver for ST-Ericsson CG2900 GPS/BT/FM controller.
@@ -39,13 +40,6 @@ static inline void store_bit(__u8 *var, size_t bit, __u8 value)
/* Supported chips */
#define CG2900_SUPP_MANUFACTURER 0x30
-#define CG2900_SUPP_REVISION_MIN 0x0100
-#define CG2900_SUPP_REVISION_MAX 0x0200
-
-/* Specific chip version data */
-#define CG2900_PG1_REV 0x0101
-#define CG2900_PG2_REV 0x0200
-#define CG2900_PG1_SPECIAL_REV 0x0700
/*
* Bluetooth
@@ -81,6 +75,20 @@ struct bt_vs_store_in_fs_cmd {
#define CG2900_VS_STORE_IN_FS_USR_ID_BD_ADDR 0xFE
+#define HCI_EV_VENDOR_SPECIFIC 0xFF
+#define CG2900_EV_VS_WRITE_FILE_BLOCK_COMPLETE 0x60
+/* BT VS Event */
+struct bt_vs_evt {
+ __u8 evt_id;
+ __u8 data[];
+} __packed;
+
+/* BT VS Write File Block Event */
+struct bt_vs_write_file_block_evt {
+ __u8 status;
+ __u8 file_blk_id;
+} __packed;
+
/* BT VS Write File Block command */
#define CG2900_BT_OP_VS_WRITE_FILE_BLOCK 0xFC2E
struct bt_vs_write_file_block_cmd {
diff --git a/drivers/staging/cg2900/mfd/cg2900_core.h b/drivers/staging/cg2900/mfd/cg2900_core.h
index bdd951a501d..a0615fa0e46 100644
--- a/drivers/staging/cg2900/mfd/cg2900_core.h
+++ b/drivers/staging/cg2900/mfd/cg2900_core.h
@@ -38,6 +38,7 @@
/* Bluetooth error codes */
#define HCI_BT_ERROR_NO_ERROR 0x00
+#define HCI_BT_WRONG_SEQ_ERROR 0xF1
/* Bluetooth lengths */
#define HCI_BT_SEND_FILE_MAX_CHUNK_SIZE 254