18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel MAX 10 Board Management Controller chip 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/mfd/core.h> 108c2ecf20Sopenharmony_ci#include <linux/mfd/intel-m10-bmc.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/mutex.h> 138c2ecf20Sopenharmony_ci#include <linux/regmap.h> 148c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cienum m10bmc_type { 178c2ecf20Sopenharmony_ci M10_N3000, 188c2ecf20Sopenharmony_ci}; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic struct mfd_cell m10bmc_pacn3000_subdevs[] = { 218c2ecf20Sopenharmony_ci { .name = "n3000bmc-hwmon" }, 228c2ecf20Sopenharmony_ci { .name = "n3000bmc-retimer" }, 238c2ecf20Sopenharmony_ci { .name = "n3000bmc-secure" }, 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct regmap_config intel_m10bmc_regmap_config = { 278c2ecf20Sopenharmony_ci .reg_bits = 32, 288c2ecf20Sopenharmony_ci .val_bits = 32, 298c2ecf20Sopenharmony_ci .reg_stride = 4, 308c2ecf20Sopenharmony_ci .max_register = M10BMC_MEM_END, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic ssize_t bmc_version_show(struct device *dev, 348c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct intel_m10bmc *ddata = dev_get_drvdata(dev); 378c2ecf20Sopenharmony_ci unsigned int val; 388c2ecf20Sopenharmony_ci int ret; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci ret = m10bmc_sys_read(ddata, M10BMC_BUILD_VER, &val); 418c2ecf20Sopenharmony_ci if (ret) 428c2ecf20Sopenharmony_ci return ret; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", val); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(bmc_version); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic ssize_t bmcfw_version_show(struct device *dev, 498c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct intel_m10bmc *ddata = dev_get_drvdata(dev); 528c2ecf20Sopenharmony_ci unsigned int val; 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ret = m10bmc_sys_read(ddata, NIOS2_FW_VERSION, &val); 568c2ecf20Sopenharmony_ci if (ret) 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return sprintf(buf, "0x%x\n", val); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(bmcfw_version); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic struct attribute *m10bmc_attrs[] = { 648c2ecf20Sopenharmony_ci &dev_attr_bmc_version.attr, 658c2ecf20Sopenharmony_ci &dev_attr_bmcfw_version.attr, 668c2ecf20Sopenharmony_ci NULL, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(m10bmc); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int check_m10bmc_version(struct intel_m10bmc *ddata) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci unsigned int v; 738c2ecf20Sopenharmony_ci int ret; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* 768c2ecf20Sopenharmony_ci * This check is to filter out the very old legacy BMC versions, 778c2ecf20Sopenharmony_ci * M10BMC_LEGACY_SYS_BASE is the offset to this old block of mmio 788c2ecf20Sopenharmony_ci * registers. In the old BMC chips, the BMC version info is stored 798c2ecf20Sopenharmony_ci * in this old version register (M10BMC_LEGACY_SYS_BASE + 808c2ecf20Sopenharmony_ci * M10BMC_BUILD_VER), so its read out value would have not been 818c2ecf20Sopenharmony_ci * LEGACY_INVALID (0xffffffff). But in new BMC chips that the 828c2ecf20Sopenharmony_ci * driver supports, the value of this register should be 838c2ecf20Sopenharmony_ci * LEGACY_INVALID. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci ret = m10bmc_raw_read(ddata, 868c2ecf20Sopenharmony_ci M10BMC_LEGACY_SYS_BASE + M10BMC_BUILD_VER, &v); 878c2ecf20Sopenharmony_ci if (ret) 888c2ecf20Sopenharmony_ci return -ENODEV; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (v != M10BMC_VER_LEGACY_INVALID) { 918c2ecf20Sopenharmony_ci dev_err(ddata->dev, "bad version M10BMC detected\n"); 928c2ecf20Sopenharmony_ci return -ENODEV; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int intel_m10_bmc_spi_probe(struct spi_device *spi) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci const struct spi_device_id *id = spi_get_device_id(spi); 1018c2ecf20Sopenharmony_ci struct device *dev = &spi->dev; 1028c2ecf20Sopenharmony_ci struct mfd_cell *cells; 1038c2ecf20Sopenharmony_ci struct intel_m10bmc *ddata; 1048c2ecf20Sopenharmony_ci int ret, n_cell; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); 1078c2ecf20Sopenharmony_ci if (!ddata) 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ddata->dev = dev; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ddata->regmap = 1138c2ecf20Sopenharmony_ci devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config); 1148c2ecf20Sopenharmony_ci if (IS_ERR(ddata->regmap)) { 1158c2ecf20Sopenharmony_ci ret = PTR_ERR(ddata->regmap); 1168c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate regmap: %d\n", ret); 1178c2ecf20Sopenharmony_ci return ret; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci spi_set_drvdata(spi, ddata); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ret = check_m10bmc_version(ddata); 1238c2ecf20Sopenharmony_ci if (ret) { 1248c2ecf20Sopenharmony_ci dev_err(dev, "Failed to identify m10bmc hardware\n"); 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci switch (id->driver_data) { 1298c2ecf20Sopenharmony_ci case M10_N3000: 1308c2ecf20Sopenharmony_ci cells = m10bmc_pacn3000_subdevs; 1318c2ecf20Sopenharmony_ci n_cell = ARRAY_SIZE(m10bmc_pacn3000_subdevs); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci default: 1348c2ecf20Sopenharmony_ci return -ENODEV; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, n_cell, 1388c2ecf20Sopenharmony_ci NULL, 0, NULL); 1398c2ecf20Sopenharmony_ci if (ret) 1408c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register sub-devices: %d\n", ret); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return ret; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic const struct spi_device_id m10bmc_spi_id[] = { 1468c2ecf20Sopenharmony_ci { "m10-n3000", M10_N3000 }, 1478c2ecf20Sopenharmony_ci { } 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, m10bmc_spi_id); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic struct spi_driver intel_m10bmc_spi_driver = { 1528c2ecf20Sopenharmony_ci .driver = { 1538c2ecf20Sopenharmony_ci .name = "intel-m10-bmc", 1548c2ecf20Sopenharmony_ci .dev_groups = m10bmc_groups, 1558c2ecf20Sopenharmony_ci }, 1568c2ecf20Sopenharmony_ci .probe = intel_m10_bmc_spi_probe, 1578c2ecf20Sopenharmony_ci .id_table = m10bmc_spi_id, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_cimodule_spi_driver(intel_m10bmc_spi_driver); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel MAX 10 BMC Device Driver"); 1628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 1638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1648c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:intel-m10-bmc"); 165