// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. * Author Wy Chuang */ #include #include #include #include #include #include #include "adapter_class.h" 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 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 struct attribute *adapter_class_attrs[] = { &dev_attr_name.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, 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, "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_AUTHOR("Wy Chuang "); MODULE_LICENSE("GPL");