Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  1// SPDX-License-Identifier: GPL-2.0-only
  2// Copyright (c) 2018-2021 Intel Corporation
  3
  4#include <linux/bitfield.h>
  5#include <linux/peci.h>
  6#include <linux/peci-cpu.h>
  7#include <linux/slab.h>
  8
  9#include "internal.h"
 10
 11/*
 12 * PECI device can be removed using sysfs, but the removal can also happen as
 13 * a result of controller being removed.
 14 * Mutex is used to protect PECI device from being double-deleted.
 15 */
 16static DEFINE_MUTEX(peci_device_del_lock);
 17
 18#define REVISION_NUM_MASK GENMASK(15, 8)
 19static int peci_get_revision(struct peci_device *device, u8 *revision)
 20{
 21	struct peci_request *req;
 22	u64 dib;
 23
 24	req = peci_xfer_get_dib(device);
 25	if (IS_ERR(req))
 26		return PTR_ERR(req);
 27
 28	/*
 29	 * PECI device may be in a state where it is unable to return a proper
 30	 * DIB, in which case it returns 0 as DIB value.
 31	 * Let's treat this as an error to avoid carrying on with the detection
 32	 * using invalid revision.
 33	 */
 34	dib = peci_request_dib_read(req);
 35	if (dib == 0) {
 36		peci_request_free(req);
 37		return -EIO;
 38	}
 39
 40	*revision = FIELD_GET(REVISION_NUM_MASK, dib);
 41
 42	peci_request_free(req);
 43
 44	return 0;
 45}
 46
 47static int peci_get_cpu_id(struct peci_device *device, u32 *cpu_id)
 48{
 49	struct peci_request *req;
 50	int ret;
 51
 52	req = peci_xfer_pkg_cfg_readl(device, PECI_PCS_PKG_ID, PECI_PKG_ID_CPU_ID);
 53	if (IS_ERR(req))
 54		return PTR_ERR(req);
 55
 56	ret = peci_request_status(req);
 57	if (ret)
 58		goto out_req_free;
 59
 60	*cpu_id = peci_request_data_readl(req);
 61out_req_free:
 62	peci_request_free(req);
 63
 64	return ret;
 65}
 66
 67static unsigned int peci_x86_cpu_family(unsigned int sig)
 68{
 69	unsigned int x86;
 70
 71	x86 = (sig >> 8) & 0xf;
 72
 73	if (x86 == 0xf)
 74		x86 += (sig >> 20) & 0xff;
 75
 76	return x86;
 77}
 78
 79static unsigned int peci_x86_cpu_model(unsigned int sig)
 80{
 81	unsigned int fam, model;
 82
 83	fam = peci_x86_cpu_family(sig);
 84
 85	model = (sig >> 4) & 0xf;
 86
 87	if (fam >= 0x6)
 88		model += ((sig >> 16) & 0xf) << 4;
 89
 90	return model;
 91}
 92
 93static int peci_device_info_init(struct peci_device *device)
 94{
 95	u8 revision;
 96	u32 cpu_id;
 97	int ret;
 98
 99	ret = peci_get_cpu_id(device, &cpu_id);
100	if (ret)
101		return ret;
102
103	device->info.x86_vfm = IFM(peci_x86_cpu_family(cpu_id), peci_x86_cpu_model(cpu_id));
104
105	ret = peci_get_revision(device, &revision);
106	if (ret)
107		return ret;
108	device->info.peci_revision = revision;
109
110	device->info.socket_id = device->addr - PECI_BASE_ADDR;
111
112	return 0;
113}
114
115static int peci_detect(struct peci_controller *controller, u8 addr)
116{
117	/*
118	 * PECI Ping is a command encoded by tx_len = 0, rx_len = 0.
119	 * We expect correct Write FCS if the device at the target address
120	 * is able to respond.
121	 */
122	struct peci_request req = { 0 };
123	int ret;
124
125	mutex_lock(&controller->bus_lock);
126	ret = controller->ops->xfer(controller, addr, &req);
127	mutex_unlock(&controller->bus_lock);
128
129	return ret;
130}
131
132static bool peci_addr_valid(u8 addr)
133{
134	return addr >= PECI_BASE_ADDR && addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX;
135}
136
137static int peci_dev_exists(struct device *dev, void *data)
138{
139	struct peci_device *device = to_peci_device(dev);
140	u8 *addr = data;
141
142	if (device->addr == *addr)
143		return -EBUSY;
144
145	return 0;
146}
147
148int peci_device_create(struct peci_controller *controller, u8 addr)
149{
150	struct peci_device *device;
151	int ret;
152
153	if (!peci_addr_valid(addr))
154		return -EINVAL;
155
156	/* Check if we have already detected this device before. */
157	ret = device_for_each_child(&controller->dev, &addr, peci_dev_exists);
158	if (ret)
159		return 0;
160
161	ret = peci_detect(controller, addr);
162	if (ret) {
163		/*
164		 * Device not present or host state doesn't allow successful
165		 * detection at this time.
166		 */
167		if (ret == -EIO || ret == -ETIMEDOUT)
168			return 0;
169
170		return ret;
171	}
172
173	device = kzalloc(sizeof(*device), GFP_KERNEL);
174	if (!device)
175		return -ENOMEM;
176
177	device_initialize(&device->dev);
178
179	device->addr = addr;
180	device->dev.parent = &controller->dev;
181	device->dev.bus = &peci_bus_type;
182	device->dev.type = &peci_device_type;
183
184	ret = peci_device_info_init(device);
185	if (ret)
186		goto err_put;
187
188	ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr);
189	if (ret)
190		goto err_put;
191
192	ret = device_add(&device->dev);
193	if (ret)
194		goto err_put;
195
196	return 0;
197
198err_put:
199	put_device(&device->dev);
200
201	return ret;
202}
203
204void peci_device_destroy(struct peci_device *device)
205{
206	mutex_lock(&peci_device_del_lock);
207	if (!device->deleted) {
208		device_unregister(&device->dev);
209		device->deleted = true;
210	}
211	mutex_unlock(&peci_device_del_lock);
212}
213
214int __peci_driver_register(struct peci_driver *driver, struct module *owner,
215			   const char *mod_name)
216{
217	driver->driver.bus = &peci_bus_type;
218	driver->driver.owner = owner;
219	driver->driver.mod_name = mod_name;
220
221	if (!driver->probe) {
222		pr_err("peci: trying to register driver without probe callback\n");
223		return -EINVAL;
224	}
225
226	if (!driver->id_table) {
227		pr_err("peci: trying to register driver without device id table\n");
228		return -EINVAL;
229	}
230
231	return driver_register(&driver->driver);
232}
233EXPORT_SYMBOL_NS_GPL(__peci_driver_register, "PECI");
234
235void peci_driver_unregister(struct peci_driver *driver)
236{
237	driver_unregister(&driver->driver);
238}
239EXPORT_SYMBOL_NS_GPL(peci_driver_unregister, "PECI");
240
241static void peci_device_release(struct device *dev)
242{
243	struct peci_device *device = to_peci_device(dev);
244
245	kfree(device);
246}
247
248const struct device_type peci_device_type = {
249	.groups		= peci_device_groups,
250	.release	= peci_device_release,
251};