341 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2// Copyright (c) 2021 Intel Corporation3 4#include <linux/auxiliary_bus.h>5#include <linux/module.h>6#include <linux/peci.h>7#include <linux/peci-cpu.h>8#include <linux/slab.h>9 10#include "internal.h"11 12/**13 * peci_temp_read() - read the maximum die temperature from PECI target device14 * @device: PECI device to which request is going to be sent15 * @temp_raw: where to store the read temperature16 *17 * It uses GetTemp PECI command.18 *19 * Return: 0 if succeeded, other values in case errors.20 */21int peci_temp_read(struct peci_device *device, s16 *temp_raw)22{23 struct peci_request *req;24 25 req = peci_xfer_get_temp(device);26 if (IS_ERR(req))27 return PTR_ERR(req);28 29 *temp_raw = peci_request_temp_read(req);30 31 peci_request_free(req);32 33 return 0;34}35EXPORT_SYMBOL_NS_GPL(peci_temp_read, PECI_CPU);36 37/**38 * peci_pcs_read() - read PCS register39 * @device: PECI device to which request is going to be sent40 * @index: PCS index41 * @param: PCS parameter42 * @data: where to store the read data43 *44 * It uses RdPkgConfig PECI command.45 *46 * Return: 0 if succeeded, other values in case errors.47 */48int peci_pcs_read(struct peci_device *device, u8 index, u16 param, u32 *data)49{50 struct peci_request *req;51 int ret;52 53 req = peci_xfer_pkg_cfg_readl(device, index, param);54 if (IS_ERR(req))55 return PTR_ERR(req);56 57 ret = peci_request_status(req);58 if (ret)59 goto out_req_free;60 61 *data = peci_request_data_readl(req);62out_req_free:63 peci_request_free(req);64 65 return ret;66}67EXPORT_SYMBOL_NS_GPL(peci_pcs_read, PECI_CPU);68 69/**70 * peci_pci_local_read() - read 32-bit memory location using raw address71 * @device: PECI device to which request is going to be sent72 * @bus: bus73 * @dev: device74 * @func: function75 * @reg: register76 * @data: where to store the read data77 *78 * It uses RdPCIConfigLocal PECI command.79 *80 * Return: 0 if succeeded, other values in case errors.81 */82int peci_pci_local_read(struct peci_device *device, u8 bus, u8 dev, u8 func,83 u16 reg, u32 *data)84{85 struct peci_request *req;86 int ret;87 88 req = peci_xfer_pci_cfg_local_readl(device, bus, dev, func, reg);89 if (IS_ERR(req))90 return PTR_ERR(req);91 92 ret = peci_request_status(req);93 if (ret)94 goto out_req_free;95 96 *data = peci_request_data_readl(req);97out_req_free:98 peci_request_free(req);99 100 return ret;101}102EXPORT_SYMBOL_NS_GPL(peci_pci_local_read, PECI_CPU);103 104/**105 * peci_ep_pci_local_read() - read 32-bit memory location using raw address106 * @device: PECI device to which request is going to be sent107 * @seg: PCI segment108 * @bus: bus109 * @dev: device110 * @func: function111 * @reg: register112 * @data: where to store the read data113 *114 * Like &peci_pci_local_read, but it uses RdEndpointConfig PECI command.115 *116 * Return: 0 if succeeded, other values in case errors.117 */118int peci_ep_pci_local_read(struct peci_device *device, u8 seg,119 u8 bus, u8 dev, u8 func, u16 reg, u32 *data)120{121 struct peci_request *req;122 int ret;123 124 req = peci_xfer_ep_pci_cfg_local_readl(device, seg, bus, dev, func, reg);125 if (IS_ERR(req))126 return PTR_ERR(req);127 128 ret = peci_request_status(req);129 if (ret)130 goto out_req_free;131 132 *data = peci_request_data_readl(req);133out_req_free:134 peci_request_free(req);135 136 return ret;137}138EXPORT_SYMBOL_NS_GPL(peci_ep_pci_local_read, PECI_CPU);139 140/**141 * peci_mmio_read() - read 32-bit memory location using 64-bit bar offset address142 * @device: PECI device to which request is going to be sent143 * @bar: PCI bar144 * @seg: PCI segment145 * @bus: bus146 * @dev: device147 * @func: function148 * @address: 64-bit MMIO address149 * @data: where to store the read data150 *151 * It uses RdEndpointConfig PECI command.152 *153 * Return: 0 if succeeded, other values in case errors.154 */155int peci_mmio_read(struct peci_device *device, u8 bar, u8 seg,156 u8 bus, u8 dev, u8 func, u64 address, u32 *data)157{158 struct peci_request *req;159 int ret;160 161 req = peci_xfer_ep_mmio64_readl(device, bar, seg, bus, dev, func, address);162 if (IS_ERR(req))163 return PTR_ERR(req);164 165 ret = peci_request_status(req);166 if (ret)167 goto out_req_free;168 169 *data = peci_request_data_readl(req);170out_req_free:171 peci_request_free(req);172 173 return ret;174}175EXPORT_SYMBOL_NS_GPL(peci_mmio_read, PECI_CPU);176 177static const char * const peci_adev_types[] = {178 "cputemp",179 "dimmtemp",180};181 182struct peci_cpu {183 struct peci_device *device;184 const struct peci_device_id *id;185};186 187static void adev_release(struct device *dev)188{189 struct auxiliary_device *adev = to_auxiliary_dev(dev);190 191 kfree(adev->name);192 kfree(adev);193}194 195static struct auxiliary_device *adev_alloc(struct peci_cpu *priv, int idx)196{197 struct peci_controller *controller = to_peci_controller(priv->device->dev.parent);198 struct auxiliary_device *adev;199 const char *name;200 int ret;201 202 adev = kzalloc(sizeof(*adev), GFP_KERNEL);203 if (!adev)204 return ERR_PTR(-ENOMEM);205 206 name = kasprintf(GFP_KERNEL, "%s.%s", peci_adev_types[idx], (const char *)priv->id->data);207 if (!name) {208 ret = -ENOMEM;209 goto free_adev;210 }211 212 adev->name = name;213 adev->dev.parent = &priv->device->dev;214 adev->dev.release = adev_release;215 adev->id = (controller->id << 16) | (priv->device->addr);216 217 ret = auxiliary_device_init(adev);218 if (ret)219 goto free_name;220 221 return adev;222 223free_name:224 kfree(name);225free_adev:226 kfree(adev);227 return ERR_PTR(ret);228}229 230static void unregister_adev(void *_adev)231{232 struct auxiliary_device *adev = _adev;233 234 auxiliary_device_delete(adev);235 auxiliary_device_uninit(adev);236}237 238static int devm_adev_add(struct device *dev, int idx)239{240 struct peci_cpu *priv = dev_get_drvdata(dev);241 struct auxiliary_device *adev;242 int ret;243 244 adev = adev_alloc(priv, idx);245 if (IS_ERR(adev))246 return PTR_ERR(adev);247 248 ret = auxiliary_device_add(adev);249 if (ret) {250 auxiliary_device_uninit(adev);251 return ret;252 }253 254 ret = devm_add_action_or_reset(&priv->device->dev, unregister_adev, adev);255 if (ret)256 return ret;257 258 return 0;259}260 261static void peci_cpu_add_adevices(struct peci_cpu *priv)262{263 struct device *dev = &priv->device->dev;264 int ret, i;265 266 for (i = 0; i < ARRAY_SIZE(peci_adev_types); i++) {267 ret = devm_adev_add(dev, i);268 if (ret) {269 dev_warn(dev, "Failed to register PECI auxiliary: %s, ret = %d\n",270 peci_adev_types[i], ret);271 continue;272 }273 }274}275 276static int277peci_cpu_probe(struct peci_device *device, const struct peci_device_id *id)278{279 struct device *dev = &device->dev;280 struct peci_cpu *priv;281 282 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);283 if (!priv)284 return -ENOMEM;285 286 dev_set_drvdata(dev, priv);287 priv->device = device;288 priv->id = id;289 290 peci_cpu_add_adevices(priv);291 292 return 0;293}294 295static const struct peci_device_id peci_cpu_device_ids[] = {296 { /* Haswell Xeon */297 .x86_vfm = INTEL_HASWELL_X,298 .data = "hsx",299 },300 { /* Broadwell Xeon */301 .x86_vfm = INTEL_BROADWELL_X,302 .data = "bdx",303 },304 { /* Broadwell Xeon D */305 .x86_vfm = INTEL_BROADWELL_D,306 .data = "bdxd",307 },308 { /* Skylake Xeon */309 .x86_vfm = INTEL_SKYLAKE_X,310 .data = "skx",311 },312 { /* Icelake Xeon */313 .x86_vfm = INTEL_ICELAKE_X,314 .data = "icx",315 },316 { /* Icelake Xeon D */317 .x86_vfm = INTEL_ICELAKE_D,318 .data = "icxd",319 },320 { /* Sapphire Rapids Xeon */321 .x86_vfm = INTEL_SAPPHIRERAPIDS_X,322 .data = "spr",323 },324 { }325};326MODULE_DEVICE_TABLE(peci, peci_cpu_device_ids);327 328static struct peci_driver peci_cpu_driver = {329 .probe = peci_cpu_probe,330 .id_table = peci_cpu_device_ids,331 .driver = {332 .name = "peci-cpu",333 },334};335module_peci_driver(peci_cpu_driver);336 337MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>");338MODULE_DESCRIPTION("PECI CPU driver");339MODULE_LICENSE("GPL");340MODULE_IMPORT_NS(PECI);341