diff options
Diffstat (limited to 'drivers/mfd/intel-peci-client.c')
-rw-r--r-- | drivers/mfd/intel-peci-client.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/drivers/mfd/intel-peci-client.c b/drivers/mfd/intel-peci-client.c new file mode 100644 index 000000000000..24f15438634c --- /dev/null +++ b/drivers/mfd/intel-peci-client.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2019 Intel Corporation + +#include <linux/bitfield.h> +#include <linux/mfd/core.h> +#include <linux/mfd/intel-peci-client.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/peci.h> + +#define CPU_ID_MODEL_MASK GENMASK(7, 4) +#define CPU_ID_FAMILY_MASK GENMASK(11, 8) +#define CPU_ID_EXT_MODEL_MASK GENMASK(19, 16) +#define CPU_ID_EXT_FAMILY_MASK GENMASK(27, 20) + +#define LOWER_NIBBLE_MASK GENMASK(3, 0) +#define UPPER_NIBBLE_MASK GENMASK(7, 4) +#define LOWER_BYTE_MASK GENMASK(7, 0) +#define UPPER_BYTE_MASK GENMASK(16, 8) + +static struct mfd_cell peci_functions[] = { + { .name = "peci-cputemp", }, + { .name = "peci-dimmtemp", }, +}; + +static const struct cpu_gen_info cpu_gen_info_table[] = { + { /* Haswell Xeon */ + .family = INTEL_FAM6, + .model = INTEL_FAM6_HASWELL_X, + .core_max = CORE_MAX_ON_HSX, + .chan_rank_max = CHAN_RANK_MAX_ON_HSX, + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX }, + { /* Broadwell Xeon */ + .family = INTEL_FAM6, + .model = INTEL_FAM6_BROADWELL_X, + .core_max = CORE_MAX_ON_BDX, + .chan_rank_max = CHAN_RANK_MAX_ON_BDX, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX }, + { /* Skylake Xeon */ + .family = INTEL_FAM6, + .model = INTEL_FAM6_SKYLAKE_X, + .core_max = CORE_MAX_ON_SKX, + .chan_rank_max = CHAN_RANK_MAX_ON_SKX, + .dimm_idx_max = DIMM_IDX_MAX_ON_SKX }, + { /* Skylake Xeon D */ + .family = INTEL_FAM6, + .model = INTEL_FAM6_SKYLAKE_XD, + .core_max = CORE_MAX_ON_SKXD, + .chan_rank_max = CHAN_RANK_MAX_ON_SKXD, + .dimm_idx_max = DIMM_IDX_MAX_ON_SKXD }, +}; + +static int peci_client_get_cpu_gen_info(struct peci_client_manager *priv) +{ + struct device *dev = &priv->client->dev; + u32 cpu_id; + u16 family; + u8 model; + int ret; + int i; + + ret = peci_get_cpu_id(priv->client->adapter, priv->client->addr, + &cpu_id); + if (ret) + return ret; + + family = FIELD_PREP(LOWER_BYTE_MASK, + FIELD_GET(CPU_ID_FAMILY_MASK, cpu_id)) | + FIELD_PREP(UPPER_BYTE_MASK, + FIELD_GET(CPU_ID_EXT_FAMILY_MASK, cpu_id)); + model = FIELD_PREP(LOWER_NIBBLE_MASK, + FIELD_GET(CPU_ID_MODEL_MASK, cpu_id)) | + FIELD_PREP(UPPER_NIBBLE_MASK, + FIELD_GET(CPU_ID_EXT_MODEL_MASK, cpu_id)); + + for (i = 0; i < ARRAY_SIZE(cpu_gen_info_table); i++) { + const struct cpu_gen_info *cpu_info = &cpu_gen_info_table[i]; + + if (family == cpu_info->family && model == cpu_info->model) { + priv->gen_info = cpu_info; + break; + } + } + + if (!priv->gen_info) { + dev_err(dev, "Can't support this CPU: 0x%x\n", cpu_id); + ret = -ENODEV; + } + + return ret; +} + +static int peci_client_probe(struct peci_client *client) +{ + struct device *dev = &client->dev; + struct peci_client_manager *priv; + uint cpu_no; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + priv->client = client; + cpu_no = client->addr - PECI_BASE_ADDR; + + ret = peci_client_get_cpu_gen_info(priv); + if (ret) + return ret; + + ret = devm_mfd_add_devices(dev, cpu_no, peci_functions, + ARRAY_SIZE(peci_functions), NULL, 0, NULL); + if (ret < 0) { + dev_err(dev, "Failed to register child devices: %d\n", ret); + return ret; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id peci_client_of_table[] = { + { .compatible = "intel,peci-client" }, + { } +}; +MODULE_DEVICE_TABLE(of, peci_client_of_table); +#endif + +static const struct peci_device_id peci_client_ids[] = { + { .name = "peci-client" }, + { } +}; +MODULE_DEVICE_TABLE(peci, peci_client_ids); + +static struct peci_driver peci_client_driver = { + .probe = peci_client_probe, + .id_table = peci_client_ids, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(peci_client_of_table), + }, +}; +module_peci_driver(peci_client_driver); + +MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); +MODULE_DESCRIPTION("PECI client driver"); +MODULE_LICENSE("GPL v2"); |