/* SPDX-License-Identifier: Apache-2.0 * Copyright(c) 2023 Cisco Systems, Inc. */ #include #include #include #include VLIB_REGISTER_LOG_CLASS (ena_log, static) = { .class_name = "ena", .subclass_name = "reg", }; static vnet_dev_rv_t ena_err (vnet_dev_t *dev, vnet_dev_rv_t rv, char *fmt, ...) { va_list va; u8 *s; va_start (va, fmt); s = va_format (0, fmt, &va); va_end (va); log_err (dev, "%v", s); vec_free (s); return rv; } static u8 * format_ena_reg_name (u8 *s, va_list *args) { int offset = va_arg (*args, int); char *reg_names[] = { #define _(o, r, rn, m) [(o) >> 2] = #rn, foreach_ena_reg #undef _ }; offset >>= 2; if (offset < 0 || offset >= ARRAY_LEN (reg_names) || reg_names[offset] == 0) return format (s, "(unknown)"); return format (s, "%s", reg_names[offset]); } void ena_reg_write (vnet_dev_t *dev, ena_reg_t reg, void *v) { ena_device_t *ed = vnet_dev_get_data (dev); u32 *p = (u32 *) ((u8 *) ed->reg_bar + reg); u32 val = *(u32 *) v; log_debug (dev, "%s: reg %U (0x%02x) value 0x%08x", __func__, format_ena_reg_name, reg, reg, val); __atomic_store_n (p, val, __ATOMIC_RELEASE); } void ena_reg_set_dma_addr (vlib_main_t *vm, vnet_dev_t *dev, u32 rlo, u32 rhi, void *p) { uword pa = vnet_dev_get_dma_addr (vm, dev, p); u32 reg = (u32) pa; ena_reg_write (dev, rlo, ®); reg = pa >> 32; ena_reg_write (dev, rhi, ®); } void ena_reg_read (vnet_dev_t *dev, ena_reg_t reg, const void *v) { ena_device_t *ed = vnet_dev_get_data (dev); vlib_main_t *vm = vlib_get_main (); u32 rv; f64 dt = 0, t0; if (ed->readless == 0) { rv = __atomic_load_n ((u32 *) ((u8 *) ed->reg_bar + reg), __ATOMIC_SEQ_CST); } else { u32 *p = (u32 *) ((u8 *) ed->reg_bar + ENA_REG_MMIO_REG_READ); ena_reg_mmio_reg_read_t rr = { .reg_off = reg, .req_id = 1 }; ed->mmio_resp->req_id = 0; ed->mmio_resp->reg_val = ~0; __atomic_store_n (p, rr.as_u32, __ATOMIC_RELEASE); t0 = vlib_time_now (vm); while (ed->mmio_resp->req_id == 0 && dt < 0.2) { CLIB_PAUSE (); dt = vlib_time_now (vm) - t0; } rv = ed->mmio_resp->reg_val; } log_debug (dev, "%s: reg %U (0x%02x) value 0x%08x dt %.3fs", __func__, format_ena_reg_name, reg, reg, rv, dt); *(u32 *) v = rv; } vnet_dev_rv_t ena_reg_reset (vlib_main_t *vm, vnet_dev_t *dev, ena_reset_reason_t reason) { ena_device_t *ed = vnet_dev_get_data (dev); ena_reg_version_t ver; ena_reg_controller_version_t ctrl_ver; ena_reg_caps_t caps = {}; ena_reg_dev_sts_t dev_sts = {}; ena_reg_dev_ctl_t reset_start = { .dev_reset = 1, .reset_reason = reason }; if (ed->readless) ena_reg_set_dma_addr (vm, dev, ENA_REG_MMIO_RESP_LO, ENA_REG_MMIO_RESP_HI, ed->mmio_resp); ena_reg_read (dev, ENA_REG_DEV_STS, &dev_sts); ena_reg_read (dev, ENA_REG_CAPS, &caps); if (caps.as_u32 == ~0 && dev_sts.as_u32 == ~0) return ena_err (dev, VNET_DEV_ERR_BUS, "failed to read regs"); if (dev_sts.ready == 0) return VNET_DEV_ERR_NOT_READY; log_debug (dev, "reg_reset: reset timeout is %u", caps.reset_timeout); ena_reg_write (dev, ENA_REG_DEV_CTL, &reset_start); if (ed->readless) ena_reg_set_dma_addr (vm, dev, ENA_REG_MMIO_RESP_LO, ENA_REG_MMIO_RESP_HI, ed->mmio_resp); while (1) { int i = 0; ena_reg_read (dev, ENA_REG_DEV_STS, &dev_sts); if (dev_sts.reset_in_progress) break; if (i++ == 20) return ena_err (dev, VNET_DEV_ERR_BUS, "failed to initiate reset"); vlib_process_suspend (vm, 0.001); } ena_reg_write (dev, ENA_REG_DEV_CTL, &(ena_reg_dev_ctl_t){}); return 0; while (1) { int i = 0; ena_reg_read (dev, ENA_REG_DEV_STS, &dev_sts); if (dev_sts.reset_in_progress == 0) break; if (i++ == 20) return ena_err (dev, VNET_DEV_ERR_BUS, "failed to complete reset"); vlib_process_suspend (vm, 0.001); } ena_reg_read (dev, ENA_REG_VERSION, &ver); ena_reg_read (dev, ENA_REG_CONTROLLER_VERSION, &ctrl_ver); log_info (dev, "version %u.%u controller_version %u.%u.%u impl_id %u\n", ver.major, ver.minor, ctrl_ver.major, ctrl_ver.minor, ctrl_ver.subminor, ctrl_ver.impl_id); return 0; }