1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Intel MAX 10 Board Management Controller chip - common code
4 *
5 * Copyright (C) 2018-2020 Intel Corporation. All rights reserved.
6 */
7
8#include <linux/bitfield.h>
9#include <linux/device.h>
10#include <linux/dev_printk.h>
11#include <linux/mfd/core.h>
12#include <linux/mfd/intel-m10-bmc.h>
13#include <linux/module.h>
14
15void m10bmc_fw_state_set(struct intel_m10bmc *m10bmc, enum m10bmc_fw_state new_state)
16{
17 /* bmcfw_state is only needed if handshake_sys_reg_nranges > 0 */
18 if (!m10bmc->info->handshake_sys_reg_nranges)
19 return;
20
21 down_write(sem: &m10bmc->bmcfw_lock);
22 m10bmc->bmcfw_state = new_state;
23 up_write(sem: &m10bmc->bmcfw_lock);
24}
25EXPORT_SYMBOL_NS_GPL(m10bmc_fw_state_set, INTEL_M10_BMC_CORE);
26
27/*
28 * For some Intel FPGA devices, the BMC firmware is not available to service
29 * handshake registers during a secure update.
30 */
31static bool m10bmc_reg_always_available(struct intel_m10bmc *m10bmc, unsigned int offset)
32{
33 if (!m10bmc->info->handshake_sys_reg_nranges)
34 return true;
35
36 return !regmap_reg_in_ranges(reg: offset, ranges: m10bmc->info->handshake_sys_reg_ranges,
37 nranges: m10bmc->info->handshake_sys_reg_nranges);
38}
39
40/*
41 * m10bmc_handshake_reg_unavailable - Checks if reg access collides with secure update state
42 * @m10bmc: M10 BMC structure
43 *
44 * For some Intel FPGA devices, the BMC firmware is not available to service
45 * handshake registers during a secure update erase and write phases.
46 *
47 * Context: @m10bmc->bmcfw_lock must be held.
48 */
49static bool m10bmc_handshake_reg_unavailable(struct intel_m10bmc *m10bmc)
50{
51 return m10bmc->bmcfw_state == M10BMC_FW_STATE_SEC_UPDATE_PREPARE ||
52 m10bmc->bmcfw_state == M10BMC_FW_STATE_SEC_UPDATE_WRITE;
53}
54
55/*
56 * This function helps to simplify the accessing of the system registers.
57 *
58 * The base of the system registers is configured through the struct
59 * csr_map.
60 */
61int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int *val)
62{
63 const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map;
64 int ret;
65
66 if (m10bmc_reg_always_available(m10bmc, offset))
67 return m10bmc_raw_read(m10bmc, addr: csr_map->base + offset, val);
68
69 down_read(sem: &m10bmc->bmcfw_lock);
70 if (m10bmc_handshake_reg_unavailable(m10bmc))
71 ret = -EBUSY; /* Reg not available during secure update */
72 else
73 ret = m10bmc_raw_read(m10bmc, addr: csr_map->base + offset, val);
74 up_read(sem: &m10bmc->bmcfw_lock);
75
76 return ret;
77}
78EXPORT_SYMBOL_NS_GPL(m10bmc_sys_read, INTEL_M10_BMC_CORE);
79
80int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset,
81 unsigned int msk, unsigned int val)
82{
83 const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map;
84 int ret;
85
86 if (m10bmc_reg_always_available(m10bmc, offset))
87 return regmap_update_bits(map: m10bmc->regmap, reg: csr_map->base + offset, mask: msk, val);
88
89 down_read(sem: &m10bmc->bmcfw_lock);
90 if (m10bmc_handshake_reg_unavailable(m10bmc))
91 ret = -EBUSY; /* Reg not available during secure update */
92 else
93 ret = regmap_update_bits(map: m10bmc->regmap, reg: csr_map->base + offset, mask: msk, val);
94 up_read(sem: &m10bmc->bmcfw_lock);
95
96 return ret;
97}
98EXPORT_SYMBOL_NS_GPL(m10bmc_sys_update_bits, INTEL_M10_BMC_CORE);
99
100static ssize_t bmc_version_show(struct device *dev,
101 struct device_attribute *attr, char *buf)
102{
103 struct intel_m10bmc *ddata = dev_get_drvdata(dev);
104 unsigned int val;
105 int ret;
106
107 ret = m10bmc_sys_read(ddata, ddata->info->csr_map->build_version, &val);
108 if (ret)
109 return ret;
110
111 return sprintf(buf, fmt: "0x%x\n", val);
112}
113static DEVICE_ATTR_RO(bmc_version);
114
115static ssize_t bmcfw_version_show(struct device *dev,
116 struct device_attribute *attr, char *buf)
117{
118 struct intel_m10bmc *ddata = dev_get_drvdata(dev);
119 unsigned int val;
120 int ret;
121
122 ret = m10bmc_sys_read(ddata, ddata->info->csr_map->fw_version, &val);
123 if (ret)
124 return ret;
125
126 return sprintf(buf, fmt: "0x%x\n", val);
127}
128static DEVICE_ATTR_RO(bmcfw_version);
129
130static ssize_t mac_address_show(struct device *dev,
131 struct device_attribute *attr, char *buf)
132{
133 struct intel_m10bmc *ddata = dev_get_drvdata(dev);
134 unsigned int macaddr_low, macaddr_high;
135 int ret;
136
137 ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_low, &macaddr_low);
138 if (ret)
139 return ret;
140
141 ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high);
142 if (ret)
143 return ret;
144
145 return sysfs_emit(buf, fmt: "%02x:%02x:%02x:%02x:%02x:%02x\n",
146 (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE1, macaddr_low),
147 (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE2, macaddr_low),
148 (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE3, macaddr_low),
149 (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE4, macaddr_low),
150 (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE5, macaddr_high),
151 (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE6, macaddr_high));
152}
153static DEVICE_ATTR_RO(mac_address);
154
155static ssize_t mac_count_show(struct device *dev,
156 struct device_attribute *attr, char *buf)
157{
158 struct intel_m10bmc *ddata = dev_get_drvdata(dev);
159 unsigned int macaddr_high;
160 int ret;
161
162 ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high);
163 if (ret)
164 return ret;
165
166 return sysfs_emit(buf, fmt: "%u\n", (u8)FIELD_GET(M10BMC_N3000_MAC_COUNT, macaddr_high));
167}
168static DEVICE_ATTR_RO(mac_count);
169
170static struct attribute *m10bmc_attrs[] = {
171 &dev_attr_bmc_version.attr,
172 &dev_attr_bmcfw_version.attr,
173 &dev_attr_mac_address.attr,
174 &dev_attr_mac_count.attr,
175 NULL,
176};
177
178static const struct attribute_group m10bmc_group = {
179 .attrs = m10bmc_attrs,
180};
181
182const struct attribute_group *m10bmc_dev_groups[] = {
183 &m10bmc_group,
184 NULL,
185};
186EXPORT_SYMBOL_NS_GPL(m10bmc_dev_groups, INTEL_M10_BMC_CORE);
187
188int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platform_info *info)
189{
190 int ret;
191
192 m10bmc->info = info;
193 dev_set_drvdata(dev: m10bmc->dev, data: m10bmc);
194 init_rwsem(&m10bmc->bmcfw_lock);
195
196 ret = devm_mfd_add_devices(dev: m10bmc->dev, PLATFORM_DEVID_AUTO,
197 cells: info->cells, n_devs: info->n_cells,
198 NULL, irq_base: 0, NULL);
199 if (ret)
200 dev_err(m10bmc->dev, "Failed to register sub-devices: %d\n", ret);
201
202 return ret;
203}
204EXPORT_SYMBOL_NS_GPL(m10bmc_dev_init, INTEL_M10_BMC_CORE);
205
206MODULE_DESCRIPTION("Intel MAX 10 BMC core driver");
207MODULE_AUTHOR("Intel Corporation");
208MODULE_LICENSE("GPL v2");
209

source code of linux/drivers/mfd/intel-m10-bmc-core.c