xref: /openbmc/linux/drivers/peci/core.c (revision 6a646d9f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright (c) 2018-2021 Intel Corporation
3 
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 
17 static DEFINE_IDA(peci_controller_ida);
18 
19 static 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 
28 struct device_type peci_controller_type = {
29 	.release	= peci_controller_dev_release,
30 };
31 
32 int 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 
46 static 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 
76 err:
77 	kfree(controller);
78 	return ERR_PTR(ret);
79 }
80 
81 static int unregister_child(struct device *dev, void *dummy)
82 {
83 	peci_device_destroy(to_peci_device(dev));
84 
85 	return 0;
86 }
87 
88 static 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 not
94 	 * 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 controller
107  * @dev: device for devm operations
108  * @ops: pointer to controller specific methods
109  *
110  * In final stage of its probe(), peci_controller driver calls
111  * 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  */
115 struct 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 for
145 	 * controller itself.
146 	 */
147 	peci_controller_scan_devices(controller);
148 
149 	return controller;
150 
151 err_fwnode:
152 	fwnode_handle_put(controller->dev.fwnode);
153 
154 	pm_runtime_disable(&controller->dev);
155 
156 err_put:
157 	put_device(&controller->dev);
158 
159 	return ERR_PTR(ret);
160 }
161 EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI);
162 
163 static const struct peci_device_id *
164 peci_bus_match_device_id(const struct peci_device_id *id, struct peci_device *device)
165 {
166 	while (id->family != 0) {
167 		if (id->family == device->info.family &&
168 		    id->model == device->info.model)
169 			return id;
170 		id++;
171 	}
172 
173 	return NULL;
174 }
175 
176 static int peci_bus_device_match(struct device *dev, struct device_driver *drv)
177 {
178 	struct peci_device *device = to_peci_device(dev);
179 	struct peci_driver *peci_drv = to_peci_driver(drv);
180 
181 	if (dev->type != &peci_device_type)
182 		return 0;
183 
184 	return !!peci_bus_match_device_id(peci_drv->id_table, device);
185 }
186 
187 static int peci_bus_device_probe(struct device *dev)
188 {
189 	struct peci_device *device = to_peci_device(dev);
190 	struct peci_driver *driver = to_peci_driver(dev->driver);
191 
192 	return driver->probe(device, peci_bus_match_device_id(driver->id_table, device));
193 }
194 
195 static void peci_bus_device_remove(struct device *dev)
196 {
197 	struct peci_device *device = to_peci_device(dev);
198 	struct peci_driver *driver = to_peci_driver(dev->driver);
199 
200 	if (driver->remove)
201 		driver->remove(device);
202 }
203 
204 struct bus_type peci_bus_type = {
205 	.name		= "peci",
206 	.match		= peci_bus_device_match,
207 	.probe		= peci_bus_device_probe,
208 	.remove		= peci_bus_device_remove,
209 	.bus_groups	= peci_bus_groups,
210 };
211 
212 static int __init peci_init(void)
213 {
214 	int ret;
215 
216 	ret = bus_register(&peci_bus_type);
217 	if (ret < 0) {
218 		pr_err("peci: failed to register PECI bus type!\n");
219 		return ret;
220 	}
221 
222 	return 0;
223 }
224 module_init(peci_init);
225 
226 static void __exit peci_exit(void)
227 {
228 	bus_unregister(&peci_bus_type);
229 }
230 module_exit(peci_exit);
231 
232 MODULE_AUTHOR("Jason M Bills <jason.m.bills@linux.intel.com>");
233 MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>");
234 MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");
235 MODULE_DESCRIPTION("PECI bus core module");
236 MODULE_LICENSE("GPL");
237