| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Copyright (c) 2023 MediaTek Inc. All Rights Reserved. |
| * |
| * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com> |
| */ |
| |
| #include <linux/device.h> |
| |
| #include "firmware.h" |
| #include "hpdma.h" |
| #include "internal.h" |
| #include "mcu.h" |
| #include "netsys.h" |
| #include "trm.h" |
| #include "tunnel.h" |
| #include "wdt.h" |
| |
| static const char *tops_role_name[__TOPS_ROLE_TYPE_MAX] = { |
| [TOPS_ROLE_TYPE_MGMT] = "tops-mgmt", |
| [TOPS_ROLE_TYPE_CLUSTER] = "tops-offload", |
| }; |
| |
| static ssize_t mtk_tops_fw_info_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| enum tops_role_type rtype; |
| struct tm tm = {0}; |
| const char *value; |
| const char *prop; |
| int len = 0; |
| u32 nattr; |
| u32 i; |
| |
| for (rtype = TOPS_ROLE_TYPE_MGMT; rtype < __TOPS_ROLE_TYPE_MAX; rtype++) { |
| mtk_tops_fw_get_built_date(rtype, &tm); |
| |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "%s FW information:\n", tops_role_name[rtype]); |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "Git revision:\t%llx\n", |
| mtk_tops_fw_get_git_commit_id(rtype)); |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "Build date:\t%04ld/%02d/%02d %02d:%02d:%02d\n", |
| tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, |
| tm.tm_hour, tm.tm_min, tm.tm_sec); |
| |
| nattr = mtk_tops_fw_attr_get_num(rtype); |
| |
| for (i = 0; i < nattr; i++) { |
| prop = mtk_tops_fw_attr_get_property(rtype, i); |
| if (!prop) |
| continue; |
| |
| value = mtk_tops_fw_attr_get_value(rtype, prop); |
| |
| len += snprintf(buf + len, PAGE_SIZE - len, |
| "%s:\t%s\n", prop, value); |
| } |
| len += snprintf(buf + len, PAGE_SIZE - len, "\n"); |
| } |
| |
| return len; |
| } |
| |
| static int mtk_tops_ctrl_fetch_port(const char *buf, int *ofs, u16 *port) |
| { |
| int nchar = 0; |
| int ret; |
| u16 p = 0; |
| |
| ret = sscanf(buf + *ofs, "%hu %n", &p, &nchar); |
| if (ret != 1) |
| return -EPERM; |
| |
| *port = htons(p); |
| |
| *ofs += nchar; |
| |
| return nchar; |
| } |
| |
| static int mtk_tops_ctrl_fetch_ip(const char *buf, int *ofs, u32 *ip) |
| { |
| int nchar = 0; |
| int ret = 0; |
| u8 tmp[4]; |
| |
| ret = sscanf(buf + *ofs, "%hhu.%hhu.%hhu.%hhu %n", |
| &tmp[3], &tmp[2], &tmp[1], &tmp[0], &nchar); |
| if (ret != 4) |
| return -EPERM; |
| |
| *ip = tmp[0] | tmp[1] << 8 | tmp[2] << 16 | tmp[3] << 24; |
| |
| *ofs += nchar; |
| |
| return nchar; |
| } |
| |
| static int mtk_tops_ctrl_fetch_mac(const char *buf, int *ofs, u8 *mac) |
| { |
| int nchar = 0; |
| int ret = 0; |
| |
| ret = sscanf(buf + *ofs, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %n", |
| &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5], &nchar); |
| if (ret != 6) |
| return -EPERM; |
| |
| *ofs += nchar; |
| |
| return 0; |
| } |
| |
| static int mtk_tops_ctrl_add_tnl(const char *buf) |
| { |
| struct tops_tnl_params tnl_params = {0}; |
| struct tops_tnl_info *tnl_info; |
| struct tops_tnl_type *tnl_type; |
| char tnl_type_name[21] = {0}; |
| int ofs = 0; |
| int ret = 0; |
| |
| ret = sscanf(buf, "%20s %n", tnl_type_name, &ofs); |
| if (ret != 1) |
| return -EPERM; |
| |
| tnl_type = mtk_tops_tnl_type_get_by_name(tnl_type_name); |
| if (unlikely(!tnl_type || !tnl_type->tnl_debug_param_setup)) |
| return -ENODEV; |
| |
| ret = mtk_tops_ctrl_fetch_mac(buf, &ofs, tnl_params.daddr); |
| if (ret < 0) |
| return ret; |
| |
| ret = mtk_tops_ctrl_fetch_mac(buf, &ofs, tnl_params.saddr); |
| if (ret < 0) |
| return ret; |
| |
| ret = mtk_tops_ctrl_fetch_ip(buf, &ofs, &tnl_params.dip); |
| if (ret < 0) |
| return ret; |
| |
| ret = mtk_tops_ctrl_fetch_ip(buf, &ofs, &tnl_params.sip); |
| if (ret < 0) |
| return ret; |
| |
| ret = mtk_tops_ctrl_fetch_port(buf, &ofs, &tnl_params.dport); |
| if (ret < 0) |
| return ret; |
| |
| ret = mtk_tops_ctrl_fetch_port(buf, &ofs, &tnl_params.sport); |
| if (ret < 0) |
| return ret; |
| |
| ret = tnl_type->tnl_debug_param_setup(buf, &ofs, &tnl_params); |
| if (ret < 0) |
| return ret; |
| |
| tnl_params.flag |= TNL_DECAP_ENABLE; |
| tnl_params.flag |= TNL_ENCAP_ENABLE; |
| tnl_params.tops_entry_proto = tnl_type->tops_entry; |
| |
| tnl_info = mtk_tops_tnl_info_alloc(); |
| if (IS_ERR(tnl_info)) |
| return -ENOMEM; |
| |
| tnl_info->flag |= TNL_INFO_DEBUG; |
| memcpy(&tnl_info->cache, &tnl_params, sizeof(struct tops_tnl_params)); |
| |
| mtk_tops_tnl_info_hash(tnl_info); |
| |
| mtk_tops_tnl_info_submit(tnl_info); |
| |
| return 0; |
| } |
| |
| static ssize_t mtk_tops_tnl_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| char cmd[21] = {0}; |
| int nchar = 0; |
| int ret = 0; |
| |
| ret = sscanf(buf, "%20s %n", cmd, &nchar); |
| |
| if (ret != 1) |
| return -EPERM; |
| |
| if (!strcmp(cmd, "NEW_TNL")) { |
| ret = mtk_tops_ctrl_add_tnl(buf + nchar); |
| if (ret) |
| return ret; |
| } |
| |
| return count; |
| } |
| |
| static int mtk_tops_trm_fetch_setting(const char *buf, |
| int *ofs, |
| char *name, |
| u32 *offset, |
| u32 *size, |
| u8 *enable) |
| { |
| int nchar = 0; |
| int ret = 0; |
| |
| ret = sscanf(buf + *ofs, "%31s %x %x %hhx %n", |
| name, offset, size, enable, &nchar); |
| if (ret != 4) |
| return -EPERM; |
| |
| *ofs += nchar; |
| |
| return nchar; |
| } |
| |
| static ssize_t mtk_tops_trm_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| char name[TRM_CONFIG_NAME_MAX_LEN] = { 0 }; |
| char cmd[21] = { 0 }; |
| int nchar = 0; |
| int ret = 0; |
| u32 offset; |
| u8 enable; |
| u32 size; |
| |
| ret = sscanf(buf, "%20s %n", cmd, &nchar); |
| if (ret != 1) |
| return -EPERM; |
| |
| if (!strcmp(cmd, "trm_dump")) { |
| ret = mtk_trm_dump(TRM_RSN_NULL); |
| if (ret) |
| return ret; |
| } else if (!strcmp(cmd, "trm_cfg_setup")) { |
| ret = mtk_tops_trm_fetch_setting(buf, &nchar, |
| name, &offset, &size, &enable); |
| if (ret < 0) |
| return ret; |
| |
| ret = mtk_trm_cfg_setup(name, offset, size, enable); |
| if (ret) |
| return ret; |
| } |
| |
| return count; |
| } |
| |
| static ssize_t mtk_tops_wdt_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, |
| size_t count) |
| { |
| char cmd[21] = {0}; |
| u32 core = 0; |
| u32 i; |
| int ret; |
| |
| ret = sscanf(buf, "%20s %x", cmd, &core); |
| if (ret != 2) |
| return -EPERM; |
| |
| core &= CORE_TOPS_MASK; |
| if (!strcmp(cmd, "WDT_TO")) { |
| for (i = 0; i < CORE_TOPS_NUM; i++) { |
| if (core & 0x1) |
| mtk_tops_wdt_trigger_timeout(i); |
| core >>= 1; |
| } |
| } else { |
| return -EINVAL; |
| } |
| |
| return count; |
| } |
| |
| static DEVICE_ATTR_RO(mtk_tops_fw_info); |
| static DEVICE_ATTR_WO(mtk_tops_tnl); |
| static DEVICE_ATTR_WO(mtk_tops_trm); |
| static DEVICE_ATTR_WO(mtk_tops_wdt); |
| |
| static struct attribute *mtk_tops_attributes[] = { |
| &dev_attr_mtk_tops_fw_info.attr, |
| &dev_attr_mtk_tops_tnl.attr, |
| &dev_attr_mtk_tops_trm.attr, |
| &dev_attr_mtk_tops_wdt.attr, |
| NULL, |
| }; |
| |
| static const struct attribute_group mtk_tops_attr_group = { |
| .name = "mtk_tops", |
| .attrs = mtk_tops_attributes, |
| }; |
| |
| int mtk_tops_ctrl_init(struct platform_device *pdev) |
| { |
| int ret = 0; |
| |
| ret = sysfs_create_group(&pdev->dev.kobj, &mtk_tops_attr_group); |
| if (ret) { |
| TOPS_ERR("create sysfs failed\n"); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| void mtk_tops_ctrl_deinit(struct platform_device *pdev) |
| { |
| sysfs_remove_group(&pdev->dev.kobj, &mtk_tops_attr_group); |
| } |