Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f700076a authored by John W. Linville's avatar John W. Linville
Browse files

Merge branch 'for-upstream' of...

parents 7be6ff65 5a34bd5f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -210,6 +210,7 @@ config BT_MRVL_SDIO
	tristate "Marvell BT-over-SDIO driver"
	depends on BT_MRVL && MMC
	select FW_LOADER
	select WANT_DEV_COREDUMP
	help
	  The driver for Marvell Bluetooth chipsets with SDIO interface.

+31 −0
Original line number Diff line number Diff line
@@ -167,6 +167,35 @@ static const struct file_operations btmrvl_hscmd_fops = {
	.llseek = default_llseek,
};

static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf,
				   size_t count, loff_t *ppos)
{
	struct btmrvl_private *priv = file->private_data;
	char buf[16];
	bool result;

	memset(buf, 0, sizeof(buf));

	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
		return -EFAULT;

	if (strtobool(buf, &result))
		return -EINVAL;

	if (!result)
		return -EINVAL;

	btmrvl_firmware_dump(priv);

	return count;
}

static const struct file_operations btmrvl_fwdump_fops = {
	.write	= btmrvl_fwdump_write,
	.open	= simple_open,
	.llseek = default_llseek,
};

void btmrvl_debugfs_init(struct hci_dev *hdev)
{
	struct btmrvl_private *priv = hci_get_drvdata(hdev);
@@ -197,6 +226,8 @@ void btmrvl_debugfs_init(struct hci_dev *hdev)
			    priv, &btmrvl_hscmd_fops);
	debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
			    priv, &btmrvl_hscfgcmd_fops);
	debugfs_create_file("fw_dump", 0200, dbg->config_dir,
			    priv, &btmrvl_fwdump_fops);

	dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
	debugfs_create_u8("curpsmode", 0444, dbg->status_dir,
+20 −0
Original line number Diff line number Diff line
@@ -32,6 +32,24 @@
/* Time to wait for command response in millisecond */
#define WAIT_UNTIL_CMD_RESP		5000

enum rdwr_status {
	RDWR_STATUS_SUCCESS = 0,
	RDWR_STATUS_FAILURE = 1,
	RDWR_STATUS_DONE = 2
};

#define FW_DUMP_MAX_NAME_LEN    8
#define FW_DUMP_HOST_READY      0xEE
#define FW_DUMP_DONE            0xFF
#define FW_DUMP_READ_DONE       0xFE

struct memory_type_mapping {
	u8 mem_name[FW_DUMP_MAX_NAME_LEN];
	u8 *mem_ptr;
	u32 mem_size;
	u8 done_flag;
};

struct btmrvl_thread {
	struct task_struct *task;
	wait_queue_head_t wait_q;
@@ -81,6 +99,7 @@ struct btmrvl_private {
				u8 *payload, u16 nb);
	int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
	int (*hw_process_int_status) (struct btmrvl_private *priv);
	void (*firmware_dump)(struct btmrvl_private *priv);
	spinlock_t driver_lock;		/* spinlock used by driver */
#ifdef CONFIG_DEBUG_FS
	void *debugfs_data;
@@ -151,6 +170,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv);
int btmrvl_enable_ps(struct btmrvl_private *priv);
int btmrvl_prepare_command(struct btmrvl_private *priv);
int btmrvl_enable_hs(struct btmrvl_private *priv);
void btmrvl_firmware_dump(struct btmrvl_private *priv);

#ifdef CONFIG_DEBUG_FS
void btmrvl_debugfs_init(struct hci_dev *hdev);
+14 −7
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/of.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <linux/mmc/sdio_func.h>

#include "btmrvl_drv.h"
#include "btmrvl_sdio.h"
@@ -42,7 +43,7 @@ void btmrvl_interrupt(struct btmrvl_private *priv)
	priv->adapter->int_count++;

	if (priv->adapter->hs_state == HS_ACTIVATED) {
		BT_DBG("BT: HS DEACTIVATED in ISR!\n");
		BT_DBG("BT: HS DEACTIVATED in ISR!");
		priv->adapter->hs_state = HS_DEACTIVATED;
	}

@@ -214,7 +215,7 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)

	ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1);
	if (ret)
		BT_ERR("module_cfg_cmd(%x) failed\n", subcmd);
		BT_ERR("module_cfg_cmd(%x) failed", subcmd);

	return ret;
}
@@ -250,7 +251,7 @@ int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)

	ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2);
	if (ret)
		BT_ERR("HSCFG command failed\n");
		BT_ERR("HSCFG command failed");

	return ret;
}
@@ -268,7 +269,7 @@ int btmrvl_enable_ps(struct btmrvl_private *priv)

	ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, &param, 1);
	if (ret)
		BT_ERR("PSMODE command failed\n");
		BT_ERR("PSMODE command failed");

	return 0;
}
@@ -281,7 +282,7 @@ int btmrvl_enable_hs(struct btmrvl_private *priv)

	ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);
	if (ret) {
		BT_ERR("Host sleep enable command failed\n");
		BT_ERR("Host sleep enable command failed");
		return ret;
	}

