2 * Copyright (C) 2014 Spreadtrum Communications Inc.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <linux/init.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/slab.h>
19 #include <linux/bug.h>
20 #include <linux/debugfs.h>
21 #include <linux/miscdevice.h>
22 #include <linux/platform_device.h>
24 #include <linux/of_device.h>
25 #include <linux/of_address.h>
28 /* cached otp blocks max 32X32 */
29 #define CACHED_BLOCK_MAX ( 32 )
30 #define CACHED_BLOCK_WIDTH ( 32 ) /* bit counts */
31 static u8 cached_otp[CACHED_BLOCK_MAX * CACHED_BLOCK_WIDTH / 8];
34 * Head entry for the doubly linked sprd otp list
36 static LIST_HEAD(sprd_otp_list);
37 static DEFINE_MUTEX(sprd_otp_mtx);
39 static struct sprd_otp_device *__sprd_otp_find(int minor)
41 struct sprd_otp_device *otp;
43 if (unlikely(list_empty(&sprd_otp_list)))
46 list_for_each_entry(otp, &sprd_otp_list, list) {
47 if (otp->misc.minor == minor) {
54 static ssize_t sprd_otp_read(struct file *file, char __user * buf,
55 size_t count, loff_t * f_pos)
58 int minor = iminor(file_inode(file));
59 struct sprd_otp_device *otp = __sprd_otp_find(minor);
60 mutex_lock(&sprd_otp_mtx);
61 if (otp && otp->ops && otp->ops->read) {
62 int i, blk_index = (int)*f_pos / otp->blk_width;
63 int blk_max = blk_index + DIV_ROUND_UP(count, otp->blk_width);
64 pr_debug("otp read blk %d - %d\n", blk_index, blk_max);
65 if (blk_max > otp->blk_max)
66 blk_max = otp->blk_max;
67 for (i = blk_index; i < blk_max; i++) {
68 int index = i * otp->blk_width;
69 u32 val = otp->ops->read(i);
70 memcpy(&cached_otp[index], &val, otp->blk_width);
73 pr_debug("otp read from %d %u %d\n", (int)*f_pos, count,
76 simple_read_from_buffer(buf, count, f_pos, cached_otp,
79 mutex_unlock(&sprd_otp_mtx);
83 static ssize_t sprd_otp_write(struct file *file, const char __user * buf,
84 size_t count, loff_t * f_pos)
87 int pos = (int)*f_pos;
88 int minor = iminor(file_inode(file));
89 struct sprd_otp_device *otp = __sprd_otp_find(minor);
90 pr_debug("otp write to %d %u %d\n", (int)*f_pos, count,
92 mutex_lock(&sprd_otp_mtx);
93 ret = simple_write_to_buffer(cached_otp, sizeof(cached_otp), f_pos,
95 if (IS_ERR_VALUE(ret))
98 if (otp && otp->ops && otp->ops->prog) {
99 int blk_index = pos / otp->blk_width;
100 pr_debug("otp prog blk %d\n", blk_index);
101 BUG_ON(count != otp->blk_width);
102 if (blk_index < otp->blk_max) {
103 int index = blk_index * otp->blk_width;
105 memcpy(&val, &cached_otp[index], otp->blk_width);
106 otp->ops->prog(blk_index, val);
110 mutex_unlock(&sprd_otp_mtx);
114 static loff_t sprd_otp_llseek(struct file *file, loff_t offset, int whence)
116 pr_debug("otp seek at %d %d\n", (int)offset, whence);
119 file->f_pos = offset;
122 file->f_pos += offset;
130 static const struct file_operations sprd_otp_fops = {
131 .owner = THIS_MODULE,
132 .read = sprd_otp_read,
133 .write = sprd_otp_write,
134 .llseek = sprd_otp_llseek,
137 static struct miscdevice dummy_otp = {
138 .minor = MISC_DYNAMIC_MINOR,
139 .name = "sprd_dummy_otp",
140 .fops = &sprd_otp_fops,
143 void *sprd_otp_register(const char *name, void *ops, int blk_max, int blk_width)
146 struct sprd_otp_device *otp;
147 otp = kzalloc(sizeof(struct sprd_otp_device), GFP_KERNEL);
149 return ERR_PTR(-ENOMEM);
150 INIT_LIST_HEAD(&otp->list);
151 otp->misc.minor = MISC_DYNAMIC_MINOR;
152 otp->misc.name = kstrdup(name, GFP_KERNEL);
153 otp->misc.fops = &sprd_otp_fops;
154 otp->blk_max = blk_max;
155 otp->blk_width = blk_width; /*MUST be bytes count */
156 BUG_ON(otp->blk_width > sizeof(u32));
157 BUG_ON(otp->blk_max * otp->blk_width > sizeof(cached_otp));
159 ret = misc_register(&otp->misc);
162 mutex_lock(&sprd_otp_mtx);
163 list_add(&otp->list, &sprd_otp_list);
164 mutex_unlock(&sprd_otp_mtx);
165 pr_info("otp register %s as miscdevice %d, block %uX%ubits\n",
166 otp->misc.name, otp->misc.minor, otp->blk_max,
168 return otp->misc.this_device;
171 static ssize_t otp_info_show(struct device *dev,
172 struct device_attribute *attr, char *buf)
174 return sprintf(buf, "cached otp blocks: %uX%ubits\n",
175 CACHED_BLOCK_MAX, CACHED_BLOCK_WIDTH);
178 static DEVICE_ATTR(info, S_IRUGO, otp_info_show, NULL);
180 static int __init sprd_otp_init(void)
183 ret = misc_register(&dummy_otp);
187 ret = device_create_file(dummy_otp.this_device, &dev_attr_info);
191 #if defined(CONFIG_OTP_SPRD_EFUSE)
194 #if defined(CONFIG_OTP_SPRD_ADIE_EFUSE)
195 sprd_adie_efuse_init();
197 #if defined(CONFIG_OTP_SPRD_LASER_FUSE)
198 sprd_adie_laserfuse_init();
203 static void __exit sprd_otp_exit(void)
205 misc_deregister(&dummy_otp);
208 module_init(sprd_otp_init);
209 module_exit(sprd_otp_exit);
211 MODULE_LICENSE("GPL v2");
212 MODULE_DESCRIPTION("Spreadtrum OTP Efuse Driver");
213 MODULE_AUTHOR("robot <zhulin.lian@spreadtrum.com>");
214 MODULE_VERSION("0.1");