diff --git a/drivers/usb/typec/adapter_class/Android.mk b/drivers/usb/typec/adapter_class/Android.mk new file mode 100755 index 000000000000..e402777aac94 --- /dev/null +++ b/drivers/usb/typec/adapter_class/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := adapter_class.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/usb/typec/adapter_class/Kbuild b/drivers/usb/typec/adapter_class/Kbuild new file mode 100755 index 000000000000..f003c3c245e9 --- /dev/null +++ b/drivers/usb/typec/adapter_class/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += adapter_class.o diff --git a/drivers/usb/typec/adapter_class/Makefile b/drivers/usb/typec/adapter_class/Makefile new file mode 100755 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/usb/typec/adapter_class/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/usb/typec/adapter_class/adapter_class.c b/drivers/usb/typec/adapter_class/adapter_class.c new file mode 100644 index 000000000000..756134ed9ee3 --- /dev/null +++ b/drivers/usb/typec/adapter_class/adapter_class.c @@ -0,0 +1,402 @@ +/* Copyright (c) 2020, 2021 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 +#include +#include +#include +#include +#include +#include +#include + +static struct class *adapter_class; + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + + return snprintf(buf, 20, "%s\n", + adapter_dev->props.alias_name ? + adapter_dev->props.alias_name : "anonymous"); +} + +static ssize_t pdo_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + struct adapter_power_cap cap; + ssize_t size = 0; + int i; + + cap.nr = 0; + cap.pdp = 0; + + adapter_dev = get_adapter_by_name("pd_adapter"); + if (adapter_dev) + pr_err("Found PD adapter [%s]\n", + adapter_dev->props.alias_name); + else + pr_err("*** Error : can't find PD adapter ***\n"); + + for (i = 0; i < ADAPTER_CAP_MAX_NR; i++) { + cap.max_mv[i] = 0; + cap.min_mv[i] = 0; + cap.ma[i] = 0; + cap.type[i] = 0; + cap.pwr_limit[i] = 0; + } + + adapter_dev_get_cap(adapter_dev, MMI_PD, &cap); + for (i = 0; i < cap.nr; i++) { + pr_err("%d: mV:%d,%d mA:%d type:%d\n", i, + cap.max_mv[i], cap.min_mv[i], cap.ma[i], cap.type[i]); + + size += snprintf(buf + size, PAGE_SIZE - size, + "%d: mV:%d,%d mA:%d type:%d\n", i, + cap.max_mv[i], cap.min_mv[i], cap.ma[i], cap.type[i]); + } + + return size; +} + +static ssize_t apdo_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + struct adapter_power_cap cap; + ssize_t size = 0; + int i; + + cap.nr = 0; + cap.pdp = 0; + + adapter_dev = get_adapter_by_name("pd_adapter"); + if (adapter_dev) + pr_err("Found PD adapter [%s]\n", + adapter_dev->props.alias_name); + else + pr_err("*** Error : can't find PD adapter ***\n"); + + for (i = 0; i < ADAPTER_CAP_MAX_NR; i++) { + cap.max_mv[i] = 0; + cap.min_mv[i] = 0; + cap.ma[i] = 0; + cap.type[i] = 0; + cap.pwr_limit[i] = 0; + } + + adapter_dev_get_cap(adapter_dev, MMI_PD_APDO, &cap); + for (i = 0; i < cap.nr; i++) { + pr_err("%d: mV:%d,%d mA:%d type:%d pwr_limit:%d pdp:%d\n", i, + cap.max_mv[i], cap.min_mv[i], cap.ma[i], + cap.type[i], cap.pwr_limit[i], cap.pdp); + + size += snprintf(buf + size, PAGE_SIZE - size, + "%d: mV:%d,%d mA:%d type:%d pwr_limit:%d pdp:%d\n", i, + cap.max_mv[i], cap.min_mv[i], cap.ma[i], + cap.type[i], cap.pwr_limit[i], cap.pdp); + } + + return size; +} + +static ssize_t pdo_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + int ret; + + adapter_dev = get_adapter_by_name("pd_adapter"); + if (adapter_dev) + pr_err("Found PD adapter [%s]\n", + adapter_dev->props.alias_name); + else + pr_err("*** Error : can't find PD adapter ***\n"); + + + ret = adapter_dev_set_cap(adapter_dev, MMI_PD, + 9000, 3000); + if (ret == MMI_ADAPTER_OK) + return snprintf(buf, 40, "select pdo to 9v3a mode ok\n"); + else + return snprintf(buf, 40, "select pdo to 9v3a mode fail\n"); +} + +static ssize_t apdo_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + int ret; + int vol = 5000; + + adapter_dev = get_adapter_by_name("pd_adapter"); + if (adapter_dev) + pr_err("Found PD adapter [%s]\n", + adapter_dev->props.alias_name); + else + pr_err("*** Error : can't find PD adapter ***\n"); + + ret = adapter_dev_set_cap(adapter_dev, MMI_PD_APDO_START, vol, 3000); + if (ret == MMI_ADAPTER_REJECT) + return snprintf(buf, 40, "start apdo reject\n"); + else if (ret != 0) + return snprintf(buf, 40, "start apdo err\n"); + + while (vol <10000) { + adapter_dev_set_cap(adapter_dev, MMI_PD_APDO, + vol, 3000); + vol += 200; + msleep(200); + } + + ret = adapter_dev_set_cap(adapter_dev, MMI_PD_APDO_END, vol, 3000); + if (ret == MMI_ADAPTER_REJECT) + return snprintf(buf, 40, "stop apdo reject\n"); + else if (ret != 0) + return snprintf(buf, 40, "stop apdo err\n"); + + return snprintf(buf, 40, "select apdo mode ok\n"); +} + + +static void adapter_device_release(struct device *dev) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + + kfree(adapter_dev); +} + +int adapter_dev_get_property(struct adapter_device *adapter_dev, + enum adapter_property sta) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->get_property) + return adapter_dev->ops->get_property(adapter_dev, sta); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_get_property); + +int adapter_dev_get_status(struct adapter_device *adapter_dev, + struct adapter_status *sta) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->get_status) + return adapter_dev->ops->get_status(adapter_dev, sta); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_get_status); + +int adapter_dev_get_output(struct adapter_device *adapter_dev, int *mV, int *mA) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->get_output) + return adapter_dev->ops->get_output(adapter_dev, mV, mA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_get_output); + +int adapter_dev_set_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, + int mV, int mA) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->set_cap) + return adapter_dev->ops->set_cap(adapter_dev, type, mV, mA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_set_cap); + + +int adapter_dev_get_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, + struct adapter_power_cap *cap) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->get_cap) + return adapter_dev->ops->get_cap(adapter_dev, type, cap); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_get_cap); + +static DEVICE_ATTR_RO(name); +static DEVICE_ATTR_RO(pdo_info); +static DEVICE_ATTR_RO(pdo_test); +static DEVICE_ATTR_RO(apdo_info); +static DEVICE_ATTR_RO(apdo_test); + +static struct attribute *adapter_class_attrs[] = { + &dev_attr_name.attr, + &dev_attr_pdo_info.attr, + &dev_attr_apdo_info.attr, + &dev_attr_pdo_test.attr, + &dev_attr_apdo_test.attr, + NULL, +}; + +static const struct attribute_group adapter_group = { + .attrs = adapter_class_attrs, +}; + +static const struct attribute_group *adapter_groups[] = { + &adapter_group, + NULL, +}; + +int register_adapter_device_notifier(struct adapter_device *adapter_dev, + struct notifier_block *nb) +{ + int ret = 0; + + if (!adapter_dev) + return -ENODEV; + + ret = srcu_notifier_chain_register(&adapter_dev->evt_nh, nb); + return ret; +} +EXPORT_SYMBOL(register_adapter_device_notifier); + +int unregister_adapter_device_notifier(struct adapter_device *adapter_dev, + struct notifier_block *nb) +{ + if (!adapter_dev) + return -ENODEV; + + return srcu_notifier_chain_unregister(&adapter_dev->evt_nh, nb); +} +EXPORT_SYMBOL(unregister_adapter_device_notifier); + +/** + * adapter_device_register - create and register a new object of + * adapter_device class. + * @name: the name of the new object + * @parent: a pointer to the parent device + * @devdata: an optional pointer to be stored for private driver use. + * The methods may retrieve it by using adapter_get_data(adapter_dev). + * @ops: the charger operations structure. + * + * Creates and registers new charger device. Returns either an + * ERR_PTR() or a pointer to the newly allocated device. + */ +struct adapter_device *adapter_device_register(const char *name, + struct device *parent, void *devdata, + const struct adapter_ops *ops, + const struct adapter_properties *props) +{ + struct adapter_device *adapter_dev; + static struct lock_class_key key; + struct srcu_notifier_head *head; + int rc; + + pr_notice("%s: name=%s\n", __func__, name); + adapter_dev = kzalloc(sizeof(*adapter_dev), GFP_KERNEL); + if (!adapter_dev) + return ERR_PTR(-ENOMEM); + + head = &adapter_dev->evt_nh; + srcu_init_notifier_head(head); + /* Rename srcu's lock to avoid LockProve warning */ + lockdep_init_map(&(&head->srcu)->dep_map, name, &key, 0); + mutex_init(&adapter_dev->ops_lock); + adapter_dev->dev.class = adapter_class; + adapter_dev->dev.parent = parent; + adapter_dev->dev.release = adapter_device_release; + dev_set_name(&adapter_dev->dev, "%s", name); + dev_set_drvdata(&adapter_dev->dev, devdata); + + /* Copy properties */ + if (props) { + memcpy(&adapter_dev->props, props, + sizeof(struct adapter_properties)); + } + rc = device_register(&adapter_dev->dev); + if (rc) { + kfree(adapter_dev); + return ERR_PTR(rc); + } + adapter_dev->ops = ops; + return adapter_dev; +} +EXPORT_SYMBOL(adapter_device_register); + +/** + * adapter_device_unregister - unregisters a switching charger device + * object. + * @adapter_dev: the switching charger device object to be unregistered + * and freed. + * + * Unregisters a previously registered via adapter_device_register object. + */ +void adapter_device_unregister(struct adapter_device *adapter_dev) +{ + if (!adapter_dev) + return; + + mutex_lock(&adapter_dev->ops_lock); + adapter_dev->ops = NULL; + mutex_unlock(&adapter_dev->ops_lock); + device_unregister(&adapter_dev->dev); +} +EXPORT_SYMBOL(adapter_device_unregister); + + +static int adapter_match_device_by_name(struct device *dev, + const void *data) +{ + const char *name = data; + + return strcmp(dev_name(dev), name) == 0; +} + +struct adapter_device *get_adapter_by_name(const char *name) +{ + struct device *dev; + + if (!name) + return (struct adapter_device *)NULL; + dev = class_find_device(adapter_class, NULL, name, + adapter_match_device_by_name); + + return dev ? to_adapter_device(dev) : NULL; + +} +EXPORT_SYMBOL(get_adapter_by_name); + +static void __exit adapter_class_exit(void) +{ + class_destroy(adapter_class); +} + +static int __init adapter_class_init(void) +{ + adapter_class = class_create(THIS_MODULE, "PD_charging_Adapter"); + if (IS_ERR(adapter_class)) { + pr_notice("Unable to create Charging Adapter class; errno = %ld\n", + PTR_ERR(adapter_class)); + return PTR_ERR(adapter_class); + } + adapter_class->dev_groups = adapter_groups; + return 0; +} + +subsys_initcall(adapter_class_init); +module_exit(adapter_class_exit); + +MODULE_DESCRIPTION("Adapter Class Device"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/usb/adapter_class.h b/include/linux/usb/adapter_class.h new file mode 100644 index 000000000000..e93859bada4f --- /dev/null +++ b/include/linux/usb/adapter_class.h @@ -0,0 +1,154 @@ +/* Copyright (c) 2020, 2021 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. + */ + +#ifndef LINUX_POWER_ADAPTER_CLASS_H +#define LINUX_POWER_ADAPTER_CLASS_H + +#include +#include +#include + + +#define ADAPTER_CAP_MAX_NR 10 + +struct adapter_power_cap { + uint8_t selected_cap_idx; + uint8_t nr; + uint8_t pdp; + uint8_t pwr_limit[ADAPTER_CAP_MAX_NR]; + int max_mv[ADAPTER_CAP_MAX_NR]; + int min_mv[ADAPTER_CAP_MAX_NR]; + int ma[ADAPTER_CAP_MAX_NR]; + int maxwatt[ADAPTER_CAP_MAX_NR]; + int minwatt[ADAPTER_CAP_MAX_NR]; + uint8_t type[ADAPTER_CAP_MAX_NR]; + int info[ADAPTER_CAP_MAX_NR]; +}; + +enum adapter_type { + MMI_PD_ADAPTER, +}; + +enum adapter_event { + MMI_PD_CONNECT_NONE, + MMI_PD_CONNECT_HARD_RESET, + MMI_PD_CONNECT_PE_READY_SNK, + MMI_PD_CONNECT_PE_READY_SNK_PD30, + MMI_PD_CONNECT_PE_READY_SNK_APDO, + MMI_PD_CONNECT_TYPEC_ONLY_SNK, + MMI_TYPEC_WD_STATUS, + MMI_TYPEC_HRESET_STATUS, +}; + +enum adapter_property { + TYPEC_RP_LEVEL, + PD_TYPE, +}; + +enum adapter_cap_type { + MMI_PD_APDO_START, + MMI_PD_APDO_END, + MMI_PD, + MMI_PD_APDO, + MMI_CAP_TYPE_UNKNOWN, +}; + +enum adapter_return_value { + MMI_ADAPTER_OK = 0, + MMI_ADAPTER_NOT_SUPPORT, + MMI_ADAPTER_TIMEOUT, + MMI_ADAPTER_REJECT, + MMI_ADAPTER_ERROR, + MMI_ADAPTER_ADJUST, +}; + + +struct adapter_status { + int temperature; + bool ocp; + bool otp; + bool ovp; +}; + +struct adapter_properties { + const char *alias_name; +}; + +struct adapter_device { + struct adapter_properties props; + const struct adapter_ops *ops; + struct mutex ops_lock; + struct device dev; + struct srcu_notifier_head evt_nh; + void *driver_data; + +}; + +struct adapter_ops { + int (*suspend)(struct adapter_device *dev, pm_message_t state); + int (*resume)(struct adapter_device *dev); + int (*get_property)(struct adapter_device *dev, + enum adapter_property pro); + int (*get_status)(struct adapter_device *dev, + struct adapter_status *sta); + int (*set_cap)(struct adapter_device *dev, enum adapter_cap_type type, + int mV, int mA); + int (*get_cap)(struct adapter_device *dev, enum adapter_cap_type type, + struct adapter_power_cap *cap); + int (*get_output)(struct adapter_device *dev, int *mV, int *mA); + +}; + +static inline void *adapter_dev_get_drvdata( + const struct adapter_device *adapter_dev) +{ + return adapter_dev->driver_data; +} + +static inline void adapter_dev_set_drvdata( + struct adapter_device *adapter_dev, void *data) +{ + adapter_dev->driver_data = data; +} + +extern struct adapter_device *adapter_device_register( + const char *name, + struct device *parent, void *devdata, const struct adapter_ops *ops, + const struct adapter_properties *props); +extern void adapter_device_unregister( + struct adapter_device *adapter_dev); +extern int register_adapter_device_notifier(struct adapter_device *adapter_dev, + struct notifier_block *nb); +extern int unregister_adapter_device_notifier( + struct adapter_device *adapter_dev, + struct notifier_block *nb); +extern struct adapter_device *get_adapter_by_name( + const char *name); + +#define to_adapter_device(obj) container_of(obj, struct adapter_device, dev) + +extern int adapter_dev_get_property(struct adapter_device *adapter_dev, + enum adapter_property sta); +extern int adapter_dev_get_status(struct adapter_device *adapter_dev, + struct adapter_status *sta); +extern int adapter_dev_get_output(struct adapter_device *adapter_dev, + int *mV, int *mA); +extern int adapter_dev_set_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, + int mV, int mA); +extern int adapter_dev_get_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, + struct adapter_power_cap *cap); + + +#endif /*LINUX_POWER_ADAPTER_CLASS_H*/ +