@@ -328,13 +329,19 @@ int btmrvl_prepare_command(struct btmrvl_private *priv)
		} else {
			ret = priv->hw_wakeup_firmware(priv);
			priv->adapter->hs_state = HS_DEACTIVATED;
			BT_DBG("BT: HS DEACTIVATED due to host activity!\n");
			BT_DBG("BT: HS DEACTIVATED due to host activity!");
		}
	}

	return ret;
}

void btmrvl_firmware_dump(struct btmrvl_private *priv)
{
	if (priv->firmware_dump)
		priv->firmware_dump(priv);
}

static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
{
	int ret = 0;
@@ -493,7 +500,7 @@ static int btmrvl_download_cal_data(struct btmrvl_private *priv,
	ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,
				   BT_CAL_HDR_LEN + len);
	if (ret)
		BT_ERR("Failed to download caibration data\n");
		BT_ERR("Failed to download caibration data");

	return 0;
}
+302 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <linux/module.h>
#include <linux/devcoredump.h>

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -33,6 +34,24 @@

#define VERSION "1.0"

static struct memory_type_mapping mem_type_mapping_tbl[] = {
	{"ITCM", NULL, 0, 0xF0},
	{"DTCM", NULL, 0, 0xF1},
	{"SQRAM", NULL, 0, 0xF2},
	{"APU", NULL, 0, 0xF3},
	{"CIU", NULL, 0, 0xF4},
	{"ICU", NULL, 0, 0xF5},
	{"MAC", NULL, 0, 0xF6},
	{"EXT7", NULL, 0, 0xF7},
	{"EXT8", NULL, 0, 0xF8},
	{"EXT9", NULL, 0, 0xF9},
	{"EXT10", NULL, 0, 0xFA},
	{"EXT11", NULL, 0, 0xFB},
	{"EXT12", NULL, 0, 0xFC},
	{"EXT13", NULL, 0, 0xFD},
	{"EXTLAST", NULL, 0, 0xFE},
};

/* The btmrvl_sdio_remove() callback function is called
 * when user removes this module from kernel space or ejects
 * the card from the slot. The driver handles these 2 cases
@@ -122,6 +141,9 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
	.int_read_to_clear = true,
	.host_int_rsr = 0x01,
	.card_misc_cfg = 0xcc,
	.fw_dump_ctrl = 0xe2,
	.fw_dump_start = 0xe3,
	.fw_dump_end = 0xea,
};

static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
@@ -130,6 +152,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
	.reg		= &btmrvl_reg_8688,
	.support_pscan_win_report = false,
	.sd_blksz_fw_dl	= 64,
	.supports_fw_dump = false,
};

static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
@@ -138,6 +161,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = {
	.reg		= &btmrvl_reg_87xx,
	.support_pscan_win_report = false,
	.sd_blksz_fw_dl	= 256,
	.supports_fw_dump = false,
};

static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
@@ -146,6 +170,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = {
	.reg		= &btmrvl_reg_87xx,
	.support_pscan_win_report = false,
	.sd_blksz_fw_dl	= 256,
	.supports_fw_dump = false,
};

static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = {
@@ -154,6 +179,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8887 = {
	.reg		= &btmrvl_reg_8887,
	.support_pscan_win_report = true,
	.sd_blksz_fw_dl	= 256,
	.supports_fw_dump = false,
};

static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
@@ -162,6 +188,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
	.reg		= &btmrvl_reg_8897,
	.support_pscan_win_report = true,
	.sd_blksz_fw_dl	= 256,
	.supports_fw_dump = true,
};

static const struct sdio_device_id btmrvl_sdio_ids[] = {
@@ -764,8 +791,8 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func)

	card = sdio_get_drvdata(func);
	if (!card || !card->priv) {
		BT_ERR("sbi_interrupt(%p) card or priv is "
				"NULL, card=%p\n", func, card);
		BT_ERR("sbi_interrupt(%p) card or priv is NULL, card=%p",
		       func, card);
		return;
	}

@@ -1080,6 +1107,277 @@ static int btmrvl_sdio_wakeup_fw(struct btmrvl_private *priv)
	return ret;
}

static void btmrvl_sdio_dump_regs(struct btmrvl_private *priv)
{
	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
	int ret = 0;
	unsigned int reg, reg_start, reg_end;
	char buf[256], *ptr;
	u8 loop, func, data;
	int MAX_LOOP = 2;

	btmrvl_sdio_wakeup_fw(priv);
	sdio_claim_host(card->func);

	for (loop = 0; loop < MAX_LOOP; loop++) {
		memset(buf, 0, sizeof(buf));
		ptr = buf;

		if (loop == 0) {
			/* Read the registers of SDIO function0 */
			func = loop;
			reg_start = 0;
			reg_end = 9;
		} else {
			func = 2;
			reg_start = 0;
			reg_end = 0x09;
		}

		ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ",
			       func, reg_start, reg_end);
		for (reg = reg_start; reg <= reg_end; reg++) {
			if (func == 0)
				data = sdio_f0_readb(card->func, reg, &ret);
			else
				data = sdio_readb(card->func, reg, &ret);

			if (!ret) {
				ptr += sprintf(ptr, "%02x ", data);
			} else {
				ptr += sprintf(ptr, "ERR");
				break;
			}
		}

		BT_INFO("%s", buf);
	}

	sdio_release_host(card->func);
}

