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