xref: /openbmc/linux/drivers/peci/core.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
16523d3b2SIwona Winiarska // SPDX-License-Identifier: GPL-2.0-only
26523d3b2SIwona Winiarska // Copyright (c) 2018-2021 Intel Corporation
36523d3b2SIwona Winiarska 
46523d3b2SIwona Winiarska #include <linux/bug.h>
56523d3b2SIwona Winiarska #include <linux/device.h>
66523d3b2SIwona Winiarska #include <linux/export.h>
76523d3b2SIwona Winiarska #include <linux/idr.h>
86523d3b2SIwona Winiarska #include <linux/module.h>
96523d3b2SIwona Winiarska #include <linux/of.h>
106523d3b2SIwona Winiarska #include <linux/peci.h>
116523d3b2SIwona Winiarska #include <linux/pm_runtime.h>
126523d3b2SIwona Winiarska #include <linux/property.h>
136523d3b2SIwona Winiarska #include <linux/slab.h>
146523d3b2SIwona Winiarska 
156523d3b2SIwona Winiarska #include "internal.h"
166523d3b2SIwona Winiarska 
176523d3b2SIwona Winiarska static DEFINE_IDA(peci_controller_ida);
186523d3b2SIwona Winiarska 
peci_controller_dev_release(struct device * dev)196523d3b2SIwona Winiarska static void peci_controller_dev_release(struct device *dev)
206523d3b2SIwona Winiarska {
216523d3b2SIwona Winiarska 	struct peci_controller *controller = to_peci_controller(dev);
226523d3b2SIwona Winiarska 
236523d3b2SIwona Winiarska 	mutex_destroy(&controller->bus_lock);
246523d3b2SIwona Winiarska 	ida_free(&peci_controller_ida, controller->id);
256523d3b2SIwona Winiarska 	kfree(controller);
266523d3b2SIwona Winiarska }
276523d3b2SIwona Winiarska 
286523d3b2SIwona Winiarska struct device_type peci_controller_type = {
296523d3b2SIwona Winiarska 	.release	= peci_controller_dev_release,
306523d3b2SIwona Winiarska };
316523d3b2SIwona Winiarska 
peci_controller_scan_devices(struct peci_controller * controller)3242bed52bSIwona Winiarska int peci_controller_scan_devices(struct peci_controller *controller)
3352857e68SIwona Winiarska {
3452857e68SIwona Winiarska 	int ret;
3552857e68SIwona Winiarska 	u8 addr;
3652857e68SIwona Winiarska 
3752857e68SIwona Winiarska 	for (addr = PECI_BASE_ADDR; addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; addr++) {
3852857e68SIwona Winiarska 		ret = peci_device_create(controller, addr);
3952857e68SIwona Winiarska 		if (ret)
4052857e68SIwona Winiarska 			return ret;
4152857e68SIwona Winiarska 	}
4252857e68SIwona Winiarska 
4352857e68SIwona Winiarska 	return 0;
4452857e68SIwona Winiarska }
4552857e68SIwona Winiarska 
peci_controller_alloc(struct device * dev,const struct peci_controller_ops * ops)466523d3b2SIwona Winiarska static struct peci_controller *peci_controller_alloc(struct device *dev,
47*8306d6f3SZev Weiss 						     const struct peci_controller_ops *ops)
486523d3b2SIwona Winiarska {
496523d3b2SIwona Winiarska 	struct peci_controller *controller;
506523d3b2SIwona Winiarska 	int ret;
516523d3b2SIwona Winiarska 
526523d3b2SIwona Winiarska 	if (!ops->xfer)
536523d3b2SIwona Winiarska 		return ERR_PTR(-EINVAL);
546523d3b2SIwona Winiarska 
556523d3b2SIwona Winiarska 	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
566523d3b2SIwona Winiarska 	if (!controller)
576523d3b2SIwona Winiarska 		return ERR_PTR(-ENOMEM);
586523d3b2SIwona Winiarska 
596523d3b2SIwona Winiarska 	ret = ida_alloc_max(&peci_controller_ida, U8_MAX, GFP_KERNEL);
606523d3b2SIwona Winiarska 	if (ret < 0)
616523d3b2SIwona Winiarska 		goto err;
626523d3b2SIwona Winiarska 	controller->id = ret;
636523d3b2SIwona Winiarska 
646523d3b2SIwona Winiarska 	controller->ops = ops;
656523d3b2SIwona Winiarska 
666523d3b2SIwona Winiarska 	controller->dev.parent = dev;
676523d3b2SIwona Winiarska 	controller->dev.bus = &peci_bus_type;
686523d3b2SIwona Winiarska 	controller->dev.type = &peci_controller_type;
696523d3b2SIwona Winiarska 
706523d3b2SIwona Winiarska 	device_initialize(&controller->dev);
716523d3b2SIwona Winiarska 
726523d3b2SIwona Winiarska 	mutex_init(&controller->bus_lock);
736523d3b2SIwona Winiarska 
746523d3b2SIwona Winiarska 	return controller;
756523d3b2SIwona Winiarska 
766523d3b2SIwona Winiarska err:
776523d3b2SIwona Winiarska 	kfree(controller);
786523d3b2SIwona Winiarska 	return ERR_PTR(ret);
796523d3b2SIwona Winiarska }
806523d3b2SIwona Winiarska 
unregister_child(struct device * dev,void * dummy)8152857e68SIwona Winiarska static int unregister_child(struct device *dev, void *dummy)
8252857e68SIwona Winiarska {
8352857e68SIwona Winiarska 	peci_device_destroy(to_peci_device(dev));
8452857e68SIwona Winiarska 
8552857e68SIwona Winiarska 	return 0;
8652857e68SIwona Winiarska }
8752857e68SIwona Winiarska 
unregister_controller(void * _controller)886523d3b2SIwona Winiarska static void unregister_controller(void *_controller)
896523d3b2SIwona Winiarska {
906523d3b2SIwona Winiarska 	struct peci_controller *controller = _controller;
916523d3b2SIwona Winiarska 
9252857e68SIwona Winiarska 	/*
9352857e68SIwona Winiarska 	 * Detach any active PECI devices. This can't fail, thus we do not
9452857e68SIwona Winiarska 	 * check the returned value.
9552857e68SIwona Winiarska 	 */
9652857e68SIwona Winiarska 	device_for_each_child_reverse(&controller->dev, NULL, unregister_child);
9752857e68SIwona Winiarska 
986523d3b2SIwona Winiarska 	device_unregister(&controller->dev);
996523d3b2SIwona Winiarska 
1006523d3b2SIwona Winiarska 	fwnode_handle_put(controller->dev.fwnode);
1016523d3b2SIwona Winiarska 
1026523d3b2SIwona Winiarska 	pm_runtime_disable(&controller->dev);
1036523d3b2SIwona Winiarska }
1046523d3b2SIwona Winiarska 
1056523d3b2SIwona Winiarska /**
1066523d3b2SIwona Winiarska  * devm_peci_controller_add() - add PECI controller
1076523d3b2SIwona Winiarska  * @dev: device for devm operations
1086523d3b2SIwona Winiarska  * @ops: pointer to controller specific methods
1096523d3b2SIwona Winiarska  *
1106523d3b2SIwona Winiarska  * In final stage of its probe(), peci_controller driver calls
1116523d3b2SIwona Winiarska  * devm_peci_controller_add() to register itself with the PECI bus.
1126523d3b2SIwona Winiarska  *
1136523d3b2SIwona Winiarska  * Return: Pointer to the newly allocated controller or ERR_PTR() in case of failure.
1146523d3b2SIwona Winiarska  */
devm_peci_controller_add(struct device * dev,const struct peci_controller_ops * ops)1156523d3b2SIwona Winiarska struct peci_controller *devm_peci_controller_add(struct device *dev,
116*8306d6f3SZev Weiss 						 const struct peci_controller_ops *ops)
1176523d3b2SIwona Winiarska {
1186523d3b2SIwona Winiarska 	struct peci_controller *controller;
1196523d3b2SIwona Winiarska 	int ret;
1206523d3b2SIwona Winiarska 
1216523d3b2SIwona Winiarska 	controller = peci_controller_alloc(dev, ops);
1226523d3b2SIwona Winiarska 	if (IS_ERR(controller))
1236523d3b2SIwona Winiarska 		return controller;
1246523d3b2SIwona Winiarska 
1256523d3b2SIwona Winiarska 	ret = dev_set_name(&controller->dev, "peci-%d", controller->id);
1266523d3b2SIwona Winiarska 	if (ret)
1276523d3b2SIwona Winiarska 		goto err_put;
1286523d3b2SIwona Winiarska 
1296523d3b2SIwona Winiarska 	pm_runtime_no_callbacks(&controller->dev);
1306523d3b2SIwona Winiarska 	pm_suspend_ignore_children(&controller->dev, true);
1316523d3b2SIwona Winiarska 	pm_runtime_enable(&controller->dev);
1326523d3b2SIwona Winiarska 
1336523d3b2SIwona Winiarska 	device_set_node(&controller->dev, fwnode_handle_get(dev_fwnode(dev)));
1346523d3b2SIwona Winiarska 
1356523d3b2SIwona Winiarska 	ret = device_add(&controller->dev);
1366523d3b2SIwona Winiarska 	if (ret)
1376523d3b2SIwona Winiarska 		goto err_fwnode;
1386523d3b2SIwona Winiarska 
1396523d3b2SIwona Winiarska 	ret = devm_add_action_or_reset(dev, unregister_controller, controller);
1406523d3b2SIwona Winiarska 	if (ret)
1416523d3b2SIwona Winiarska 		return ERR_PTR(ret);
1426523d3b2SIwona Winiarska 
14352857e68SIwona Winiarska 	/*
14452857e68SIwona Winiarska 	 * Ignoring retval since failures during scan are non-critical for
14552857e68SIwona Winiarska 	 * controller itself.
14652857e68SIwona Winiarska 	 */
14752857e68SIwona Winiarska 	peci_controller_scan_devices(controller);
14852857e68SIwona Winiarska 
1496523d3b2SIwona Winiarska 	return controller;
1506523d3b2SIwona Winiarska 
1516523d3b2SIwona Winiarska err_fwnode:
1526523d3b2SIwona Winiarska 	fwnode_handle_put(controller->dev.fwnode);
1536523d3b2SIwona Winiarska 
1546523d3b2SIwona Winiarska 	pm_runtime_disable(&controller->dev);
1556523d3b2SIwona Winiarska 
1566523d3b2SIwona Winiarska err_put:
1576523d3b2SIwona Winiarska 	put_device(&controller->dev);
1586523d3b2SIwona Winiarska 
1596523d3b2SIwona Winiarska 	return ERR_PTR(ret);
1606523d3b2SIwona Winiarska }
1616523d3b2SIwona Winiarska EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI);
1626523d3b2SIwona Winiarska 
1636b8145b0SIwona Winiarska static const struct peci_device_id *
peci_bus_match_device_id(const struct peci_device_id * id,struct peci_device * device)1646b8145b0SIwona Winiarska peci_bus_match_device_id(const struct peci_device_id *id, struct peci_device *device)
1656b8145b0SIwona Winiarska {
1666b8145b0SIwona Winiarska 	while (id->family != 0) {
1676b8145b0SIwona Winiarska 		if (id->family == device->info.family &&
1686b8145b0SIwona Winiarska 		    id->model == device->info.model)
1696b8145b0SIwona Winiarska 			return id;
1706b8145b0SIwona Winiarska 		id++;
1716b8145b0SIwona Winiarska 	}
1726b8145b0SIwona Winiarska 
1736b8145b0SIwona Winiarska 	return NULL;
1746b8145b0SIwona Winiarska }
1756b8145b0SIwona Winiarska 
peci_bus_device_match(struct device * dev,struct device_driver * drv)1766b8145b0SIwona Winiarska static int peci_bus_device_match(struct device *dev, struct device_driver *drv)
1776b8145b0SIwona Winiarska {
1786b8145b0SIwona Winiarska 	struct peci_device *device = to_peci_device(dev);
1796b8145b0SIwona Winiarska 	struct peci_driver *peci_drv = to_peci_driver(drv);
1806b8145b0SIwona Winiarska 
1816b8145b0SIwona Winiarska 	if (dev->type != &peci_device_type)
1826b8145b0SIwona Winiarska 		return 0;
1836b8145b0SIwona Winiarska 
1846b8145b0SIwona Winiarska 	return !!peci_bus_match_device_id(peci_drv->id_table, device);
1856b8145b0SIwona Winiarska }
1866b8145b0SIwona Winiarska 
peci_bus_device_probe(struct device * dev)1876b8145b0SIwona Winiarska static int peci_bus_device_probe(struct device *dev)
1886b8145b0SIwona Winiarska {
1896b8145b0SIwona Winiarska 	struct peci_device *device = to_peci_device(dev);
1906b8145b0SIwona Winiarska 	struct peci_driver *driver = to_peci_driver(dev->driver);
1916b8145b0SIwona Winiarska 
1926b8145b0SIwona Winiarska 	return driver->probe(device, peci_bus_match_device_id(driver->id_table, device));
1936b8145b0SIwona Winiarska }
1946b8145b0SIwona Winiarska 
peci_bus_device_remove(struct device * dev)1956b8145b0SIwona Winiarska static void peci_bus_device_remove(struct device *dev)
1966b8145b0SIwona Winiarska {
1976b8145b0SIwona Winiarska 	struct peci_device *device = to_peci_device(dev);
1986b8145b0SIwona Winiarska 	struct peci_driver *driver = to_peci_driver(dev->driver);
1996b8145b0SIwona Winiarska 
2006b8145b0SIwona Winiarska 	if (driver->remove)
2016b8145b0SIwona Winiarska 		driver->remove(device);
2026b8145b0SIwona Winiarska }
2036b8145b0SIwona Winiarska 
2046523d3b2SIwona Winiarska struct bus_type peci_bus_type = {
2056523d3b2SIwona Winiarska 	.name		= "peci",
2066b8145b0SIwona Winiarska 	.match		= peci_bus_device_match,
2076b8145b0SIwona Winiarska 	.probe		= peci_bus_device_probe,
2086b8145b0SIwona Winiarska 	.remove		= peci_bus_device_remove,
20942bed52bSIwona Winiarska 	.bus_groups	= peci_bus_groups,
2106523d3b2SIwona Winiarska };
2116523d3b2SIwona Winiarska 
peci_init(void)2126523d3b2SIwona Winiarska static int __init peci_init(void)
2136523d3b2SIwona Winiarska {
2146523d3b2SIwona Winiarska 	int ret;
2156523d3b2SIwona Winiarska 
2166523d3b2SIwona Winiarska 	ret = bus_register(&peci_bus_type);
2176523d3b2SIwona Winiarska 	if (ret < 0) {
2186523d3b2SIwona Winiarska 		pr_err("peci: failed to register PECI bus type!\n");
2196523d3b2SIwona Winiarska 		return ret;
2206523d3b2SIwona Winiarska 	}
2216523d3b2SIwona Winiarska 
2226523d3b2SIwona Winiarska 	return 0;
2236523d3b2SIwona Winiarska }
2246523d3b2SIwona Winiarska module_init(peci_init);
2256523d3b2SIwona Winiarska 
peci_exit(void)2266523d3b2SIwona Winiarska static void __exit peci_exit(void)
2276523d3b2SIwona Winiarska {
2286523d3b2SIwona Winiarska 	bus_unregister(&peci_bus_type);
2296523d3b2SIwona Winiarska }
2306523d3b2SIwona Winiarska module_exit(peci_exit);
2316523d3b2SIwona Winiarska 
2326523d3b2SIwona Winiarska MODULE_AUTHOR("Jason M Bills <jason.m.bills@linux.intel.com>");
2336523d3b2SIwona Winiarska MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
2346523d3b2SIwona Winiarska MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");
2356523d3b2SIwona Winiarska MODULE_DESCRIPTION("PECI bus core module");
2366523d3b2SIwona Winiarska MODULE_LICENSE("GPL");
237