blob: a186ea8c4940a7407ab8e37c5c6e4087264da932 [file] [log] [blame]
/* Copyright (c) 2015-2016, 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, 00200, 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, 00200, 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");