// 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");
// 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");