251 lines · 5.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only// Copyright (c) 2018-2021 Intel Corporation #include <linux/bitfield.h>#include <linux/peci.h>#include <linux/peci-cpu.h>#include <linux/slab.h> #include "internal.h" /* * PECI device can be removed using sysfs, but the removal can also happen as * a result of controller being removed. * Mutex is used to protect PECI device from being double-deleted. */static DEFINE_MUTEX(peci_device_del_lock); #define REVISION_NUM_MASK GENMASK(15, 8)static int peci_get_revision(struct peci_device *device, u8 *revision){	struct peci_request *req;	u64 dib; 	req = peci_xfer_get_dib(device);	if (IS_ERR(req))		return PTR_ERR(req); 	/*	 * PECI device may be in a state where it is unable to return a proper	 * DIB, in which case it returns 0 as DIB value.	 * Let's treat this as an error to avoid carrying on with the detection	 * using invalid revision.	 */	dib = peci_request_dib_read(req);	if (dib == 0) {		peci_request_free(req);		return -EIO;	} 	*revision = FIELD_GET(REVISION_NUM_MASK, dib); 	peci_request_free(req); 	return 0;} static int peci_get_cpu_id(struct peci_device *device, u32 *cpu_id){	struct peci_request *req;	int ret; 	req = peci_xfer_pkg_cfg_readl(device, PECI_PCS_PKG_ID, PECI_PKG_ID_CPU_ID);	if (IS_ERR(req))		return PTR_ERR(req); 	ret = peci_request_status(req);	if (ret)		goto out_req_free; 	*cpu_id = peci_request_data_readl(req);out_req_free:	peci_request_free(req); 	return ret;} static unsigned int peci_x86_cpu_family(unsigned int sig){	unsigned int x86; 	x86 = (sig >> 8) & 0xf; 	if (x86 == 0xf)		x86 += (sig >> 20) & 0xff; 	return x86;} static unsigned int peci_x86_cpu_model(unsigned int sig){	unsigned int fam, model; 	fam = peci_x86_cpu_family(sig); 	model = (sig >> 4) & 0xf; 	if (fam >= 0x6)		model += ((sig >> 16) & 0xf) << 4; 	return model;} static int peci_device_info_init(struct peci_device *device){	u8 revision;	u32 cpu_id;	int ret; 	ret = peci_get_cpu_id(device, &cpu_id);	if (ret)		return ret; 	device->info.x86_vfm = IFM(peci_x86_cpu_family(cpu_id), peci_x86_cpu_model(cpu_id)); 	ret = peci_get_revision(device, &revision);	if (ret)		return ret;	device->info.peci_revision = revision; 	device->info.socket_id = device->addr - PECI_BASE_ADDR; 	return 0;} static int peci_detect(struct peci_controller *controller, u8 addr){	/*	 * PECI Ping is a command encoded by tx_len = 0, rx_len = 0.	 * We expect correct Write FCS if the device at the target address	 * is able to respond.	 */	struct peci_request req = { 0 };	int ret; 	mutex_lock(&controller->bus_lock);	ret = controller->ops->xfer(controller, addr, &req);	mutex_unlock(&controller->bus_lock); 	return ret;} static bool peci_addr_valid(u8 addr){	return addr >= PECI_BASE_ADDR && addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX;} static int peci_dev_exists(struct device *dev, void *data){	struct peci_device *device = to_peci_device(dev);	u8 *addr = data; 	if (device->addr == *addr)		return -EBUSY; 	return 0;} int peci_device_create(struct peci_controller *controller, u8 addr){	struct peci_device *device;	int ret; 	if (!peci_addr_valid(addr))		return -EINVAL; 	/* Check if we have already detected this device before. */	ret = device_for_each_child(&controller->dev, &addr, peci_dev_exists);	if (ret)		return 0; 	ret = peci_detect(controller, addr);	if (ret) {		/*		 * Device not present or host state doesn't allow successful		 * detection at this time.		 */		if (ret == -EIO || ret == -ETIMEDOUT)			return 0; 		return ret;	} 	device = kzalloc_obj(*device);	if (!device)		return -ENOMEM; 	device_initialize(&device->dev); 	device->addr = addr;	device->dev.parent = &controller->dev;	device->dev.bus = &peci_bus_type;	device->dev.type = &peci_device_type; 	ret = peci_device_info_init(device);	if (ret)		goto err_put; 	ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr);	if (ret)		goto err_put; 	ret = device_add(&device->dev);	if (ret)		goto err_put; 	return 0; err_put:	put_device(&device->dev); 	return ret;} void peci_device_destroy(struct peci_device *device){	mutex_lock(&peci_device_del_lock);	if (!device->deleted) {		device_unregister(&device->dev);		device->deleted = true;	}	mutex_unlock(&peci_device_del_lock);} int __peci_driver_register(struct peci_driver *driver, struct module *owner,			   const char *mod_name){	driver->driver.bus = &peci_bus_type;	driver->driver.owner = owner;	driver->driver.mod_name = mod_name; 	if (!driver->probe) {		pr_err("peci: trying to register driver without probe callback\n");		return -EINVAL;	} 	if (!driver->id_table) {		pr_err("peci: trying to register driver without device id table\n");		return -EINVAL;	} 	return driver_register(&driver->driver);}EXPORT_SYMBOL_NS_GPL(__peci_driver_register, "PECI"); void peci_driver_unregister(struct peci_driver *driver){	driver_unregister(&driver->driver);}EXPORT_SYMBOL_NS_GPL(peci_driver_unregister, "PECI"); static void peci_device_release(struct device *dev){	struct peci_device *device = to_peci_device(dev); 	kfree(device);} const struct device_type peci_device_type = {	.groups		= peci_device_groups,	.release	= peci_device_release,};