diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-16 15:40:26 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-16 15:40:26 -0400 |
commit | c813b4e16ead3c3df98ac84419d4df2adf33fe01 (patch) | |
tree | 2ca4a5b6966d833b6149e3dda7a4e85d1255779c /drivers/uio | |
parent | c8d8a2321f9c4ee18fbcc399fdc2a77e580a03b9 (diff) | |
parent | 02683ffdf655b4ae15245376ba6fea6d9e5829a6 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6: (46 commits)
UIO: Fix mapping of logical and virtual memory
UIO: add automata sercos3 pci card support
UIO: Change driver name of uio_pdrv
UIO: Add alignment warnings for uio-mem
Driver core: add bus_sort_breadthfirst() function
NET: convert the phy_device file to use bus_find_device_by_name
kobject: Cleanup kobject_rename and !CONFIG_SYSFS
kobject: Fix kobject_rename and !CONFIG_SYSFS
sysfs: Make dir and name args to sysfs_notify() const
platform: add new device registration helper
sysfs: use ilookup5() instead of ilookup5_nowait()
PNP: create device attributes via default device attributes
Driver core: make bus_find_device_by_name() more robust
usb: turn dev_warn+WARN_ON combos into dev_WARN
debug: use dev_WARN() rather than WARN_ON() in device_pm_add()
debug: Introduce a dev_WARN() function
sysfs: fix deadlock
device model: Do a quickcheck for driver binding before doing an expensive check
Driver core: Fix cleanup in device_create_vargs().
Driver core: Clarify device cleanup.
...
Diffstat (limited to 'drivers/uio')
-rw-r--r-- | drivers/uio/Kconfig | 13 | ||||
-rw-r--r-- | drivers/uio/Makefile | 1 | ||||
-rw-r--r-- | drivers/uio/uio.c | 26 | ||||
-rw-r--r-- | drivers/uio/uio_pdrv.c | 2 | ||||
-rw-r--r-- | drivers/uio/uio_sercos3.c | 243 |
5 files changed, 279 insertions, 6 deletions
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 4190be64917f..04b954cfce76 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig | |||
@@ -58,4 +58,17 @@ config UIO_SMX | |||
58 | 58 | ||
59 | If you compile this as a module, it will be called uio_smx. | 59 | If you compile this as a module, it will be called uio_smx. |
60 | 60 | ||
61 | config UIO_SERCOS3 | ||
62 | tristate "Automata Sercos III PCI card driver" | ||
63 | default n | ||
64 | help | ||
65 | Userspace I/O interface for the Sercos III PCI card from | ||
66 | Automata GmbH. The userspace part of this driver will be | ||
67 | available for download from the Automata GmbH web site. | ||
68 | |||
69 | Automata GmbH: http://www.automataweb.com | ||
70 | Sercos III interface: http://www.sercos.com | ||
71 | |||
72 | If you compile this as a module, it will be called uio_sercos3. | ||
73 | |||
61 | endif | 74 | endif |
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile index 8667bbdef904..e69558149859 100644 --- a/drivers/uio/Makefile +++ b/drivers/uio/Makefile | |||
@@ -3,3 +3,4 @@ obj-$(CONFIG_UIO_CIF) += uio_cif.o | |||
3 | obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o | 3 | obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o |
4 | obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o | 4 | obj-$(CONFIG_UIO_PDRV_GENIRQ) += uio_pdrv_genirq.o |
5 | obj-$(CONFIG_UIO_SMX) += uio_smx.o | 5 | obj-$(CONFIG_UIO_SMX) += uio_smx.o |
6 | obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o | ||
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 3a6934bf7131..5dccf057a7dd 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c | |||
@@ -67,6 +67,11 @@ static ssize_t map_size_show(struct uio_mem *mem, char *buf) | |||
67 | return sprintf(buf, "0x%lx\n", mem->size); | 67 | return sprintf(buf, "0x%lx\n", mem->size); |
68 | } | 68 | } |
69 | 69 | ||
70 | static ssize_t map_offset_show(struct uio_mem *mem, char *buf) | ||
71 | { | ||
72 | return sprintf(buf, "0x%lx\n", mem->addr & ~PAGE_MASK); | ||
73 | } | ||
74 | |||
70 | struct uio_sysfs_entry { | 75 | struct uio_sysfs_entry { |
71 | struct attribute attr; | 76 | struct attribute attr; |
72 | ssize_t (*show)(struct uio_mem *, char *); | 77 | ssize_t (*show)(struct uio_mem *, char *); |
@@ -77,10 +82,13 @@ static struct uio_sysfs_entry addr_attribute = | |||
77 | __ATTR(addr, S_IRUGO, map_addr_show, NULL); | 82 | __ATTR(addr, S_IRUGO, map_addr_show, NULL); |
78 | static struct uio_sysfs_entry size_attribute = | 83 | static struct uio_sysfs_entry size_attribute = |
79 | __ATTR(size, S_IRUGO, map_size_show, NULL); | 84 | __ATTR(size, S_IRUGO, map_size_show, NULL); |
85 | static struct uio_sysfs_entry offset_attribute = | ||
86 | __ATTR(offset, S_IRUGO, map_offset_show, NULL); | ||
80 | 87 | ||
81 | static struct attribute *attrs[] = { | 88 | static struct attribute *attrs[] = { |
82 | &addr_attribute.attr, | 89 | &addr_attribute.attr, |
83 | &size_attribute.attr, | 90 | &size_attribute.attr, |
91 | &offset_attribute.attr, | ||
84 | NULL, /* need to NULL terminate the list of attributes */ | 92 | NULL, /* need to NULL terminate the list of attributes */ |
85 | }; | 93 | }; |
86 | 94 | ||
@@ -482,15 +490,23 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
482 | { | 490 | { |
483 | struct uio_device *idev = vma->vm_private_data; | 491 | struct uio_device *idev = vma->vm_private_data; |
484 | struct page *page; | 492 | struct page *page; |
493 | unsigned long offset; | ||
485 | 494 | ||
486 | int mi = uio_find_mem_index(vma); | 495 | int mi = uio_find_mem_index(vma); |
487 | if (mi < 0) | 496 | if (mi < 0) |
488 | return VM_FAULT_SIGBUS; | 497 | return VM_FAULT_SIGBUS; |
489 | 498 | ||
499 | /* | ||
500 | * We need to subtract mi because userspace uses offset = N*PAGE_SIZE | ||
501 | * to use mem[N]. | ||
502 | */ | ||
503 | offset = (vmf->pgoff - mi) << PAGE_SHIFT; | ||
504 | |||
490 | if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL) | 505 | if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL) |
491 | page = virt_to_page(idev->info->mem[mi].addr); | 506 | page = virt_to_page(idev->info->mem[mi].addr + offset); |
492 | else | 507 | else |
493 | page = vmalloc_to_page((void*)idev->info->mem[mi].addr); | 508 | page = vmalloc_to_page((void *)idev->info->mem[mi].addr |
509 | + offset); | ||
494 | get_page(page); | 510 | get_page(page); |
495 | vmf->page = page; | 511 | vmf->page = page; |
496 | return 0; | 512 | return 0; |
@@ -682,9 +698,9 @@ int __uio_register_device(struct module *owner, | |||
682 | if (ret) | 698 | if (ret) |
683 | goto err_get_minor; | 699 | goto err_get_minor; |
684 | 700 | ||
685 | idev->dev = device_create_drvdata(uio_class->class, parent, | 701 | idev->dev = device_create(uio_class->class, parent, |
686 | MKDEV(uio_major, idev->minor), idev, | 702 | MKDEV(uio_major, idev->minor), idev, |
687 | "uio%d", idev->minor); | 703 | "uio%d", idev->minor); |
688 | if (IS_ERR(idev->dev)) { | 704 | if (IS_ERR(idev->dev)) { |
689 | printk(KERN_ERR "UIO: device register failed\n"); | 705 | printk(KERN_ERR "UIO: device register failed\n"); |
690 | ret = PTR_ERR(idev->dev); | 706 | ret = PTR_ERR(idev->dev); |
diff --git a/drivers/uio/uio_pdrv.c b/drivers/uio/uio_pdrv.c index 0b4ef39cd85d..d494ce9288c3 100644 --- a/drivers/uio/uio_pdrv.c +++ b/drivers/uio/uio_pdrv.c | |||
@@ -12,7 +12,7 @@ | |||
12 | #include <linux/uio_driver.h> | 12 | #include <linux/uio_driver.h> |
13 | #include <linux/stringify.h> | 13 | #include <linux/stringify.h> |
14 | 14 | ||
15 | #define DRIVER_NAME "uio" | 15 | #define DRIVER_NAME "uio_pdrv" |
16 | 16 | ||
17 | struct uio_platdata { | 17 | struct uio_platdata { |
18 | struct uio_info *uioinfo; | 18 | struct uio_info *uioinfo; |
diff --git a/drivers/uio/uio_sercos3.c b/drivers/uio/uio_sercos3.c new file mode 100644 index 000000000000..a6d1b2bc47f3 --- /dev/null +++ b/drivers/uio/uio_sercos3.c | |||
@@ -0,0 +1,243 @@ | |||
1 | /* sercos3: UIO driver for the Automata Sercos III PCI card | ||
2 | |||
3 | Copyright (C) 2008 Linutronix GmbH | ||
4 | Author: John Ogness <john.ogness@linutronix.de> | ||
5 | |||
6 | This is a straight-forward UIO driver, where interrupts are disabled | ||
7 | by the interrupt handler and re-enabled via a write to the UIO device | ||
8 | by the userspace-part. | ||
9 | |||
10 | The only part that may seem odd is the use of a logical OR when | ||
11 | storing and restoring enabled interrupts. This is done because the | ||
12 | userspace-part could directly modify the Interrupt Enable Register | ||
13 | at any time. To reduce possible conflicts, the kernel driver uses | ||
14 | a logical OR to make more controlled changes (rather than blindly | ||
15 | overwriting previous values). | ||
16 | |||
17 | Race conditions exist if the userspace-part directly modifies the | ||
18 | Interrupt Enable Register while in operation. The consequences are | ||
19 | that certain interrupts would fail to be enabled or disabled. For | ||
20 | this reason, the userspace-part should only directly modify the | ||
21 | Interrupt Enable Register at the beginning (to get things going). | ||
22 | The userspace-part can safely disable interrupts at any time using | ||
23 | a write to the UIO device. | ||
24 | */ | ||
25 | |||
26 | #include <linux/device.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/uio_driver.h> | ||
30 | #include <linux/io.h> | ||
31 | |||
32 | /* ID's for SERCOS III PCI card (PLX 9030) */ | ||
33 | #define SERCOS_SUB_VENDOR_ID 0x1971 | ||
34 | #define SERCOS_SUB_SYSID_3530 0x3530 | ||
35 | #define SERCOS_SUB_SYSID_3535 0x3535 | ||
36 | #define SERCOS_SUB_SYSID_3780 0x3780 | ||
37 | |||
38 | /* Interrupt Enable Register */ | ||
39 | #define IER0_OFFSET 0x08 | ||
40 | |||
41 | /* Interrupt Status Register */ | ||
42 | #define ISR0_OFFSET 0x18 | ||
43 | |||
44 | struct sercos3_priv { | ||
45 | u32 ier0_cache; | ||
46 | spinlock_t ier0_cache_lock; | ||
47 | }; | ||
48 | |||
49 | /* this function assumes ier0_cache_lock is locked! */ | ||
50 | static void sercos3_disable_interrupts(struct uio_info *info, | ||
51 | struct sercos3_priv *priv) | ||
52 | { | ||
53 | void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET; | ||
54 | |||
55 | /* add enabled interrupts to cache */ | ||
56 | priv->ier0_cache |= ioread32(ier0); | ||
57 | |||
58 | /* disable interrupts */ | ||
59 | iowrite32(0, ier0); | ||
60 | } | ||
61 | |||
62 | /* this function assumes ier0_cache_lock is locked! */ | ||
63 | static void sercos3_enable_interrupts(struct uio_info *info, | ||
64 | struct sercos3_priv *priv) | ||
65 | { | ||
66 | void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET; | ||
67 | |||
68 | /* restore previously enabled interrupts */ | ||
69 | iowrite32(ioread32(ier0) | priv->ier0_cache, ier0); | ||
70 | priv->ier0_cache = 0; | ||
71 | } | ||
72 | |||
73 | static irqreturn_t sercos3_handler(int irq, struct uio_info *info) | ||
74 | { | ||
75 | struct sercos3_priv *priv = info->priv; | ||
76 | void __iomem *isr0 = info->mem[3].internal_addr + ISR0_OFFSET; | ||
77 | void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET; | ||
78 | |||
79 | if (!(ioread32(isr0) & ioread32(ier0))) | ||
80 | return IRQ_NONE; | ||
81 | |||
82 | spin_lock(&priv->ier0_cache_lock); | ||
83 | sercos3_disable_interrupts(info, priv); | ||
84 | spin_unlock(&priv->ier0_cache_lock); | ||
85 | |||
86 | return IRQ_HANDLED; | ||
87 | } | ||
88 | |||
89 | static int sercos3_irqcontrol(struct uio_info *info, s32 irq_on) | ||
90 | { | ||
91 | struct sercos3_priv *priv = info->priv; | ||
92 | |||
93 | spin_lock_irq(&priv->ier0_cache_lock); | ||
94 | if (irq_on) | ||
95 | sercos3_enable_interrupts(info, priv); | ||
96 | else | ||
97 | sercos3_disable_interrupts(info, priv); | ||
98 | spin_unlock_irq(&priv->ier0_cache_lock); | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static int sercos3_setup_iomem(struct pci_dev *dev, struct uio_info *info, | ||
104 | int n, int pci_bar) | ||
105 | { | ||
106 | info->mem[n].addr = pci_resource_start(dev, pci_bar); | ||
107 | if (!info->mem[n].addr) | ||
108 | return -1; | ||
109 | info->mem[n].internal_addr = ioremap(pci_resource_start(dev, pci_bar), | ||
110 | pci_resource_len(dev, pci_bar)); | ||
111 | if (!info->mem[n].internal_addr) | ||
112 | return -1; | ||
113 | info->mem[n].size = pci_resource_len(dev, pci_bar); | ||
114 | info->mem[n].memtype = UIO_MEM_PHYS; | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int __devinit sercos3_pci_probe(struct pci_dev *dev, | ||
119 | const struct pci_device_id *id) | ||
120 | { | ||
121 | struct uio_info *info; | ||
122 | struct sercos3_priv *priv; | ||
123 | int i; | ||
124 | |||
125 | info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); | ||
126 | if (!info) | ||
127 | return -ENOMEM; | ||
128 | |||
129 | priv = kzalloc(sizeof(struct sercos3_priv), GFP_KERNEL); | ||
130 | if (!priv) | ||
131 | goto out_free; | ||
132 | |||
133 | if (pci_enable_device(dev)) | ||
134 | goto out_free_priv; | ||
135 | |||
136 | if (pci_request_regions(dev, "sercos3")) | ||
137 | goto out_disable; | ||
138 | |||
139 | /* we only need PCI BAR's 0, 2, 3, 4, 5 */ | ||
140 | if (sercos3_setup_iomem(dev, info, 0, 0)) | ||
141 | goto out_unmap; | ||
142 | if (sercos3_setup_iomem(dev, info, 1, 2)) | ||
143 | goto out_unmap; | ||
144 | if (sercos3_setup_iomem(dev, info, 2, 3)) | ||
145 | goto out_unmap; | ||
146 | if (sercos3_setup_iomem(dev, info, 3, 4)) | ||
147 | goto out_unmap; | ||
148 | if (sercos3_setup_iomem(dev, info, 4, 5)) | ||
149 | goto out_unmap; | ||
150 | |||
151 | spin_lock_init(&priv->ier0_cache_lock); | ||
152 | info->priv = priv; | ||
153 | info->name = "Sercos_III_PCI"; | ||
154 | info->version = "0.0.1"; | ||
155 | info->irq = dev->irq; | ||
156 | info->irq_flags = IRQF_DISABLED | IRQF_SHARED; | ||
157 | info->handler = sercos3_handler; | ||
158 | info->irqcontrol = sercos3_irqcontrol; | ||
159 | |||
160 | pci_set_drvdata(dev, info); | ||
161 | |||
162 | if (uio_register_device(&dev->dev, info)) | ||
163 | goto out_unmap; | ||
164 | |||
165 | return 0; | ||
166 | |||
167 | out_unmap: | ||
168 | for (i = 0; i < 5; i++) { | ||
169 | if (info->mem[i].internal_addr) | ||
170 | iounmap(info->mem[i].internal_addr); | ||
171 | } | ||
172 | pci_release_regions(dev); | ||
173 | out_disable: | ||
174 | pci_disable_device(dev); | ||
175 | out_free_priv: | ||
176 | kfree(priv); | ||
177 | out_free: | ||
178 | kfree(info); | ||
179 | return -ENODEV; | ||
180 | } | ||
181 | |||
182 | static void sercos3_pci_remove(struct pci_dev *dev) | ||
183 | { | ||
184 | struct uio_info *info = pci_get_drvdata(dev); | ||
185 | int i; | ||
186 | |||
187 | uio_unregister_device(info); | ||
188 | pci_release_regions(dev); | ||
189 | pci_disable_device(dev); | ||
190 | pci_set_drvdata(dev, NULL); | ||
191 | for (i = 0; i < 5; i++) { | ||
192 | if (info->mem[i].internal_addr) | ||
193 | iounmap(info->mem[i].internal_addr); | ||
194 | } | ||
195 | kfree(info->priv); | ||
196 | kfree(info); | ||
197 | } | ||
198 | |||
199 | static struct pci_device_id sercos3_pci_ids[] __devinitdata = { | ||
200 | { | ||
201 | .vendor = PCI_VENDOR_ID_PLX, | ||
202 | .device = PCI_DEVICE_ID_PLX_9030, | ||
203 | .subvendor = SERCOS_SUB_VENDOR_ID, | ||
204 | .subdevice = SERCOS_SUB_SYSID_3530, | ||
205 | }, | ||
206 | { | ||
207 | .vendor = PCI_VENDOR_ID_PLX, | ||
208 | .device = PCI_DEVICE_ID_PLX_9030, | ||
209 | .subvendor = SERCOS_SUB_VENDOR_ID, | ||
210 | .subdevice = SERCOS_SUB_SYSID_3535, | ||
211 | }, | ||
212 | { | ||
213 | .vendor = PCI_VENDOR_ID_PLX, | ||
214 | .device = PCI_DEVICE_ID_PLX_9030, | ||
215 | .subvendor = SERCOS_SUB_VENDOR_ID, | ||
216 | .subdevice = SERCOS_SUB_SYSID_3780, | ||
217 | }, | ||
218 | { 0, } | ||
219 | }; | ||
220 | |||
221 | static struct pci_driver sercos3_pci_driver = { | ||
222 | .name = "sercos3", | ||
223 | .id_table = sercos3_pci_ids, | ||
224 | .probe = sercos3_pci_probe, | ||
225 | .remove = sercos3_pci_remove, | ||
226 | }; | ||
227 | |||
228 | static int __init sercos3_init_module(void) | ||
229 | { | ||
230 | return pci_register_driver(&sercos3_pci_driver); | ||
231 | } | ||
232 | |||
233 | static void __exit sercos3_exit_module(void) | ||
234 | { | ||
235 | pci_unregister_driver(&sercos3_pci_driver); | ||
236 | } | ||
237 | |||
238 | module_init(sercos3_init_module); | ||
239 | module_exit(sercos3_exit_module); | ||
240 | |||
241 | MODULE_DESCRIPTION("UIO driver for the Automata Sercos III PCI card"); | ||
242 | MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>"); | ||
243 | MODULE_LICENSE("GPL v2"); | ||