// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include "efuse.h" #include #include #include #include #define EFUSE_DEVICE_NAME "efuse" #define EFUSE_CLASS_NAME "efuse" static struct aml_efuse_key efuse_key = { .num = 0, .infos = NULL, }; static struct aml_efuse_lockable_check efusecheck = { .main_cmd = 0, .item_num = 0, .infos = NULL, }; struct efuse_obj_field_t efuse_field; static char mrk_name[16] = {}; static unsigned int mrk_checksum[7] = {-1, -1, -1, -1, -1, -1, -1}; struct aml_efuse_cmd efuse_cmd; unsigned int efuse_pattern_size; void __iomem *sharemem_input_base; void __iomem *sharemem_output_base; unsigned int efuse_obj_cmd_status; unsigned int efuse_cali_item_read; #define DEFINE_EFUSEKEY_SHOW_ATTR(keyname) \ static ssize_t keyname##_show(const struct class *class, \ const struct class_attribute *attr, \ char *buf) \ { \ ssize_t ret; \ \ ret = efuse_user_attr_show(#keyname, buf); \ return ret; \ } DEFINE_EFUSEKEY_SHOW_ATTR(mac) DEFINE_EFUSEKEY_SHOW_ATTR(mac_bt) DEFINE_EFUSEKEY_SHOW_ATTR(mac_wifi) DEFINE_EFUSEKEY_SHOW_ATTR(usid) #ifndef EFUSE_READ_ONLY #define DEFINE_EFUSEKEY_STORE_ATTR(keyname) \ static ssize_t keyname##_store(const struct class *class, \ const struct class_attribute *attr, \ const char *buf, size_t count) \ { \ ssize_t ret; \ \ ret = efuse_user_attr_store(#keyname, buf, count); \ return ret; \ } DEFINE_EFUSEKEY_STORE_ATTR(mac) DEFINE_EFUSEKEY_STORE_ATTR(mac_bt) DEFINE_EFUSEKEY_STORE_ATTR(mac_wifi) DEFINE_EFUSEKEY_STORE_ATTR(usid) #endif int efuse_getinfo(char *item, struct efusekey_info *info) { int i; int ret = -EINVAL; for (i = 0; i < efuse_key.num; i++) { if (strcmp(efuse_key.infos[i].keyname, item) == 0) { strcpy(info->keyname, efuse_key.infos[i].keyname); info->offset = efuse_key.infos[i].offset; info->size = efuse_key.infos[i].size; ret = 0; break; } } if (ret < 0) pr_err("%s item not found.\n", item); return ret; } /*return: 0:is configurated, -1: don't cfg*/ int efuse_burn_lockable_is_cfg(char *itemname) { int ret = -1; int i; for (i = 0; i < efusecheck.item_num; i++) { if (strncmp(itemname, efusecheck.infos[i].itemname, sizeof(efusecheck.infos[i].itemname)) == 0) { ret = 0; break; } } return ret; } /* * return: 1:burned(wrote), 0: not write, -1: fail */ int efuse_burn_check_burned(char *itemname) { int i; int ret = -1; int subcmd; for (i = 0; i < efusecheck.item_num; i++) { if (strcmp(itemname, efusecheck.infos[i].itemname) == 0) { subcmd = efusecheck.infos[i].subcmd; ret = efuse_amlogic_check_lockable_item(subcmd); break; } } return ret; } static int efuse_open(struct inode *inode, struct file *file) { struct aml_efuse_dev *devp; devp = container_of(inode->i_cdev, struct aml_efuse_dev, cdev); file->private_data = devp; return 0; } static int efuse_release(struct inode *inode, struct file *file) { return 0; } static loff_t efuse_llseek(struct file *filp, loff_t off, int whence) { loff_t newpos; ssize_t max_size; max_size = efuse_get_max(); if (max_size <= 0) return -EINVAL; switch (whence) { case 0: /* SEEK_SET */ newpos = off; break; case 1: /* SEEK_CUR */ newpos = filp->f_pos + off; break; case 2: /* SEEK_END */ newpos = max_size + off; break; default: /* can't happen */ return -EINVAL; } if (newpos < 0 || newpos >= max_size) return -EINVAL; filp->f_pos = newpos; return newpos; } static long efuse_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; struct efusekey_info info; switch (cmd) { case EFUSE_INFO_GET: if (copy_from_user((void *)&info, argp, sizeof(info))) { pr_err("%s: copy_from_user fail\n", __func__); return -EFAULT; } if (efuse_getinfo(info.keyname, &info) < 0) { pr_err("efuse: %s is not found\n", info.keyname); return -EFAULT; } if (copy_to_user(argp, &info, sizeof(info))) { pr_err("%s: copy_to_user fail\n", __func__); return -EFAULT; } break; default: return -ENOTTY; } return 0; } #ifdef CONFIG_COMPAT static long efuse_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long args) { long ret; args = (unsigned long)compat_ptr(args); ret = efuse_unlocked_ioctl(filp, cmd, args); return ret; } #endif static ssize_t efuse_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { ssize_t ret; unsigned int pos = (unsigned int)*ppos; unsigned char *op = NULL; unsigned int max_size; ret = efuse_get_max(); if (ret < 0) { pr_err("efuse: failed to get userdata max size\n"); goto exit; } max_size = (unsigned int)ret; if (pos >= max_size || count > max_size || count > max_size - pos) { ret = -EINVAL; pr_err("efuse: data range over userdata range\n"); goto exit; } op = kzalloc((sizeof(char) * count), GFP_KERNEL); if (!op) { ret = -ENOMEM; pr_err("efuse: failed to allocate memory\n"); goto exit; } memset(op, 0, count); ret = efuse_read_usr(op, count, ppos); if (ret < 0) { kfree(op); pr_err("efuse: read user data fail!\n"); goto exit; } ret = copy_to_user((void *)buf, (void *)op, count); kfree(op); if (ret) { ret = -EFAULT; pr_err("%s: copy_to_user fail!!\n", __func__); goto exit; } ret = count; exit: return ret; } static ssize_t efuse_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { #ifndef EFUSE_READ_ONLY ssize_t ret; unsigned int pos = (unsigned int)*ppos; unsigned char *op = NULL; unsigned int max_size; ret = efuse_get_max(); if (ret < 0) { pr_err("efuse: failed to get userdata max size\n"); goto exit; } max_size = (unsigned int)ret; if (pos >= max_size || count > max_size || count > max_size - pos) { ret = -EINVAL; pr_err("efuse: data range over userdata range\n"); goto exit; } op = kzalloc((sizeof(char) * count), GFP_KERNEL); if (!op) { ret = -ENOMEM; pr_err("efuse: failed to allocate memory\n"); goto exit; } memset(op, 0, count); if (copy_from_user((void *)op, (void *)buf, count)) { kfree(op); ret = -EFAULT; pr_err("%s: copy_from_user fail!!\n", __func__); goto exit; } ret = efuse_write_usr(op, count, ppos); kfree(op); if (ret < 0) { pr_err("efuse: write user area %d bytes data fail\n", (unsigned int)count); goto exit; } pr_info("efuse: write user area %d bytes data OK\n", (unsigned int)ret); ret = count; exit: return ret; #else pr_err("no permission to write!!\n"); return -EPERM; #endif } static const struct file_operations efuse_fops = { .owner = THIS_MODULE, .llseek = efuse_llseek, .open = efuse_open, .release = efuse_release, .read = efuse_read, .write = efuse_write, .unlocked_ioctl = efuse_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = efuse_compat_ioctl, #endif }; ssize_t efuse_user_attr_store(char *name, const char *buf, size_t count) { #ifndef EFUSE_READ_ONLY char *op = NULL; ssize_t ret; int i; const char *c, *s; struct efusekey_info info; unsigned int uint_val; loff_t pos; if (efuse_getinfo(name, &info) < 0) { ret = -EINVAL; pr_err("efuse: %s is not found\n", name); goto exit; } op = kzalloc(sizeof(char) * count, GFP_KERNEL); if (!op) { ret = -ENOMEM; pr_err("efuse: failed to allocate memory\n"); goto exit; } memset(op, 0, count); memcpy(op, buf, count); c = ":"; s = op; if (strstr(s, c)) { for (i = 0; i < info.size; i++) { uint_val = 0; if (i != info.size - 1) { ret = sscanf(s, "%x:", &uint_val); if (ret != 1 || uint_val > 0xff) ret = -EINVAL; } else { ret = kstrtou8(s, 16, (unsigned char *)&uint_val); } if (ret < 0) { kfree(op); pr_err("efuse: get key input data fail\n"); goto exit; } op[i] = uint_val; s += 2; if (!strncmp(s, c, 1)) s++; } } else if ((op[count - 1] != 0x0A && count != info.size) || count - 1 > info.size || count < info.size) { kfree(op); ret = -EINVAL; pr_err("efuse: key data length not match\n"); goto exit; } pos = ((loff_t)(info.offset)) & 0xffffffff; ret = efuse_write_usr(op, info.size, &pos); kfree(op); if (ret < 0) { pr_err("efuse: write user area %d bytes data fail\n", (unsigned int)info.size); goto exit; } pr_info("efuse: write user area %d bytes data OK\n", (unsigned int)ret); ret = count; exit: return ret; #else pr_err("no permission to write!!\n"); return -EPERM; #endif } EXPORT_SYMBOL(efuse_user_attr_store); ssize_t efuse_user_attr_show(char *name, char *buf) { char *op = NULL; ssize_t ret; ssize_t len = 0; struct efusekey_info info; int i; loff_t pos; if (efuse_getinfo(name, &info) < 0) { ret = -EINVAL; pr_err("efuse: %s is not found\n", name); goto exit; } op = kzalloc(sizeof(char) * info.size, GFP_KERNEL); if (!op) { ret = -ENOMEM; pr_err("efuse: failed to allocate memory\n"); goto exit; } memset(op, 0, info.size); pos = ((loff_t)(info.offset)) & 0xffffffff; ret = efuse_read_usr(op, info.size, &pos); if (ret < 0) { kfree(op); pr_err("efuse: read user data fail!\n"); goto exit; } for (i = 0; i < info.size; i++) { if (i != 0 && (i % 16 == 0)) len += sprintf(buf + len, "\n"); if (i % 16 == 0) len += sprintf(buf + len, "0x%02x: ", i); len += sprintf(buf + len, "%02x ", op[i]); } len += sprintf(buf + len, "\n"); kfree(op); ret = len; exit: return ret; } ssize_t efuse_user_attr_read(char *name, char *buf) { char *op = NULL; ssize_t ret; struct efusekey_info info; loff_t pos; if (efuse_getinfo(name, &info) < 0) { ret = -EINVAL; pr_err("efuse: %s is not found\n", name); goto exit; } op = kzalloc(sizeof(char) * info.size, GFP_KERNEL); if (!op) { ret = -ENOMEM; pr_err("efuse: failed to allocate memory\n"); goto exit; } memset(op, 0, info.size); pos = ((loff_t)(info.offset)) & 0xffffffff; ret = efuse_read_usr(op, info.size, &pos); if (ret < 0) { kfree(op); pr_err("efuse: read user data fail!\n"); goto exit; } memcpy(buf, op, info.size); kfree(op); ret = (ssize_t)info.size; exit: return ret; } static ssize_t userdata_show(const struct class *class, const struct class_attribute *attr, char *buf) { char *op = NULL; ssize_t ret; ssize_t len = 0; int i; loff_t offset = 0; unsigned int max_size; ret = efuse_get_max(); if (ret < 0) { pr_err("efuse: failed to get userdata max size\n"); goto exit; } max_size = (unsigned int)ret; op = kcalloc(max_size, sizeof(char), GFP_KERNEL); if (!op) { ret = -ENOMEM; pr_err("efuse: failed to allocate memory\n"); goto exit; } memset(op, 0, max_size); ret = efuse_read_usr(op, max_size, &offset); if (ret < 0) { kfree(op); pr_err("efuse: read user data error!!\n"); goto exit; } for (i = 0; i < ret; i++) { if (i != 0 && (i % 16 == 0)) len += sprintf(buf + len, "\n"); if (i % 16 == 0) len += sprintf(buf + len, "0x%02x: ", i); len += sprintf(buf + len, "%02x ", op[i]); } len += sprintf(buf + len, "\n"); kfree(op); ret = len; exit: return ret; } #ifndef EFUSE_READ_ONLY static ssize_t userdata_store(const struct class *class, const struct class_attribute *attr, const char *buf, size_t count) { ssize_t ret; loff_t offset = 0; char *op = NULL; unsigned int max_size; ret = efuse_get_max(); if (ret < 0) { pr_err("efuse: failed to get userdata max size\n"); goto exit; } max_size = (unsigned int)ret; if (count > max_size) { ret = -EINVAL; pr_err("efuse: data length over userdata max size\n"); goto exit; } op = kzalloc(sizeof(char) * count, GFP_KERNEL); if (!op) { ret = -ENOMEM; pr_err("efuse: failed to allocate memory\n"); goto exit; } memset(op, 0, count); memcpy(op, buf, count); ret = efuse_write_usr(op, count, &offset); kfree(op); if (ret < 0) { pr_err("efuse: write user area %d bytes data fail\n", (unsigned int)count); goto exit; } pr_info("efuse: write user area %d bytes data OK\n", (unsigned int)ret); ret = count; exit: return ret; } #endif static ssize_t amlogic_set_store(const struct class *class, const struct class_attribute *attr, const char *buf, size_t count) { ssize_t ret; char *op = NULL; if (count != efuse_pattern_size) { ret = -EINVAL; pr_err("efuse: bad pattern size, only support size %d!\n", efuse_pattern_size); goto exit; } op = kzalloc(sizeof(char) * count, GFP_KERNEL); if (!op) { ret = -ENOMEM; pr_err("efuse: failed to allocate memory!\n"); goto exit; } memset(op, 0, count); memcpy(op, buf, count); ret = efuse_amlogic_set(op, count); kfree(op); if (ret) { pr_err("efuse: pattern programming fail! ret: %d\n", (unsigned int)ret); ret = -EINVAL; goto exit; } pr_info("efuse: pattern programming success!\n"); ret = count; exit: return ret; } static ssize_t secureboot_check_show(const struct class *cla, const struct class_attribute *attr, char *buf) { ssize_t n = 0; int ret; struct aml_efuse_dev *efuse_dev; efuse_dev = container_of(cla, struct aml_efuse_dev, cls); if (!efuse_dev->reg_base) ret = -EINVAL; else ret = readl(efuse_dev->reg_base) & efuse_dev->secureboot_mask; if (ret < 0) n = sprintf(buf, "fail"); else if (ret == 0) n = sprintf(buf, "raw"); else n = sprintf(buf, "encrypt"); return n; } static ssize_t checkburn_show(const struct class *cla, const struct class_attribute *attr, char *buf) { ssize_t n = 0; struct aml_efuse_dev *efuse_dev; efuse_dev = container_of(cla, struct aml_efuse_dev, cls); if (efuse_dev->name[0]) { if (efuse_burn_lockable_is_cfg(efuse_dev->name) == 0) { n = efuse_burn_check_burned(efuse_dev->name); if (n == 1) n = sprintf(buf, "wrote"); else if (n == 0) n = sprintf(buf, "notwrite"); else n = sprintf(buf, "error"); } else { n = sprintf(buf, "nocfg"); } } else { n = sprintf(buf, "unknown"); } return n; } static ssize_t checkburn_store(const struct class *cla, const struct class_attribute *attr, const char *buf, size_t count) { ssize_t n = 0; struct aml_efuse_dev *efuse_dev; efuse_dev = container_of(cla, struct aml_efuse_dev, cls); n = strlen(buf); pr_notice("%s:%d %s,n=%zd\n", __func__, __LINE__, buf, n); n--; //discard '\n' if (n >= EFUSE_CHECK_NAME_LEN) n = EFUSE_CHECK_NAME_LEN - 1; memset(efuse_dev->name, 0, sizeof(efuse_dev->name)); memcpy(efuse_dev->name, buf, n); return count; } static ssize_t checklist_show(const struct class *class, const struct class_attribute *attr, char *buf) { int i; ssize_t n = 0; for (i = 0; i < efusecheck.item_num; i++) { n += sprintf(&buf[n], efusecheck.infos[i].itemname); n += sprintf(&buf[n], "\n"); } return n; } static int key_item_parse_dt(const struct device_node *np_efusekey, int index, struct efusekey_info *infos) { const phandle *phandle; struct device_node *np_key; char *propname; const char *keyname; int ret; int name_size; propname = kasprintf(GFP_KERNEL, "key%d", index); phandle = of_get_property(np_efusekey, propname, NULL); if (!phandle) { ret = -EINVAL; pr_err("failed to find match %s\n", propname); goto exit; } np_key = of_find_node_by_phandle(be32_to_cpup(phandle)); if (!np_key) { ret = -EINVAL; pr_err("failed to find device node %s\n", propname); goto exit; } ret = of_property_read_string(np_key, "keyname", &keyname); if (ret < 0) { pr_err("failed to get keyname item\n"); goto exit; } name_size = EFUSE_KEY_NAME_LEN - 1; memcpy(infos[index].keyname, keyname, strlen(keyname) > name_size ? name_size : strlen(keyname)); ret = of_property_read_u32(np_key, "offset", &infos[index].offset); if (ret < 0) { pr_err("failed to get key offset item\n"); goto exit; } ret = of_property_read_u32(np_key, "size", &infos[index].size); if (ret < 0) { pr_err("failed to get key size item\n"); goto exit; } pr_debug("efusekey name: %15s\toffset: %5d\tsize: %5d\n", infos[index].keyname, infos[index].offset, infos[index].size); ret = 0; exit: kfree(propname); return ret; } static int get_efusekey_info(struct device_node *np) { const phandle *phandle; struct device_node *np_efusekey = NULL; int index; int ret; phandle = of_get_property(np, "key", NULL); if (!phandle) { ret = -EINVAL; pr_err("failed to find match efuse key\n"); goto exit; } np_efusekey = of_find_node_by_phandle(be32_to_cpup(phandle)); if (!np_efusekey) { ret = -EINVAL; pr_err("failed to find device node efusekey\n"); goto exit; } ret = of_property_read_u32(np_efusekey, "keynum", &efuse_key.num); if (ret < 0) { pr_err("failed to get efusekey num item\n"); goto exit; } if (efuse_key.num <= 0) { ret = -EINVAL; pr_err("efusekey num config error\n"); goto exit; } pr_debug("efusekey num: %d\n", efuse_key.num); efuse_key.infos = kzalloc((sizeof(struct efusekey_info)) * efuse_key.num, GFP_KERNEL); if (!efuse_key.infos) { ret = -ENOMEM; pr_err("fail to alloc enough mem for efusekey_infos\n"); goto exit; } for (index = 0; index < efuse_key.num; index++) { ret = key_item_parse_dt(np_efusekey, index, efuse_key.infos); if (ret < 0) { kfree(efuse_key.infos); goto exit; } } return 0; exit: return ret; } static int check_item_parse_dt(const struct device_node *np_efusecheck, int index, struct lockable_info *infos) { const phandle *phandle; struct device_node *np_check; char *propname; const char *checkname; int ret; int name_size; propname = kasprintf(GFP_KERNEL, "check%d", index); phandle = of_get_property(np_efusecheck, propname, NULL); if (!phandle) { ret = -EINVAL; pr_err("failed to find match %s\n", propname); goto exit; } np_check = of_find_node_by_phandle(be32_to_cpup(phandle)); if (!np_check) { ret = -EINVAL; pr_err("failed to find device node %s\n", propname); goto exit; } ret = of_property_read_string(np_check, "checkname", &checkname); if (ret < 0) { pr_err("failed to get checkname item\n"); goto exit; } name_size = EFUSE_CHECK_NAME_LEN - 1; memcpy(infos[index].itemname, checkname, strlen(checkname) > name_size ? name_size : strlen(checkname)); ret = of_property_read_u32(np_check, "subcmd", &infos[index].subcmd); if (ret < 0) { pr_err("failed to get subcmd item\n"); goto exit; } pr_debug("efusecheck name: %15s subcmd: 0x%16x\n", infos[index].itemname, infos[index].subcmd); ret = 0; exit: kfree(propname); return ret; } static int get_efusecheck_info(struct device_node *np) { const phandle *phandle; struct device_node *np_ec = NULL; int index; int ret; phandle = of_get_property(np, "check", NULL); if (!phandle) { ret = -EINVAL; pr_err("failed to find match efuse check\n"); goto exit; } np_ec = of_find_node_by_phandle(be32_to_cpup(phandle)); if (!np_ec) { ret = -EINVAL; pr_err("failed to find device node check\n"); goto exit; } ret = of_property_read_u32(np_ec, "maincmd", &efusecheck.main_cmd); if (ret < 0) { efusecheck.main_cmd = EFUSE_READ_CALI_ITEM; pr_err("don't cfg efusecheck maincmd, used default:0x%x\n", efusecheck.main_cmd); pr_notice("don't cfg efusecheck maincmd, used default:0x%x\n", efusecheck.main_cmd); } else { pr_debug("efuse check maincmd:0x%x\n", efusecheck.main_cmd); } ret = of_property_read_u32(np_ec, "checknum", &efusecheck.item_num); if (ret < 0) { pr_err("failed to get efusecheck num item\n"); goto exit; } if (efusecheck.item_num <= 0) { ret = -EINVAL; pr_err("efusecheck num config error\n"); goto exit; } pr_debug("efusecheck num: %d\n", efusecheck.item_num); efusecheck.infos = kzalloc((sizeof(struct lockable_info)) * efusecheck.item_num, GFP_KERNEL); if (!efusecheck.infos) { ret = -ENOMEM; pr_err("fail to alloc enough mem for efusecheck_item\n"); goto exit; } for (index = 0; index < efusecheck.item_num; index++) { ret = check_item_parse_dt(np_ec, index, efusecheck.infos); if (ret < 0) { kfree(efusecheck.infos); goto exit; } } return 0; exit: return ret; } static char *efuse_obj_err_parse(uint32_t efuse_obj_err_status) { char *err_char = NULL; switch (efuse_obj_err_status) { case EFUSE_OBJ_ERR_INVALID_DATA: err_char = "invalid data"; break; case EFUSE_OBJ_ERR_NOT_FOUND: err_char = "field not found"; break; case EFUSE_OBJ_ERR_SIZE: err_char = "size not match"; break; case EFUSE_OBJ_ERR_NOT_SUPPORT: err_char = "cmd not support"; break; case EFUSE_OBJ_ERR_ACCESS: err_char = "access denied"; break; case EFUSE_OBJ_ERR_WRITE_PROTECTED: err_char = "write protected"; break; case EFUSE_OBJ_ERR_TAG: err_char = "invalid encrypted data tag. check device pub key and re-encrypt"; break; case EFUSE_OBJ_ERR_SOC_NOT_SUPPORT: err_char = "soc not support"; break; case EFUSE_OBJ_ERR_INTERNAL: case EFUSE_OBJ_ERR_OTHER_INTERNAL: err_char = "internal error"; break; default: err_char = "unknown error"; break; } return err_char; } static DEFINE_MUTEX(efuse_obj_mutex); static ssize_t efuse_obj_store(const struct class *class, const struct class_attribute *attr, const char *buf, size_t count) { int rc = -EINVAL; char argv[3][130]; char argc; char *name = NULL; char *data = NULL; argc = sscanf(buf, "%s %s %s", argv[0], argv[1], argv[2]); if (argc < 2) { pr_err("Invalid number of arguments %d\n", argc); return -EINVAL; } mutex_lock(&efuse_obj_mutex); memset(&efuse_field, 0, sizeof(efuse_field)); if (strcmp(argv[0], "get") == 0) { // $0 get field if (argc == 2) { name = argv[1]; rc = efuse_obj_get_data(name); if (rc == EFUSE_OBJ_SUCCESS) { rc = count; } else { pr_err("Error get efuse object: %s: %d\n", efuse_obj_err_parse(rc), rc); rc = -rc; } } else { pr_err("Error: too many arguments %d\n", argc); rc = -EINVAL; } } else if (strcmp(argv[0], "set") == 0) { // $0 set field data if (argc == 3) { name = argv[1]; data = argv[2]; rc = efuse_obj_set_data(name, data); if (rc == EFUSE_OBJ_SUCCESS) { rc = count; } else { pr_err("Error set efuse object: %s: %d\n", efuse_obj_err_parse(rc), rc); rc = -rc; } } else { pr_err("Error: too few arguments %d\n", argc); rc = -EINVAL; } } else if (strcmp(argv[0], "set_enc") == 0) { // $0 set field data if (argc == 3) { name = argv[1]; data = argv[2]; rc = efuse_obj_set_enc_data(name, data); if (rc == EFUSE_OBJ_SUCCESS) { rc = count; } else { pr_err("Error set_enc efuse object: %s: %d\n", efuse_obj_err_parse(rc), rc); rc = -rc; } } else { pr_err("Error: too few arguments %d\n", argc); rc = -EINVAL; } } else if (strcmp(argv[0], "lock") == 0) { // $0 lock field if (argc == 2) { name = argv[1]; rc = efuse_obj_lock(name); if (rc == EFUSE_OBJ_SUCCESS) { rc = count; } else { pr_err("Error lock efuse object: %s: %d\n", efuse_obj_err_parse(rc), rc); rc = -rc; } } else { pr_err("Error: too many arguments %d\n", argc); rc = -EINVAL; } } else if (strcmp(argv[0], "get_lock") == 0) { // $0 get_lock field if (argc == 2) { name = argv[1]; rc = efuse_obj_get_lock(name); if (rc == EFUSE_OBJ_SUCCESS) { rc = count; } else { pr_err("Error get_lock efuse object: %s: %d\n", efuse_obj_err_parse(rc), rc); rc = -rc; } } else { pr_err("Error: too many arguments %d\n", argc); rc = -EINVAL; } } else { pr_err("Error: not support efuse object cmd \"%s\"\n", argv[0]); rc = -EINVAL; } mutex_unlock(&efuse_obj_mutex); return rc; } static ssize_t efuse_obj_show(const struct class *class, const struct class_attribute *attr, char *buf) { ssize_t len = 0; int i; mutex_lock(&efuse_obj_mutex); for (i = 0; i < efuse_field.size; i++) len += sprintf(buf + len, "%02x%s", efuse_field.data[i], ((i % 16 == 15) || (i == efuse_field.size - 1) ? "\n" : " ")); memset(&efuse_field, 0, sizeof(efuse_field)); mutex_unlock(&efuse_obj_mutex); return len; } static ssize_t efuse_mrk_store(const struct class *cla, const struct class_attribute *attr, const char *buf, size_t count) { int ret = 0; mutex_lock(&efuse_obj_mutex); memset(mrk_name, 0, sizeof(mrk_name)); memset(mrk_checksum, 0, sizeof(mrk_checksum)); strncpy(mrk_name, buf, count); for (ret = 0; ret < sizeof(mrk_name); ret++) if (mrk_name[ret] == '\n') { /* remove enter key */ mrk_name[ret] = '\0'; break; } /* short checknum */ ret = efuse_mrk_get_checknum(mrk_name, 0, mrk_checksum); mrk_checksum[5] = ret; /* long checknum */ ret = efuse_mrk_get_checknum(mrk_name, 1, mrk_checksum + 1); mrk_checksum[6] = ret; mutex_unlock(&efuse_obj_mutex); return count; } static ssize_t efuse_mrk_show(const struct class *class, const struct class_attribute *attr, char *buf) { int ret; int count = 0; mutex_lock(&efuse_obj_mutex); ret = mrk_checksum[5]; switch (ret) { case 0: count = sprintf(buf, "%s short checknum:%08x\n", mrk_name, mrk_checksum[0]); break; case EFUSE_MRK_CHECKNUM_NOT_SUPPORTED: count = sprintf(buf, "MRK field %s not supported short checknum\n", mrk_name); break; case EFUSE_MRK_CHECKNUM_INVALID_ARGUMENT: count = sprintf(buf, "get mrk short checknum for %s failed, MRK field may not be written\n", mrk_name); break; default: count = 0; break; } ret = mrk_checksum[6]; switch (ret) { case 0: count += sprintf(buf + count, "%s long checknum:%08x %08x %08x %08x\n", mrk_name, mrk_checksum[1], mrk_checksum[2], mrk_checksum[3], mrk_checksum[4]); break; case EFUSE_MRK_CHECKNUM_NOT_SUPPORTED: count += sprintf(buf + count, "MRK field %s not supported long checknum\n", mrk_name); break; case EFUSE_MRK_CHECKNUM_INVALID_ARGUMENT: count += sprintf(buf + count, "get mrk long checknum for %s failed, MRK field may not be written\n", mrk_name); break; default: count = 0; break; } mutex_unlock(&efuse_obj_mutex); return count; } static EFUSE_CLASS_ATTR(userdata); static EFUSE_CLASS_ATTR(mac); static EFUSE_CLASS_ATTR(mac_bt); static EFUSE_CLASS_ATTR(mac_wifi); static EFUSE_CLASS_ATTR(usid); static EFUSE_CLASS_ATTR(efuse_obj); static EFUSE_CLASS_ATTR(efuse_mrk); static CLASS_ATTR_WO(amlogic_set); static CLASS_ATTR_RO(secureboot_check); static EFUSE_CLASS_ATTR(checkburn); static CLASS_ATTR_RO(checklist); static struct attribute *efuse_class_attrs[] = { &class_attr_userdata.attr, &class_attr_mac.attr, &class_attr_mac_bt.attr, &class_attr_mac_wifi.attr, &class_attr_usid.attr, &class_attr_efuse_obj.attr, &class_attr_efuse_mrk.attr, &class_attr_amlogic_set.attr, &class_attr_secureboot_check.attr, &class_attr_checkburn.attr, &class_attr_checklist.attr, NULL, }; ATTRIBUTE_GROUPS(efuse_class); static int efuse_probe(struct platform_device *pdev) { int ret; struct device *devp; struct device_node *np = pdev->dev.of_node; struct clk *efuse_clk; struct aml_efuse_dev *efuse_dev; struct resource *reg_mem = NULL; void __iomem *reg_base = NULL; unsigned int secureboot_mask = 0; efuse_dev = devm_kzalloc(&pdev->dev, sizeof(*efuse_dev), GFP_KERNEL); if (!efuse_dev) { ret = -ENOMEM; dev_err(&pdev->dev, "failed to alloc enough mem for efuse_dev\n"); goto out; } efuse_dev->pdev = pdev; platform_set_drvdata(pdev, efuse_dev); efuse_clk = devm_clk_get(&pdev->dev, "efuse_clk"); if (IS_ERR(efuse_clk)) { dev_dbg(&pdev->dev, "can't get efuse clk gate, use default clk\n"); } else { ret = clk_prepare_enable(efuse_clk); if (ret) { dev_err(&pdev->dev, "failed to enable efuse clk gate\n"); goto error1; } } of_node_get(np); ret = of_property_read_u32(np, "read_cmd", &efuse_cmd.read_cmd); if (ret) { dev_err(&pdev->dev, "please config read_cmd\n"); goto error1; } ret = of_property_read_u32(np, "write_cmd", &efuse_cmd.write_cmd); if (ret) { dev_err(&pdev->dev, "failed to write_cmd\n"); goto error1; } ret = of_property_read_u32(np, "get_max_cmd", &efuse_cmd.get_max_cmd); if (ret) { dev_err(&pdev->dev, "failed to get_max_cmd\n"); goto error1; } ret = of_property_read_u32(np, "mem_in_base_cmd", &efuse_cmd.mem_in_base_cmd); if (ret) { dev_err(&pdev->dev, "failed to mem_in_base_cmd\n"); goto error1; } ret = of_property_read_u32(np, "mem_out_base_cmd", &efuse_cmd.mem_out_base_cmd); if (ret) { dev_err(&pdev->dev, "failed to mem_out_base_cmd\n"); goto error1; } ret = of_property_read_u32(np, "efuse_pattern_size", &efuse_pattern_size); if (ret) { efuse_pattern_size = EFUSE_PATTERN_SIZE; pr_err("can't get efuse_pattern_size, use default size0x%x\n", efuse_pattern_size); } else { pr_debug("efuse_pattern_size:0x%x\n", efuse_pattern_size); } ret = of_property_read_u32(np, "efuse_obj_cmd_status", &efuse_obj_cmd_status); if (ret) efuse_obj_cmd_status = 0; ret = of_property_read_u32(np, "efuse_cali_item_read", &efuse_cali_item_read); if (ret) efuse_cali_item_read = 0; reg_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!IS_ERR_OR_NULL(reg_mem)) { reg_base = devm_ioremap_resource(&pdev->dev, reg_mem); if (IS_ERR(reg_base)) { dev_err(&pdev->dev, "reg0: cannot obtain I/O memory region.\n"); ret = PTR_ERR(reg_base); goto error1; } else { ret = of_property_read_u32(np, "secureboot_mask", &secureboot_mask); if (ret) { dev_err(&pdev->dev, "can't get reg secureboot_mask\n"); goto error1; } } } else { dev_err(&pdev->dev, "can't get reg resource\n"); } get_efusekey_info(np); get_efusecheck_info(np); ret = alloc_chrdev_region(&efuse_dev->devno, 0, 1, EFUSE_DEVICE_NAME); if (ret < 0) { dev_err(&pdev->dev, "fail to allocate major number\n "); goto error1; } efuse_dev->reg_base = reg_base; efuse_dev->secureboot_mask = secureboot_mask; efuse_dev->cls.name = EFUSE_CLASS_NAME; efuse_dev->cls.class_groups = efuse_class_groups; ret = class_register(&efuse_dev->cls); if (ret) goto error2; cdev_init(&efuse_dev->cdev, &efuse_fops); efuse_dev->cdev.owner = THIS_MODULE; ret = cdev_add(&efuse_dev->cdev, efuse_dev->devno, 1); if (ret) { dev_err(&pdev->dev, "failed to add device\n"); goto error3; } devp = device_create(&efuse_dev->cls, NULL, efuse_dev->devno, NULL, "efuse"); if (IS_ERR(devp)) { dev_err(&pdev->dev, "failed to create device node\n"); ret = PTR_ERR(devp); goto error4; } sharemem_input_base = get_meson_sm_input_base(); sharemem_output_base = get_meson_sm_output_base(); dev_dbg(&pdev->dev, "device %s created OK\n", EFUSE_DEVICE_NAME); return 0; error4: cdev_del(&efuse_dev->cdev); error3: class_unregister(&efuse_dev->cls); error2: unregister_chrdev_region(efuse_dev->devno, 1); error1: devm_kfree(&pdev->dev, efuse_dev); out: return ret; } static void efuse_remove(struct platform_device *pdev) { struct aml_efuse_dev *efuse_dev = platform_get_drvdata(pdev); unregister_chrdev_region(efuse_dev->devno, 1); device_destroy(&efuse_dev->cls, efuse_dev->devno); cdev_del(&efuse_dev->cdev); class_unregister(&efuse_dev->cls); platform_set_drvdata(pdev, NULL); } static const struct of_device_id efuse_dt_match[] = { { .compatible = "amlogic, efuse", }, {} }; static struct platform_driver efuse_driver = { .probe = efuse_probe, .remove = efuse_remove, .driver = { .name = EFUSE_DEVICE_NAME, .of_match_table = efuse_dt_match, .owner = THIS_MODULE, }, }; int __init aml_efuse_init(void) { return platform_driver_register(&efuse_driver); } void aml_efuse_exit(void) { platform_driver_unregister(&efuse_driver); }