1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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#include <linux/slab.h>
32
33
34#define SERCOS_SUB_VENDOR_ID 0x1971
35#define SERCOS_SUB_SYSID_3530 0x3530
36#define SERCOS_SUB_SYSID_3535 0x3535
37#define SERCOS_SUB_SYSID_3780 0x3780
38
39
40#define IER0_OFFSET 0x08
41
42
43#define ISR0_OFFSET 0x18
44
45struct sercos3_priv {
46 u32 ier0_cache;
47 spinlock_t ier0_cache_lock;
48};
49
50
51static void sercos3_disable_interrupts(struct uio_info *info,
52 struct sercos3_priv *priv)
53{
54 void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
55
56
57 priv->ier0_cache |= ioread32(ier0);
58
59
60 iowrite32(0, ier0);
61}
62
63
64static void sercos3_enable_interrupts(struct uio_info *info,
65 struct sercos3_priv *priv)
66{
67 void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
68
69
70 iowrite32(ioread32(ier0) | priv->ier0_cache, ier0);
71 priv->ier0_cache = 0;
72}
73
74static irqreturn_t sercos3_handler(int irq, struct uio_info *info)
75{
76 struct sercos3_priv *priv = info->priv;
77 void __iomem *isr0 = info->mem[3].internal_addr + ISR0_OFFSET;
78 void __iomem *ier0 = info->mem[3].internal_addr + IER0_OFFSET;
79
80 if (!(ioread32(isr0) & ioread32(ier0)))
81 return IRQ_NONE;
82
83 spin_lock(&priv->ier0_cache_lock);
84 sercos3_disable_interrupts(info, priv);
85 spin_unlock(&priv->ier0_cache_lock);
86
87 return IRQ_HANDLED;
88}
89
90static int sercos3_irqcontrol(struct uio_info *info, s32 irq_on)
91{
92 struct sercos3_priv *priv = info->priv;
93
94 spin_lock_irq(&priv->ier0_cache_lock);
95 if (irq_on)
96 sercos3_enable_interrupts(info, priv);
97 else
98 sercos3_disable_interrupts(info, priv);
99 spin_unlock_irq(&priv->ier0_cache_lock);
100
101 return 0;
102}
103
104static int sercos3_setup_iomem(struct pci_dev *dev, struct uio_info *info,
105 int n, int pci_bar)
106{
107 info->mem[n].addr = pci_resource_start(dev, pci_bar);
108 if (!info->mem[n].addr)
109 return -1;
110 info->mem[n].internal_addr = ioremap(pci_resource_start(dev, pci_bar),
111 pci_resource_len(dev, pci_bar));
112 if (!info->mem[n].internal_addr)
113 return -1;
114 info->mem[n].size = pci_resource_len(dev, pci_bar);
115 info->mem[n].memtype = UIO_MEM_PHYS;
116 return 0;
117}
118
119static int sercos3_pci_probe(struct pci_dev *dev,
120 const struct pci_device_id *id)
121{
122 struct uio_info *info;
123 struct sercos3_priv *priv;
124 int i;
125
126 info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
127 if (!info)
128 return -ENOMEM;
129
130 priv = kzalloc(sizeof(struct sercos3_priv), GFP_KERNEL);
131 if (!priv)
132 goto out_free;
133
134 if (pci_enable_device(dev))
135 goto out_free_priv;
136
137 if (pci_request_regions(dev, "sercos3"))
138 goto out_disable;
139
140
141 if (sercos3_setup_iomem(dev, info, 0, 0))
142 goto out_unmap;
143 if (sercos3_setup_iomem(dev, info, 1, 2))
144 goto out_unmap;
145 if (sercos3_setup_iomem(dev, info, 2, 3))
146 goto out_unmap;
147 if (sercos3_setup_iomem(dev, info, 3, 4))
148 goto out_unmap;
149 if (sercos3_setup_iomem(dev, info, 4, 5))
150 goto out_unmap;
151
152 spin_lock_init(&priv->ier0_cache_lock);
153 info->priv = priv;
154 info->name = "Sercos_III_PCI";
155 info->version = "0.0.1";
156 info->irq = dev->irq;
157 info->irq_flags = IRQF_SHARED;
158 info->handler = sercos3_handler;
159 info->irqcontrol = sercos3_irqcontrol;
160
161 pci_set_drvdata(dev, info);
162
163 if (uio_register_device(&dev->dev, info))
164 goto out_unmap;
165
166 return 0;
167
168out_unmap:
169 for (i = 0; i < 5; i++) {
170 if (info->mem[i].internal_addr)
171 iounmap(info->mem[i].internal_addr);
172 }
173 pci_release_regions(dev);
174out_disable:
175 pci_disable_device(dev);
176out_free_priv:
177 kfree(priv);
178out_free:
179 kfree(info);
180 return -ENODEV;
181}
182
183static void sercos3_pci_remove(struct pci_dev *dev)
184{
185 struct uio_info *info = pci_get_drvdata(dev);
186 int i;
187
188 uio_unregister_device(info);
189 pci_release_regions(dev);
190 pci_disable_device(dev);
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
199static struct pci_device_id sercos3_pci_ids[] = {
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
221static 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
228module_pci_driver(sercos3_pci_driver);
229MODULE_DESCRIPTION("UIO driver for the Automata Sercos III PCI card");
230MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>");
231MODULE_LICENSE("GPL v2");
232