/**************************************************************************** * * Copyright (c) 2014 - 2016 Samsung Electronics Co., Ltd. All rights reserved * ****************************************************************************/ #include #include #include #include "mifproc.h" #include "scsc_mif_abs.h" #include "miframman.h" #define MX_MAX_PROC_RAMMAN 2 /* Number of RAMMANs to track */ static struct proc_dir_entry *procfs_dir; static struct proc_dir_entry *procfs_dir_ramman[MX_MAX_PROC_RAMMAN]; /* WARNING --- SINGLETON FOR THE TIME BEING */ /* EXTEND PROC ENTRIES IF NEEDED!!!!! */ static struct scsc_mif_abs *mif_global; static int mifprocfs_open_file_generic(struct inode *inode, struct file *file) { file->private_data = MIF_PDE_DATA(inode); return 0; } #ifdef CONFIG_SCSC_PCIE MIF_PROCFS_RW_FILE_OPS(mif_dump); MIF_PROCFS_RW_FILE_OPS(mif_writemem); MIF_PROCFS_RW_FILE_OPS(mif_reg); #endif /* miframman ops */ MIF_PROCFS_RO_FILE_OPS(ramman_total); MIF_PROCFS_RO_FILE_OPS(ramman_offset); MIF_PROCFS_RO_FILE_OPS(ramman_start); MIF_PROCFS_RO_FILE_OPS(ramman_free); MIF_PROCFS_RO_FILE_OPS(ramman_used); MIF_PROCFS_RO_FILE_OPS(ramman_size); MIF_PROCFS_SEQ_FILE_OPS(ramman_list); #ifdef CONFIG_SCSC_PCIE static ssize_t mifprocfs_mif_reg_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); /* Avoid unused parameter error */ (void)file; pos += scnprintf(buf + pos, bufsz - pos, "%s\n", "OK"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t mifprocfs_mif_writemem_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); /* Avoid unused parameter error */ (void)file; pos += scnprintf(buf + pos, bufsz - pos, "%s\n", "OK"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t mifprocfs_mif_dump_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); /* Avoid unused parameter error */ (void)file; pos += scnprintf(buf + pos, bufsz - pos, "%s\n", "OK"); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } static ssize_t mifprocfs_mif_writemem_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; char *sptr, *token; unsigned int len = 0, pass = 0; u32 value = 0, address = 0; int match = 0; void *mem; /* Avoid unused parameter error */ (void)file; (void)ppos; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; sptr = buf; while ((token = strsep(&sptr, " ")) != NULL) { switch (pass) { /* register */ case 0: if ((token[0] == '0') && (token[1] == 'x')) { if (kstrtou32(token, 16, &address)) { SCSC_TAG_INFO(MIF, "Wrong format:
\n"); SCSC_TAG_INFO(MIF, "Example: \"0xaaaabbbb 0xcafecafe\"\n"); goto error; } } else { SCSC_TAG_INFO(MIF, "Wrong format:
\n"); SCSC_TAG_INFO(MIF, "Example: \"0xaaaabbbb 0xcafecafe\"\n"); goto error; } break; /* value */ case 1: if ((token[0] == '0') && (token[1] == 'x')) { if (kstrtou32(token, 16, &value)) { SCSC_TAG_INFO(MIF, "Wrong format:
\n"); SCSC_TAG_INFO(MIF, "Example: \"0xaaaabbbb 0xcafecafe\"\n"); goto error; } } else { SCSC_TAG_INFO(MIF, "Wrong format:
\n"); SCSC_TAG_INFO(MIF, "Example: \"0xaaaabbbb 0xcafecafe\"\n"); goto error; } break; } pass++; } if (pass != 2 && !match) { SCSC_TAG_INFO(MIF, "Wrong format:
\n"); SCSC_TAG_INFO(MIF, "Example: \"0xaaaabbbb 0xcafecafe\"\n"); goto error; } /* Get memory offset */ mem = mif_global->get_mifram_ptr(mif_global, 0); if (!mem) { SCSC_TAG_INFO(MIF, "Mem not allocated\n"); goto error; } SCSC_TAG_INFO(MIF, "Setting value 0x%x at address 0x%x offset\n", value, address); *((u32 *)(mem + address)) = value; error: return count; } static ssize_t mifprocfs_mif_dump_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; char *sptr, *token; unsigned int len = 0, pass = 0; u32 address = 0; u32 size; u8 unit; void *mem; (void)file; (void)ppos; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; sptr = buf; while ((token = strsep(&sptr, " ")) != NULL) { switch (pass) { /* address */ case 0: if ((token[0] == '0') && (token[1] == 'x')) { if (kstrtou32(token, 16, &address)) { SCSC_TAG_INFO(MIF, "Incorrect format,,,address should start by 0x\n"); SCSC_TAG_INFO(MIF, "Example: \"0xaaaabbbb 256 8\"\n"); goto error; } SCSC_TAG_INFO(MIF, "address %d 0x%x\n", address, address); } else { SCSC_TAG_INFO(MIF, "Incorrect format,,,address should start by 0x\n"); SCSC_TAG_INFO(MIF, "Example: \"0xaaaabbbb 256 8\"\n"); goto error; } break; /* size */ case 1: if (kstrtou32(token, 0, &size)) { SCSC_TAG_INFO(MIF, "Incorrect format,,, for size\n"); goto error; } SCSC_TAG_INFO(MIF, "size: %d\n", size); break; /* unit */ case 2: if (kstrtou8(token, 0, &unit)) { SCSC_TAG_INFO(MIF, "Incorrect format,,, for unit\n"); goto error; } if ((unit != 8) && (unit != 16) && (unit != 32)) { SCSC_TAG_INFO(MIF, "Unit %d should be 8/16/32\n", unit); goto error; } SCSC_TAG_INFO(MIF, "unit: %d\n", unit); break; } pass++; } if (pass != 3) { SCSC_TAG_INFO(MIF, "Wrong format: \n"); SCSC_TAG_INFO(MIF, "Example: \"0xaaaabbbb 256 8\"\n"); goto error; } mem = mif_global->get_mifram_ptr(mif_global, 0); if (!mem) { SCSC_TAG_INFO(MIF, "Mem not allocated\n"); goto error; } /* Add offset */ mem = mem + address; SCSC_TAG_INFO(MIF, "Phy addr :%p ref addr :%x\n", mem, address); SCSC_TAG_INFO(MIF, "------------------------------------------------------------------------\n"); print_hex_dump(KERN_WARNING, "ref addr offset: ", DUMP_PREFIX_OFFSET, 16, unit/8, mem, size, 1); SCSC_TAG_INFO(MIF, "------------------------------------------------------------------------\n"); error: return count; } static ssize_t mifprocfs_mif_reg_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { void *mem; if (!mif_global) { SCSC_TAG_INFO(MIF, "Endpoint not registered\n"); return 0; } mem = mif_global->get_mifram_ptr(mif_global, 0); SCSC_TAG_INFO(MIF, "Phy addr :%p\n", mem); mif_global->mif_dump_registers(mif_global); return count; } #endif /* * TODO: Add here any debug message should be exported static int mifprocfs_mif_dbg_show(struct seq_file *m, void *v) { (void)v; if (!mif_global) { seq_puts(m, "endpoint not registered"); return 0; } return 0; } */ /* Total space in the memory region containing this ramman (assumes one region per ramman) */ static ssize_t mifprocfs_ramman_total_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); struct miframman *ramman = (struct miframman *)file->private_data; pos += scnprintf(buf + pos, bufsz - pos, "%zd\n", (ptrdiff_t)(ramman->start_dram - ramman->start_region) + ramman->size_pool); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } /* Offset of the pool within the region (the space before is reserved by FW) */ static ssize_t mifprocfs_ramman_offset_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); struct miframman *ramman = (struct miframman *)file->private_data; pos += scnprintf(buf + pos, bufsz - pos, "%zd\n", (ptrdiff_t)(ramman->start_dram - ramman->start_region)); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } /* Start address of the pool within the region */ static ssize_t mifprocfs_ramman_start_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); struct miframman *ramman = (struct miframman *)file->private_data; pos += scnprintf(buf + pos, bufsz - pos, "%p\n", ramman->start_dram); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } /* Size of the pool */ static ssize_t mifprocfs_ramman_size_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); struct miframman *ramman = (struct miframman *)file->private_data; pos += scnprintf(buf + pos, bufsz - pos, "%zd\n", ramman->size_pool); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } /* Space remaining within the pool */ static ssize_t mifprocfs_ramman_free_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); struct miframman *ramman = (struct miframman *)file->private_data; pos += scnprintf(buf + pos, bufsz - pos, "%u\n", ramman->free_mem); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } /* Bytes used within the pool */ static ssize_t mifprocfs_ramman_used_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { char buf[128]; int pos = 0; const size_t bufsz = sizeof(buf); struct miframman *ramman = (struct miframman *)file->private_data; pos += scnprintf(buf + pos, bufsz - pos, "%zd\n", ramman->size_pool - (size_t)ramman->free_mem); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } /* List allocations per ramman */ static int mifprocfs_ramman_list_show(struct seq_file *m, void *v) { struct miframman *ramman = (struct miframman *)m->private; (void)v; miframman_log(ramman, m); return 0; } static const char *procdir = "driver/mif_ctrl"; static int refcount; #define MIF_DIRLEN 128 static struct proc_dir_entry *create_procfs_dir(void) { char dir[MIF_DIRLEN]; if (refcount++ == 0) { (void)snprintf(dir, sizeof(dir), "%s", procdir); procfs_dir = proc_mkdir(dir, NULL); } return procfs_dir; } static void destroy_procfs_dir(void) { char dir[MIF_DIRLEN]; if (--refcount == 0) { (void)snprintf(dir, sizeof(dir), "%s", procdir); remove_proc_entry(dir, NULL); procfs_dir = NULL; } WARN_ON(refcount < 0); } int mifproc_create_proc_dir(struct scsc_mif_abs *mif) { struct proc_dir_entry *parent; /* WARNING --- SINGLETON FOR THE TIME BEING */ /* EXTEND PROC ENTRIES IF NEEDED!!!!! */ if (mif_global) return -EBUSY; /* Ref the root dir */ parent = create_procfs_dir(); if (parent) { #if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)) parent->data = NULL; #endif #ifdef CONFIG_SCSC_PCIE MIF_PROCFS_ADD_FILE(NULL, mif_writemem, parent, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MIF_PROCFS_ADD_FILE(NULL, mif_dump, parent, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MIF_PROCFS_ADD_FILE(NULL, mif_reg, parent, S_IRUSR | S_IRGRP); #endif } else { SCSC_TAG_INFO(MIF, "failed to create /proc dir\n"); return -EINVAL; } mif_global = mif; return 0; /* err: return -EINVAL; */ } void mifproc_remove_proc_dir(void) { if (procfs_dir) { MIF_PROCFS_REMOVE_FILE(mif_writemem, procfs_dir); MIF_PROCFS_REMOVE_FILE(mif_dump, procfs_dir); MIF_PROCFS_REMOVE_FILE(mif_reg, procfs_dir); /* De-ref the root dir */ destroy_procfs_dir(); } mif_global = NULL; } /* /proc/driver/mif_ctrl/rammanX */ static const char *ramman_procdir = "ramman"; static struct miframman *proc_miframman[MX_MAX_PROC_RAMMAN]; static int ramman_instance; int mifproc_create_ramman_proc_dir(struct miframman *ramman) { char dir[MIF_DIRLEN]; struct proc_dir_entry *parent; struct proc_dir_entry *root; if ((ramman_instance > ARRAY_SIZE(proc_miframman) - 1)) return -EINVAL; /* Ref the root dir /proc/driver/mif_ctrl */ root = create_procfs_dir(); if (!root) return -EINVAL; (void)snprintf(dir, sizeof(dir), "%s%d", ramman_procdir, ramman_instance); parent = proc_mkdir(dir, root); if (parent) { #if (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 4, 0)) parent->data = NULL; #endif MIF_PROCFS_ADD_FILE(ramman, ramman_total, parent, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MIF_PROCFS_ADD_FILE(ramman, ramman_offset, parent, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MIF_PROCFS_ADD_FILE(ramman, ramman_start, parent, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MIF_PROCFS_ADD_FILE(ramman, ramman_size, parent, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MIF_PROCFS_ADD_FILE(ramman, ramman_free, parent, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MIF_PROCFS_ADD_FILE(ramman, ramman_used, parent, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); MIF_PROCFS_SEQ_ADD_FILE(ramman, ramman_list, parent, S_IRUSR | S_IRGRP | S_IROTH); procfs_dir_ramman[ramman_instance] = parent; proc_miframman[ramman_instance] = ramman; ramman_instance++; } else { SCSC_TAG_INFO(MIF, "failed to create /proc dir\n"); destroy_procfs_dir(); return -EINVAL; } return 0; } void mifproc_remove_ramman_proc_dir(struct miframman *ramman) { (void)ramman; if (ramman_instance <= 0) { WARN_ON(ramman_instance < 0); return; } --ramman_instance; if (procfs_dir_ramman[ramman_instance]) { char dir[MIF_DIRLEN]; MIF_PROCFS_REMOVE_FILE(ramman_total, procfs_dir_ramman[ramman_instance]); MIF_PROCFS_REMOVE_FILE(ramman_offset, procfs_dir_ramman[ramman_instance]); MIF_PROCFS_REMOVE_FILE(ramman_start, procfs_dir_ramman[ramman_instance]); MIF_PROCFS_REMOVE_FILE(ramman_size, procfs_dir_ramman[ramman_instance]); MIF_PROCFS_REMOVE_FILE(ramman_free, procfs_dir_ramman[ramman_instance]); MIF_PROCFS_REMOVE_FILE(ramman_used, procfs_dir_ramman[ramman_instance]); MIF_PROCFS_REMOVE_FILE(ramman_list, procfs_dir_ramman[ramman_instance]); (void)snprintf(dir, sizeof(dir), "%s%d", ramman_procdir, ramman_instance); remove_proc_entry(dir, procfs_dir); procfs_dir_ramman[ramman_instance] = NULL; proc_miframman[ramman_instance] = NULL; } /* De-ref the root dir */ destroy_procfs_dir(); }