| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Intel MAX 10 Board Management Controller chip - common code |
| * |
| * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. |
| */ |
| |
| #include <linux/bitfield.h> |
| #include <linux/device.h> |
| #include <linux/dev_printk.h> |
| #include <linux/mfd/core.h> |
| #include <linux/mfd/intel-m10-bmc.h> |
| #include <linux/module.h> |
| |
| void m10bmc_fw_state_set(struct intel_m10bmc *m10bmc, enum m10bmc_fw_state new_state) |
| { |
| /* bmcfw_state is only needed if handshake_sys_reg_nranges > 0 */ |
| if (!m10bmc->info->handshake_sys_reg_nranges) |
| return; |
| |
| down_write(&m10bmc->bmcfw_lock); |
| m10bmc->bmcfw_state = new_state; |
| up_write(&m10bmc->bmcfw_lock); |
| } |
| EXPORT_SYMBOL_NS_GPL(m10bmc_fw_state_set, INTEL_M10_BMC_CORE); |
| |
| /* |
| * For some Intel FPGA devices, the BMC firmware is not available to service |
| * handshake registers during a secure update. |
| */ |
| static bool m10bmc_reg_always_available(struct intel_m10bmc *m10bmc, unsigned int offset) |
| { |
| if (!m10bmc->info->handshake_sys_reg_nranges) |
| return true; |
| |
| return !regmap_reg_in_ranges(offset, m10bmc->info->handshake_sys_reg_ranges, |
| m10bmc->info->handshake_sys_reg_nranges); |
| } |
| |
| /* |
| * m10bmc_handshake_reg_unavailable - Checks if reg access collides with secure update state |
| * @m10bmc: M10 BMC structure |
| * |
| * For some Intel FPGA devices, the BMC firmware is not available to service |
| * handshake registers during a secure update erase and write phases. |
| * |
| * Context: @m10bmc->bmcfw_lock must be held. |
| */ |
| static bool m10bmc_handshake_reg_unavailable(struct intel_m10bmc *m10bmc) |
| { |
| return m10bmc->bmcfw_state == M10BMC_FW_STATE_SEC_UPDATE_PREPARE || |
| m10bmc->bmcfw_state == M10BMC_FW_STATE_SEC_UPDATE_WRITE; |
| } |
| |
| /* |
| * This function helps to simplify the accessing of the system registers. |
| * |
| * The base of the system registers is configured through the struct |
| * csr_map. |
| */ |
| int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int *val) |
| { |
| const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; |
| int ret; |
| |
| if (m10bmc_reg_always_available(m10bmc, offset)) |
| return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); |
| |
| down_read(&m10bmc->bmcfw_lock); |
| if (m10bmc_handshake_reg_unavailable(m10bmc)) |
| ret = -EBUSY; /* Reg not available during secure update */ |
| else |
| ret = m10bmc_raw_read(m10bmc, csr_map->base + offset, val); |
| up_read(&m10bmc->bmcfw_lock); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_NS_GPL(m10bmc_sys_read, INTEL_M10_BMC_CORE); |
| |
| int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset, |
| unsigned int msk, unsigned int val) |
| { |
| const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; |
| int ret; |
| |
| if (m10bmc_reg_always_available(m10bmc, offset)) |
| return regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); |
| |
| down_read(&m10bmc->bmcfw_lock); |
| if (m10bmc_handshake_reg_unavailable(m10bmc)) |
| ret = -EBUSY; /* Reg not available during secure update */ |
| else |
| ret = regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); |
| up_read(&m10bmc->bmcfw_lock); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_NS_GPL(m10bmc_sys_update_bits, INTEL_M10_BMC_CORE); |
| |
| static ssize_t bmc_version_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct intel_m10bmc *ddata = dev_get_drvdata(dev); |
| unsigned int val; |
| int ret; |
| |
| ret = m10bmc_sys_read(ddata, ddata->info->csr_map->build_version, &val); |
| if (ret) |
| return ret; |
| |
| return sprintf(buf, "0x%x\n", val); |
| } |
| static DEVICE_ATTR_RO(bmc_version); |
| |
| static ssize_t bmcfw_version_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct intel_m10bmc *ddata = dev_get_drvdata(dev); |
| unsigned int val; |
| int ret; |
| |
| ret = m10bmc_sys_read(ddata, ddata->info->csr_map->fw_version, &val); |
| if (ret) |
| return ret; |
| |
| return sprintf(buf, "0x%x\n", val); |
| } |
| static DEVICE_ATTR_RO(bmcfw_version); |
| |
| static ssize_t mac_address_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct intel_m10bmc *ddata = dev_get_drvdata(dev); |
| unsigned int macaddr_low, macaddr_high; |
| int ret; |
| |
| ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_low, &macaddr_low); |
| if (ret) |
| return ret; |
| |
| ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high); |
| if (ret) |
| return ret; |
| |
| return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", |
| (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE1, macaddr_low), |
| (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE2, macaddr_low), |
| (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE3, macaddr_low), |
| (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE4, macaddr_low), |
| (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE5, macaddr_high), |
| (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE6, macaddr_high)); |
| } |
| static DEVICE_ATTR_RO(mac_address); |
| |
| static ssize_t mac_count_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| struct intel_m10bmc *ddata = dev_get_drvdata(dev); |
| unsigned int macaddr_high; |
| int ret; |
| |
| ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high); |
| if (ret) |
| return ret; |
| |
| return sysfs_emit(buf, "%u\n", (u8)FIELD_GET(M10BMC_N3000_MAC_COUNT, macaddr_high)); |
| } |
| static DEVICE_ATTR_RO(mac_count); |
| |
| static struct attribute *m10bmc_attrs[] = { |
| &dev_attr_bmc_version.attr, |
| &dev_attr_bmcfw_version.attr, |
| &dev_attr_mac_address.attr, |
| &dev_attr_mac_count.attr, |
| NULL, |
| }; |
| |
| static const struct attribute_group m10bmc_group = { |
| .attrs = m10bmc_attrs, |
| }; |
| |
| const struct attribute_group *m10bmc_dev_groups[] = { |
| &m10bmc_group, |
| NULL, |
| }; |
| EXPORT_SYMBOL_NS_GPL(m10bmc_dev_groups, INTEL_M10_BMC_CORE); |
| |
| int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platform_info *info) |
| { |
| int ret; |
| |
| m10bmc->info = info; |
| dev_set_drvdata(m10bmc->dev, m10bmc); |
| init_rwsem(&m10bmc->bmcfw_lock); |
| |
| ret = devm_mfd_add_devices(m10bmc->dev, PLATFORM_DEVID_AUTO, |
| info->cells, info->n_cells, |
| NULL, 0, NULL); |
| if (ret) |
| dev_err(m10bmc->dev, "Failed to register sub-devices: %d\n", ret); |
| |
| return ret; |
| } |
| EXPORT_SYMBOL_NS_GPL(m10bmc_dev_init, INTEL_M10_BMC_CORE); |
| |
| MODULE_DESCRIPTION("Intel MAX 10 BMC core driver"); |
| MODULE_AUTHOR("Intel Corporation"); |
| MODULE_LICENSE("GPL v2"); |