msm: SSR: Make subsystem restart into a bus

With a bus type in place for subsystem restart we can easily
discern what subsystems are present and see what state they are
in (either online or offline at the moment). We also support
polling of the state sysfs node so that programs can monitor
state transitions.

This is mostly a copy paste job from PIL with a 's/pil/subsys/g'.

Change-Id: I93443bbc9748468d6fc51d96ce4a3872617cec36
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
This commit is contained in:
Stephen Boyd 2012-06-21 19:10:48 -07:00
parent 922ce9fbad
commit 96bafdcd94
3 changed files with 203 additions and 47 deletions

View File

@ -0,0 +1,18 @@
What: /sys/bus/msm_subsys/devices/.../name
Date: July 2012
Contact: Stephen Boyd <sboyd@codeaurora.org>
Description:
Shows the name of the subsystem.
What: /sys/bus/msm_subsys/devices/.../state
Date: July 2012
Contact: Stephen Boyd <sboyd@codeaurora.org>
Description:
Shows the state state of a subsystem. Current states
supported are:
OFFLINE - subsystem is offline
ONLINE - subsystem is online
This file supports poll(3) to detect when a subsystem changes
state. Use POLLPRI to detect state changes.

View File

@ -27,8 +27,21 @@ enum {
RESET_LEVEL_MAX
};
struct device;
struct module;
/**
* struct subsys_desc - subsystem descriptor
* @name: name of subsystem
* @depends_on: subsystem this subsystem depends on to operate
* @dev: parent device
* @owner: module the descriptor belongs to
*/
struct subsys_desc {
const char *name;
const char *depends_on;
struct device *dev;
struct module *owner;
int (*shutdown)(const struct subsys_desc *desc);
int (*powerup)(const struct subsys_desc *desc);

View File

@ -27,6 +27,8 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/idr.h>
#include <asm/current.h>
@ -52,9 +54,18 @@ struct restart_log {
struct list_head list;
};
enum subsys_state {
SUBSYS_OFFLINE,
SUBSYS_ONLINE,
};
static const char * const subsys_states[] = {
[SUBSYS_OFFLINE] = "OFFLINE",
[SUBSYS_ONLINE] = "ONLINE",
};
struct subsys_device {
struct subsys_desc *desc;
struct list_head list;
struct wake_lock wake_lock;
char wlname[64];
struct work_struct work;
@ -62,6 +73,11 @@ struct subsys_device {
int restart_count;
void *notify;
struct device dev;
struct module *owner;
int count;
enum subsys_state state;
int id;
struct mutex shutdown_lock;
struct mutex powerup_lock;
@ -69,14 +85,43 @@ struct subsys_device {
void *restart_order;
};
static struct subsys_device *to_subsys(struct device *d)
{
return container_of(d, struct subsys_device, dev);
}
static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "%s\n", to_subsys(dev)->desc->name);
}
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
enum subsys_state state = to_subsys(dev)->state;
return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
}
static struct device_attribute subsys_attrs[] = {
__ATTR_RO(name),
__ATTR_RO(state),
__ATTR_NULL,
};
static struct bus_type subsys_bus_type = {
.name = "msm_subsys",
.dev_attrs = subsys_attrs,
};
static DEFINE_IDA(subsys_ida);
static int enable_ramdumps;
module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
struct workqueue_struct *ssr_wq;
static LIST_HEAD(restart_log_list);
static LIST_HEAD(subsystem_list);
static DEFINE_MUTEX(subsystem_list_lock);
static DEFINE_MUTEX(soc_order_reg_lock);
static DEFINE_MUTEX(restart_log_mutex);
@ -312,6 +357,24 @@ static void subsystem_powerup(struct subsys_device *dev, void *data)
panic("[%p]: Failed to powerup %s!", current, name);
}
static int __find_subsys(struct device *dev, void *data)
{
struct subsys_device *subsys = to_subsys(dev);
return !strcmp(subsys->desc->name, data);
}
static struct subsys_device *find_subsys(const char *str)
{
struct device *dev;
if (!str)
return NULL;
dev = bus_find_device(&subsys_bus_type, NULL, (void *)str,
__find_subsys);
return dev ? to_subsys(dev) : NULL;
}
static void subsystem_restart_wq_func(struct work_struct *work)
{
struct subsys_device *dev = container_of(work,
@ -436,8 +499,17 @@ static void __subsystem_restart_dev(struct subsys_device *dev)
int subsystem_restart_dev(struct subsys_device *dev)
{
const char *name = dev->desc->name;
const char *name;
if (!get_device(&dev->dev))
return -ENODEV;
if (!try_module_get(dev->owner)) {
put_device(&dev->dev);
return -ENODEV;
}
name = dev->desc->name;
pr_info("Restart sequence requested for %s, restart_level = %d.\n",
name, restart_level);
@ -454,6 +526,8 @@ int subsystem_restart_dev(struct subsys_device *dev)
panic("subsys-restart: Unknown restart level!\n");
break;
}
module_put(dev->owner);
put_device(&dev->dev);
return 0;
}
@ -461,69 +535,108 @@ EXPORT_SYMBOL(subsystem_restart_dev);
int subsystem_restart(const char *name)
{
struct subsys_device *dev;
int ret;
struct subsys_device *dev = find_subsys(name);
mutex_lock(&subsystem_list_lock);
list_for_each_entry(dev, &subsystem_list, list)
if (!strncmp(dev->desc->name, name, SUBSYS_NAME_MAX_LENGTH))
goto found;
dev = NULL;
found:
mutex_unlock(&subsystem_list_lock);
if (dev)
return subsystem_restart_dev(dev);
return -ENODEV;
if (!dev)
return -ENODEV;
ret = subsystem_restart_dev(dev);
put_device(&dev->dev);
return ret;
}
EXPORT_SYMBOL(subsystem_restart);
static void subsys_device_release(struct device *dev)
{
struct subsys_device *subsys = to_subsys(dev);
wake_lock_destroy(&subsys->wake_lock);
mutex_destroy(&subsys->shutdown_lock);
mutex_destroy(&subsys->powerup_lock);
ida_simple_remove(&subsys_ida, subsys->id);
kfree(subsys);
}
struct subsys_device *subsys_register(struct subsys_desc *desc)
{
struct subsys_device *dev;
struct subsys_device *subsys;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
if (!subsys)
return ERR_PTR(-ENOMEM);
dev->desc = desc;
dev->notify = subsys_notif_add_subsys(desc->name);
dev->restart_order = update_restart_order(dev);
subsys->desc = desc;
subsys->owner = desc->owner;
subsys->dev.parent = desc->dev;
subsys->dev.bus = &subsys_bus_type;
subsys->dev.release = subsys_device_release;
snprintf(dev->wlname, sizeof(dev->wlname), "ssr(%s)", desc->name);
wake_lock_init(&dev->wake_lock, WAKE_LOCK_SUSPEND, dev->wlname);
INIT_WORK(&dev->work, subsystem_restart_wq_func);
spin_lock_init(&dev->restart_lock);
subsys->notify = subsys_notif_add_subsys(desc->name);
subsys->restart_order = update_restart_order(subsys);
mutex_init(&dev->shutdown_lock);
mutex_init(&dev->powerup_lock);
snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
INIT_WORK(&subsys->work, subsystem_restart_wq_func);
spin_lock_init(&subsys->restart_lock);
mutex_lock(&subsystem_list_lock);
list_add(&dev->list, &subsystem_list);
mutex_unlock(&subsystem_list_lock);
subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
if (subsys->id < 0) {
ret = subsys->id;
goto err_ida;
}
dev_set_name(&subsys->dev, "subsys%d", subsys->id);
return dev;
mutex_init(&subsys->shutdown_lock);
mutex_init(&subsys->powerup_lock);
ret = device_register(&subsys->dev);
if (ret) {
put_device(&subsys->dev);
goto err_register;
}
return subsys;
err_register:
mutex_destroy(&subsys->shutdown_lock);
mutex_destroy(&subsys->powerup_lock);
ida_simple_remove(&subsys_ida, subsys->id);
err_ida:
wake_lock_destroy(&subsys->wake_lock);
kfree(subsys);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(subsys_register);
void subsys_unregister(struct subsys_device *dev)
void subsys_unregister(struct subsys_device *subsys)
{
if (IS_ERR_OR_NULL(dev))
if (IS_ERR_OR_NULL(subsys))
return;
mutex_lock(&subsystem_list_lock);
list_del(&dev->list);
mutex_unlock(&subsystem_list_lock);
wake_lock_destroy(&dev->wake_lock);
kfree(dev);
if (get_device(&subsys->dev)) {
mutex_lock(&subsys->powerup_lock);
WARN_ON(subsys->count);
device_unregister(&subsys->dev);
mutex_unlock(&subsys->powerup_lock);
put_device(&subsys->dev);
}
}
EXPORT_SYMBOL(subsys_unregister);
static int subsys_panic(struct device *dev, void *data)
{
struct subsys_device *subsys = to_subsys(dev);
if (subsys->desc->crash_shutdown)
subsys->desc->crash_shutdown(subsys->desc);
return 0;
}
static int ssr_panic_handler(struct notifier_block *this,
unsigned long event, void *ptr)
{
struct subsys_device *dev;
list_for_each_entry(dev, &subsystem_list, list)
if (dev->desc->crash_shutdown)
dev->desc->crash_shutdown(dev->desc);
bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
return NOTIFY_DONE;
}
@ -579,11 +692,23 @@ static int __init ssr_init_soc_restart_orders(void)
static int __init subsys_restart_init(void)
{
ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
if (!ssr_wq)
panic("Couldn't allocate workqueue for subsystem restart.\n");
int ret;
return ssr_init_soc_restart_orders();
ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
BUG_ON(!ssr_wq);
ret = bus_register(&subsys_bus_type);
if (ret)
goto err_bus;
ret = ssr_init_soc_restart_orders();
if (ret)
goto err_soc;
return 0;
err_soc:
bus_unregister(&subsys_bus_type);
err_bus:
destroy_workqueue(ssr_wq);
return ret;
}
arch_initcall(subsys_restart_init);