brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · 25e4657 Raw
236 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright (c) 2018-2021 Intel Corporation3 4#include <linux/bug.h>5#include <linux/device.h>6#include <linux/export.h>7#include <linux/idr.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/peci.h>11#include <linux/pm_runtime.h>12#include <linux/property.h>13#include <linux/slab.h>14 15#include "internal.h"16 17static DEFINE_IDA(peci_controller_ida);18 19static void peci_controller_dev_release(struct device *dev)20{21	struct peci_controller *controller = to_peci_controller(dev);22 23	mutex_destroy(&controller->bus_lock);24	ida_free(&peci_controller_ida, controller->id);25	kfree(controller);26}27 28const struct device_type peci_controller_type = {29	.release	= peci_controller_dev_release,30};31 32int peci_controller_scan_devices(struct peci_controller *controller)33{34	int ret;35	u8 addr;36 37	for (addr = PECI_BASE_ADDR; addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; addr++) {38		ret = peci_device_create(controller, addr);39		if (ret)40			return ret;41	}42 43	return 0;44}45 46static struct peci_controller *peci_controller_alloc(struct device *dev,47						     const struct peci_controller_ops *ops)48{49	struct peci_controller *controller;50	int ret;51 52	if (!ops->xfer)53		return ERR_PTR(-EINVAL);54 55	controller = kzalloc(sizeof(*controller), GFP_KERNEL);56	if (!controller)57		return ERR_PTR(-ENOMEM);58 59	ret = ida_alloc_max(&peci_controller_ida, U8_MAX, GFP_KERNEL);60	if (ret < 0)61		goto err;62	controller->id = ret;63 64	controller->ops = ops;65 66	controller->dev.parent = dev;67	controller->dev.bus = &peci_bus_type;68	controller->dev.type = &peci_controller_type;69 70	device_initialize(&controller->dev);71 72	mutex_init(&controller->bus_lock);73 74	return controller;75 76err:77	kfree(controller);78	return ERR_PTR(ret);79}80 81static int unregister_child(struct device *dev, void *dummy)82{83	peci_device_destroy(to_peci_device(dev));84 85	return 0;86}87 88static void unregister_controller(void *_controller)89{90	struct peci_controller *controller = _controller;91 92	/*93	 * Detach any active PECI devices. This can't fail, thus we do not94	 * check the returned value.95	 */96	device_for_each_child_reverse(&controller->dev, NULL, unregister_child);97 98	device_unregister(&controller->dev);99 100	fwnode_handle_put(controller->dev.fwnode);101 102	pm_runtime_disable(&controller->dev);103}104 105/**106 * devm_peci_controller_add() - add PECI controller107 * @dev: device for devm operations108 * @ops: pointer to controller specific methods109 *110 * In final stage of its probe(), peci_controller driver calls111 * devm_peci_controller_add() to register itself with the PECI bus.112 *113 * Return: Pointer to the newly allocated controller or ERR_PTR() in case of failure.114 */115struct peci_controller *devm_peci_controller_add(struct device *dev,116						 const struct peci_controller_ops *ops)117{118	struct peci_controller *controller;119	int ret;120 121	controller = peci_controller_alloc(dev, ops);122	if (IS_ERR(controller))123		return controller;124 125	ret = dev_set_name(&controller->dev, "peci-%d", controller->id);126	if (ret)127		goto err_put;128 129	pm_runtime_no_callbacks(&controller->dev);130	pm_suspend_ignore_children(&controller->dev, true);131	pm_runtime_enable(&controller->dev);132 133	device_set_node(&controller->dev, fwnode_handle_get(dev_fwnode(dev)));134 135	ret = device_add(&controller->dev);136	if (ret)137		goto err_fwnode;138 139	ret = devm_add_action_or_reset(dev, unregister_controller, controller);140	if (ret)141		return ERR_PTR(ret);142 143	/*144	 * Ignoring retval since failures during scan are non-critical for145	 * controller itself.146	 */147	peci_controller_scan_devices(controller);148 149	return controller;150 151err_fwnode:152	fwnode_handle_put(controller->dev.fwnode);153 154	pm_runtime_disable(&controller->dev);155 156err_put:157	put_device(&controller->dev);158 159	return ERR_PTR(ret);160}161EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI);162 163static const struct peci_device_id *164peci_bus_match_device_id(const struct peci_device_id *id, struct peci_device *device)165{166	while (id->x86_vfm != 0) {167		if (id->x86_vfm == device->info.x86_vfm)168			return id;169		id++;170	}171 172	return NULL;173}174 175static int peci_bus_device_match(struct device *dev, const struct device_driver *drv)176{177	struct peci_device *device = to_peci_device(dev);178	const struct peci_driver *peci_drv = to_peci_driver(drv);179 180	if (dev->type != &peci_device_type)181		return 0;182 183	return !!peci_bus_match_device_id(peci_drv->id_table, device);184}185 186static int peci_bus_device_probe(struct device *dev)187{188	struct peci_device *device = to_peci_device(dev);189	struct peci_driver *driver = to_peci_driver(dev->driver);190 191	return driver->probe(device, peci_bus_match_device_id(driver->id_table, device));192}193 194static void peci_bus_device_remove(struct device *dev)195{196	struct peci_device *device = to_peci_device(dev);197	struct peci_driver *driver = to_peci_driver(dev->driver);198 199	if (driver->remove)200		driver->remove(device);201}202 203const struct bus_type peci_bus_type = {204	.name		= "peci",205	.match		= peci_bus_device_match,206	.probe		= peci_bus_device_probe,207	.remove		= peci_bus_device_remove,208	.bus_groups	= peci_bus_groups,209};210 211static int __init peci_init(void)212{213	int ret;214 215	ret = bus_register(&peci_bus_type);216	if (ret < 0) {217		pr_err("peci: failed to register PECI bus type!\n");218		return ret;219	}220 221	return 0;222}223module_init(peci_init);224 225static void __exit peci_exit(void)226{227	bus_unregister(&peci_bus_type);228}229module_exit(peci_exit);230 231MODULE_AUTHOR("Jason M Bills <jason.m.bills@linux.intel.com>");232MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");233MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");234MODULE_DESCRIPTION("PECI bus core module");235MODULE_LICENSE("GPL");236