1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2006, 2007 Eugene Konev <ejka@openwrt.org>
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Parts of the VLYNQ specification can be found here:
6*4882a593Smuzhiyun * http://www.ti.com/litv/pdf/sprue36a
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/init.h>
10*4882a593Smuzhiyun #include <linux/types.h>
11*4882a593Smuzhiyun #include <linux/kernel.h>
12*4882a593Smuzhiyun #include <linux/string.h>
13*4882a593Smuzhiyun #include <linux/device.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/errno.h>
16*4882a593Smuzhiyun #include <linux/platform_device.h>
17*4882a593Smuzhiyun #include <linux/interrupt.h>
18*4882a593Smuzhiyun #include <linux/delay.h>
19*4882a593Smuzhiyun #include <linux/io.h>
20*4882a593Smuzhiyun #include <linux/slab.h>
21*4882a593Smuzhiyun #include <linux/irq.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #include <linux/vlynq.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define VLYNQ_CTRL_PM_ENABLE 0x80000000
26*4882a593Smuzhiyun #define VLYNQ_CTRL_CLOCK_INT 0x00008000
27*4882a593Smuzhiyun #define VLYNQ_CTRL_CLOCK_DIV(x) (((x) & 7) << 16)
28*4882a593Smuzhiyun #define VLYNQ_CTRL_INT_LOCAL 0x00004000
29*4882a593Smuzhiyun #define VLYNQ_CTRL_INT_ENABLE 0x00002000
30*4882a593Smuzhiyun #define VLYNQ_CTRL_INT_VECTOR(x) (((x) & 0x1f) << 8)
31*4882a593Smuzhiyun #define VLYNQ_CTRL_INT2CFG 0x00000080
32*4882a593Smuzhiyun #define VLYNQ_CTRL_RESET 0x00000001
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun #define VLYNQ_CTRL_CLOCK_MASK (0x7 << 16)
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun #define VLYNQ_INT_OFFSET 0x00000014
37*4882a593Smuzhiyun #define VLYNQ_REMOTE_OFFSET 0x00000080
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun #define VLYNQ_STATUS_LINK 0x00000001
40*4882a593Smuzhiyun #define VLYNQ_STATUS_LERROR 0x00000080
41*4882a593Smuzhiyun #define VLYNQ_STATUS_RERROR 0x00000100
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun #define VINT_ENABLE 0x00000100
44*4882a593Smuzhiyun #define VINT_TYPE_EDGE 0x00000080
45*4882a593Smuzhiyun #define VINT_LEVEL_LOW 0x00000040
46*4882a593Smuzhiyun #define VINT_VECTOR(x) ((x) & 0x1f)
47*4882a593Smuzhiyun #define VINT_OFFSET(irq) (8 * ((irq) % 4))
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun #define VLYNQ_AUTONEGO_V2 0x00010000
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun struct vlynq_regs {
52*4882a593Smuzhiyun u32 revision;
53*4882a593Smuzhiyun u32 control;
54*4882a593Smuzhiyun u32 status;
55*4882a593Smuzhiyun u32 int_prio;
56*4882a593Smuzhiyun u32 int_status;
57*4882a593Smuzhiyun u32 int_pending;
58*4882a593Smuzhiyun u32 int_ptr;
59*4882a593Smuzhiyun u32 tx_offset;
60*4882a593Smuzhiyun struct vlynq_mapping rx_mapping[4];
61*4882a593Smuzhiyun u32 chip;
62*4882a593Smuzhiyun u32 autonego;
63*4882a593Smuzhiyun u32 unused[6];
64*4882a593Smuzhiyun u32 int_device[8];
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun #ifdef CONFIG_VLYNQ_DEBUG
vlynq_dump_regs(struct vlynq_device * dev)68*4882a593Smuzhiyun static void vlynq_dump_regs(struct vlynq_device *dev)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun int i;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun printk(KERN_DEBUG "VLYNQ local=%p remote=%p\n",
73*4882a593Smuzhiyun dev->local, dev->remote);
74*4882a593Smuzhiyun for (i = 0; i < 32; i++) {
75*4882a593Smuzhiyun printk(KERN_DEBUG "VLYNQ: local %d: %08x\n",
76*4882a593Smuzhiyun i + 1, ((u32 *)dev->local)[i]);
77*4882a593Smuzhiyun printk(KERN_DEBUG "VLYNQ: remote %d: %08x\n",
78*4882a593Smuzhiyun i + 1, ((u32 *)dev->remote)[i]);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun
vlynq_dump_mem(u32 * base,int count)82*4882a593Smuzhiyun static void vlynq_dump_mem(u32 *base, int count)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun int i;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun for (i = 0; i < (count + 3) / 4; i++) {
87*4882a593Smuzhiyun if (i % 4 == 0)
88*4882a593Smuzhiyun printk(KERN_DEBUG "\nMEM[0x%04x]:", i * 4);
89*4882a593Smuzhiyun printk(KERN_DEBUG " 0x%08x", *(base + i));
90*4882a593Smuzhiyun }
91*4882a593Smuzhiyun printk(KERN_DEBUG "\n");
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun #endif
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun /* Check the VLYNQ link status with a given device */
vlynq_linked(struct vlynq_device * dev)96*4882a593Smuzhiyun static int vlynq_linked(struct vlynq_device *dev)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun int i;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun for (i = 0; i < 100; i++)
101*4882a593Smuzhiyun if (readl(&dev->local->status) & VLYNQ_STATUS_LINK)
102*4882a593Smuzhiyun return 1;
103*4882a593Smuzhiyun else
104*4882a593Smuzhiyun cpu_relax();
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun return 0;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
vlynq_reset(struct vlynq_device * dev)109*4882a593Smuzhiyun static void vlynq_reset(struct vlynq_device *dev)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun writel(readl(&dev->local->control) | VLYNQ_CTRL_RESET,
112*4882a593Smuzhiyun &dev->local->control);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* Wait for the devices to finish resetting */
115*4882a593Smuzhiyun msleep(5);
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun /* Remove reset bit */
118*4882a593Smuzhiyun writel(readl(&dev->local->control) & ~VLYNQ_CTRL_RESET,
119*4882a593Smuzhiyun &dev->local->control);
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun /* Give some time for the devices to settle */
122*4882a593Smuzhiyun msleep(5);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
vlynq_irq_unmask(struct irq_data * d)125*4882a593Smuzhiyun static void vlynq_irq_unmask(struct irq_data *d)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun struct vlynq_device *dev = irq_data_get_irq_chip_data(d);
128*4882a593Smuzhiyun int virq;
129*4882a593Smuzhiyun u32 val;
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun BUG_ON(!dev);
132*4882a593Smuzhiyun virq = d->irq - dev->irq_start;
133*4882a593Smuzhiyun val = readl(&dev->remote->int_device[virq >> 2]);
134*4882a593Smuzhiyun val |= (VINT_ENABLE | virq) << VINT_OFFSET(virq);
135*4882a593Smuzhiyun writel(val, &dev->remote->int_device[virq >> 2]);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
vlynq_irq_mask(struct irq_data * d)138*4882a593Smuzhiyun static void vlynq_irq_mask(struct irq_data *d)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun struct vlynq_device *dev = irq_data_get_irq_chip_data(d);
141*4882a593Smuzhiyun int virq;
142*4882a593Smuzhiyun u32 val;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun BUG_ON(!dev);
145*4882a593Smuzhiyun virq = d->irq - dev->irq_start;
146*4882a593Smuzhiyun val = readl(&dev->remote->int_device[virq >> 2]);
147*4882a593Smuzhiyun val &= ~(VINT_ENABLE << VINT_OFFSET(virq));
148*4882a593Smuzhiyun writel(val, &dev->remote->int_device[virq >> 2]);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
vlynq_irq_type(struct irq_data * d,unsigned int flow_type)151*4882a593Smuzhiyun static int vlynq_irq_type(struct irq_data *d, unsigned int flow_type)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun struct vlynq_device *dev = irq_data_get_irq_chip_data(d);
154*4882a593Smuzhiyun int virq;
155*4882a593Smuzhiyun u32 val;
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun BUG_ON(!dev);
158*4882a593Smuzhiyun virq = d->irq - dev->irq_start;
159*4882a593Smuzhiyun val = readl(&dev->remote->int_device[virq >> 2]);
160*4882a593Smuzhiyun switch (flow_type & IRQ_TYPE_SENSE_MASK) {
161*4882a593Smuzhiyun case IRQ_TYPE_EDGE_RISING:
162*4882a593Smuzhiyun case IRQ_TYPE_EDGE_FALLING:
163*4882a593Smuzhiyun case IRQ_TYPE_EDGE_BOTH:
164*4882a593Smuzhiyun val |= VINT_TYPE_EDGE << VINT_OFFSET(virq);
165*4882a593Smuzhiyun val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
166*4882a593Smuzhiyun break;
167*4882a593Smuzhiyun case IRQ_TYPE_LEVEL_HIGH:
168*4882a593Smuzhiyun val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
169*4882a593Smuzhiyun val &= ~(VINT_LEVEL_LOW << VINT_OFFSET(virq));
170*4882a593Smuzhiyun break;
171*4882a593Smuzhiyun case IRQ_TYPE_LEVEL_LOW:
172*4882a593Smuzhiyun val &= ~(VINT_TYPE_EDGE << VINT_OFFSET(virq));
173*4882a593Smuzhiyun val |= VINT_LEVEL_LOW << VINT_OFFSET(virq);
174*4882a593Smuzhiyun break;
175*4882a593Smuzhiyun default:
176*4882a593Smuzhiyun return -EINVAL;
177*4882a593Smuzhiyun }
178*4882a593Smuzhiyun writel(val, &dev->remote->int_device[virq >> 2]);
179*4882a593Smuzhiyun return 0;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
vlynq_local_ack(struct irq_data * d)182*4882a593Smuzhiyun static void vlynq_local_ack(struct irq_data *d)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun struct vlynq_device *dev = irq_data_get_irq_chip_data(d);
185*4882a593Smuzhiyun u32 status = readl(&dev->local->status);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun pr_debug("%s: local status: 0x%08x\n",
188*4882a593Smuzhiyun dev_name(&dev->dev), status);
189*4882a593Smuzhiyun writel(status, &dev->local->status);
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
vlynq_remote_ack(struct irq_data * d)192*4882a593Smuzhiyun static void vlynq_remote_ack(struct irq_data *d)
193*4882a593Smuzhiyun {
194*4882a593Smuzhiyun struct vlynq_device *dev = irq_data_get_irq_chip_data(d);
195*4882a593Smuzhiyun u32 status = readl(&dev->remote->status);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun pr_debug("%s: remote status: 0x%08x\n",
198*4882a593Smuzhiyun dev_name(&dev->dev), status);
199*4882a593Smuzhiyun writel(status, &dev->remote->status);
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
vlynq_irq(int irq,void * dev_id)202*4882a593Smuzhiyun static irqreturn_t vlynq_irq(int irq, void *dev_id)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun struct vlynq_device *dev = dev_id;
205*4882a593Smuzhiyun u32 status;
206*4882a593Smuzhiyun int virq = 0;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun status = readl(&dev->local->int_status);
209*4882a593Smuzhiyun writel(status, &dev->local->int_status);
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun if (unlikely(!status))
212*4882a593Smuzhiyun spurious_interrupt();
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun while (status) {
215*4882a593Smuzhiyun if (status & 1)
216*4882a593Smuzhiyun do_IRQ(dev->irq_start + virq);
217*4882a593Smuzhiyun status >>= 1;
218*4882a593Smuzhiyun virq++;
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun return IRQ_HANDLED;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun static struct irq_chip vlynq_irq_chip = {
225*4882a593Smuzhiyun .name = "vlynq",
226*4882a593Smuzhiyun .irq_unmask = vlynq_irq_unmask,
227*4882a593Smuzhiyun .irq_mask = vlynq_irq_mask,
228*4882a593Smuzhiyun .irq_set_type = vlynq_irq_type,
229*4882a593Smuzhiyun };
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun static struct irq_chip vlynq_local_chip = {
232*4882a593Smuzhiyun .name = "vlynq local error",
233*4882a593Smuzhiyun .irq_unmask = vlynq_irq_unmask,
234*4882a593Smuzhiyun .irq_mask = vlynq_irq_mask,
235*4882a593Smuzhiyun .irq_ack = vlynq_local_ack,
236*4882a593Smuzhiyun };
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun static struct irq_chip vlynq_remote_chip = {
239*4882a593Smuzhiyun .name = "vlynq local error",
240*4882a593Smuzhiyun .irq_unmask = vlynq_irq_unmask,
241*4882a593Smuzhiyun .irq_mask = vlynq_irq_mask,
242*4882a593Smuzhiyun .irq_ack = vlynq_remote_ack,
243*4882a593Smuzhiyun };
244*4882a593Smuzhiyun
vlynq_setup_irq(struct vlynq_device * dev)245*4882a593Smuzhiyun static int vlynq_setup_irq(struct vlynq_device *dev)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun u32 val;
248*4882a593Smuzhiyun int i, virq;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun if (dev->local_irq == dev->remote_irq) {
251*4882a593Smuzhiyun printk(KERN_ERR
252*4882a593Smuzhiyun "%s: local vlynq irq should be different from remote\n",
253*4882a593Smuzhiyun dev_name(&dev->dev));
254*4882a593Smuzhiyun return -EINVAL;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun /* Clear local and remote error bits */
258*4882a593Smuzhiyun writel(readl(&dev->local->status), &dev->local->status);
259*4882a593Smuzhiyun writel(readl(&dev->remote->status), &dev->remote->status);
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun /* Now setup interrupts */
262*4882a593Smuzhiyun val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq);
263*4882a593Smuzhiyun val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL |
264*4882a593Smuzhiyun VLYNQ_CTRL_INT2CFG;
265*4882a593Smuzhiyun val |= readl(&dev->local->control);
266*4882a593Smuzhiyun writel(VLYNQ_INT_OFFSET, &dev->local->int_ptr);
267*4882a593Smuzhiyun writel(val, &dev->local->control);
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq);
270*4882a593Smuzhiyun val |= VLYNQ_CTRL_INT_ENABLE;
271*4882a593Smuzhiyun val |= readl(&dev->remote->control);
272*4882a593Smuzhiyun writel(VLYNQ_INT_OFFSET, &dev->remote->int_ptr);
273*4882a593Smuzhiyun writel(val, &dev->remote->int_ptr);
274*4882a593Smuzhiyun writel(val, &dev->remote->control);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun for (i = dev->irq_start; i <= dev->irq_end; i++) {
277*4882a593Smuzhiyun virq = i - dev->irq_start;
278*4882a593Smuzhiyun if (virq == dev->local_irq) {
279*4882a593Smuzhiyun irq_set_chip_and_handler(i, &vlynq_local_chip,
280*4882a593Smuzhiyun handle_level_irq);
281*4882a593Smuzhiyun irq_set_chip_data(i, dev);
282*4882a593Smuzhiyun } else if (virq == dev->remote_irq) {
283*4882a593Smuzhiyun irq_set_chip_and_handler(i, &vlynq_remote_chip,
284*4882a593Smuzhiyun handle_level_irq);
285*4882a593Smuzhiyun irq_set_chip_data(i, dev);
286*4882a593Smuzhiyun } else {
287*4882a593Smuzhiyun irq_set_chip_and_handler(i, &vlynq_irq_chip,
288*4882a593Smuzhiyun handle_simple_irq);
289*4882a593Smuzhiyun irq_set_chip_data(i, dev);
290*4882a593Smuzhiyun writel(0, &dev->remote->int_device[virq >> 2]);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun if (request_irq(dev->irq, vlynq_irq, IRQF_SHARED, "vlynq", dev)) {
295*4882a593Smuzhiyun printk(KERN_ERR "%s: request_irq failed\n",
296*4882a593Smuzhiyun dev_name(&dev->dev));
297*4882a593Smuzhiyun return -EAGAIN;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun return 0;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
vlynq_device_release(struct device * dev)303*4882a593Smuzhiyun static void vlynq_device_release(struct device *dev)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun struct vlynq_device *vdev = to_vlynq_device(dev);
306*4882a593Smuzhiyun kfree(vdev);
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun
vlynq_device_match(struct device * dev,struct device_driver * drv)309*4882a593Smuzhiyun static int vlynq_device_match(struct device *dev,
310*4882a593Smuzhiyun struct device_driver *drv)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun struct vlynq_device *vdev = to_vlynq_device(dev);
313*4882a593Smuzhiyun struct vlynq_driver *vdrv = to_vlynq_driver(drv);
314*4882a593Smuzhiyun struct vlynq_device_id *ids = vdrv->id_table;
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun while (ids->id) {
317*4882a593Smuzhiyun if (ids->id == vdev->dev_id) {
318*4882a593Smuzhiyun vdev->divisor = ids->divisor;
319*4882a593Smuzhiyun vlynq_set_drvdata(vdev, ids);
320*4882a593Smuzhiyun printk(KERN_INFO "Driver found for VLYNQ "
321*4882a593Smuzhiyun "device: %08x\n", vdev->dev_id);
322*4882a593Smuzhiyun return 1;
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun printk(KERN_DEBUG "Not using the %08x VLYNQ device's driver"
325*4882a593Smuzhiyun " for VLYNQ device: %08x\n", ids->id, vdev->dev_id);
326*4882a593Smuzhiyun ids++;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun return 0;
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
vlynq_device_probe(struct device * dev)331*4882a593Smuzhiyun static int vlynq_device_probe(struct device *dev)
332*4882a593Smuzhiyun {
333*4882a593Smuzhiyun struct vlynq_device *vdev = to_vlynq_device(dev);
334*4882a593Smuzhiyun struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
335*4882a593Smuzhiyun struct vlynq_device_id *id = vlynq_get_drvdata(vdev);
336*4882a593Smuzhiyun int result = -ENODEV;
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun if (drv->probe)
339*4882a593Smuzhiyun result = drv->probe(vdev, id);
340*4882a593Smuzhiyun if (result)
341*4882a593Smuzhiyun put_device(dev);
342*4882a593Smuzhiyun return result;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun
vlynq_device_remove(struct device * dev)345*4882a593Smuzhiyun static int vlynq_device_remove(struct device *dev)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun struct vlynq_driver *drv = to_vlynq_driver(dev->driver);
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun if (drv->remove)
350*4882a593Smuzhiyun drv->remove(to_vlynq_device(dev));
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun return 0;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
__vlynq_register_driver(struct vlynq_driver * driver,struct module * owner)355*4882a593Smuzhiyun int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun driver->driver.name = driver->name;
358*4882a593Smuzhiyun driver->driver.bus = &vlynq_bus_type;
359*4882a593Smuzhiyun return driver_register(&driver->driver);
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun EXPORT_SYMBOL(__vlynq_register_driver);
362*4882a593Smuzhiyun
vlynq_unregister_driver(struct vlynq_driver * driver)363*4882a593Smuzhiyun void vlynq_unregister_driver(struct vlynq_driver *driver)
364*4882a593Smuzhiyun {
365*4882a593Smuzhiyun driver_unregister(&driver->driver);
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun EXPORT_SYMBOL(vlynq_unregister_driver);
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun /*
370*4882a593Smuzhiyun * A VLYNQ remote device can clock the VLYNQ bus master
371*4882a593Smuzhiyun * using a dedicated clock line. In that case, both the
372*4882a593Smuzhiyun * remove device and the bus master should have the same
373*4882a593Smuzhiyun * serial clock dividers configured. Iterate through the
374*4882a593Smuzhiyun * 8 possible dividers until we actually link with the
375*4882a593Smuzhiyun * device.
376*4882a593Smuzhiyun */
__vlynq_try_remote(struct vlynq_device * dev)377*4882a593Smuzhiyun static int __vlynq_try_remote(struct vlynq_device *dev)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun int i;
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun vlynq_reset(dev);
382*4882a593Smuzhiyun for (i = dev->dev_id ? vlynq_rdiv2 : vlynq_rdiv8; dev->dev_id ?
383*4882a593Smuzhiyun i <= vlynq_rdiv8 : i >= vlynq_rdiv2;
384*4882a593Smuzhiyun dev->dev_id ? i++ : i--) {
385*4882a593Smuzhiyun
386*4882a593Smuzhiyun if (!vlynq_linked(dev))
387*4882a593Smuzhiyun break;
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun writel((readl(&dev->remote->control) &
390*4882a593Smuzhiyun ~VLYNQ_CTRL_CLOCK_MASK) |
391*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_INT |
392*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1),
393*4882a593Smuzhiyun &dev->remote->control);
394*4882a593Smuzhiyun writel((readl(&dev->local->control)
395*4882a593Smuzhiyun & ~(VLYNQ_CTRL_CLOCK_INT |
396*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_MASK)) |
397*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1),
398*4882a593Smuzhiyun &dev->local->control);
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun if (vlynq_linked(dev)) {
401*4882a593Smuzhiyun printk(KERN_DEBUG
402*4882a593Smuzhiyun "%s: using remote clock divisor %d\n",
403*4882a593Smuzhiyun dev_name(&dev->dev), i - vlynq_rdiv1 + 1);
404*4882a593Smuzhiyun dev->divisor = i;
405*4882a593Smuzhiyun return 0;
406*4882a593Smuzhiyun } else {
407*4882a593Smuzhiyun vlynq_reset(dev);
408*4882a593Smuzhiyun }
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun return -ENODEV;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun /*
415*4882a593Smuzhiyun * A VLYNQ remote device can be clocked by the VLYNQ bus
416*4882a593Smuzhiyun * master using a dedicated clock line. In that case, only
417*4882a593Smuzhiyun * the bus master configures the serial clock divider.
418*4882a593Smuzhiyun * Iterate through the 8 possible dividers until we
419*4882a593Smuzhiyun * actually get a link with the device.
420*4882a593Smuzhiyun */
__vlynq_try_local(struct vlynq_device * dev)421*4882a593Smuzhiyun static int __vlynq_try_local(struct vlynq_device *dev)
422*4882a593Smuzhiyun {
423*4882a593Smuzhiyun int i;
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun vlynq_reset(dev);
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun for (i = dev->dev_id ? vlynq_ldiv2 : vlynq_ldiv8; dev->dev_id ?
428*4882a593Smuzhiyun i <= vlynq_ldiv8 : i >= vlynq_ldiv2;
429*4882a593Smuzhiyun dev->dev_id ? i++ : i--) {
430*4882a593Smuzhiyun
431*4882a593Smuzhiyun writel((readl(&dev->local->control) &
432*4882a593Smuzhiyun ~VLYNQ_CTRL_CLOCK_MASK) |
433*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_INT |
434*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1),
435*4882a593Smuzhiyun &dev->local->control);
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun if (vlynq_linked(dev)) {
438*4882a593Smuzhiyun printk(KERN_DEBUG
439*4882a593Smuzhiyun "%s: using local clock divisor %d\n",
440*4882a593Smuzhiyun dev_name(&dev->dev), i - vlynq_ldiv1 + 1);
441*4882a593Smuzhiyun dev->divisor = i;
442*4882a593Smuzhiyun return 0;
443*4882a593Smuzhiyun } else {
444*4882a593Smuzhiyun vlynq_reset(dev);
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun return -ENODEV;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun /*
452*4882a593Smuzhiyun * When using external clocking method, serial clock
453*4882a593Smuzhiyun * is supplied by an external oscillator, therefore we
454*4882a593Smuzhiyun * should mask the local clock bit in the clock control
455*4882a593Smuzhiyun * register for both the bus master and the remote device.
456*4882a593Smuzhiyun */
__vlynq_try_external(struct vlynq_device * dev)457*4882a593Smuzhiyun static int __vlynq_try_external(struct vlynq_device *dev)
458*4882a593Smuzhiyun {
459*4882a593Smuzhiyun vlynq_reset(dev);
460*4882a593Smuzhiyun if (!vlynq_linked(dev))
461*4882a593Smuzhiyun return -ENODEV;
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun writel((readl(&dev->remote->control) &
464*4882a593Smuzhiyun ~VLYNQ_CTRL_CLOCK_INT),
465*4882a593Smuzhiyun &dev->remote->control);
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun writel((readl(&dev->local->control) &
468*4882a593Smuzhiyun ~VLYNQ_CTRL_CLOCK_INT),
469*4882a593Smuzhiyun &dev->local->control);
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun if (vlynq_linked(dev)) {
472*4882a593Smuzhiyun printk(KERN_DEBUG "%s: using external clock\n",
473*4882a593Smuzhiyun dev_name(&dev->dev));
474*4882a593Smuzhiyun dev->divisor = vlynq_div_external;
475*4882a593Smuzhiyun return 0;
476*4882a593Smuzhiyun }
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun return -ENODEV;
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun
__vlynq_enable_device(struct vlynq_device * dev)481*4882a593Smuzhiyun static int __vlynq_enable_device(struct vlynq_device *dev)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun int result;
484*4882a593Smuzhiyun struct plat_vlynq_ops *ops = dev->dev.platform_data;
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun result = ops->on(dev);
487*4882a593Smuzhiyun if (result)
488*4882a593Smuzhiyun return result;
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun switch (dev->divisor) {
491*4882a593Smuzhiyun case vlynq_div_external:
492*4882a593Smuzhiyun case vlynq_div_auto:
493*4882a593Smuzhiyun /* When the device is brought from reset it should have clock
494*4882a593Smuzhiyun * generation negotiated by hardware.
495*4882a593Smuzhiyun * Check which device is generating clocks and perform setup
496*4882a593Smuzhiyun * accordingly */
497*4882a593Smuzhiyun if (vlynq_linked(dev) && readl(&dev->remote->control) &
498*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_INT) {
499*4882a593Smuzhiyun if (!__vlynq_try_remote(dev) ||
500*4882a593Smuzhiyun !__vlynq_try_local(dev) ||
501*4882a593Smuzhiyun !__vlynq_try_external(dev))
502*4882a593Smuzhiyun return 0;
503*4882a593Smuzhiyun } else {
504*4882a593Smuzhiyun if (!__vlynq_try_external(dev) ||
505*4882a593Smuzhiyun !__vlynq_try_local(dev) ||
506*4882a593Smuzhiyun !__vlynq_try_remote(dev))
507*4882a593Smuzhiyun return 0;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun break;
510*4882a593Smuzhiyun case vlynq_ldiv1:
511*4882a593Smuzhiyun case vlynq_ldiv2:
512*4882a593Smuzhiyun case vlynq_ldiv3:
513*4882a593Smuzhiyun case vlynq_ldiv4:
514*4882a593Smuzhiyun case vlynq_ldiv5:
515*4882a593Smuzhiyun case vlynq_ldiv6:
516*4882a593Smuzhiyun case vlynq_ldiv7:
517*4882a593Smuzhiyun case vlynq_ldiv8:
518*4882a593Smuzhiyun writel(VLYNQ_CTRL_CLOCK_INT |
519*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
520*4882a593Smuzhiyun vlynq_ldiv1), &dev->local->control);
521*4882a593Smuzhiyun writel(0, &dev->remote->control);
522*4882a593Smuzhiyun if (vlynq_linked(dev)) {
523*4882a593Smuzhiyun printk(KERN_DEBUG
524*4882a593Smuzhiyun "%s: using local clock divisor %d\n",
525*4882a593Smuzhiyun dev_name(&dev->dev),
526*4882a593Smuzhiyun dev->divisor - vlynq_ldiv1 + 1);
527*4882a593Smuzhiyun return 0;
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun break;
530*4882a593Smuzhiyun case vlynq_rdiv1:
531*4882a593Smuzhiyun case vlynq_rdiv2:
532*4882a593Smuzhiyun case vlynq_rdiv3:
533*4882a593Smuzhiyun case vlynq_rdiv4:
534*4882a593Smuzhiyun case vlynq_rdiv5:
535*4882a593Smuzhiyun case vlynq_rdiv6:
536*4882a593Smuzhiyun case vlynq_rdiv7:
537*4882a593Smuzhiyun case vlynq_rdiv8:
538*4882a593Smuzhiyun writel(0, &dev->local->control);
539*4882a593Smuzhiyun writel(VLYNQ_CTRL_CLOCK_INT |
540*4882a593Smuzhiyun VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
541*4882a593Smuzhiyun vlynq_rdiv1), &dev->remote->control);
542*4882a593Smuzhiyun if (vlynq_linked(dev)) {
543*4882a593Smuzhiyun printk(KERN_DEBUG
544*4882a593Smuzhiyun "%s: using remote clock divisor %d\n",
545*4882a593Smuzhiyun dev_name(&dev->dev),
546*4882a593Smuzhiyun dev->divisor - vlynq_rdiv1 + 1);
547*4882a593Smuzhiyun return 0;
548*4882a593Smuzhiyun }
549*4882a593Smuzhiyun break;
550*4882a593Smuzhiyun }
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun ops->off(dev);
553*4882a593Smuzhiyun return -ENODEV;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun
vlynq_enable_device(struct vlynq_device * dev)556*4882a593Smuzhiyun int vlynq_enable_device(struct vlynq_device *dev)
557*4882a593Smuzhiyun {
558*4882a593Smuzhiyun struct plat_vlynq_ops *ops = dev->dev.platform_data;
559*4882a593Smuzhiyun int result = -ENODEV;
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun result = __vlynq_enable_device(dev);
562*4882a593Smuzhiyun if (result)
563*4882a593Smuzhiyun return result;
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun result = vlynq_setup_irq(dev);
566*4882a593Smuzhiyun if (result)
567*4882a593Smuzhiyun ops->off(dev);
568*4882a593Smuzhiyun
569*4882a593Smuzhiyun dev->enabled = !result;
570*4882a593Smuzhiyun return result;
571*4882a593Smuzhiyun }
572*4882a593Smuzhiyun EXPORT_SYMBOL(vlynq_enable_device);
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun
vlynq_disable_device(struct vlynq_device * dev)575*4882a593Smuzhiyun void vlynq_disable_device(struct vlynq_device *dev)
576*4882a593Smuzhiyun {
577*4882a593Smuzhiyun struct plat_vlynq_ops *ops = dev->dev.platform_data;
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun dev->enabled = 0;
580*4882a593Smuzhiyun free_irq(dev->irq, dev);
581*4882a593Smuzhiyun ops->off(dev);
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun EXPORT_SYMBOL(vlynq_disable_device);
584*4882a593Smuzhiyun
vlynq_set_local_mapping(struct vlynq_device * dev,u32 tx_offset,struct vlynq_mapping * mapping)585*4882a593Smuzhiyun int vlynq_set_local_mapping(struct vlynq_device *dev, u32 tx_offset,
586*4882a593Smuzhiyun struct vlynq_mapping *mapping)
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun int i;
589*4882a593Smuzhiyun
590*4882a593Smuzhiyun if (!dev->enabled)
591*4882a593Smuzhiyun return -ENXIO;
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun writel(tx_offset, &dev->local->tx_offset);
594*4882a593Smuzhiyun for (i = 0; i < 4; i++) {
595*4882a593Smuzhiyun writel(mapping[i].offset, &dev->local->rx_mapping[i].offset);
596*4882a593Smuzhiyun writel(mapping[i].size, &dev->local->rx_mapping[i].size);
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun return 0;
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun EXPORT_SYMBOL(vlynq_set_local_mapping);
601*4882a593Smuzhiyun
vlynq_set_remote_mapping(struct vlynq_device * dev,u32 tx_offset,struct vlynq_mapping * mapping)602*4882a593Smuzhiyun int vlynq_set_remote_mapping(struct vlynq_device *dev, u32 tx_offset,
603*4882a593Smuzhiyun struct vlynq_mapping *mapping)
604*4882a593Smuzhiyun {
605*4882a593Smuzhiyun int i;
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun if (!dev->enabled)
608*4882a593Smuzhiyun return -ENXIO;
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun writel(tx_offset, &dev->remote->tx_offset);
611*4882a593Smuzhiyun for (i = 0; i < 4; i++) {
612*4882a593Smuzhiyun writel(mapping[i].offset, &dev->remote->rx_mapping[i].offset);
613*4882a593Smuzhiyun writel(mapping[i].size, &dev->remote->rx_mapping[i].size);
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun return 0;
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun EXPORT_SYMBOL(vlynq_set_remote_mapping);
618*4882a593Smuzhiyun
vlynq_set_local_irq(struct vlynq_device * dev,int virq)619*4882a593Smuzhiyun int vlynq_set_local_irq(struct vlynq_device *dev, int virq)
620*4882a593Smuzhiyun {
621*4882a593Smuzhiyun int irq = dev->irq_start + virq;
622*4882a593Smuzhiyun if (dev->enabled)
623*4882a593Smuzhiyun return -EBUSY;
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun if ((irq < dev->irq_start) || (irq > dev->irq_end))
626*4882a593Smuzhiyun return -EINVAL;
627*4882a593Smuzhiyun
628*4882a593Smuzhiyun if (virq == dev->remote_irq)
629*4882a593Smuzhiyun return -EINVAL;
630*4882a593Smuzhiyun
631*4882a593Smuzhiyun dev->local_irq = virq;
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun return 0;
634*4882a593Smuzhiyun }
635*4882a593Smuzhiyun EXPORT_SYMBOL(vlynq_set_local_irq);
636*4882a593Smuzhiyun
vlynq_set_remote_irq(struct vlynq_device * dev,int virq)637*4882a593Smuzhiyun int vlynq_set_remote_irq(struct vlynq_device *dev, int virq)
638*4882a593Smuzhiyun {
639*4882a593Smuzhiyun int irq = dev->irq_start + virq;
640*4882a593Smuzhiyun if (dev->enabled)
641*4882a593Smuzhiyun return -EBUSY;
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun if ((irq < dev->irq_start) || (irq > dev->irq_end))
644*4882a593Smuzhiyun return -EINVAL;
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun if (virq == dev->local_irq)
647*4882a593Smuzhiyun return -EINVAL;
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun dev->remote_irq = virq;
650*4882a593Smuzhiyun
651*4882a593Smuzhiyun return 0;
652*4882a593Smuzhiyun }
653*4882a593Smuzhiyun EXPORT_SYMBOL(vlynq_set_remote_irq);
654*4882a593Smuzhiyun
vlynq_probe(struct platform_device * pdev)655*4882a593Smuzhiyun static int vlynq_probe(struct platform_device *pdev)
656*4882a593Smuzhiyun {
657*4882a593Smuzhiyun struct vlynq_device *dev;
658*4882a593Smuzhiyun struct resource *regs_res, *mem_res, *irq_res;
659*4882a593Smuzhiyun int len, result;
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun regs_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
662*4882a593Smuzhiyun if (!regs_res)
663*4882a593Smuzhiyun return -ENODEV;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun mem_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
666*4882a593Smuzhiyun if (!mem_res)
667*4882a593Smuzhiyun return -ENODEV;
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "devirq");
670*4882a593Smuzhiyun if (!irq_res)
671*4882a593Smuzhiyun return -ENODEV;
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun dev = kzalloc(sizeof(*dev), GFP_KERNEL);
674*4882a593Smuzhiyun if (!dev) {
675*4882a593Smuzhiyun printk(KERN_ERR
676*4882a593Smuzhiyun "vlynq: failed to allocate device structure\n");
677*4882a593Smuzhiyun return -ENOMEM;
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun dev->id = pdev->id;
681*4882a593Smuzhiyun dev->dev.bus = &vlynq_bus_type;
682*4882a593Smuzhiyun dev->dev.parent = &pdev->dev;
683*4882a593Smuzhiyun dev_set_name(&dev->dev, "vlynq%d", dev->id);
684*4882a593Smuzhiyun dev->dev.platform_data = pdev->dev.platform_data;
685*4882a593Smuzhiyun dev->dev.release = vlynq_device_release;
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun dev->regs_start = regs_res->start;
688*4882a593Smuzhiyun dev->regs_end = regs_res->end;
689*4882a593Smuzhiyun dev->mem_start = mem_res->start;
690*4882a593Smuzhiyun dev->mem_end = mem_res->end;
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun len = resource_size(regs_res);
693*4882a593Smuzhiyun if (!request_mem_region(regs_res->start, len, dev_name(&dev->dev))) {
694*4882a593Smuzhiyun printk(KERN_ERR "%s: Can't request vlynq registers\n",
695*4882a593Smuzhiyun dev_name(&dev->dev));
696*4882a593Smuzhiyun result = -ENXIO;
697*4882a593Smuzhiyun goto fail_request;
698*4882a593Smuzhiyun }
699*4882a593Smuzhiyun
700*4882a593Smuzhiyun dev->local = ioremap(regs_res->start, len);
701*4882a593Smuzhiyun if (!dev->local) {
702*4882a593Smuzhiyun printk(KERN_ERR "%s: Can't remap vlynq registers\n",
703*4882a593Smuzhiyun dev_name(&dev->dev));
704*4882a593Smuzhiyun result = -ENXIO;
705*4882a593Smuzhiyun goto fail_remap;
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun dev->remote = (struct vlynq_regs *)((void *)dev->local +
709*4882a593Smuzhiyun VLYNQ_REMOTE_OFFSET);
710*4882a593Smuzhiyun
711*4882a593Smuzhiyun dev->irq = platform_get_irq_byname(pdev, "irq");
712*4882a593Smuzhiyun dev->irq_start = irq_res->start;
713*4882a593Smuzhiyun dev->irq_end = irq_res->end;
714*4882a593Smuzhiyun dev->local_irq = dev->irq_end - dev->irq_start;
715*4882a593Smuzhiyun dev->remote_irq = dev->local_irq - 1;
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun if (device_register(&dev->dev))
718*4882a593Smuzhiyun goto fail_register;
719*4882a593Smuzhiyun platform_set_drvdata(pdev, dev);
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n",
722*4882a593Smuzhiyun dev_name(&dev->dev), (void *)dev->regs_start, dev->irq,
723*4882a593Smuzhiyun (void *)dev->mem_start);
724*4882a593Smuzhiyun
725*4882a593Smuzhiyun dev->dev_id = 0;
726*4882a593Smuzhiyun dev->divisor = vlynq_div_auto;
727*4882a593Smuzhiyun result = __vlynq_enable_device(dev);
728*4882a593Smuzhiyun if (result == 0) {
729*4882a593Smuzhiyun dev->dev_id = readl(&dev->remote->chip);
730*4882a593Smuzhiyun ((struct plat_vlynq_ops *)(dev->dev.platform_data))->off(dev);
731*4882a593Smuzhiyun }
732*4882a593Smuzhiyun if (dev->dev_id)
733*4882a593Smuzhiyun printk(KERN_INFO "Found a VLYNQ device: %08x\n", dev->dev_id);
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun return 0;
736*4882a593Smuzhiyun
737*4882a593Smuzhiyun fail_register:
738*4882a593Smuzhiyun iounmap(dev->local);
739*4882a593Smuzhiyun fail_remap:
740*4882a593Smuzhiyun fail_request:
741*4882a593Smuzhiyun release_mem_region(regs_res->start, len);
742*4882a593Smuzhiyun kfree(dev);
743*4882a593Smuzhiyun return result;
744*4882a593Smuzhiyun }
745*4882a593Smuzhiyun
vlynq_remove(struct platform_device * pdev)746*4882a593Smuzhiyun static int vlynq_remove(struct platform_device *pdev)
747*4882a593Smuzhiyun {
748*4882a593Smuzhiyun struct vlynq_device *dev = platform_get_drvdata(pdev);
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun device_unregister(&dev->dev);
751*4882a593Smuzhiyun iounmap(dev->local);
752*4882a593Smuzhiyun release_mem_region(dev->regs_start,
753*4882a593Smuzhiyun dev->regs_end - dev->regs_start + 1);
754*4882a593Smuzhiyun
755*4882a593Smuzhiyun kfree(dev);
756*4882a593Smuzhiyun
757*4882a593Smuzhiyun return 0;
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun static struct platform_driver vlynq_platform_driver = {
761*4882a593Smuzhiyun .driver.name = "vlynq",
762*4882a593Smuzhiyun .probe = vlynq_probe,
763*4882a593Smuzhiyun .remove = vlynq_remove,
764*4882a593Smuzhiyun };
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun struct bus_type vlynq_bus_type = {
767*4882a593Smuzhiyun .name = "vlynq",
768*4882a593Smuzhiyun .match = vlynq_device_match,
769*4882a593Smuzhiyun .probe = vlynq_device_probe,
770*4882a593Smuzhiyun .remove = vlynq_device_remove,
771*4882a593Smuzhiyun };
772*4882a593Smuzhiyun EXPORT_SYMBOL(vlynq_bus_type);
773*4882a593Smuzhiyun
vlynq_init(void)774*4882a593Smuzhiyun static int vlynq_init(void)
775*4882a593Smuzhiyun {
776*4882a593Smuzhiyun int res = 0;
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun res = bus_register(&vlynq_bus_type);
779*4882a593Smuzhiyun if (res)
780*4882a593Smuzhiyun goto fail_bus;
781*4882a593Smuzhiyun
782*4882a593Smuzhiyun res = platform_driver_register(&vlynq_platform_driver);
783*4882a593Smuzhiyun if (res)
784*4882a593Smuzhiyun goto fail_platform;
785*4882a593Smuzhiyun
786*4882a593Smuzhiyun return 0;
787*4882a593Smuzhiyun
788*4882a593Smuzhiyun fail_platform:
789*4882a593Smuzhiyun bus_unregister(&vlynq_bus_type);
790*4882a593Smuzhiyun fail_bus:
791*4882a593Smuzhiyun return res;
792*4882a593Smuzhiyun }
793*4882a593Smuzhiyun
vlynq_exit(void)794*4882a593Smuzhiyun static void vlynq_exit(void)
795*4882a593Smuzhiyun {
796*4882a593Smuzhiyun platform_driver_unregister(&vlynq_platform_driver);
797*4882a593Smuzhiyun bus_unregister(&vlynq_bus_type);
798*4882a593Smuzhiyun }
799*4882a593Smuzhiyun
800*4882a593Smuzhiyun module_init(vlynq_init);
801*4882a593Smuzhiyun module_exit(vlynq_exit);
802