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

Commit 7fafce23 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "esoc: Add debug engine for external modems."

parents b9264119 a2cee7ae
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -49,4 +49,13 @@ config ESOC_MDM_DRV
	  such as mdm9x25/mdm9x35/mdm9x45/mdm9x55/QSC. Allows the primary soc to put the
	  external modem in a specific mode. Also listens for events on the
	  external modem.

config ESOC_MDM_DBG_ENG
	tristate "debug engine for 4x series external modems"
	depends on ESOC_MDM_DRV
	help
	  Provides a user interface to mask out certain commands sent
	  by command engine to the external modem. Also allows masking
	  of certain notifications being sent to the external modem.

endif
+1 −0
Original line number Diff line number Diff line
@@ -6,3 +6,4 @@ obj-$(CONFIG_ESOC_DEV) += esoc_dev.o
obj-$(CONFIG_ESOC_CLIENT)	+= esoc_client.o
obj-$(CONFIG_ESOC_MDM_4x)	+= esoc-mdm-pon.o esoc-mdm-4x.o
obj-$(CONFIG_ESOC_MDM_DRV)	+= esoc-mdm-drv.o
obj-$(CONFIG_ESOC_MDM_DBG_ENG)	+= esoc-mdm-dbg-eng.o
+204 −0
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include <linux/atomic.h>
#include <linux/device.h>
#include "esoc.h"

/*
 * cmd_mask : Specifies if a command/notifier is masked, and
 * whats the trigger value for mask to take effect.
 * @mask_trigger: trigger value for mask.
 * @mask: boolean to determine if command should be masked.
 */
struct esoc_mask {
	atomic_t mask_trigger;
	bool mask;
};

/*
 * manual_to_esoc_cmd: Converts a user provided command
 * to a corresponding esoc command.
 * @cmd: ESOC command
 * @manual_cmd: user specified command string.
 */
struct manual_to_esoc_cmd {
	unsigned int cmd;
	char manual_cmd[20];
};

/*
 * manual_to_esoc_notify: Converts a user provided notification
 * to corresponding esoc notification for Primary SOC.
 * @notfication: ESOC notification.
 * @manual_notifier: user specified notification string.
 */
struct manual_to_esoc_notify {
	unsigned int notify;
	char manual_notify[20];
};

static const struct manual_to_esoc_cmd cmd_map[] = {
	{
		.cmd = ESOC_PWR_ON,
		.manual_cmd = "PON",
	},
	{
		.cmd = ESOC_PREPARE_DEBUG,
		.manual_cmd = "ENTER_DLOAD",
	},
	{	.cmd = ESOC_PWR_OFF,
		.manual_cmd = "POFF",
	},
	{
		.cmd = ESOC_FORCE_PWR_OFF,
		.manual_cmd = "FORCE_POFF",
	},
};

static struct esoc_mask cmd_mask[] = {
	[ESOC_PWR_ON] = {
		.mask = false,
		.mask_trigger = ATOMIC_INIT(1),
	},
	[ESOC_PREPARE_DEBUG] = {
		.mask = false,
		.mask_trigger = ATOMIC_INIT(0),
	},
	[ESOC_PWR_OFF] = {
		.mask = false,
		.mask_trigger = ATOMIC_INIT(0),
	},
	[ESOC_FORCE_PWR_OFF] = {
		.mask = false,
		.mask_trigger = ATOMIC_INIT(0),
	},
};

static const struct manual_to_esoc_notify notify_map[] = {
	{
		.notify = ESOC_PRIMARY_REBOOT,
		.manual_notify = "REBOOT",
	},
	{
		.notify = ESOC_PRIMARY_CRASH,
		.manual_notify = "PANIC",
	},
};

static struct esoc_mask notify_mask[] = {
	[ESOC_PRIMARY_REBOOT] = {
		.mask = false,
		.mask_trigger = ATOMIC_INIT(0),
	},
	[ESOC_PRIMARY_CRASH] = {
		.mask = false,
		.mask_trigger = ATOMIC_INIT(0),
	},
};

bool dbg_check_cmd_mask(unsigned int cmd)
{
	pr_debug("command to mask %d\n", cmd);
	if (cmd_mask[cmd].mask)
		return atomic_add_negative(-1, &cmd_mask[cmd].mask_trigger);
	else
		return false;
}
EXPORT_SYMBOL(dbg_check_cmd_mask);

bool dbg_check_notify_mask(unsigned int notify)
{
	pr_debug("notifier to mask %d\n", notify);
	if (notify_mask[notify].mask)
		return atomic_add_negative(-1,
					&notify_mask[notify].mask_trigger);
	else
		return false;
}
EXPORT_SYMBOL(dbg_check_notify_mask);
/*
 * Create driver attributes that let you mask
 * specific commands.
 */
static ssize_t cmd_mask_store(struct device_driver *drv, const char *buf,
							size_t count)
{
	unsigned int cmd, i;

	pr_debug("user input command %s", buf);
	for (i = 0; i < ARRAY_SIZE(cmd_map); i++) {
		if (!strcmp(cmd_map[i].manual_cmd, buf)) {
			/*
			 * Map manual command string to ESOC command
			 * set mask for ESOC command
			 */
			cmd = cmd_map[i].cmd;
			cmd_mask[cmd].mask = true;
			pr_debug("Setting mask for manual command %s\n",
								buf);
			break;
		}
	}
	if (i >= ARRAY_SIZE(cmd_map))
		pr_err("invalid command specified\n");
	return count;
}
static DRIVER_ATTR(command_mask, S_IWUSR, NULL, cmd_mask_store);

