summaryrefslogtreecommitdiff
path: root/drivers/staging/cg2900/mfd/cg2900_chip.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/cg2900/mfd/cg2900_chip.c')
-rw-r--r--drivers/staging/cg2900/mfd/cg2900_chip.c153
1 files changed, 128 insertions, 25 deletions
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);