summaryrefslogtreecommitdiff
path: root/drivers/peci/core.c
blob: 73ad0a47fa9d826fa8f2e38ea0bc84b3d6f2e6b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 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);
}

struct device_type peci_controller_type = {
	.release	= peci_controller_dev_release,
};

static struct peci_controller *peci_controller_alloc(struct device *dev,
						     struct peci_controller_ops *ops)
{
	struct peci_controller *controller;
	int ret;

	if (!ops->xfer)
		return ERR_PTR(-EINVAL);

	controller = kzalloc(sizeof(*controller), GFP_KERNEL);
	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 void unregister_controller(void *_controller)
{
	struct peci_controller *controller = _controller;

	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,
						 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);

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

struct bus_type peci_bus_type = {
	.name		= "peci",
};

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