| #include <linux/device.h> |
| #include <linux/module.h> |
| |
| #include <linux/notifier.h> |
| #include <linux/ccic/ccic_notifier.h> |
| #include <linux/sec_sysfs.h> |
| #include <linux/ccic/ccic_sysfs.h> |
| #ifdef CONFIG_BATTERY_SAMSUNG |
| #ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER |
| #include <linux/battery/battery_notifier.h> |
| #endif |
| #endif |
| #include <linux/usb_notify.h> |
| |
| #define DEBUG |
| #define SET_CCIC_NOTIFIER_BLOCK(nb, fn, dev) do { \ |
| (nb)->notifier_call = (fn); \ |
| (nb)->priority = (dev); \ |
| } while (0) |
| |
| #define DESTROY_CCIC_NOTIFIER_BLOCK(nb) \ |
| SET_CCIC_NOTIFIER_BLOCK(nb, NULL, -1) |
| |
| static struct ccic_notifier_struct ccic_notifier; |
| |
| struct device *ccic_device; |
| static int ccic_notifier_init_done = 0; |
| int ccic_notifier_init(void); |
| |
| char CCIC_NOTI_DEST_Print[CCIC_NOTI_DEST_NUM][10] = |
| { |
| {"INITIAL"}, |
| {"USB"}, |
| {"BATTERY"}, |
| {"PDIC"}, |
| {"MUIC"}, |
| {"CCIC"}, |
| {"MANAGER"}, |
| {"DP"}, |
| {"DPUSB"}, |
| {"ALL"}, |
| }; |
| |
| char CCIC_NOTI_ID_Print[CCIC_NOTI_ID_NUM][20] = |
| { |
| {"ID_INITIAL"}, |
| {"ID_ATTACH"}, |
| {"ID_RID"}, |
| {"ID_USB"}, |
| {"ID_POWER_STATUS"}, |
| {"ID_WATER"}, |
| {"ID_VCONN"}, |
| {"ID_OTG"}, |
| {"ID_TA"}, |
| {"ID_DP_CONNECT"}, |
| {"ID_DP_HPD"}, |
| {"ID_DP_LINK_CONF"}, |
| {"ID_DP_USB"}, |
| {"ID_ROLE_SWAP"}, |
| {"ID_FAC"}, |
| }; |
| |
| char CCIC_NOTI_RID_Print[CCIC_NOTI_RID_NUM][15] = |
| { |
| {"RID_UNDEFINED"}, |
| {"RID_000K"}, |
| {"RID_001K"}, |
| {"RID_255K"}, |
| {"RID_301K"}, |
| {"RID_523K"}, |
| {"RID_619K"}, |
| {"RID_OPEN"}, |
| }; |
| |
| char CCIC_NOTI_USB_STATUS_Print[CCIC_NOTI_USB_STATUS_NUM][20] = |
| { |
| {"USB_DETACH"}, |
| {"USB_ATTACH_DFP"}, |
| {"USB_ATTACH_UFP"}, |
| {"USB_ATTACH_DRP"}, |
| {"USB_ATTACH_NO_USB"}, |
| }; |
| |
| int ccic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier, |
| ccic_notifier_device_t listener) |
| { |
| int ret = 0; |
| |
| pr_info("%s: listener=%d register\n", __func__, listener); |
| |
| /* Check if CCIC Notifier is ready. */ |
| if(!ccic_notifier_init_done) |
| ccic_notifier_init(); |
| |
| if (!ccic_device) { |
| pr_err("%s: Not Initialized...\n", __func__); |
| return -1; |
| } |
| |
| SET_CCIC_NOTIFIER_BLOCK(nb, notifier, listener); |
| ret = blocking_notifier_chain_register(&(ccic_notifier.notifier_call_chain), nb); |
| if (ret < 0) |
| pr_err("%s: blocking_notifier_chain_register error(%d)\n", |
| __func__, ret); |
| |
| /* current ccic's attached_device status notify */ |
| nb->notifier_call(nb, 0, |
| &(ccic_notifier.ccic_template)); |
| |
| return ret; |
| } |
| |
| int ccic_notifier_unregister(struct notifier_block *nb) |
| { |
| int ret = 0; |
| |
| pr_info("%s: listener=%d unregister\n", __func__, nb->priority); |
| |
| ret = blocking_notifier_chain_unregister(&(ccic_notifier.notifier_call_chain), nb); |
| if (ret < 0) |
| pr_err("%s: blocking_notifier_chain_unregister error(%d)\n", |
| __func__, ret); |
| DESTROY_CCIC_NOTIFIER_BLOCK(nb); |
| |
| return ret; |
| } |
| |
| void ccic_uevent_work(int id, int state) |
| { |
| char *water[2] = { "CCIC=WATER", NULL }; |
| char *dry[2] = { "CCIC=DRY", NULL }; |
| char *vconn[2] = { "CCIC=VCONN", NULL }; |
| #if defined(CONFIG_SEC_FACTORY) |
| char ccicrid[15] = {0,}; |
| char *rid[2] = {ccicrid, NULL}; |
| char ccicFacErr[20] = {0,}; |
| char *facErr[2] = {ccicFacErr, NULL}; |
| #endif |
| |
| pr_info("usb: %s: id=%s state=%d\n", __func__, CCIC_NOTI_ID_Print[id], state); |
| |
| switch (id) { |
| case CCIC_NOTIFY_ID_WATER: |
| if (state) |
| kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, water); |
| else |
| kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, dry); |
| break; |
| case CCIC_NOTIFY_ID_VCONN: |
| kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, vconn); |
| break; |
| #if defined(CONFIG_SEC_FACTORY) |
| case CCIC_NOTIFY_ID_RID: |
| snprintf(ccicrid, sizeof(ccicrid), "%s", |
| (state<CCIC_NOTI_RID_NUM)? CCIC_NOTI_RID_Print[state] : CCIC_NOTI_RID_Print[0]); |
| kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, rid); |
| break; |
| case CCIC_NOTIFY_ID_FAC: |
| snprintf(ccicFacErr, sizeof(ccicFacErr), "%s:%d", |
| "ERR_STATE", state); |
| kobject_uevent_env(&ccic_device->kobj, KOBJ_CHANGE, facErr); |
| break; |
| #endif |
| default: |
| break; |
| } |
| } |
| |
| /* ccic's attached_device attach broadcast */ |
| int ccic_notifier_notify(CC_NOTI_TYPEDEF *p_noti, void *pd, int pdic_attach) |
| { |
| int ret = 0; |
| ccic_notifier.ccic_template = *p_noti; |
| |
| switch (p_noti->id) { |
| #ifdef CONFIG_BATTERY_SAMSUNG |
| #ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER |
| case CCIC_NOTIFY_ID_POWER_STATUS: /* PDIC_NOTIFY_EVENT_PD_SINK */ |
| pr_info("%s: src:%01x dest:%01x id:%02x " |
| "attach:%02x cable_type:%02x rprd:%01x\n", __func__, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->cable_type, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->rprd); |
| |
| if (pd != NULL) { |
| if (!((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach && |
| ((struct pdic_notifier_struct *)pd)->event != PDIC_NOTIFY_EVENT_CCIC_ATTACH) { |
| ((struct pdic_notifier_struct *)pd)->event = PDIC_NOTIFY_EVENT_DETACH; |
| } |
| ccic_notifier.ccic_template.pd = pd; |
| |
| pr_info("%s: PD event:%d, num:%d, sel:%d \n", __func__, |
| ((struct pdic_notifier_struct *)pd)->event, |
| ((struct pdic_notifier_struct *)pd)->sink_status.available_pdo_num, |
| ((struct pdic_notifier_struct *)pd)->sink_status.selected_pdo_num); |
| } |
| break; |
| #endif |
| #endif |
| case CCIC_NOTIFY_ID_ATTACH: |
| pr_info("%s: src:%01x dest:%01x id:%02x " |
| "attach:%02x cable_type:%02x rprd:%01x\n", __func__, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->cable_type, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->rprd); |
| break; |
| case CCIC_NOTIFY_ID_RID: |
| pr_info("%s: src:%01x dest:%01x id:%02x rid:%02x\n", __func__, |
| ((CC_NOTI_RID_TYPEDEF *)p_noti)->src, |
| ((CC_NOTI_RID_TYPEDEF *)p_noti)->dest, |
| ((CC_NOTI_RID_TYPEDEF *)p_noti)->id, |
| ((CC_NOTI_RID_TYPEDEF *)p_noti)->rid); |
| #if defined(CONFIG_SEC_FACTORY) |
| ccic_uevent_work(CCIC_NOTIFY_ID_RID,((CC_NOTI_RID_TYPEDEF *)p_noti)->rid); |
| #endif |
| break; |
| #ifdef CONFIG_SEC_FACTORY |
| case CCIC_NOTIFY_ID_FAC: |
| pr_info("%s: src:%01x dest:%01x id:%02x ErrState:%02x\n", __func__, |
| p_noti->src, p_noti->dest, p_noti->id, p_noti->sub1); |
| ccic_uevent_work(CCIC_NOTIFY_ID_FAC, p_noti->sub1); |
| return 0; |
| #endif |
| case CCIC_NOTIFY_ID_WATER: |
| pr_info("%s: src:%01x dest:%01x id:%02x attach:%02x\n", __func__, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach); |
| ccic_uevent_work(CCIC_NOTIFY_ID_WATER, ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach); |
| #ifdef CONFIG_SEC_FACTORY |
| return 0; |
| #endif |
| break; |
| case CCIC_NOTIFY_ID_VCONN: |
| ccic_uevent_work(CCIC_NOTIFY_ID_VCONN, 0); |
| break; |
| case CCIC_NOTIFY_ID_ROLE_SWAP: |
| pr_info("%s: src:%01x dest:%01x id:%02x sub1:%02x\n", __func__, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->src, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->dest, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->id, |
| ((CC_NOTI_ATTACH_TYPEDEF *)p_noti)->attach); |
| break; |
| default: |
| pr_info("%s: src:%01x dest:%01x id:%02x " |
| "sub1:%d sub2:%02x sub3:%02x\n", __func__, |
| ((CC_NOTI_TYPEDEF *)p_noti)->src, |
| ((CC_NOTI_TYPEDEF *)p_noti)->dest, |
| ((CC_NOTI_TYPEDEF *)p_noti)->id, |
| ((CC_NOTI_TYPEDEF *)p_noti)->sub1, |
| ((CC_NOTI_TYPEDEF *)p_noti)->sub2, |
| ((CC_NOTI_TYPEDEF *)p_noti)->sub3); |
| break; |
| } |
| #ifdef CONFIG_USB_NOTIFY_PROC_LOG |
| store_usblog_notify(NOTIFY_CCIC_EVENT, (void*)p_noti , NULL); |
| #endif |
| ret = blocking_notifier_call_chain(&(ccic_notifier.notifier_call_chain), |
| p_noti->id, &(ccic_notifier.ccic_template)); |
| |
| |
| switch (ret) { |
| case NOTIFY_STOP_MASK: |
| case NOTIFY_BAD: |
| pr_err("%s: notify error occur(0x%x)\n", __func__, ret); |
| break; |
| case NOTIFY_DONE: |
| case NOTIFY_OK: |
| pr_info("%s: notify done(0x%x)\n", __func__, ret); |
| break; |
| default: |
| pr_info("%s: notify status unknown(0x%x)\n", __func__, ret); |
| break; |
| } |
| |
| return ret; |
| |
| } |
| |
| int ccic_notifier_init(void) |
| { |
| int ret = 0; |
| |
| pr_info("%s\n", __func__); |
| if (ccic_notifier_init_done) { |
| pr_err("%s already registered\n", __func__); |
| goto out; |
| } |
| ccic_notifier_init_done = 1; |
| ccic_device = sec_device_create(NULL, "ccic"); |
| if (IS_ERR(ccic_device)) { |
| pr_err("%s Failed to create device(switch)!\n", __func__); |
| ret = -ENODEV; |
| goto out; |
| } |
| |
| /* create sysfs group */ |
| ret = sysfs_create_group(&ccic_device->kobj, &ccic_sysfs_group); |
| if (ret) { |
| pr_err("%s: ccic sysfs fail, ret %d", __func__, ret); |
| goto out; |
| } |
| |
| |
| BLOCKING_INIT_NOTIFIER_HEAD(&(ccic_notifier.notifier_call_chain)); |
| |
| out: |
| return ret; |
| } |
| |
| static void __exit ccic_notifier_exit(void) |
| { |
| pr_info("%s: exit\n", __func__); |
| } |
| |
| device_initcall(ccic_notifier_init); |
| module_exit(ccic_notifier_exit); |