Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  1 /* IBM POWER Barrier Synchronization Register Driver
  2  *
  3  * Copyright IBM Corporation 2008
  4  *
  5  * Author: Sonny Rao <sonnyrao@us.ibm.com>
  6  *
  7  * This program is free software; you can redistribute it and/or modify
  8  * it under the terms of the GNU General Public License as published by
  9  * the Free Software Foundation; either version 2 of the License, or
 10  * (at your option) any later version.
 11  *
 12  * This program is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15  * GNU General Public License for more details.
 16  *
 17  * You should have received a copy of the GNU General Public License
 18  * along with this program; if not, write to the Free Software
 19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 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  This driver exposes a special register which can be used for fast
 35  synchronization across a large SMP machine.  The hardware is exposed
 36  as an array of bytes where each process will write to one of the bytes to
 37  indicate it has finished the current stage and this update is broadcast to
 38  all processors without having to bounce a cacheline between them. In
 39  POWER5 and POWER6 there is one of these registers per SMP,  but it is
 40  presented in two forms; first, it is given as a whole and then as a number
 41  of smaller registers which alias to parts of the single whole register.
 42  This can potentially allow multiple groups of processes to each have their
 43  own private synchronization device.
 44 
 45  Note that this hardware *must* be written to using *only* single byte writes.
 46  It may be read using 1, 2, 4, or 8 byte loads which must be aligned since
 47  this region is treated as cache-inhibited  processes should also use a
 48  full sync before and after writing to the BSR to ensure all stores and
 49  the BSR update have made it to all chips in the system
 50 */
 51 
 52 /* This is arbitrary number, up to Power6 it's been 17 or fewer  */
 53 #define BSR_MAX_DEVS (32)
 54 
 55 struct bsr_dev {
 56         u64      bsr_addr;     /* Real address */
 57         u64      bsr_len;      /* length of mem region we can map */
 58         unsigned bsr_bytes;    /* size of the BSR reg itself */
 59         unsigned bsr_stride;   /* interval at which BSR repeats in the page */
 60         unsigned bsr_type;     /* maps to enum below */
 61         unsigned bsr_num;      /* bsr id number for its type */
 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 
 73 static unsigned total_bsr_devs;
 74 static struct list_head bsr_devs = LIST_HEAD_INIT(bsr_devs);
 75 static struct class *bsr_class;
 76 static int bsr_major;
 77 
 78 enum {
 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 
 88 static unsigned bsr_types[BSR_MAX];
 89 
 90 static ssize_t
 91 bsr_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 
 97 static ssize_t
 98 bsr_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 
104 static ssize_t
105 bsr_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 
111 static 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 
118 static 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         /* check for the case of a small BSR device and map one 4k page for it*/
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 
143 static 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 
152 static const struct file_operations bsr_fops = {
153         .owner = THIS_MODULE,
154         .mmap  = bsr_mmap,
155         .open  = bsr_open,
156 };
157 
158 static 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 
172 static 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                 /* if we have a bsr_len of > 4k and less then PAGE_SIZE (64k pages) */
218                 /* we can only map 4k of it, so only advertise the 4k in sysfs */
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 
277 static 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 
292 static 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 
338 static 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 
350 module_init(bsr_init);
351 module_exit(bsr_exit);
352 MODULE_LICENSE("GPL");
353 MODULE_AUTHOR("Sonny Rao <sonnyrao@us.ibm.com>");
354 
  This page was automatically generated by the LXR engine.