235 lines · 5.4 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only// Copyright (c) 2018-2021 Intel Corporation #include <linux/bug.h>#include <linux/device.h>#include <linux/export.h>#include <linux/idr.h>#include <linux/module.h>#include <linux/of.h>#include <linux/peci.h>#include <linux/pm_runtime.h>#include <linux/property.h>#include <linux/slab.h> #include "internal.h" static DEFINE_IDA(peci_controller_ida); static void peci_controller_dev_release(struct device *dev){	struct peci_controller *controller = to_peci_controller(dev); 	mutex_destroy(&controller->bus_lock);	ida_free(&peci_controller_ida, controller->id);	kfree(controller);} const struct device_type peci_controller_type = {	.release	= peci_controller_dev_release,}; int peci_controller_scan_devices(struct peci_controller *controller){	int ret;	u8 addr; 	for (addr = PECI_BASE_ADDR; addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; addr++) {		ret = peci_device_create(controller, addr);		if (ret)			return ret;	} 	return 0;} static struct peci_controller *peci_controller_alloc(struct device *dev,						     const struct peci_controller_ops *ops){	struct peci_controller *controller;	int ret; 	if (!ops->xfer)		return ERR_PTR(-EINVAL); 	controller = kzalloc_obj(*controller);	if (!controller)		return ERR_PTR(-ENOMEM); 	ret = ida_alloc_max(&peci_controller_ida, U8_MAX, GFP_KERNEL);	if (ret < 0)		goto err;	controller->id = ret; 	controller->ops = ops; 	controller->dev.parent = dev;	controller->dev.bus = &peci_bus_type;	controller->dev.type = &peci_controller_type; 	device_initialize(&controller->dev); 	mutex_init(&controller->bus_lock); 	return controller; err:	kfree(controller);	return ERR_PTR(ret);} static int unregister_child(struct device *dev, void *dummy){	peci_device_destroy(to_peci_device(dev)); 	return 0;} static void unregister_controller(void *_controller){	struct peci_controller *controller = _controller; 	/*	 * Detach any active PECI devices. This can't fail, thus we do not	 * check the returned value.	 */	device_for_each_child_reverse(&controller->dev, NULL, unregister_child); 	device_unregister(&controller->dev); 	fwnode_handle_put(controller->dev.fwnode); 	pm_runtime_disable(&controller->dev);} /** * devm_peci_controller_add() - add PECI controller * @dev: device for devm operations * @ops: pointer to controller specific methods * * In final stage of its probe(), peci_controller driver calls * devm_peci_controller_add() to register itself with the PECI bus. * * Return: Pointer to the newly allocated controller or ERR_PTR() in case of failure. */struct peci_controller *devm_peci_controller_add(struct device *dev,						 const struct peci_controller_ops *ops){	struct peci_controller *controller;	int ret; 	controller = peci_controller_alloc(dev, ops);	if (IS_ERR(controller))		return controller; 	ret = dev_set_name(&controller->dev, "peci-%d", controller->id);	if (ret)		goto err_put; 	pm_runtime_no_callbacks(&controller->dev);	pm_suspend_ignore_children(&controller->dev, true);	pm_runtime_enable(&controller->dev); 	device_set_node(&controller->dev, fwnode_handle_get(dev_fwnode(dev))); 	ret = device_add(&controller->dev);	if (ret)		goto err_fwnode; 	ret = devm_add_action_or_reset(dev, unregister_controller, controller);	if (ret)		return ERR_PTR(ret); 	/*	 * Ignoring retval since failures during scan are non-critical for	 * controller itself.	 */	peci_controller_scan_devices(controller); 	return controller; err_fwnode:	fwnode_handle_put(controller->dev.fwnode); 	pm_runtime_disable(&controller->dev); err_put:	put_device(&controller->dev); 	return ERR_PTR(ret);}EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, "PECI"); static const struct peci_device_id *peci_bus_match_device_id(const struct peci_device_id *id, struct peci_device *device){	while (id->x86_vfm != 0) {		if (id->x86_vfm == device->info.x86_vfm)			return id;		id++;	} 	return NULL;} static int peci_bus_device_match(struct device *dev, const struct device_driver *drv){	struct peci_device *device = to_peci_device(dev);	const struct peci_driver *peci_drv = to_peci_driver(drv); 	if (dev->type != &peci_device_type)		return 0; 	return !!peci_bus_match_device_id(peci_drv->id_table, device);} static int peci_bus_device_probe(struct device *dev){	struct peci_device *device = to_peci_device(dev);	struct peci_driver *driver = to_peci_driver(dev->driver); 	return driver->probe(device, peci_bus_match_device_id(driver->id_table, device));} static void peci_bus_device_remove(struct device *dev){	struct peci_device *device = to_peci_device(dev);	struct peci_driver *driver = to_peci_driver(dev->driver); 	if (driver->remove)		driver->remove(device);} const struct bus_type peci_bus_type = {	.name		= "peci",	.match		= peci_bus_device_match,	.probe		= peci_bus_device_probe,	.remove		= peci_bus_device_remove,	.bus_groups	= peci_bus_groups,}; static int __init peci_init(void){	int ret; 	ret = bus_register(&peci_bus_type);	if (ret < 0) {		pr_err("peci: failed to register PECI bus type!\n");		return ret;	} 	return 0;}module_init(peci_init); static void __exit peci_exit(void){	bus_unregister(&peci_bus_type);}module_exit(peci_exit); MODULE_AUTHOR("Jason M Bills <jason.m.bills@linux.intel.com>");MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");MODULE_DESCRIPTION("PECI bus core module");MODULE_LICENSE("GPL");

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t