Loading drivers/soc/qcom/bgcom_interface.c +149 −0 Original line number Diff line number Diff line Loading @@ -21,11 +21,37 @@ #include <linux/errno.h> #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/kobject.h> #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/subsystem_notif.h> #include "bgcom.h" #include "linux/bgcom_interface.h" #define BGCOM "bg_com_dev" enum { SSR_DOMAIN_BG, SSR_DOMAIN_MODEM, SSR_DOMAIN_MAX, }; struct bg_event { enum bg_event_type e_type; }; struct service_info { const char name[32]; int domain_id; void *handle; struct notifier_block *nb; }; static char *ssr_domains[] = { "bg-wear", "modem", }; static DEFINE_MUTEX(bg_char_mutex); static struct cdev bg_cdev; static struct class *bg_class; Loading @@ -35,6 +61,23 @@ static int device_open; static void *handle; static struct bgcom_open_config_type config_type; /** * send_uevent(): send events to user space * pce : ssr event handle value * Return: 0 on success, standard Linux error code on error * * It adds pce value to event and broadcasts to user space. */ static int send_uevent(struct bg_event *pce) { char event_string[32]; char *envp[2] = { event_string, NULL }; snprintf(event_string, ARRAY_SIZE(event_string), "BG_EVENT=%d", pce->e_type); return kobject_uevent_env(&dev_ret->kobj, KOBJ_CHANGE, envp); } static int bgcom_char_open(struct inode *inode, struct file *file) { int ret; Loading Loading @@ -146,6 +189,12 @@ static long bg_com_ioctl(struct file *filp, if (ret < 0) pr_err("bgchar_write_cmd failed\n"); break; case SET_SPI_FREE: ret = bgcom_set_spi_state(BGCOM_SPI_FREE); break; case SET_SPI_BUSY: ret = bgcom_set_spi_state(BGCOM_SPI_BUSY); break; default: ret = -ENOIOCTLCMD; } Loading Loading @@ -213,6 +262,106 @@ static void __exit exit_bg_com_dev(void) unregister_chrdev_region(bg_dev, 1); } /** *ssr_bg_cb(): callback function is called *by ssr framework when BG goes down, up and during ramdump *collection. It handles BG shutdown and power up events. */ static int ssr_bg_cb(struct notifier_block *this, unsigned long opcode, void *data) { struct bg_event bge; switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: bge.e_type = BG_BEFORE_POWER_DOWN; send_uevent(&bge); break; case SUBSYS_AFTER_POWERUP: bge.e_type = BG_AFTER_POWER_UP; send_uevent(&bge); break; } return NOTIFY_DONE; } /** *ssr_modem_cb(): callback function is called *by ssr framework when modem goes down, up and during ramdump *collection. It handles modem shutdown and power up events. */ static int ssr_modem_cb(struct notifier_block *this, unsigned long opcode, void *data) { struct bg_event modeme; switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: modeme.e_type = MODEM_BEFORE_POWER_DOWN; send_uevent(&modeme); break; case SUBSYS_AFTER_POWERUP: modeme.e_type = MODEM_AFTER_POWER_UP; send_uevent(&modeme); break; } return NOTIFY_DONE; } static struct notifier_block ssr_modem_nb = { .notifier_call = ssr_modem_cb, .priority = 0, }; static struct notifier_block ssr_bg_nb = { .notifier_call = ssr_bg_cb, .priority = 0, }; static struct service_info service_data[2] = { { .name = "SSR_BG", .domain_id = SSR_DOMAIN_BG, .nb = &ssr_bg_nb, .handle = NULL, }, { .name = "SSR_MODEM", .domain_id = SSR_DOMAIN_MODEM, .nb = &ssr_modem_nb, .handle = NULL, }, }; /** * ssr_register checks that domain id should be in range and register * SSR framework for value at domain id. */ static int __init ssr_register(void) { int i; for (i = 0; i < ARRAY_SIZE(service_data); i++) { if ((service_data[i].domain_id < 0) || (service_data[i].domain_id >= SSR_DOMAIN_MAX)) { pr_err("Invalid service ID = %d\n", service_data[i].domain_id); } else { service_data[i].handle = subsys_notif_register_notifier( ssr_domains[service_data[i].domain_id], service_data[i].nb); if (IS_ERR_OR_NULL(service_data[i].handle)) { pr_err("subsys register failed for id = %d", service_data[i].domain_id); service_data[i].handle = NULL; } } } return 0; } module_init(init_bg_com_dev); late_initcall(ssr_register); module_exit(exit_bg_com_dev); MODULE_LICENSE("GPL v2"); include/uapi/linux/bgcom_interface.h +16 −1 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ #define BGCOM_REG_READ 0 #define BGCOM_AHB_READ 1 #define BGCOM_AHB_WRITE 2 #define BGCOM_SET_SPI_FREE 3 #define BGCOM_SET_SPI_BUSY 4 #define EXCHANGE_CODE 'V' struct bg_ui_data { Loading @@ -13,6 +15,13 @@ struct bg_ui_data { uint32_t num_of_words; }; enum bg_event_type { BG_BEFORE_POWER_DOWN = 1, BG_AFTER_POWER_UP, MODEM_BEFORE_POWER_DOWN, MODEM_AFTER_POWER_UP, }; #define REG_READ \ _IOWR(EXCHANGE_CODE, BGCOM_REG_READ, \ struct bg_ui_data) Loading @@ -22,4 +31,10 @@ struct bg_ui_data { #define AHB_WRITE \ _IOW(EXCHANGE_CODE, BGCOM_AHB_WRITE, \ struct bg_ui_data) #define SET_SPI_FREE \ _IOR(EXCHANGE_CODE, BGCOM_SET_SPI_FREE, \ struct bg_ui_data) #define SET_SPI_BUSY \ _IOR(EXCHANGE_CODE, BGCOM_SET_SPI_BUSY, \ struct bg_ui_data) #endif Loading
drivers/soc/qcom/bgcom_interface.c +149 −0 Original line number Diff line number Diff line Loading @@ -21,11 +21,37 @@ #include <linux/errno.h> #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/kobject.h> #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/subsystem_notif.h> #include "bgcom.h" #include "linux/bgcom_interface.h" #define BGCOM "bg_com_dev" enum { SSR_DOMAIN_BG, SSR_DOMAIN_MODEM, SSR_DOMAIN_MAX, }; struct bg_event { enum bg_event_type e_type; }; struct service_info { const char name[32]; int domain_id; void *handle; struct notifier_block *nb; }; static char *ssr_domains[] = { "bg-wear", "modem", }; static DEFINE_MUTEX(bg_char_mutex); static struct cdev bg_cdev; static struct class *bg_class; Loading @@ -35,6 +61,23 @@ static int device_open; static void *handle; static struct bgcom_open_config_type config_type; /** * send_uevent(): send events to user space * pce : ssr event handle value * Return: 0 on success, standard Linux error code on error * * It adds pce value to event and broadcasts to user space. */ static int send_uevent(struct bg_event *pce) { char event_string[32]; char *envp[2] = { event_string, NULL }; snprintf(event_string, ARRAY_SIZE(event_string), "BG_EVENT=%d", pce->e_type); return kobject_uevent_env(&dev_ret->kobj, KOBJ_CHANGE, envp); } static int bgcom_char_open(struct inode *inode, struct file *file) { int ret; Loading Loading @@ -146,6 +189,12 @@ static long bg_com_ioctl(struct file *filp, if (ret < 0) pr_err("bgchar_write_cmd failed\n"); break; case SET_SPI_FREE: ret = bgcom_set_spi_state(BGCOM_SPI_FREE); break; case SET_SPI_BUSY: ret = bgcom_set_spi_state(BGCOM_SPI_BUSY); break; default: ret = -ENOIOCTLCMD; } Loading Loading @@ -213,6 +262,106 @@ static void __exit exit_bg_com_dev(void) unregister_chrdev_region(bg_dev, 1); } /** *ssr_bg_cb(): callback function is called *by ssr framework when BG goes down, up and during ramdump *collection. It handles BG shutdown and power up events. */ static int ssr_bg_cb(struct notifier_block *this, unsigned long opcode, void *data) { struct bg_event bge; switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: bge.e_type = BG_BEFORE_POWER_DOWN; send_uevent(&bge); break; case SUBSYS_AFTER_POWERUP: bge.e_type = BG_AFTER_POWER_UP; send_uevent(&bge); break; } return NOTIFY_DONE; } /** *ssr_modem_cb(): callback function is called *by ssr framework when modem goes down, up and during ramdump *collection. It handles modem shutdown and power up events. */ static int ssr_modem_cb(struct notifier_block *this, unsigned long opcode, void *data) { struct bg_event modeme; switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: modeme.e_type = MODEM_BEFORE_POWER_DOWN; send_uevent(&modeme); break; case SUBSYS_AFTER_POWERUP: modeme.e_type = MODEM_AFTER_POWER_UP; send_uevent(&modeme); break; } return NOTIFY_DONE; } static struct notifier_block ssr_modem_nb = { .notifier_call = ssr_modem_cb, .priority = 0, }; static struct notifier_block ssr_bg_nb = { .notifier_call = ssr_bg_cb, .priority = 0, }; static struct service_info service_data[2] = { { .name = "SSR_BG", .domain_id = SSR_DOMAIN_BG, .nb = &ssr_bg_nb, .handle = NULL, }, { .name = "SSR_MODEM", .domain_id = SSR_DOMAIN_MODEM, .nb = &ssr_modem_nb, .handle = NULL, }, }; /** * ssr_register checks that domain id should be in range and register * SSR framework for value at domain id. */ static int __init ssr_register(void) { int i; for (i = 0; i < ARRAY_SIZE(service_data); i++) { if ((service_data[i].domain_id < 0) || (service_data[i].domain_id >= SSR_DOMAIN_MAX)) { pr_err("Invalid service ID = %d\n", service_data[i].domain_id); } else { service_data[i].handle = subsys_notif_register_notifier( ssr_domains[service_data[i].domain_id], service_data[i].nb); if (IS_ERR_OR_NULL(service_data[i].handle)) { pr_err("subsys register failed for id = %d", service_data[i].domain_id); service_data[i].handle = NULL; } } } return 0; } module_init(init_bg_com_dev); late_initcall(ssr_register); module_exit(exit_bg_com_dev); MODULE_LICENSE("GPL v2");
include/uapi/linux/bgcom_interface.h +16 −1 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ #define BGCOM_REG_READ 0 #define BGCOM_AHB_READ 1 #define BGCOM_AHB_WRITE 2 #define BGCOM_SET_SPI_FREE 3 #define BGCOM_SET_SPI_BUSY 4 #define EXCHANGE_CODE 'V' struct bg_ui_data { Loading @@ -13,6 +15,13 @@ struct bg_ui_data { uint32_t num_of_words; }; enum bg_event_type { BG_BEFORE_POWER_DOWN = 1, BG_AFTER_POWER_UP, MODEM_BEFORE_POWER_DOWN, MODEM_AFTER_POWER_UP, }; #define REG_READ \ _IOWR(EXCHANGE_CODE, BGCOM_REG_READ, \ struct bg_ui_data) Loading @@ -22,4 +31,10 @@ struct bg_ui_data { #define AHB_WRITE \ _IOW(EXCHANGE_CODE, BGCOM_AHB_WRITE, \ struct bg_ui_data) #define SET_SPI_FREE \ _IOR(EXCHANGE_CODE, BGCOM_SET_SPI_FREE, \ struct bg_ui_data) #define SET_SPI_BUSY \ _IOR(EXCHANGE_CODE, BGCOM_SET_SPI_BUSY, \ struct bg_ui_data) #endif