linux/drivers/mfd/intel-m10-bmc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Intel MAX 10 Board Management Controller chip
   4 *
   5 * Copyright (C) 2018-2020 Intel Corporation. All rights reserved.
   6 */
   7#include <linux/bitfield.h>
   8#include <linux/init.h>
   9#include <linux/mfd/core.h>
  10#include <linux/mfd/intel-m10-bmc.h>
  11#include <linux/module.h>
  12#include <linux/mutex.h>
  13#include <linux/regmap.h>
  14#include <linux/spi/spi.h>
  15
  16enum m10bmc_type {
  17        M10_N3000,
  18        M10_D5005,
  19        M10_N5010,
  20};
  21
  22static struct mfd_cell m10bmc_d5005_subdevs[] = {
  23        { .name = "d5005bmc-hwmon" },
  24};
  25
  26static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
  27        { .name = "n3000bmc-hwmon" },
  28        { .name = "n3000bmc-retimer" },
  29        { .name = "n3000bmc-secure" },
  30};
  31
  32static struct mfd_cell m10bmc_n5010_subdevs[] = {
  33        { .name = "n5010bmc-hwmon" },
  34};
  35
  36static const struct regmap_range m10bmc_regmap_range[] = {
  37        regmap_reg_range(M10BMC_LEGACY_BUILD_VER, M10BMC_LEGACY_BUILD_VER),
  38        regmap_reg_range(M10BMC_SYS_BASE, M10BMC_SYS_END),
  39        regmap_reg_range(M10BMC_FLASH_BASE, M10BMC_FLASH_END),
  40};
  41
  42static const struct regmap_access_table m10bmc_access_table = {
  43        .yes_ranges     = m10bmc_regmap_range,
  44        .n_yes_ranges   = ARRAY_SIZE(m10bmc_regmap_range),
  45};
  46
  47static struct regmap_config intel_m10bmc_regmap_config = {
  48        .reg_bits = 32,
  49        .val_bits = 32,
  50        .reg_stride = 4,
  51        .wr_table = &m10bmc_access_table,
  52        .rd_table = &m10bmc_access_table,
  53        .max_register = M10BMC_MEM_END,
  54};
  55
  56static ssize_t bmc_version_show(struct device *dev,
  57                                struct device_attribute *attr, char *buf)
  58{
  59        struct intel_m10bmc *ddata = dev_get_drvdata(dev);
  60        unsigned int val;
  61        int ret;
  62
  63        ret = m10bmc_sys_read(ddata, M10BMC_BUILD_VER, &val);
  64        if (ret)
  65                return ret;
  66
  67        return sprintf(buf, "0x%x\n", val);
  68}
  69static DEVICE_ATTR_RO(bmc_version);
  70
  71static ssize_t bmcfw_version_show(struct device *dev,
  72                                  struct device_attribute *attr, char *buf)
  73{
  74        struct intel_m10bmc *ddata = dev_get_drvdata(dev);
  75        unsigned int val;
  76        int ret;
  77
  78        ret = m10bmc_sys_read(ddata, NIOS2_FW_VERSION, &val);
  79        if (ret)
  80                return ret;
  81
  82        return sprintf(buf, "0x%x\n", val);
  83}
  84static DEVICE_ATTR_RO(bmcfw_version);
  85
  86static ssize_t mac_address_show(struct device *dev,
  87                                struct device_attribute *attr, char *buf)
  88{
  89        struct intel_m10bmc *max10 = dev_get_drvdata(dev);
  90        unsigned int macaddr_low, macaddr_high;
  91        int ret;
  92
  93        ret = m10bmc_sys_read(max10, M10BMC_MAC_LOW, &macaddr_low);
  94        if (ret)
  95                return ret;
  96
  97        ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high);
  98        if (ret)
  99                return ret;
 100
 101        return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
 102                          (u8)FIELD_GET(M10BMC_MAC_BYTE1, macaddr_low),
 103                          (u8)FIELD_GET(M10BMC_MAC_BYTE2, macaddr_low),
 104                          (u8)FIELD_GET(M10BMC_MAC_BYTE3, macaddr_low),
 105                          (u8)FIELD_GET(M10BMC_MAC_BYTE4, macaddr_low),
 106                          (u8)FIELD_GET(M10BMC_MAC_BYTE5, macaddr_high),
 107                          (u8)FIELD_GET(M10BMC_MAC_BYTE6, macaddr_high));
 108}
 109static DEVICE_ATTR_RO(mac_address);
 110
 111static ssize_t mac_count_show(struct device *dev,
 112                              struct device_attribute *attr, char *buf)
 113{
 114        struct intel_m10bmc *max10 = dev_get_drvdata(dev);
 115        unsigned int macaddr_high;
 116        int ret;
 117
 118        ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high);
 119        if (ret)
 120                return ret;
 121
 122        return sysfs_emit(buf, "%u\n",
 123                          (u8)FIELD_GET(M10BMC_MAC_COUNT, macaddr_high));
 124}
 125static DEVICE_ATTR_RO(mac_count);
 126
 127static struct attribute *m10bmc_attrs[] = {
 128        &dev_attr_bmc_version.attr,
 129        &dev_attr_bmcfw_version.attr,
 130        &dev_attr_mac_address.attr,
 131        &dev_attr_mac_count.attr,
 132        NULL,
 133};
 134ATTRIBUTE_GROUPS(m10bmc);
 135
 136static int check_m10bmc_version(struct intel_m10bmc *ddata)
 137{
 138        unsigned int v;
 139        int ret;
 140
 141        /*
 142         * This check is to filter out the very old legacy BMC versions. In the
 143         * old BMC chips, the BMC version info is stored in the old version
 144         * register (M10BMC_LEGACY_BUILD_VER), so its read out value would have
 145         * not been M10BMC_VER_LEGACY_INVALID (0xffffffff). But in new BMC
 146         * chips that the driver supports, the value of this register should be
 147         * M10BMC_VER_LEGACY_INVALID.
 148         */
 149        ret = m10bmc_raw_read(ddata, M10BMC_LEGACY_BUILD_VER, &v);
 150        if (ret)
 151                return -ENODEV;
 152
 153        if (v != M10BMC_VER_LEGACY_INVALID) {
 154                dev_err(ddata->dev, "bad version M10BMC detected\n");
 155                return -ENODEV;
 156        }
 157
 158        return 0;
 159}
 160
 161static int intel_m10_bmc_spi_probe(struct spi_device *spi)
 162{
 163        const struct spi_device_id *id = spi_get_device_id(spi);
 164        struct device *dev = &spi->dev;
 165        struct mfd_cell *cells;
 166        struct intel_m10bmc *ddata;
 167        int ret, n_cell;
 168
 169        ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
 170        if (!ddata)
 171                return -ENOMEM;
 172
 173        ddata->dev = dev;
 174
 175        ddata->regmap =
 176                devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config);
 177        if (IS_ERR(ddata->regmap)) {
 178                ret = PTR_ERR(ddata->regmap);
 179                dev_err(dev, "Failed to allocate regmap: %d\n", ret);
 180                return ret;
 181        }
 182
 183        spi_set_drvdata(spi, ddata);
 184
 185        ret = check_m10bmc_version(ddata);
 186        if (ret) {
 187                dev_err(dev, "Failed to identify m10bmc hardware\n");
 188                return ret;
 189        }
 190
 191        switch (id->driver_data) {
 192        case M10_N3000:
 193                cells = m10bmc_pacn3000_subdevs;
 194                n_cell = ARRAY_SIZE(m10bmc_pacn3000_subdevs);
 195                break;
 196        case M10_D5005:
 197                cells = m10bmc_d5005_subdevs;
 198                n_cell = ARRAY_SIZE(m10bmc_d5005_subdevs);
 199                break;
 200        case M10_N5010:
 201                cells = m10bmc_n5010_subdevs;
 202                n_cell = ARRAY_SIZE(m10bmc_n5010_subdevs);
 203                break;
 204        default:
 205                return -ENODEV;
 206        }
 207
 208        ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, n_cell,
 209                                   NULL, 0, NULL);
 210        if (ret)
 211                dev_err(dev, "Failed to register sub-devices: %d\n", ret);
 212
 213        return ret;
 214}
 215
 216static const struct spi_device_id m10bmc_spi_id[] = {
 217        { "m10-n3000", M10_N3000 },
 218        { "m10-d5005", M10_D5005 },
 219        { "m10-n5010", M10_N5010 },
 220        { }
 221};
 222MODULE_DEVICE_TABLE(spi, m10bmc_spi_id);
 223
 224static struct spi_driver intel_m10bmc_spi_driver = {
 225        .driver = {
 226                .name = "intel-m10-bmc",
 227                .dev_groups = m10bmc_groups,
 228        },
 229        .probe = intel_m10_bmc_spi_probe,
 230        .id_table = m10bmc_spi_id,
 231};
 232module_spi_driver(intel_m10bmc_spi_driver);
 233
 234MODULE_DESCRIPTION("Intel MAX 10 BMC Device Driver");
 235MODULE_AUTHOR("Intel Corporation");
 236MODULE_LICENSE("GPL v2");
 237MODULE_ALIAS("spi:intel-m10-bmc");
 238