/* This function read/write firmware */
static enum
rdwr_status btmrvl_sdio_rdwr_firmware(struct btmrvl_private *priv,
				      u8 doneflag)
{
	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
	int ret, tries;
	u8 ctrl_data = 0;

	sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl,
		    &ret);

	if (ret) {
		BT_ERR("SDIO write err");
		return RDWR_STATUS_FAILURE;
	}

	for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
		ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
				       &ret);

		if (ret) {
			BT_ERR("SDIO read err");
			return RDWR_STATUS_FAILURE;
		}

		if (ctrl_data == FW_DUMP_DONE)
			break;
		if (doneflag && ctrl_data == doneflag)
			return RDWR_STATUS_DONE;
		if (ctrl_data != FW_DUMP_HOST_READY) {
			BT_INFO("The ctrl reg was changed, re-try again!");
			sdio_writeb(card->func, FW_DUMP_HOST_READY,
				    card->reg->fw_dump_ctrl, &ret);
			if (ret) {
				BT_ERR("SDIO write err");
				return RDWR_STATUS_FAILURE;
			}
		}
		usleep_range(100, 200);
	}

	if (ctrl_data == FW_DUMP_HOST_READY) {
		BT_ERR("Fail to pull ctrl_data");
		return RDWR_STATUS_FAILURE;
	}

	return RDWR_STATUS_SUCCESS;
}

