Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mfd: intel-m10-bmc: Manage access to MAX 10 fw handshake registers

On some MAX 10 cards, the BMC firmware is not available to service
handshake registers during secure update erase and write phases at
normal speeds. This problem affects at least hwmon driver. When the MAX
10 hwmon driver tries to read the sensor values during a secure update,
the reads are slowed down (e.g., reading all D5005 sensors takes ~24s
which is magnitudes worse than the normal <0.02s).

Manage access to the handshake registers using a rw semaphore and a FW
state variable to prevent accesses during those secure update phases
and return -EBUSY instead.

If handshake_sys_reg_nranges == 0, don't update bwcfw_state as it is not
used. This avoids the locking cost.

Co-developed-by: Russ Weight <russell.h.weight@intel.com>
Signed-off-by: Russ Weight <russell.h.weight@intel.com>
Co-developed-by: Xu Yilun <yilun.xu@intel.com>
Signed-off-by: Xu Yilun <yilun.xu@intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Lee Jones <lee@kernel.org>
Link: https://lore.kernel.org/r/20230417092653.16487-5-ilpo.jarvinen@linux.intel.com

authored by

Ilpo Järvinen and committed by
Lee Jones
867cae44 e9c154ee

+121 -5
+14 -3
drivers/fpga/intel-m10-bmc-sec-update.c
··· 544 544 if (ret != FW_UPLOAD_ERR_NONE) 545 545 goto unlock_flash; 546 546 547 + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_SEC_UPDATE_PREPARE); 548 + 547 549 ret = rsu_update_init(sec); 548 550 if (ret != FW_UPLOAD_ERR_NONE) 549 - goto unlock_flash; 551 + goto fw_state_exit; 550 552 551 553 ret = rsu_prog_ready(sec); 552 554 if (ret != FW_UPLOAD_ERR_NONE) 553 - goto unlock_flash; 555 + goto fw_state_exit; 554 556 555 557 if (sec->cancel_request) { 556 558 ret = rsu_cancel(sec); 557 - goto unlock_flash; 559 + goto fw_state_exit; 558 560 } 559 561 562 + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_SEC_UPDATE_WRITE); 563 + 560 564 return FW_UPLOAD_ERR_NONE; 565 + 566 + fw_state_exit: 567 + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_NORMAL); 561 568 562 569 unlock_flash: 563 570 if (sec->m10bmc->flash_bulk_ops) ··· 614 607 if (sec->cancel_request) 615 608 return rsu_cancel(sec); 616 609 610 + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_SEC_UPDATE_PROGRAM); 611 + 617 612 result = rsu_send_data(sec); 618 613 if (result != FW_UPLOAD_ERR_NONE) 619 614 return result; ··· 658 649 struct m10bmc_sec *sec = fwl->dd_handle; 659 650 660 651 (void)rsu_cancel(sec); 652 + 653 + m10bmc_fw_state_set(sec->m10bmc, M10BMC_FW_STATE_NORMAL); 661 654 662 655 if (sec->m10bmc->flash_bulk_ops) 663 656 sec->m10bmc->flash_bulk_ops->unlock_write(sec->m10bmc);
+65 -2
drivers/mfd/intel-m10-bmc-core.c
··· 12 12 #include <linux/mfd/intel-m10-bmc.h> 13 13 #include <linux/module.h> 14 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(&m10bmc->bmcfw_lock); 22 + m10bmc->bmcfw_state = new_state; 23 + up_write(&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(offset, m10bmc->info->handshake_sys_reg_ranges, 37 + 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 + 15 55 /* 16 56 * This function helps to simplify the accessing of the system registers. 17 57 * ··· 61 21 int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int *val) 62 22 { 63 23 const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; 24 + int ret; 64 25 65 - return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); 26 + if (m10bmc_reg_always_available(m10bmc, offset)) 27 + return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); 28 + 29 + down_read(&m10bmc->bmcfw_lock); 30 + if (m10bmc_handshake_reg_unavailable(m10bmc)) 31 + ret = -EBUSY; /* Reg not available during secure update */ 32 + else 33 + ret = m10bmc_raw_read(m10bmc, csr_map->base + offset, val); 34 + up_read(&m10bmc->bmcfw_lock); 35 + 36 + return ret; 66 37 } 67 38 EXPORT_SYMBOL_NS_GPL(m10bmc_sys_read, INTEL_M10_BMC_CORE); 68 39 ··· 81 30 unsigned int msk, unsigned int val) 82 31 { 83 32 const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; 33 + int ret; 84 34 85 - return regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); 35 + if (m10bmc_reg_always_available(m10bmc, offset)) 36 + return regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); 37 + 38 + down_read(&m10bmc->bmcfw_lock); 39 + if (m10bmc_handshake_reg_unavailable(m10bmc)) 40 + ret = -EBUSY; /* Reg not available during secure update */ 41 + else 42 + ret = regmap_update_bits(m10bmc->regmap, csr_map->base + offset, msk, val); 43 + up_read(&m10bmc->bmcfw_lock); 44 + 45 + return ret; 86 46 } 87 47 EXPORT_SYMBOL_NS_GPL(m10bmc_sys_update_bits, INTEL_M10_BMC_CORE); 88 48 ··· 191 129 192 130 m10bmc->info = info; 193 131 dev_set_drvdata(m10bmc->dev, m10bmc); 132 + init_rwsem(&m10bmc->bmcfw_lock); 194 133 195 134 ret = devm_mfd_add_devices(m10bmc->dev, PLATFORM_DEVID_AUTO, 196 135 info->cells, info->n_cells,
+14
drivers/mfd/intel-m10-bmc-spi.c
··· 116 116 { .name = "d5005bmc-sec-update" }, 117 117 }; 118 118 119 + static const struct regmap_range m10bmc_d5005_fw_handshake_regs[] = { 120 + regmap_reg_range(M10BMC_N3000_TELEM_START, M10BMC_D5005_TELEM_END), 121 + }; 122 + 119 123 static struct mfd_cell m10bmc_pacn3000_subdevs[] = { 120 124 { .name = "n3000bmc-hwmon" }, 121 125 { .name = "n3000bmc-retimer" }, 122 126 { .name = "n3000bmc-sec-update" }, 127 + }; 128 + 129 + static const struct regmap_range m10bmc_n3000_fw_handshake_regs[] = { 130 + regmap_reg_range(M10BMC_N3000_TELEM_START, M10BMC_N3000_TELEM_END), 123 131 }; 124 132 125 133 static struct mfd_cell m10bmc_n5010_subdevs[] = { ··· 137 129 static const struct intel_m10bmc_platform_info m10bmc_spi_n3000 = { 138 130 .cells = m10bmc_pacn3000_subdevs, 139 131 .n_cells = ARRAY_SIZE(m10bmc_pacn3000_subdevs), 132 + .handshake_sys_reg_ranges = m10bmc_n3000_fw_handshake_regs, 133 + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_n3000_fw_handshake_regs), 140 134 .csr_map = &m10bmc_n3000_csr_map, 141 135 }; 142 136 143 137 static const struct intel_m10bmc_platform_info m10bmc_spi_d5005 = { 144 138 .cells = m10bmc_d5005_subdevs, 145 139 .n_cells = ARRAY_SIZE(m10bmc_d5005_subdevs), 140 + .handshake_sys_reg_ranges = m10bmc_d5005_fw_handshake_regs, 141 + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_d5005_fw_handshake_regs), 146 142 .csr_map = &m10bmc_n3000_csr_map, 147 143 }; 148 144 149 145 static const struct intel_m10bmc_platform_info m10bmc_spi_n5010 = { 150 146 .cells = m10bmc_n5010_subdevs, 151 147 .n_cells = ARRAY_SIZE(m10bmc_n5010_subdevs), 148 + .handshake_sys_reg_ranges = m10bmc_n3000_fw_handshake_regs, 149 + .handshake_sys_reg_nranges = ARRAY_SIZE(m10bmc_n3000_fw_handshake_regs), 152 150 .csr_map = &m10bmc_n3000_csr_map, 153 151 }; 154 152
+28
include/linux/mfd/intel-m10-bmc.h
··· 11 11 #include <linux/bits.h> 12 12 #include <linux/dev_printk.h> 13 13 #include <linux/regmap.h> 14 + #include <linux/rwsem.h> 14 15 15 16 #define M10BMC_N3000_LEGACY_BUILD_VER 0x300468 16 17 #define M10BMC_N3000_SYS_BASE 0x300800 ··· 39 38 #define M10BMC_N3000_VER_MAJOR_MSK GENMASK(23, 16) 40 39 #define M10BMC_N3000_VER_PCB_INFO_MSK GENMASK(31, 24) 41 40 #define M10BMC_N3000_VER_LEGACY_INVALID 0xffffffff 41 + 42 + /* Telemetry registers */ 43 + #define M10BMC_N3000_TELEM_START 0x100 44 + #define M10BMC_N3000_TELEM_END 0x250 45 + #define M10BMC_D5005_TELEM_END 0x300 42 46 43 47 /* Secure update doorbell register, in system register region */ 44 48 #define M10BMC_N3000_DOORBELL 0x400 ··· 211 205 * struct intel_m10bmc_platform_info - Intel MAX 10 BMC platform specific information 212 206 * @cells: MFD cells 213 207 * @n_cells: MFD cells ARRAY_SIZE() 208 + * @handshake_sys_reg_ranges: array of register ranges for fw handshake regs 209 + * @handshake_sys_reg_nranges: number of register ranges for fw handshake regs 214 210 * @csr_map: the mappings for register definition of MAX10 BMC 215 211 */ 216 212 struct intel_m10bmc_platform_info { 217 213 struct mfd_cell *cells; 218 214 int n_cells; 215 + const struct regmap_range *handshake_sys_reg_ranges; 216 + unsigned int handshake_sys_reg_nranges; 219 217 const struct m10bmc_csr_map *csr_map; 220 218 }; 221 219 ··· 242 232 void (*unlock_write)(struct intel_m10bmc *m10bmc); 243 233 }; 244 234 235 + enum m10bmc_fw_state { 236 + M10BMC_FW_STATE_NORMAL, 237 + M10BMC_FW_STATE_SEC_UPDATE_PREPARE, 238 + M10BMC_FW_STATE_SEC_UPDATE_WRITE, 239 + M10BMC_FW_STATE_SEC_UPDATE_PROGRAM, 240 + }; 241 + 245 242 /** 246 243 * struct intel_m10bmc - Intel MAX 10 BMC parent driver data structure 247 244 * @dev: this device 248 245 * @regmap: the regmap used to access registers by m10bmc itself 249 246 * @info: the platform information for MAX10 BMC 250 247 * @flash_bulk_ops: optional device specific operations for flash R/W 248 + * @bmcfw_lock: read/write semaphore to BMC firmware running state 249 + * @bmcfw_state: BMC firmware running state. Available only when 250 + * handshake_sys_reg_nranges > 0. 251 251 */ 252 252 struct intel_m10bmc { 253 253 struct device *dev; 254 254 struct regmap *regmap; 255 255 const struct intel_m10bmc_platform_info *info; 256 256 const struct intel_m10bmc_flash_bulk_ops *flash_bulk_ops; 257 + struct rw_semaphore bmcfw_lock; /* Protects bmcfw_state */ 258 + enum m10bmc_fw_state bmcfw_state; 257 259 }; 258 260 259 261 /* ··· 292 270 int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, unsigned int *val); 293 271 int m10bmc_sys_update_bits(struct intel_m10bmc *m10bmc, unsigned int offset, 294 272 unsigned int msk, unsigned int val); 273 + 274 + /* 275 + * Track the state of the firmware, as it is not available for register 276 + * handshakes during secure updates on some MAX 10 cards. 277 + */ 278 + void m10bmc_fw_state_set(struct intel_m10bmc *m10bmc, enum m10bmc_fw_state new_state); 295 279 296 280 /* 297 281 * MAX10 BMC Core support