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 | |
15 | void 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 | } |
25 | EXPORT_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 | */ |
31 | static 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 | */ |
49 | static 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 | */ |
61 | int 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 | } |
78 | EXPORT_SYMBOL_NS_GPL(m10bmc_sys_read, INTEL_M10_BMC_CORE); |
79 | |
80 | int 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 | } |
98 | EXPORT_SYMBOL_NS_GPL(m10bmc_sys_update_bits, INTEL_M10_BMC_CORE); |
99 | |
100 | static 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 | } |
113 | static DEVICE_ATTR_RO(bmc_version); |
114 | |
115 | static 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 | } |
128 | static DEVICE_ATTR_RO(bmcfw_version); |
129 | |
130 | static 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 | } |
153 | static DEVICE_ATTR_RO(mac_address); |
154 | |
155 | static 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 | } |
168 | static DEVICE_ATTR_RO(mac_count); |
169 | |
170 | static 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 | |
178 | static const struct attribute_group m10bmc_group = { |
179 | .attrs = m10bmc_attrs, |
180 | }; |
181 | |
182 | const struct attribute_group *m10bmc_dev_groups[] = { |
183 | &m10bmc_group, |
184 | NULL, |
185 | }; |
186 | EXPORT_SYMBOL_NS_GPL(m10bmc_dev_groups, INTEL_M10_BMC_CORE); |
187 | |
188 | int 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 | } |
204 | EXPORT_SYMBOL_NS_GPL(m10bmc_dev_init, INTEL_M10_BMC_CORE); |
205 | |
206 | MODULE_DESCRIPTION("Intel MAX 10 BMC core driver" ); |
207 | MODULE_AUTHOR("Intel Corporation" ); |
208 | MODULE_LICENSE("GPL v2" ); |
209 | |