static ssize_t notifier_mask_store(struct device_driver *drv, const char *buf,
							size_t count)
{
	unsigned int notify, i;

	pr_debug("user input notifier %s", buf);
	for (i = 0; i < ARRAY_SIZE(notify_map); i++) {
		if (!strcmp(buf, notify_map[i].manual_notify)) {
			/*
			 * Map manual notifier string to primary soc
			 * notifier. Also set mask for the notifier.
			 */
			notify = notify_map[i].notify;
			notify_mask[notify].mask = true;
			pr_debug("Setting mask for manual notification %s\n",
									buf);
			break;
		}
	}
	if (i >= ARRAY_SIZE(notify_map))
		pr_err("invalid notifier specified\n");
	return count;
}
static DRIVER_ATTR(notifier_mask, S_IWUSR, NULL, notifier_mask_store);

int mdm_dbg_eng_init(struct esoc_drv *esoc_drv)
{
	int ret;
	struct device_driver *drv = &esoc_drv->driver;

	ret = driver_create_file(drv, &driver_attr_command_mask);
	if (ret) {
		pr_err("Unable to create command mask file\n");
		goto cmd_mask_err;
	}
	ret = driver_create_file(drv, &driver_attr_notifier_mask);
	if (ret) {
		pr_err("Unable to create notify mask file\n");
		goto notify_mask_err;
	}
	return 0;
notify_mask_err:
	driver_remove_file(drv, &driver_attr_command_mask);
cmd_mask_err:
	return ret;
}
EXPORT_SYMBOL(mdm_dbg_eng_init);
MODULE_LICENSE("GPL V2");
+29 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/workqueue.h>
#include <linux/reboot.h>
#include "esoc.h"
#include "mdm-dbg.h"

enum {
	 PWR_OFF = 0x1,
@@ -48,6 +49,8 @@ static int esoc_msm_restart_handler(struct notifier_block *nb,
	struct esoc_clink *esoc_clink = mdm_drv->esoc_clink;
	const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
	if (action == SYS_RESTART) {
		if (mdm_dbg_stall_notify(ESOC_PRIMARY_REBOOT))
			return NOTIFY_OK;
		dev_dbg(&esoc_clink->dev, "Notifying esoc of cold reboot\n");
		clink_ops->notify(ESOC_PRIMARY_REBOOT, esoc_clink);
	}
@@ -100,6 +103,8 @@ static void mdm_crash_shutdown(const struct subsys_desc *mdm_subsys)
							struct esoc_clink,
								subsys);
	const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
	if (mdm_dbg_stall_notify(ESOC_PRIMARY_CRASH))
		return;
	clink_ops->notify(ESOC_PRIMARY_CRASH, esoc_clink);
}

@@ -113,6 +118,12 @@ static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys,
	const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;

	if (mdm_drv->mode == CRASH || mdm_drv->mode == PEER_CRASH) {
		if (mdm_dbg_stall_cmd(ESOC_PREPARE_DEBUG))
			/* We want to mask debug command.
			 * In this case return success
			 * to move to next stage
			 */
			return 0;
		ret = clink_ops->cmd_exe(ESOC_PREPARE_DEBUG,
							esoc_clink);
		if (ret) {
@@ -124,8 +135,15 @@ static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys,
		if (esoc_clink->subsys.sysmon_shutdown_ret)
			ret = clink_ops->cmd_exe(ESOC_FORCE_PWR_OFF,
							esoc_clink);
		else
		else {
			if (mdm_dbg_stall_cmd(ESOC_PWR_OFF))
				/* Since power off command is masked
				 * we return success, and leave the state
				 * of the command engine as is.
				 */
				return 0;
			ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink);
		}
		if (ret) {
			dev_err(&esoc_clink->dev, "failed to exe power off\n");
			return ret;
@@ -149,6 +167,8 @@ static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
		wait_for_completion(&mdm_drv->req_eng_wait);
	}
	if (mdm_drv->mode == PWR_OFF) {
		if (mdm_dbg_stall_cmd(ESOC_PWR_ON))
			return -EBUSY;
		ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
		if (ret) {
			dev_err(&esoc_clink->dev, "pwr on fail\n");
@@ -238,6 +258,14 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv)
	ret = register_reboot_notifier(&mdm_drv->esoc_restart);
	if (ret)
		dev_err(&esoc_clink->dev, "register for reboot failed\n");
	ret = mdm_dbg_eng_init(drv);
	if (ret) {
		debug_init_done = false;
		dev_err(&esoc_clink->dev, "dbg engine failure\n");
	} else {
		dev_dbg(&esoc_clink->dev, "dbg engine initialized\n");
		debug_init_done = true;
	}
	return 0;
queue_err:
	esoc_clink_unregister_ssr(esoc_clink);

drivers/esoc/mdm-dbg.h

0 → 100644
+54 −0
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

static bool debug_init_done;

#ifndef CONFIG_ESOC_MDM_DBG_ENG

static inline bool dbg_check_cmd_mask(unsigned int cmd)
{
	return false;
}

static inline bool dbg_check_notify_mask(unsigned int notify)
{
	return false;
}

static inline int mdm_dbg_eng_init(struct esoc_drv *drv)
{
	return 0;
}

#else
extern bool dbg_check_cmd_mask(unsigned int cmd);
extern bool dbg_check_notify_mask(unsigned int notify);
extern int mdm_dbg_eng_init(struct esoc_drv *drv);
#endif

static inline bool mdm_dbg_stall_cmd(unsigned int cmd)
{
	if (debug_init_done)
		return dbg_check_cmd_mask(cmd);
	else
		return false;
}

static inline bool mdm_dbg_stall_notify(unsigned int notify)
{
	if (debug_init_done)
		return dbg_check_notify_mask(notify);
	else
		return false;
}