brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · 37ca7dd Raw
252 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright (c) 2018-2021 Intel Corporation3 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 as13 * 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 proper30	 * DIB, in which case it returns 0 as DIB value.31	 * Let's treat this as an error to avoid carrying on with the detection32	 * 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 address120	 * 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 successful165		 * 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};252