// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2015-2022 Graphcore Ltd. */ /* * IPU device driver. * * Description: sysfs attributes */ #include #include #include #include #include #include "pci.h" #include "chrdev.h" #include "ipu_driver.h" #include "ipu_signals.h" static u64 *get_curr_nlc_err_cnt(struct ipu_device *ipu_dev, u32 nlc_base) { int idx; for (idx = 0; idx < NUM_NLCS; idx++) { if (ipu_dev->nlc_data[idx].nlc_base == nlc_base) return &ipu_dev->nlc_data[idx].acc_cor_err_cnt; } return NULL; } /* * return the NLC register base address * given an attribute of the form "nlc_w_1b_..." */ static u32 lookup_nlc_base(struct ipu_device *ipu_dev, const char *attr_name) { char nlc_encoding = 0; int idx; if (attr_name && strlen(attr_name) >= 8) { char nlc_number_idx = attr_name[6] - '0'; char nlc_letter_idx = attr_name[7] - 'a'; /* convert to hex, e.g. nlc_w_1b becomes 0x1b */ nlc_encoding = (nlc_number_idx << 4) + 0xa + nlc_letter_idx; for (idx = 0; idx < NUM_NLCS; idx++) { if (ipu_dev->nlc_data[idx].nlc_encoding == nlc_encoding) return ipu_dev->nlc_data[idx].nlc_base; } } return 0; } /* * Update the NLC correctable error count of provided attr_name. It requires * ipu_dev->nlc_mutex to be taken beforehand. If NLC link is not up, the error * count is not updated. * * return the updated NLC correctable error count. */ static u64 nlc_errcnt_update_unsafe(struct device *dev, const char *attr_name) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); u32 nlc_base; u64 *nlc_errcnt; u32 nlc_errcnt_shift = ARCH_MAP(NLC_CSR_CORERRCNT_SHIFT); u32 nlc_errcnt_mask = ARCH_MAP(NLC_CSR_CORERRCNT_MASK); u32 nlc_error_shift = ARCH_MAP(NLC_CSR_CORERROR_SHIFT); u32 nlc_error_mask = ARCH_MAP(NLC_CSR_CORERROR_MASK); u32 nlc_csr; u32 new_val; nlc_base = lookup_nlc_base(ipu_dev, attr_name); if (!nlc_base) { if (attr_name) dev_err(dev, "ipu%d: Unable to find NLC %s", ipu_dev->filename_id, attr_name); return 0; } nlc_errcnt = get_curr_nlc_err_cnt(ipu_dev, nlc_base); if (!is_nlc_link_up(ipu_dev, nlc_base)) return *nlc_errcnt; /* Read current error count; will be zero'ed after reading */ nlc_csr = readl(ipu_dev->config + nlc_base + ARCH_MAP(NLC_CSR_OFFSET)); new_val = ((nlc_csr >> nlc_errcnt_shift) & nlc_errcnt_mask); if (new_val > 0) { /* Add it to accumulated error count - saturate if needed. */ check_and_saturate_u64(nlc_errcnt, new_val); check_and_saturate_u64(&ipu_dev->nlc_total_errcnt, new_val); check_and_saturate_u64(&ipu_dev->nlc_total_errcnt_session, new_val); /* Zero the register error count, otherwise the next sysfs read * would accumulate the same value again. */ writel(((1u & nlc_error_mask) << nlc_error_shift), ipu_dev->config + nlc_base + ARCH_MAP(NLC_CSR_OFFSET)); } return *nlc_errcnt; } static ssize_t nlc_errcnt_show(struct device *dev, struct device_attribute *attr, char *buf) { u64 nlc_errcnt; struct ipu_device *ipu_dev = dev_get_drvdata(dev); mutex_lock(&ipu_dev->nlc_mutex); nlc_errcnt = nlc_errcnt_update_unsafe(dev, attr->attr.name); mutex_unlock(&ipu_dev->nlc_mutex); return sprintf(buf, "%llu\n", nlc_errcnt); } static DEVICE_ATTR(nlc_w_0b_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_w_0c_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_w_1b_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_w_1c_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_e_2a_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_e_2b_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_e_2c_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_e_3a_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_e_3b_errcnt, 0444, nlc_errcnt_show, NULL); static DEVICE_ATTR(nlc_e_3c_errcnt, 0444, nlc_errcnt_show, NULL); static struct attribute *nlc_attrs[] = { &dev_attr_nlc_w_0b_errcnt.attr, &dev_attr_nlc_w_0c_errcnt.attr, &dev_attr_nlc_w_1b_errcnt.attr, &dev_attr_nlc_w_1c_errcnt.attr, &dev_attr_nlc_e_2a_errcnt.attr, &dev_attr_nlc_e_2b_errcnt.attr, &dev_attr_nlc_e_2c_errcnt.attr, &dev_attr_nlc_e_3a_errcnt.attr, &dev_attr_nlc_e_3b_errcnt.attr, &dev_attr_nlc_e_3c_errcnt.attr, NULL }; static struct attribute_group nlc_group = { .attrs = nlc_attrs, .name = "nlc" /* .name holds the containing dir name */ }; static ssize_t spb_error_pkt_count_show(struct device *dev, struct device_attribute *attr, char *buf) { u64 spb_error_pkt_count; struct ipu_device *ipu_dev = dev_get_drvdata(dev); mutex_lock(&ipu_dev->spb_mutex); spb_error_pkt_count_update_unsafe(ipu_dev); spb_error_pkt_count = ipu_dev->spb_error_pkt_count; mutex_unlock(&ipu_dev->spb_mutex); return sprintf(buf, "%llu\n", spb_error_pkt_count); } static ssize_t spb_error_pkt_count_session_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); if (count == 2 && buf[0] == '0') { mutex_lock(&ipu_dev->spb_mutex); spb_error_pkt_count_update_unsafe(ipu_dev); ipu_dev->spb_error_pkt_count_session = 0; mutex_unlock(&ipu_dev->spb_mutex); ipu_info(ipu_dev, "ipu%d: session's error packet count in SPB reset", ipu_dev->filename_id); } else { dev_err(dev, "ipu%d: Use 0 to clear spb/error_pkt_count_session", ipu_dev->filename_id); } return count; } static ssize_t spb_error_pkt_count_session_show (struct device *dev, struct device_attribute *attr, char *buf) { u64 spb_error_pkt_count_session; struct ipu_device *ipu_dev = dev_get_drvdata(dev); mutex_lock(&ipu_dev->spb_mutex); spb_error_pkt_count_update_unsafe(ipu_dev); spb_error_pkt_count_session = ipu_dev->spb_error_pkt_count_session; mutex_unlock(&ipu_dev->spb_mutex); return sprintf(buf, "%llu\n", spb_error_pkt_count_session); } static DEVICE_ATTR(error_pkt_count, 0444, spb_error_pkt_count_show, NULL); static DEVICE_ATTR(error_pkt_count_session, 0644, spb_error_pkt_count_session_show, spb_error_pkt_count_session_store); static struct attribute *spb_attrs[] = { &dev_attr_error_pkt_count.attr, &dev_attr_error_pkt_count_session.attr, NULL }; static struct attribute_group spb_group = { .attrs = spb_attrs, .name = "spb" /* .name holds the containing dir name */ }; static ssize_t slot_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%s\n", ipu_dev->slot); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t fw_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%llu\n", ipu_dev->fw_ver); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t firmware_version_str_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; if (!ipu_dev->icu_has_firmware_ver_str) return 0; len = sprintf(buf, "%s\n", ipu_dev->firmware_version_str); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t bootloader_version_str_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; if (!ipu_dev->icu_has_bootloader_ver_str) return 0; len = sprintf(buf, "%s\n", ipu_dev->bootloader_version_str); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t serial_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%llu\n", ipu_dev->serial); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t tile_clk_speed_show (struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%u\n", atomic_read(&ipu_dev->tile_clk_speed)); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t tile_clk_speed_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len = 0; u32 poll_secs = 0; /* Limit poll to 300 seconds */ if (strlen(buf) > 0) { len = kstrtou32(buf, 10, &poll_secs); if (len < 0) { count = 0; } else { if (poll_secs > 300)/* 5 minutes */ poll_secs = 300; ipu_dev->tile_clk_speed_poll_speed = poll_secs; if (poll_secs) start_tile_clk_speed_thread(dev); else if (poll_secs == 0) stop_tile_clk_speed_thread(dev); } } return count; } struct error_record { u32 tile_id; u32 errors; }; static int compare_error_record(const void *lhs, const void *rhs) { u32 lhs_u32 = ((struct error_record *)lhs)->errors; u32 rhs_u32 = ((struct error_record *)rhs)->errors; if (lhs_u32 < rhs_u32) return 1; if (lhs_u32 > rhs_u32) return -1; return 0; } static ssize_t ipu_error_threshold_debug_show(struct device *dev, struct device_attribute *attr, char *buf) { int i; struct ipu_device *ipu_dev = dev_get_drvdata(dev); u32 num_tiles = ARCH_MAP(IPU_TILE_INSTANCES_WITH_REPAIR); s64 seconds = ktime_divns(ktime_get_real(), NSEC_PER_SEC); u32 threshold = seconds - ipu_dev->ipu_error_threshold_seconds; int last_record_len = 0; const char *ending = "...\n"; const unsigned int ending_len = strlen(ending); int len = sprintf(buf, "Detected parity errors:\n\n"); struct error_record *records = kmalloc_array(num_tiles, sizeof(struct error_record), GFP_KERNEL); if (!records) { dev_err(dev, "failed to allocate memory for sysfs error threshold"); return len; } for (i = 0; i < num_tiles; i++) { u32 resp[2]; u32 encoded_tile_id; int ret_val; encoded_tile_id = i << ICU_CMD_STORE_IPU_PARITY_ERROR_IPU_SHIFT; ret_val = icu_mailbox_transaction(ipu_dev, (u32[]) {ICU_CMD_STORE_IPU_PARITY_ERROR, 0, encoded_tile_id | ipu_dev->icu_index, threshold}, resp, sizeof(resp)); records[i].tile_id = i; records[i].errors = resp[ICU_RESP_STORE_IPU_PARITY_ERROR_NUM_ERRORS_INDEX]; } sort(records, num_tiles, sizeof(struct error_record), compare_error_record, NULL); // next printed string will be equal or shorter than // the previous one, so using the last_record_len is OK for (i = 0; i < num_tiles && (last_record_len + len < PAGE_SIZE - ending_len); i++) { if (records[i].errors) { last_record_len = sprintf(buf + len, "Tile: %4u, count: %u\n", records[i].tile_id, records[i].errors); len += last_record_len; } if (i < num_tiles - 1 && len + last_record_len > PAGE_SIZE) len += sprintf(buf + len, ending); } if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); kfree(records); return len; } static ssize_t parity_init_flag_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%u\n", ipu_dev->parity_init_flag); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t filename_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%u\n", ipu_dev->filename_id); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static struct task_struct *get_ipu_user_task(struct ipu_device *ipu_dev) { if (!ipu_dev->user_pid) return NULL; return get_pid_task(ipu_dev->user_pid, PIDTYPE_PID); } static ssize_t user_pid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); struct pid_namespace *current_ns = task_active_pid_ns(current); struct upid *upid = NULL; int level = 0; int len = 0; if (ipu_dev && ipu_dev->user_pid && current_ns) { for (level = 0; level <= ipu_dev->user_pid->level; ++level) { upid = ipu_dev->user_pid->numbers + level; if (upid->ns != current_ns) continue; len = sprintf(buf, "%d\n", upid->nr); break; } /* * If we didn't find pid, write "0" to indicate unknown * process */ if (len == 0) len = sprintf(buf, "0\n"); } if (len == 0) len = sprintf(buf, "\n"); // nothing attached else if (len < 0) { dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); } return len; } static ssize_t contiguous_mem_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; if (ipu_dev->memory_size > 0) len = sprintf(buf, "1\n"); else len = sprintf(buf, "0\n"); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t pcie_secondary_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; if (ipu_dev->board_type == IPU_BOARD_TYPE_C600 && ipu_dev->config_sec && ipu_dev->exchange_sec) len = sprintf(buf, "1\n"); else len = sprintf(buf, "0\n"); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } /* * Update all the NLC correctable error counters. It requires * ipu_dev->nlc_mutex to be taken beforehand. */ static void nlc_errcnt_update_all_unsafe(struct device *dev) { struct attribute **nlc_attr_p = nlc_attrs; /* iterate through all attributes in nlc_attrs */ while (*nlc_attr_p) { struct attribute *nlc_attr = *nlc_attr_p; /* update only the attributes with name containing _errcnt */ if (strstr(nlc_attr->name, "_errcnt")) (void)nlc_errcnt_update_unsafe(dev, nlc_attr->name); nlc_attr_p++; } } static ssize_t nlc_total_errcnt_show(struct device *dev, struct device_attribute *attr, char *buf) { u64 total_errcnt = 0; struct ipu_device *ipu_dev = dev_get_drvdata(dev); mutex_lock(&ipu_dev->nlc_mutex); nlc_errcnt_update_all_unsafe(dev); total_errcnt = ipu_dev->nlc_total_errcnt; mutex_unlock(&ipu_dev->nlc_mutex); return sprintf(buf, "%llu\n", total_errcnt); } static ssize_t nlc_total_errcnt_session_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); if (count == 2 && buf[0] == '0') { mutex_lock(&ipu_dev->nlc_mutex); nlc_errcnt_update_all_unsafe(dev); ipu_dev->nlc_total_errcnt_session = 0; mutex_unlock(&ipu_dev->nlc_mutex); ipu_info(ipu_dev, "ipu%d: session's NLC correctable error count reset", ipu_dev->filename_id); } else { dev_err(dev, "ipu%d: Use 0 to clear nlc_total_errcnt_session", ipu_dev->filename_id); } return count; } static ssize_t nlc_total_errcnt_session_show (struct device *dev, struct device_attribute *attr, char *buf) { u64 total_errcnt = 0; struct ipu_device *ipu_dev = dev_get_drvdata(dev); mutex_lock(&ipu_dev->nlc_mutex); nlc_errcnt_update_all_unsafe(dev); total_errcnt = ipu_dev->nlc_total_errcnt_session; mutex_unlock(&ipu_dev->nlc_mutex); return sprintf(buf, "%llu\n", total_errcnt); } static ssize_t ipu_fake_parity_error_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len = 0; u32 tile_id; len = kstrtou32(buf, 10, &tile_id); if (len < 0 || tile_id > ARCH_MAP(IPU_TILE_INSTANCES_WITH_REPAIR)) { ipu_err(ipu_dev, "ipu%d: invalid tile id (%u)", ipu_dev->filename_id, tile_id); count = 0; } else { ipu_info(ipu_dev, "ipu%d: parity error faked on tile %u", ipu_dev->filename_id, tile_id); ipu_dev->ipu_fake_parity_error = tile_id; } if (len < 0) count = 0; return count; } static ssize_t ipu_error_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%u\n", ipu_dev->ipu_error_state); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t ipu_error_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len = 0; u32 state; len = kstrtou32(buf, 10, &state); if (len < 0 || state >= NUM_ERROR_STATE) { dev_err(dev, "ipu%d: Set invalid error state (%d)", ipu_dev->filename_id, state); } else { ipu_info(ipu_dev, "ipu%d: Set error state to %d", ipu_dev->filename_id, state); ipu_dev->ipu_error_state = state; icu_mailbox_transaction(ipu_dev, (u32[]) {ICU_CMD_SET_DRIVER_ERROR_STATE, ipu_dev->icu_index, state, 0, 0}, NULL, 0); } if (len < 0) count = 0; return count; } static ssize_t ipu_error_threshold_num_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%u\n", ipu_dev->ipu_error_threshold_num); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t ipu_error_threshold_seconds_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%u\n", ipu_dev->ipu_error_threshold_seconds); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t ipu_error_threshold_num_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len = 0; u32 value; len = kstrtou32(buf, 10, &value); if (len < 0 || len > 255) { dev_err(dev, "ipu%d: Set invalid error threshold number (%d)", ipu_dev->filename_id, value); } else { ipu_info(ipu_dev, "ipu%d: Set error threshold number to %d", ipu_dev->filename_id, value); ipu_dev->ipu_error_threshold_num = value; } if (len < 0) count = 0; return count; } static ssize_t ipu_error_threshold_seconds_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len = 0; u32 value; len = kstrtou32(buf, 10, &value); if (len < 0) { dev_err(dev, "ipu%d: Set invalid error threshold seconds (%d)", ipu_dev->filename_id, value); } else { ipu_info(ipu_dev, "ipu%d: Set error threshold seconds to %d", ipu_dev->filename_id, value); ipu_dev->ipu_error_threshold_seconds = value; } if (len < 0) count = 0; return count; } static ssize_t hexopt_total_bytes_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); u32 hexopt_page_size = ipu_dev->hexopt_constants->HEXOPT_PAGE_SIZE; u32 hexopt_nb_entries = ipu_dev->hexopt_constants->HEXOPT_NB_ENTRIES; int len; len = sprintf(buf, "%llu\n", (u64)hexopt_page_size * hexopt_nb_entries); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t hexopt_used_bytes_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%llu\n", (u64)atomic64_read(&ipu_dev->hexopt_used_bytes)); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t hexoatt_total_bytes_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); u64 hexoatt_size = 0; u64 hexopt_size; int len; hexopt_size = (u64)ipu_dev->hexopt_constants->HEXOPT_PAGE_SIZE * ipu_dev->hexopt_constants->HEXOPT_NB_ENTRIES; if ((u64)ipu_dev->memory_size > hexopt_size) hexoatt_size = (u64)ipu_dev->memory_size - hexopt_size; len = sprintf(buf, "%llu\n", hexoatt_size); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t hexoatt_used_bytes_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%llu\n", (u64)atomic64_read(&ipu_dev->hexoatt_used_bytes)); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t throttle_log_thresholds_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%u,%u,%u\n", atomic_read(&ipu_dev->clk_throttle_threshold1), atomic_read(&ipu_dev->clk_throttle_threshold2), atomic_read(&ipu_dev->clk_throttle_threshold3)); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t user_data_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%u\n", ipu_dev->user_data); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t board_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%s\n", ipu_dev->board_name); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static ssize_t board_variant_name_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int len; len = sprintf(buf, "%s\n", ipu_dev->board_variant_name); if (len <= 0) dev_err(dev, "ipu%d: Invalid sprintf len: %d", ipu_dev->filename_id, len); return len; } static DEVICE_ATTR_RO(slot); static DEVICE_ATTR_RO(fw_ver); static DEVICE_ATTR_RO(firmware_version_str); static DEVICE_ATTR_RO(bootloader_version_str); static DEVICE_ATTR_RO(serial); static DEVICE_ATTR_RW(tile_clk_speed); static DEVICE_ATTR_RO(parity_init_flag); static DEVICE_ATTR_RO(filename_id); static DEVICE_ATTR_RO(user_pid); static DEVICE_ATTR_RO(contiguous_mem); static DEVICE_ATTR_RO(nlc_total_errcnt); static DEVICE_ATTR_RW(nlc_total_errcnt_session); static DEVICE_ATTR_RO(hexopt_total_bytes); static DEVICE_ATTR_RO(hexopt_used_bytes); static DEVICE_ATTR_RO(hexoatt_total_bytes); static DEVICE_ATTR_RO(hexoatt_used_bytes); static DEVICE_ATTR_RO(throttle_log_thresholds); static DEVICE_ATTR_RO(user_data); static DEVICE_ATTR_RO(board_name); static DEVICE_ATTR_RO(board_variant_name); static DEVICE_ATTR_RW(ipu_error_state); static DEVICE_ATTR_RW(ipu_error_threshold_num); static DEVICE_ATTR_RW(ipu_error_threshold_seconds); static DEVICE_ATTR_WO(ipu_fake_parity_error); static DEVICE_ATTR_RO(ipu_error_threshold_debug); static DEVICE_ATTR_RO(pcie_secondary); static struct attribute *ipu_attrs[] = { &dev_attr_slot.attr, &dev_attr_fw_ver.attr, &dev_attr_firmware_version_str.attr, &dev_attr_bootloader_version_str.attr, &dev_attr_serial.attr, &dev_attr_tile_clk_speed.attr, &dev_attr_parity_init_flag.attr, &dev_attr_filename_id.attr, &dev_attr_user_pid.attr, &dev_attr_contiguous_mem.attr, &dev_attr_nlc_total_errcnt.attr, &dev_attr_nlc_total_errcnt_session.attr, &dev_attr_hexopt_total_bytes.attr, &dev_attr_hexopt_used_bytes.attr, &dev_attr_hexoatt_total_bytes.attr, &dev_attr_hexoatt_used_bytes.attr, &dev_attr_throttle_log_thresholds.attr, &dev_attr_user_data.attr, &dev_attr_board_name.attr, &dev_attr_board_variant_name.attr, &dev_attr_ipu_error_state.attr, &dev_attr_ipu_error_threshold_num.attr, &dev_attr_ipu_error_threshold_seconds.attr, &dev_attr_ipu_fake_parity_error.attr, &dev_attr_ipu_error_threshold_debug.attr, &dev_attr_pcie_secondary.attr, NULL }; static const struct attribute_group ipu_group = { .attrs = ipu_attrs, }; static const struct attribute_group *ipu_groups[] = { &ipu_group, &nlc_group, NULL, }; /* * HWMON attributes */ static ssize_t ipu_hwmon_sensor_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); struct task_struct *task = get_ipu_user_task(ipu_dev); int index = to_sensor_dev_attr(attr)->index; if (index >= NUM_ALL_SENSORS) { dev_err(dev, "ipu%d: Invalid sensor index: %d", ipu_dev->filename_id, index); return 0; } if (ipu_dev->icu_supports_telemetry) { if (index >= TEMP_SENSOR_DIE_START && index <= TEMP_SENSOR_DIE_END) { icu_read_ipu_die_temps(ipu_dev); } else if (index >= TEMP_SENSOR_BOARD_START && index <= TEMP_SENSOR_BOARD_END) { icu_read_board_temps(ipu_dev); } else if (index >= POWER_SENSOR_START && index <= POWER_SENSOR_END) { icu_read_ipu_power(ipu_dev); } } else if (!task) { /* if ICU telemetry is not supported sensor data will only be * valid if there is a process */ return sprintf(buf, "\n"); } if (!ipu_dev->sensor[index].valid) return sprintf(buf, "\n"); return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ipu_dev->sensor[index].value)); } static ssize_t ipu_hwmon_label_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ipu_device *ipu_dev = dev_get_drvdata(dev); int index = to_sensor_dev_attr(attr)->index; if (index <= TEMP_SENSOR_DIE_END) { return scnprintf(buf, PAGE_SIZE, "Chip temp sensor %d\n", index); } else if (index <= TEMP_SENSOR_BOARD_END) { return scnprintf(buf, PAGE_SIZE, "Board temp sensor %d\n", index); } else if (index <= POWER_SENSOR_END) { return scnprintf(buf, PAGE_SIZE, "Board power sensor %d\n", index - POWER_SENSOR_START); } else if (index <= APP_SIZES_END) { return scnprintf(buf, PAGE_SIZE, "App sizes %d\n", index - APP_SIZES_START); } else if (index <= IPU_BUSY_DURATION_END) { return scnprintf(buf, PAGE_SIZE, "IPU busy durations %d\n", index - IPU_BUSY_DURATION_START); } dev_err(dev, "ipu%d: Invalid sensor label index: %d", ipu_dev->filename_id, index); return sprintf(buf, "\n"); } /* Die and board temperature */ static SENSOR_DEVICE_ATTR(temp1_input, 0644, ipu_hwmon_sensor_show, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_input, 0644, ipu_hwmon_sensor_show, NULL, 1); static SENSOR_DEVICE_ATTR(temp3_input, 0644, ipu_hwmon_sensor_show, NULL, 2); static SENSOR_DEVICE_ATTR(temp4_input, 0644, ipu_hwmon_sensor_show, NULL, 3); static SENSOR_DEVICE_ATTR(temp5_input, 0644, ipu_hwmon_sensor_show, NULL, 4); static SENSOR_DEVICE_ATTR(temp6_input, 0644, ipu_hwmon_sensor_show, NULL, 5); static SENSOR_DEVICE_ATTR(temp7_input, 0644, ipu_hwmon_sensor_show, NULL, 6); static SENSOR_DEVICE_ATTR(temp8_input, 0644, ipu_hwmon_sensor_show, NULL, 7); static SENSOR_DEVICE_ATTR(temp1_label, 0644, ipu_hwmon_label_show, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_label, 0644, ipu_hwmon_label_show, NULL, 1); static SENSOR_DEVICE_ATTR(temp3_label, 0644, ipu_hwmon_label_show, NULL, 2); static SENSOR_DEVICE_ATTR(temp4_label, 0644, ipu_hwmon_label_show, NULL, 3); static SENSOR_DEVICE_ATTR(temp5_label, 0644, ipu_hwmon_label_show, NULL, 4); static SENSOR_DEVICE_ATTR(temp6_label, 0644, ipu_hwmon_label_show, NULL, 5); static SENSOR_DEVICE_ATTR(temp7_label, 0644, ipu_hwmon_label_show, NULL, 6); static SENSOR_DEVICE_ATTR(temp8_label, 0644, ipu_hwmon_label_show, NULL, 7); /* power */ static SENSOR_DEVICE_ATTR(power1_input, 0644, ipu_hwmon_sensor_show, NULL, 8); static SENSOR_DEVICE_ATTR(power2_input, 0644, ipu_hwmon_sensor_show, NULL, 9); static SENSOR_DEVICE_ATTR(power3_input, 0644, ipu_hwmon_sensor_show, NULL, 10); static SENSOR_DEVICE_ATTR(power1_label, 0644, ipu_hwmon_label_show, NULL, 8); static SENSOR_DEVICE_ATTR(power2_label, 0644, ipu_hwmon_label_show, NULL, 9); static SENSOR_DEVICE_ATTR(power3_label, 0644, ipu_hwmon_label_show, NULL, 10); /* app sizes */ static SENSOR_DEVICE_ATTR(app_size1_input, 0644, ipu_hwmon_sensor_show, NULL, 11); static SENSOR_DEVICE_ATTR(app_size2_input, 0644, ipu_hwmon_sensor_show, NULL, 12); static SENSOR_DEVICE_ATTR(app_size3_input, 0644, ipu_hwmon_sensor_show, NULL, 13); static SENSOR_DEVICE_ATTR(app_size1_label, 0644, ipu_hwmon_label_show, NULL, 11); static SENSOR_DEVICE_ATTR(app_size2_label, 0644, ipu_hwmon_label_show, NULL, 12); static SENSOR_DEVICE_ATTR(app_size3_label, 0644, ipu_hwmon_label_show, NULL, 13); /* ipu busy duration */ static SENSOR_DEVICE_ATTR(ipu_busy_duration0_input, 0644, ipu_hwmon_sensor_show, NULL, 14); static SENSOR_DEVICE_ATTR(ipu_busy_duration1_input, 0644, ipu_hwmon_sensor_show, NULL, 15); static SENSOR_DEVICE_ATTR(ipu_busy_duration0_label, 0644, ipu_hwmon_sensor_show, NULL, 14); static SENSOR_DEVICE_ATTR(ipu_busy_duration1_label, 0644, ipu_hwmon_sensor_show, NULL, 15); static struct attribute *ipu_hwmon_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp4_input.dev_attr.attr, &sensor_dev_attr_temp5_input.dev_attr.attr, &sensor_dev_attr_temp6_input.dev_attr.attr, &sensor_dev_attr_temp7_input.dev_attr.attr, &sensor_dev_attr_temp8_input.dev_attr.attr, &sensor_dev_attr_temp1_label.dev_attr.attr, &sensor_dev_attr_temp2_label.dev_attr.attr, &sensor_dev_attr_temp3_label.dev_attr.attr, &sensor_dev_attr_temp4_label.dev_attr.attr, &sensor_dev_attr_temp5_label.dev_attr.attr, &sensor_dev_attr_temp6_label.dev_attr.attr, &sensor_dev_attr_temp7_label.dev_attr.attr, &sensor_dev_attr_temp8_label.dev_attr.attr, &sensor_dev_attr_power1_input.dev_attr.attr, &sensor_dev_attr_power2_input.dev_attr.attr, &sensor_dev_attr_power3_input.dev_attr.attr, &sensor_dev_attr_power1_label.dev_attr.attr, &sensor_dev_attr_power2_label.dev_attr.attr, &sensor_dev_attr_power3_label.dev_attr.attr, &sensor_dev_attr_app_size1_input.dev_attr.attr, &sensor_dev_attr_app_size2_input.dev_attr.attr, &sensor_dev_attr_app_size3_input.dev_attr.attr, &sensor_dev_attr_app_size1_label.dev_attr.attr, &sensor_dev_attr_app_size2_label.dev_attr.attr, &sensor_dev_attr_app_size3_label.dev_attr.attr, &sensor_dev_attr_ipu_busy_duration0_input.dev_attr.attr, &sensor_dev_attr_ipu_busy_duration1_input.dev_attr.attr, &sensor_dev_attr_ipu_busy_duration0_label.dev_attr.attr, &sensor_dev_attr_ipu_busy_duration1_label.dev_attr.attr, NULL }; static const struct attribute_group ipu_hwmon_attrgroup = { .attrs = ipu_hwmon_attrs }; static const struct attribute_group *ipu_hwmon_groups[] = { &ipu_hwmon_attrgroup, NULL }; static void populate_nlc_data(struct ipu_device *ipu_dev) { int cnt = 0; mutex_init(&ipu_dev->nlc_mutex); ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_W_0B_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x0b; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_W_0C_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x0c; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_W_1B_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x1b; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_W_1C_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x1c; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_E_2A_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x2a; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_E_2B_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x2b; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_E_2C_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x2c; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_E_3A_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x3a; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_E_3B_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x3b; cnt++; ipu_dev->nlc_data[cnt].nlc_base = ARCH_MAP(SOC_NLC_E_3C_BASE); ipu_dev->nlc_data[cnt].nlc_encoding = 0x3c; cnt++; if (cnt != NUM_NLCS) ipu_info_ratelimited(ipu_dev, "ipu%d: mismatch in NLC data", ipu_dev->filename_id); } int ipu_sysfs_add_attributes(struct ipu_device *ipu_dev) { struct pci_dev *dev = ipu_dev->pci_dev; int err; dev->dev.groups = ipu_groups; ipu_dev->hwmon_dev = hwmon_device_register_with_groups(&dev->dev, "ipu", ipu_dev, ipu_hwmon_groups); if (IS_ERR(ipu_dev->hwmon_dev)) { int ret = PTR_ERR(ipu_dev->hwmon_dev); dev_err(&dev->dev, "Unable to register hwmon device: %d\n", ret); return ret; } err = sysfs_create_group(&dev->dev.kobj, &ipu_group); if (err) { dev_err(&dev->dev, "ipu%d: sysfs ipu group creation failed", ipu_dev->filename_id); return 1; } err = sysfs_create_group(&dev->dev.kobj, &nlc_group); if (err) { dev_err(&dev->dev, "ipu%d: sysfs nlc group creation failed", ipu_dev->filename_id); return 1; } if (check_spb_supported(ipu_dev)) { err = sysfs_create_group(&dev->dev.kobj, &spb_group); if (err) { dev_err(&dev->dev, "ipu%d: sysfs spb group creation failed", ipu_dev->filename_id); return 1; } mutex_init(&ipu_dev->spb_mutex); } populate_nlc_data(ipu_dev); return 0; } void ipu_sysfs_remove_attributes(struct ipu_device *ipu_dev) { struct pci_dev *dev = ipu_dev->pci_dev; sysfs_remove_group(&dev->dev.kobj, &nlc_group); sysfs_remove_group(&dev->dev.kobj, &ipu_group); sysfs_remove_group(&dev->dev.kobj, &ipu_hwmon_attrgroup); if (check_spb_supported(ipu_dev)) sysfs_remove_group(&dev->dev.kobj, &spb_group); if (ipu_dev->hwmon_dev) hwmon_device_unregister(ipu_dev->hwmon_dev); }