/* This function dump sdio register and memory data */
static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
{
	struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
	int ret = 0;
	unsigned int reg, reg_start, reg_end;
	enum rdwr_status stat;
	u8 *dbg_ptr, *end_ptr, *fw_dump_data, *fw_dump_ptr;
	u8 dump_num, idx, i, read_reg, doneflag = 0;
	u32 memory_size, fw_dump_len = 0;

	/* dump sdio register first */
	btmrvl_sdio_dump_regs(priv);

	if (!card->supports_fw_dump) {
		BT_ERR("Firmware dump not supported for this card!");
		return;
	}

	for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
		struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];

		if (entry->mem_ptr) {
			vfree(entry->mem_ptr);
			entry->mem_ptr = NULL;
		}
		entry->mem_size = 0;
	}

	btmrvl_sdio_wakeup_fw(priv);
	sdio_claim_host(card->func);

	BT_INFO("== btmrvl firmware dump start ==");

	stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
	if (stat == RDWR_STATUS_FAILURE)
		goto done;

	reg = card->reg->fw_dump_start;
	/* Read the number of the memories which will dump */
	dump_num = sdio_readb(card->func, reg, &ret);

	if (ret) {
		BT_ERR("SDIO read memory length err");
		goto done;
	}

	/* Read the length of every memory which will dump */
	for (idx = 0; idx < dump_num; idx++) {
		struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];

		stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
		if (stat == RDWR_STATUS_FAILURE)
			goto done;

		memory_size = 0;
		reg = card->reg->fw_dump_start;
		for (i = 0; i < 4; i++) {
			read_reg = sdio_readb(card->func, reg, &ret);
			if (ret) {
				BT_ERR("SDIO read err");
				goto done;
			}
			memory_size |= (read_reg << i*8);
			reg++;
		}

		if (memory_size == 0) {
			BT_INFO("Firmware dump finished!");
			break;
		}

		BT_INFO("%s_SIZE=0x%x", entry->mem_name, memory_size);
		entry->mem_ptr = vzalloc(memory_size + 1);
		entry->mem_size = memory_size;
		if (!entry->mem_ptr) {
			BT_ERR("Vzalloc %s failed", entry->mem_name);
			goto done;
		}

		fw_dump_len += (strlen("========Start dump ") +
				strlen(entry->mem_name) +
				strlen("========\n") +
				(memory_size + 1) +
				strlen("\n========End dump========\n"));

		dbg_ptr = entry->mem_ptr;
		end_ptr = dbg_ptr + memory_size;

		doneflag = entry->done_flag;
		BT_INFO("Start %s output, please wait...",
			entry->mem_name);

		do {
			stat = btmrvl_sdio_rdwr_firmware(priv, doneflag);
			if (stat == RDWR_STATUS_FAILURE)
				goto done;

			reg_start = card->reg->fw_dump_start;
			reg_end = card->reg->fw_dump_end;
			for (reg = reg_start; reg <= reg_end; reg++) {
				*dbg_ptr = sdio_readb(card->func, reg, &ret);
				if (ret) {
					BT_ERR("SDIO read err");
					goto done;
				}
				if (dbg_ptr < end_ptr)
					dbg_ptr++;
				else
					BT_ERR("Allocated buffer not enough");
			}

			if (stat != RDWR_STATUS_DONE) {
				continue;
			} else {
				BT_INFO("%s done: size=0x%tx",
					entry->mem_name,
					dbg_ptr - entry->mem_ptr);
				break;
			}
		} while (1);
	}

	BT_INFO("== btmrvl firmware dump end ==");

done:
	sdio_release_host(card->func);

	if (fw_dump_len == 0)
		return;

	fw_dump_data = vzalloc(fw_dump_len+1);
	if (!fw_dump_data) {
		BT_ERR("Vzalloc fw_dump_data fail!");
		return;
	}
	fw_dump_ptr = fw_dump_data;

	/* Dump all the memory data into single file, a userspace script will
	   be used to split all the memory data to multiple files*/
	BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump start");
	for (idx = 0; idx < dump_num; idx++) {
		struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];

		if (entry->mem_ptr) {
			strcpy(fw_dump_ptr, "========Start dump ");
			fw_dump_ptr += strlen("========Start dump ");

			strcpy(fw_dump_ptr, entry->mem_name);
			fw_dump_ptr += strlen(entry->mem_name);

			strcpy(fw_dump_ptr, "========\n");
			fw_dump_ptr += strlen("========\n");

			memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size);
			fw_dump_ptr += entry->mem_size;

			strcpy(fw_dump_ptr, "\n========End dump========\n");
			fw_dump_ptr += strlen("\n========End dump========\n");

			vfree(mem_type_mapping_tbl[idx].mem_ptr);
			mem_type_mapping_tbl[idx].mem_ptr = NULL;
		}
	}

	/* fw_dump_data will be free in device coredump release function
	   after 5 min*/
	dev_coredumpv(&priv->btmrvl_dev.hcidev->dev, fw_dump_data,
		      fw_dump_len, GFP_KERNEL);
	BT_INFO("== btmrvl firmware dump to /sys/class/devcoredump end");
}

static int btmrvl_sdio_probe(struct sdio_func *func,
					const struct sdio_device_id *id)
{
@@ -1103,6 +1401,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
		card->reg = data->reg;
		card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
		card->support_pscan_win_report = data->support_pscan_win_report;
		card->supports_fw_dump = data->supports_fw_dump;
	}

	if (btmrvl_sdio_register_dev(card) < 0) {
@@ -1134,6 +1433,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
	priv->hw_host_to_card = btmrvl_sdio_host_to_card;
	priv->hw_wakeup_firmware = btmrvl_sdio_wakeup_fw;
	priv->hw_process_int_status = btmrvl_sdio_process_int_status;
	priv->firmware_dump = btmrvl_sdio_dump_firmware;

	if (btmrvl_register_hdev(priv)) {
		BT_ERR("Register hdev failed!");
Loading