1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#include <linux/kernel.h>
23#include <linux/of.h>
24#include <linux/of_device.h>
25#include <linux/of_platform.h>
26#include <linux/module.h>
27#include <linux/cdev.h>
28#include <linux/list.h>
29#include <linux/mm.h>
30#include <asm/pgtable.h>
31#include <asm/io.h>
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53#define BSR_MAX_DEVS (32)
54
55struct bsr_dev {
56 u64 bsr_addr;
57 u64 bsr_len;
58 unsigned bsr_bytes;
59 unsigned bsr_stride;
60 unsigned bsr_type;
61 unsigned bsr_num;
62 int bsr_minor;
63
64 struct list_head bsr_list;
65
66 dev_t bsr_dev;
67 struct cdev bsr_cdev;
68 struct device *bsr_device;
69 char bsr_name[32];
70
71};
72
73static unsigned total_bsr_devs;
74static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
75static struct class *bsr_class;
76static int bsr_major;
77
78enum {
79 BSR_8 = 0,
80 BSR_16 = 1,
81 BSR_64 = 2,
82 BSR_128 = 3,
83 BSR_4096 = 4,
84 BSR_UNKNOWN = 5,
85 BSR_MAX = 6,
86};
87
88static unsigned bsr_types[BSR_MAX];
89
90static ssize_t
91bsr_size_show(struct device *dev, struct device_attribute *attr, char *buf)
92{
93 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
94 return sprintf(buf, "%u\n", bsr_dev->bsr_bytes);
95}
96
97static ssize_t
98bsr_stride_show(struct device *dev, struct device_attribute *attr, char *buf)
99{
100 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
101 return sprintf(buf, "%u\n", bsr_dev->bsr_stride);
102}
103
104static ssize_t
105bsr_len_show(struct device *dev, struct device_attribute *attr, char *buf)
106{
107 struct bsr_dev *bsr_dev = dev_get_drvdata(dev);
108 return sprintf(buf, "%llu\n", bsr_dev->bsr_len);
109}
110
111static struct device_attribute bsr_dev_attrs[] = {
112 __ATTR(bsr_size, S_IRUGO, bsr_size_show, NULL),
113 __ATTR(bsr_stride, S_IRUGO, bsr_stride_show, NULL),
114 __ATTR(bsr_length, S_IRUGO, bsr_len_show, NULL),
115 __ATTR_NULL
116};
117
118static int bsr_mmap(struct file *filp, struct vm_area_struct *vma)
119{
120 unsigned long size = vma->vm_end - vma->vm_start;
121 struct bsr_dev *dev = filp->private_data;
122 int ret;
123
124 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
125
126
127 if (dev->bsr_len < PAGE_SIZE && size == PAGE_SIZE)
128 ret = remap_4k_pfn(vma, vma->vm_start, dev->bsr_addr >> 12,
129 vma->vm_page_prot);
130 else if (size <= dev->bsr_len)
131 ret = io_remap_pfn_range(vma, vma->vm_start,
132 dev->bsr_addr >> PAGE_SHIFT,
133 size, vma->vm_page_prot);
134 else
135 return -EINVAL;
136
137 if (ret)
138 return -EAGAIN;
139
140 return 0;
141}
142
143static int bsr_open(struct inode * inode, struct file * filp)
144{
145 struct cdev *cdev = inode->i_cdev;
146 struct bsr_dev *dev = container_of(cdev, struct bsr_dev, bsr_cdev);
147
148 filp->private_data = dev;
149 return 0;
150}
151
152static const struct file_operations bsr_fops = {
153 .owner = THIS_MODULE,
154 .mmap = bsr_mmap,
155 .open = bsr_open,
156};
157
158static void bsr_cleanup_devs(void)
159{
160 struct bsr_dev *cur, *n;
161
162 list_for_each_entry_safe(cur, n, &bsr_devs, bsr_list) {
163 if (cur->bsr_device) {
164 cdev_del(&cur->bsr_cdev);
165 device_del(cur->bsr_device);
166 }
167 list_del(&cur->bsr_list);
168 kfree(cur);
169 }
170}
171
172static int bsr_add_node(struct device_node *bn)
173{
174 int bsr_stride_len, bsr_bytes_len, num_bsr_devs;
175 const u32 *bsr_stride;
176 const u32 *bsr_bytes;
177 unsigned i;
178 int ret = -ENODEV;
179
180 bsr_stride = of_get_property(bn, "ibm,lock-stride", &bsr_stride_len);
181 bsr_bytes = of_get_property(bn, "ibm,#lock-bytes", &bsr_bytes_len);
182
183 if (!bsr_stride || !bsr_bytes ||
184 (bsr_stride_len != bsr_bytes_len)) {
185 printk(KERN_ERR "bsr of-node has missing/incorrect property\n");
186 return ret;
187 }
188
189 num_bsr_devs = bsr_bytes_len / sizeof(u32);
190
191 for (i = 0 ; i < num_bsr_devs; i++) {
192 struct bsr_dev *cur = kzalloc(sizeof(struct bsr_dev),
193 GFP_KERNEL);
194 struct resource res;
195 int result;
196
197 if (!cur) {
198 printk(KERN_ERR "Unable to alloc bsr dev\n");
199 ret = -ENOMEM;
200 goto out_err;
201 }
202
203 result = of_address_to_resource(bn, i, &res);
204 if (result < 0) {
205 printk(KERN_ERR "bsr of-node has invalid reg property, skipping\n");
206 kfree(cur);
207 continue;
208 }
209
210 cur->bsr_minor = i + total_bsr_devs;
211 cur->bsr_addr = res.start;
212 cur->bsr_len = res.end - res.start + 1;
213 cur->bsr_bytes = bsr_bytes[i];
214 cur->bsr_stride = bsr_stride[i];
215 cur->bsr_dev = MKDEV(bsr_major, i + total_bsr_devs);
216
217
218
219 if (cur->bsr_len > 4096 && cur->bsr_len < PAGE_SIZE)
220 cur->bsr_len = 4096;
221
222 switch(cur->bsr_bytes) {
223 case 8:
224 cur->bsr_type = BSR_8;
225 break;
226 case 16:
227 cur->bsr_type = BSR_16;
228 break;
229 case 64:
230 cur->bsr_type = BSR_64;
231 break;
232 case 128:
233 cur->bsr_type = BSR_128;
234 break;
235 case 4096:
236 cur->bsr_type = BSR_4096;
237 break;
238 default:
239 cur->bsr_type = BSR_UNKNOWN;
240 }
241
242 cur->bsr_num = bsr_types[cur->bsr_type];
243 snprintf(cur->bsr_name, 32, "bsr%d_%d",
244 cur->bsr_bytes, cur->bsr_num);
245
246 cdev_init(&cur->bsr_cdev, &bsr_fops);
247 result = cdev_add(&cur->bsr_cdev, cur->bsr_dev, 1);
248 if (result) {
249 kfree(cur);
250 goto out_err;
251 }
252
253 cur->bsr_device = device_create(bsr_class, NULL, cur->bsr_dev,
254 cur, cur->bsr_name);
255 if (!cur->bsr_device) {
256 printk(KERN_ERR "device_create failed for %s\n",
257 cur->bsr_name);
258 cdev_del(&cur->bsr_cdev);
259 kfree(cur);
260 goto out_err;
261 }
262
263 bsr_types[cur->bsr_type] = cur->bsr_num + 1;
264 list_add_tail(&cur->bsr_list, &bsr_devs);
265 }
266
267 total_bsr_devs += num_bsr_devs;
268
269 return 0;
270
271 out_err:
272
273 bsr_cleanup_devs();
274 return ret;
275}
276
277static int bsr_create_devs(struct device_node *bn)
278{
279 int ret;
280
281 while (bn) {
282 ret = bsr_add_node(bn);
283 if (ret) {
284 of_node_put(bn);
285 return ret;
286 }
287 bn = of_find_compatible_node(bn, NULL, "ibm,bsr");
288 }
289 return 0;
290}
291
292static int __init bsr_init(void)
293{
294 struct device_node *np;
295 dev_t bsr_dev = MKDEV(bsr_major, 0);
296 int ret = -ENODEV;
297 int result;
298
299 np = of_find_compatible_node(NULL, NULL, "ibm,bsr");
300 if (!np)
301 goto out_err;
302
303 bsr_class = class_create(THIS_MODULE, "bsr");
304 if (IS_ERR(bsr_class)) {
305 printk(KERN_ERR "class_create() failed for bsr_class\n");
306 goto out_err_1;
307 }
308 bsr_class->dev_attrs = bsr_dev_attrs;
309
310 result = alloc_chrdev_region(&bsr_dev, 0, BSR_MAX_DEVS, "bsr");
311 bsr_major = MAJOR(bsr_dev);
312 if (result < 0) {
313 printk(KERN_ERR "alloc_chrdev_region() failed for bsr\n");
314 goto out_err_2;
315 }
316
317 if ((ret = bsr_create_devs(np)) < 0) {
318 np = NULL;
319 goto out_err_3;
320 }
321
322 return 0;
323
324 out_err_3:
325 unregister_chrdev_region(bsr_dev, BSR_MAX_DEVS);
326
327 out_err_2:
328 class_destroy(bsr_class);
329
330 out_err_1:
331 of_node_put(np);
332
333 out_err:
334
335 return ret;
336}
337
338static void __exit bsr_exit(void)
339{
340
341 bsr_cleanup_devs();
342
343 if (bsr_class)
344 class_destroy(bsr_class);
345
346 if (bsr_major)
347 unregister_chrdev_region(MKDEV(bsr_major, 0), BSR_MAX_DEVS);
348}
349
350module_init(bsr_init);
351module_exit(bsr_exit);
352MODULE_LICENSE("GPL");
353MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");
354