tizen 2.4 release
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / drivers / misc / sprd_otp / core.c
1 /*
2  * Copyright (C) 2014 Spreadtrum Communications Inc.
3  *
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.
8  *
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.
13  *
14  */
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>
23 #include <linux/of.h>
24 #include <linux/of_device.h>
25 #include <linux/of_address.h>
26 #include "sprd_otp.h"
27
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];
32
33 /*
34  * Head entry for the doubly linked sprd otp list
35  */
36 static LIST_HEAD(sprd_otp_list);
37 static DEFINE_MUTEX(sprd_otp_mtx);
38
39 static struct sprd_otp_device *__sprd_otp_find(int minor)
40 {
41         struct sprd_otp_device *otp;
42
43         if (unlikely(list_empty(&sprd_otp_list)))
44                 return NULL;
45
46         list_for_each_entry(otp, &sprd_otp_list, list) {
47                 if (otp->misc.minor == minor) {
48                         return otp;
49                 }
50         }
51         return NULL;
52 }
53
54 static ssize_t sprd_otp_read(struct file *file, char __user * buf,
55                              size_t count, loff_t * f_pos)
56 {
57         int ret;
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);
71                 }
72         }
73         pr_debug("otp read from %d %u %d\n", (int)*f_pos, count,
74                  (int)file->f_pos);
75         ret =
76             simple_read_from_buffer(buf, count, f_pos, cached_otp,
77                                     sizeof(cached_otp));
78
79         mutex_unlock(&sprd_otp_mtx);
80         return ret;
81 }
82
83 static ssize_t sprd_otp_write(struct file *file, const char __user * buf,
84                               size_t count, loff_t * f_pos)
85 {
86         ssize_t ret;
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,
91                  (int)file->f_pos);
92         mutex_lock(&sprd_otp_mtx);
93         ret = simple_write_to_buffer(cached_otp, sizeof(cached_otp), f_pos,
94                                      buf, count);
95         if (IS_ERR_VALUE(ret))
96                 goto out;
97
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;
104                         u32 val = 0;
105                         memcpy(&val, &cached_otp[index], otp->blk_width);
106                         otp->ops->prog(blk_index, val);
107                 }
108         }
109 out:
110         mutex_unlock(&sprd_otp_mtx);
111         return ret;
112 }
113
114 static loff_t sprd_otp_llseek(struct file *file, loff_t offset, int whence)
115 {
116         pr_debug("otp seek at %d %d\n", (int)offset, whence);
117         switch (whence) {
118         case SEEK_SET:
119                 file->f_pos = offset;
120                 break;
121         case SEEK_CUR:
122                 file->f_pos += offset;
123                 break;
124         default:
125                 return -EINVAL;
126         }
127         return file->f_pos;
128 }
129
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,
135 };
136
137 static struct miscdevice dummy_otp = {
138         .minor = MISC_DYNAMIC_MINOR,
139         .name = "sprd_dummy_otp",
140         .fops = &sprd_otp_fops,
141 };
142
143 void *sprd_otp_register(const char *name, void *ops, int blk_max, int blk_width)
144 {
145         int ret;
146         struct sprd_otp_device *otp;
147         otp = kzalloc(sizeof(struct sprd_otp_device), GFP_KERNEL);
148         if (unlikely(!otp))
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));
158         otp->ops = ops;
159         ret = misc_register(&otp->misc);
160         if (ret)
161                 return ERR_PTR(ret);
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,
167                 otp->blk_width * 8);
168         return otp->misc.this_device;
169 }
170
171 static ssize_t otp_info_show(struct device *dev,
172                              struct device_attribute *attr, char *buf)
173 {
174         return sprintf(buf, "cached otp blocks: %uX%ubits\n",
175                        CACHED_BLOCK_MAX, CACHED_BLOCK_WIDTH);
176 }
177
178 static DEVICE_ATTR(info, S_IRUGO, otp_info_show, NULL);
179
180 static int __init sprd_otp_init(void)
181 {
182         int ret;
183         ret = misc_register(&dummy_otp);
184         if (ret)
185                 return ret;
186
187         ret = device_create_file(dummy_otp.this_device, &dev_attr_info);
188         if (ret)
189                 return ret;
190
191 #if defined(CONFIG_OTP_SPRD_EFUSE)
192         sprd_efuse_init();
193 #endif
194 #if defined(CONFIG_OTP_SPRD_ADIE_EFUSE)
195         sprd_adie_efuse_init();
196 #endif
197 #if defined(CONFIG_OTP_SPRD_LASER_FUSE)
198         sprd_adie_laserfuse_init();
199 #endif
200         return ret;
201 }
202
203 static void __exit sprd_otp_exit(void)
204 {
205         misc_deregister(&dummy_otp);
206 }
207
208 module_init(sprd_otp_init);
209 module_exit(sprd_otp_exit);
210
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");