vlynq cleanups:
authorEugene Konev <ejka@openwrt.org>
Wed, 12 Sep 2007 12:23:56 +0000 (12:23 +0000)
committerEugene Konev <ejka@openwrt.org>
Wed, 12 Sep 2007 12:23:56 +0000 (12:23 +0000)
 * drivers can now set a clock divisor
 * irq handling cleanup, drivers now can handle error irqs themselves
 * style cleanup

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@8759 3c298f89-4303-0410-b956-a3cf2f4a3e73

target/linux/ar7/files/arch/mips/ar7/vlynq-pci.c
target/linux/ar7/files/arch/mips/ar7/vlynq.c
target/linux/ar7/files/include/asm-mips/ar7/vlynq.h

index 9f3b63d..ffcfa62 100644 (file)
@@ -143,7 +143,8 @@ static inline u32 vlynq_read(u32 val, int size) {
        return val;
 }
 
-static int vlynq_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val)
+static int vlynq_config_read(struct pci_bus *bus, unsigned int devfn,
+                            int where, int size, u32 *val)
 {
        struct vlynq_device *dev;
        struct vlynq_pci_private *priv;
@@ -194,7 +195,7 @@ static int vlynq_config_read(struct pci_bus *bus, unsigned int devfn, int where,
        case PCI_BASE_ADDRESS_3:
                resno = (where - PCI_BASE_ADDRESS_0) >> 2;
                if (priv->sz_mask & (1 << resno)) {
-                       priv->sz_mask &= ~(1 << resno);
+                       priv->sz_mask &= ~(1 << resno);
                        *val = priv->config->rx_mapping[resno].size;
                } else {
                        *val = vlynq_get_mapped(dev, resno);
@@ -214,14 +215,15 @@ static int vlynq_config_read(struct pci_bus *bus, unsigned int devfn, int where,
                *val = 1;
                break;
        default:
-               printk("%s: Read of unknown register 0x%x (size %d)\n", 
-                      dev->dev.bus_id, where, size);
+               printk(KERN_NOTICE "%s: Read of unknown register 0x%x "
+                      "(size %d)\n", dev->dev.bus_id, where, size);
                return PCIBIOS_BAD_REGISTER_NUMBER;
        }
        return PCIBIOS_SUCCESSFUL;
 }
 
-static int vlynq_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
+static int vlynq_config_write(struct pci_bus *bus, unsigned int devfn,
+                             int where, int size, u32 val)
 {
        struct vlynq_device *dev;
        struct vlynq_pci_private *priv;
@@ -275,8 +277,9 @@ static int vlynq_config_write(struct pci_bus *bus, unsigned int devfn, int where
        case PCI_ROM_ADDRESS:
                break;
        default:
-               printk("%s: Write to unknown register 0x%x (size %d) value=0x%x\n", 
-                      dev->dev.bus_id, where, size, val);
+               printk(KERN_NOTICE "%s: Write to unknown register 0x%x "
+                      "(size %d) value=0x%x\n", dev->dev.bus_id, where, size,
+                      val);
                return PCIBIOS_BAD_REGISTER_NUMBER;
        }
        return PCIBIOS_SUCCESSFUL;
@@ -309,6 +312,7 @@ static int vlynq_pci_probe(struct vlynq_device *dev)
        if (result)
                return result;
 
+       dev->divisor = vlynq_ldiv4;
        result = vlynq_device_enable(dev);
        if (result)
                return result;
@@ -319,17 +323,17 @@ static int vlynq_pci_probe(struct vlynq_device *dev)
                        config = &known_devices[i];
 
        if (!config) {
-               printk("vlynq-pci: skipping unknown device "
-                      "%04x:%04x at %s\n", chip_id >> 16, 
+               printk(KERN_DEBUG "vlynq-pci: skipping unknown device "
+                      "%04x:%04x at %s\n", chip_id >> 16,
                       chip_id & 0xffff, dev->dev.bus_id);
                result = -ENODEV;
                goto fail;
        }
 
-       printk("vlynq-pci: attaching device %s at %s\n",
+       printk(KERN_NOTICE "vlynq-pci: attaching device %s at %s\n",
               config->name, dev->dev.bus_id);
 
-       priv = kmalloc(sizeof(struct vlynq_pci_private), GFP_KERNEL);
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
                printk(KERN_ERR "%s: failed to allocate private data\n",
                       dev->dev.bus_id);
@@ -337,7 +341,6 @@ static int vlynq_pci_probe(struct vlynq_device *dev)
                goto fail;
        }
 
-       memset(priv, 0, sizeof(struct vlynq_pci_private));
        priv->latency = 64;
        priv->cache_line = 32;
        priv->config = config;
@@ -402,7 +405,7 @@ int vlynq_pci_init(void)
 {
        int res;
        res = vlynq_register_driver(&vlynq_pci);
-       if (res) 
+       if (res)
                return res;
 
        register_pci_controller(&vlynq_controller);
index 88e9746..76dbd55 100644 (file)
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
-#include <linux/delay.h>
 #include <linux/device.h>
-#include <linux/ioport.h>
 #include <linux/errno.h>
 #include <linux/platform_device.h>
-#include <linux/irq.h>
 #include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/io.h>
 
-#include <asm/addrspace.h>
-#include <asm/ar7/ar7.h>
 #include <asm/ar7/vlynq.h>
 
-#define PER_DEVICE_IRQS 32
+#define PER_DEVICE_IRQS                        32
 
-#define VLYNQ_CTRL_PM_ENABLE       0x80000000
-#define VLYNQ_CTRL_CLOCK_INT       0x00008000
-#define VLYNQ_CTRL_CLOCK_DIV(x)    (((x) & 7) << 16)
-#define VLYNQ_CTRL_INT_LOCAL       0x00004000
-#define VLYNQ_CTRL_INT_ENABLE      0x00002000
-#define VLYNQ_CTRL_INT_VECTOR(x)   (((x) & 0x1f) << 8)
-#define VLYNQ_CTRL_INT2CFG         0x00000080
-#define VLYNQ_CTRL_RESET           0x00000001
+#define VLYNQ_CTRL_PM_ENABLE           0x80000000
+#define VLYNQ_CTRL_CLOCK_INT           0x00008000
+#define VLYNQ_CTRL_CLOCK_DIV(x)                (((x) & 7) << 16)
+#define VLYNQ_CTRL_INT_LOCAL           0x00004000
+#define VLYNQ_CTRL_INT_ENABLE          0x00002000
+#define VLYNQ_CTRL_INT_VECTOR(x)       (((x) & 0x1f) << 8)
+#define VLYNQ_CTRL_INT2CFG             0x00000080
+#define VLYNQ_CTRL_RESET               0x00000001
 
-#define VLYNQ_STATUS_LINK          0x00000001
-#define VLYNQ_STATUS_LERROR        0x00000080
-#define VLYNQ_STATUS_RERROR        0x00000100
+#define VLYNQ_INT_OFFSET               0x00000014
+#define VLYNQ_REMOTE_OFFSET            0x00000080
 
-#define VINT_ENABLE    0x00000100
-#define VINT_TYPE_EDGE 0x00000080
-#define VINT_LEVEL_LOW 0x00000040
-#define VINT_VECTOR(x) ((x) & 0x1f)
-#define VINT_OFFSET(irq) (8 * ((irq) % 4))
+#define VLYNQ_STATUS_LINK              0x00000001
+#define VLYNQ_STATUS_LERROR            0x00000080
+#define VLYNQ_STATUS_RERROR            0x00000100
 
-#define VLYNQ_AUTONEGO_V2          0x00010000
+#define VINT_ENABLE                    0x00000100
+#define VINT_TYPE_EDGE                 0x00000080
+#define VINT_LEVEL_LOW                 0x00000040
+#define VINT_VECTOR(x)                 ((x) & 0x1f)
+#define VINT_OFFSET(irq)               (8 * ((irq) % 4))
+
+#define VLYNQ_AUTONEGO_V2              0x00010000
 
 struct vlynq_regs {
        u32 revision;
@@ -105,11 +103,11 @@ int vlynq_linked(struct vlynq_device *dev)
 {
        int i;
 
-       for (i = 0; i < 10; i++)
+       for (i = 0; i < 100; i++)
                if (vlynq_reg_read(dev->local->status) & VLYNQ_STATUS_LINK)
                        return 1;
                else
-                       mdelay(1);
+                       cpu_relax();
 
        return 0;
 }
@@ -143,7 +141,7 @@ static void vlynq_irq_mask(unsigned int irq)
 static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
 {
        u32 val;
-       struct vlynq_device *dev = irq_desc[irq].chip_data;
+       struct vlynq_device *dev = get_irq_chip_data(irq);
        int virq;
 
        BUG_ON(!dev);
@@ -171,28 +169,41 @@ static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
        return 0;
 }
 
+static void vlynq_local_ack(unsigned int irq)
+{
+       struct vlynq_device *dev = get_irq_chip_data(irq);
+       u32 status = vlynq_reg_read(dev->local->status);
+       if (printk_ratelimit())
+               printk(KERN_DEBUG "%s: local status: 0x%08x\n",
+                      dev->dev.bus_id, status);
+       vlynq_reg_write(dev->local->status, status);
+}
+
+static void vlynq_remote_ack(unsigned int irq)
+{
+       struct vlynq_device *dev = get_irq_chip_data(irq);
+       u32 status = vlynq_reg_read(dev->remote->status);
+       if (printk_ratelimit())
+               printk(KERN_DEBUG "%s: remote status: 0x%08x\n",
+                      dev->dev.bus_id, status);
+       vlynq_reg_write(dev->remote->status, status);
+}
+
+#warning FIXME: process one irq per call
 static irqreturn_t vlynq_irq(int irq, void *dev_id)
 {
        struct vlynq_device *dev = dev_id;
-       u32 status, ack;
+       u32 status;
        int virq = 0;
 
        status = vlynq_reg_read(dev->local->int_status);
        vlynq_reg_write(dev->local->int_status, status);
 
-       if (status & (1 << dev->local_irq)) { /* Local vlynq IRQ. Ack */
-               ack = vlynq_reg_read(dev->local->status);
-               vlynq_reg_write(dev->local->status, ack);
-       }
-
-       if (status & (1 << dev->remote_irq)) { /* Remote vlynq IRQ. Ack */
-               ack = vlynq_reg_read(dev->remote->status);
-               vlynq_reg_write(dev->remote->status, ack);
-       }
+       if (unlikely(!status))
+               spurious_interrupt();
 
-       status &= ~((1 << dev->local_irq) | (1 << dev->remote_irq));
        while (status) {
-               if (status & 1) /* Remote device IRQ. Pass. */
+               if (status & 1)
                        do_IRQ(dev->irq_start + virq);
                status >>= 1;
                virq++;
@@ -208,40 +219,70 @@ static struct irq_chip vlynq_irq_chip = {
        .set_type = vlynq_irq_type,
 };
 
+static struct irq_chip vlynq_local_chip = {
+       .name = "vlynq local error",
+       .unmask = vlynq_irq_unmask,
+       .mask = vlynq_irq_mask,
+       .ack = vlynq_local_ack,
+};
+
+static struct irq_chip vlynq_remote_chip = {
+       .name = "vlynq local error",
+       .unmask = vlynq_irq_unmask,
+       .mask = vlynq_irq_mask,
+       .ack = vlynq_remote_ack,
+};
+
 static int vlynq_setup_irq(struct vlynq_device *dev)
 {
        u32 val;
        int i;
 
        if (dev->local_irq == dev->remote_irq) {
-               printk(KERN_WARNING
-                       "%s: local vlynq irq should be different from remote\n",
-                       dev->dev.bus_id);
+               printk(KERN_ERR
+                      "%s: local vlynq irq should be different from remote\n",
+                      dev->dev.bus_id);
                return -EINVAL;
        }
 
+       /* Clear local and remote error bits */
+       vlynq_reg_write(dev->local->status, vlynq_reg_read(dev->local->status));
+       vlynq_reg_write(dev->remote->status,
+                       vlynq_reg_read(dev->remote->status));
+
+       /* Now setup interrupts */
        val = VLYNQ_CTRL_INT_VECTOR(dev->local_irq);
        val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL |
                VLYNQ_CTRL_INT2CFG;
        val |= vlynq_reg_read(dev->local->control);
-       vlynq_reg_write(dev->local->int_ptr, 0x14);
+       vlynq_reg_write(dev->local->int_ptr, VLYNQ_INT_OFFSET);
        vlynq_reg_write(dev->local->control, val);
 
        val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq);
        val |= VLYNQ_CTRL_INT_ENABLE;
        val |= vlynq_reg_read(dev->remote->control);
-       vlynq_reg_write(dev->remote->int_ptr, 0x14);
+       vlynq_reg_write(dev->remote->int_ptr, VLYNQ_INT_OFFSET);
        vlynq_reg_write(dev->remote->control, val);
 
        for (i = 0; i < PER_DEVICE_IRQS; i++) {
-               if ((i == dev->local_irq) || (i == dev->remote_irq))
-                       continue;
-               set_irq_chip(dev->irq_start + i, &vlynq_irq_chip);
-               set_irq_chip_data(dev->irq_start + i, dev);
-               vlynq_reg_write(dev->remote->int_device[i >> 2], 0);
+               if (i == dev->local_irq) {
+                       set_irq_chip_and_handler(dev->irq_start + i,
+                                                &vlynq_local_chip,
+                                                handle_level_irq);
+                       set_irq_chip_data(dev->irq_start + i, dev);
+               } else if (i == dev->remote_irq) {
+                       set_irq_chip_and_handler(dev->irq_start + i,
+                                                &vlynq_local_chip,
+                                                handle_level_irq);
+                       set_irq_chip_data(dev->irq_start + i, dev);
+               } else {
+                       set_irq_chip(dev->irq_start + i, &vlynq_irq_chip);
+                       set_irq_chip_data(dev->irq_start + i, dev);
+                       vlynq_reg_write(dev->remote->int_device[i >> 2], 0);
+               }
        }
 
-       if (request_irq(dev->irq, vlynq_irq, SA_SHIRQ, "vlynq", dev)) {
+       if (request_irq(dev->irq, vlynq_irq, IRQF_SHARED, "vlynq", dev)) {
                printk(KERN_ERR "%s: request_irq failed\n", dev->dev.bus_id);
                return -EAGAIN;
        }
@@ -280,7 +321,6 @@ int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner)
 {
        driver->driver.name = driver->name;
        driver->driver.bus = &vlynq_bus_type;
-/*     driver->driver.owner = owner;*/
        return driver_register(&driver->driver);
 }
 EXPORT_SYMBOL(__vlynq_register_driver);
@@ -293,35 +333,86 @@ EXPORT_SYMBOL(vlynq_unregister_driver);
 
 int vlynq_device_enable(struct vlynq_device *dev)
 {
-       u32 div;
-       int result;
+       int i, result;
        struct plat_vlynq_ops *ops = dev->dev.platform_data;
 
        result = ops->on(dev);
        if (result)
                return result;
 
-       vlynq_reg_write(dev->local->control, 0);
-       vlynq_reg_write(dev->remote->control, 0);
-
-/*
-       if (vlynq_linked(dev)) {
-               printk(KERN_INFO "%s: linked (using external clock)\n",
-                       dev->dev.bus_id);
-               return vlynq_setup_irq(dev);
-       }
-*/
-
-       for (div = 1; div <= 8; div++) {
-               mdelay(20); 
-               vlynq_reg_write(dev->local->control, VLYNQ_CTRL_CLOCK_INT | 
-                       VLYNQ_CTRL_CLOCK_DIV(div - 1));
+       switch (dev->divisor) {
+       case vlynq_div_auto:
+               /* First try locally supplied clock */
+               vlynq_reg_write(dev->remote->control, 0);
+               for (i = vlynq_ldiv1; i <= vlynq_ldiv8; i++) {
+                       vlynq_reg_write(dev->local->control,
+                                       VLYNQ_CTRL_CLOCK_INT |
+                                       VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1));
+                       if (vlynq_linked(dev)) {
+                               printk(KERN_DEBUG
+                                      "%s: using local clock divisor %d\n",
+                                      dev->dev.bus_id, i - vlynq_ldiv1 + 1);
+                               return vlynq_setup_irq(dev);
+                       }
+               }
+               /* Then remotely supplied clock */
+               vlynq_reg_write(dev->local->control, 0);
+               for (i = vlynq_rdiv1; i <= vlynq_rdiv8; i++) {
+                       vlynq_reg_write(dev->remote->control,
+                                       VLYNQ_CTRL_CLOCK_INT |
+                                       VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1));
+                       if (vlynq_linked(dev)) {
+                               printk(KERN_DEBUG
+                                      "%s: using remote clock divisor %d\n",
+                                      dev->dev.bus_id, i - vlynq_rdiv1 + 1);
+                               return vlynq_setup_irq(dev);
+                       }
+               }
+               /* At last, externally supplied clock */
+               vlynq_reg_write(dev->remote->control, 0);
+               if (vlynq_linked(dev)) {
+                       printk(KERN_DEBUG "%s: using external clock\n",
+                              dev->dev.bus_id);
+                       return vlynq_setup_irq(dev);
+               }
+               break;
+       case vlynq_ldiv1: case vlynq_ldiv2: case vlynq_ldiv3: case vlynq_ldiv4:
+       case vlynq_ldiv5: case vlynq_ldiv6: case vlynq_ldiv7: case vlynq_ldiv8:
                vlynq_reg_write(dev->remote->control, 0);
+               vlynq_reg_write(dev->local->control,
+                               VLYNQ_CTRL_CLOCK_INT |
+                               VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
+                                                    vlynq_ldiv1));
                if (vlynq_linked(dev)) {
-                       printk(KERN_INFO "%s: linked (using internal clock, div: %d)\n",
-                               dev->dev.bus_id, div);
+                       printk(KERN_DEBUG
+                              "%s: using local clock divisor %d\n",
+                              dev->dev.bus_id, dev->divisor - vlynq_ldiv1 + 1);
                        return vlynq_setup_irq(dev);
                }
+               break;
+       case vlynq_rdiv1: case vlynq_rdiv2: case vlynq_rdiv3: case vlynq_rdiv4:
+       case vlynq_rdiv5: case vlynq_rdiv6: case vlynq_rdiv7: case vlynq_rdiv8:
+               vlynq_reg_write(dev->local->control, 0);
+               vlynq_reg_write(dev->remote->control,
+                               VLYNQ_CTRL_CLOCK_INT |
+                               VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
+                                                    vlynq_rdiv1));
+               if (vlynq_linked(dev)) {
+                       printk(KERN_DEBUG
+                              "%s: using remote clock divisor %d\n",
+                              dev->dev.bus_id, dev->divisor - vlynq_rdiv1 + 1);
+                       return vlynq_setup_irq(dev);
+               }
+               break;
+       case vlynq_div_external:
+               vlynq_reg_write(dev->local->control, 0);
+               vlynq_reg_write(dev->remote->control, 0);
+               if (vlynq_linked(dev)) {
+                       printk(KERN_DEBUG "%s: using external clock\n",
+                              dev->dev.bus_id);
+                       return vlynq_setup_irq(dev);
+               }
+               break;
        }
 
        return -ENODEV;
@@ -369,9 +460,6 @@ int vlynq_virq_to_irq(struct vlynq_device *dev, int virq)
        if ((virq < 0) || (virq >= PER_DEVICE_IRQS))
                return -EINVAL;
 
-       if ((virq == dev->local_irq) || (virq == dev->remote_irq))
-               return -EINVAL;
-
        return dev->irq_start + virq;
 }
 
@@ -430,9 +518,10 @@ static int vlynq_probe(struct platform_device *pdev)
        if (!irq_res)
                return -ENODEV;
 
-       dev = kzalloc(sizeof(struct vlynq_device), GFP_KERNEL);
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev) {
-               printk(KERN_ERR "vlynq: failed to allocate device structure\n");
+               printk(KERN_ERR
+                      "vlynq: failed to allocate device structure\n");
                return -ENOMEM;
        }
 
@@ -452,20 +541,21 @@ static int vlynq_probe(struct platform_device *pdev)
        len = regs_res->end - regs_res->start;
        if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) {
                printk(KERN_ERR "%s: Can't request vlynq registers\n",
-                                                       dev->dev.bus_id);
+                      dev->dev.bus_id);
                result = -ENXIO;
                goto fail_request;
        }
 
-       dev->local = ioremap_nocache(regs_res->start, len);
+       dev->local = ioremap(regs_res->start, len);
        if (!dev->local) {
                printk(KERN_ERR "%s: Can't remap vlynq registers\n",
-                                                       dev->dev.bus_id);
+                      dev->dev.bus_id);
                result = -ENXIO;
                goto fail_remap;
        }
 
-       dev->remote = (struct vlynq_regs *)((u32)dev->local + 128);
+       dev->remote = (struct vlynq_regs *)((u32)dev->local +
+                                           VLYNQ_REMOTE_OFFSET);
 
        dev->irq = platform_get_irq_byname(pdev, "irq");
        dev->irq_start = irq_res->start;
@@ -484,8 +574,8 @@ static int vlynq_probe(struct platform_device *pdev)
        return 0;
 
 fail_register:
-fail_remap:
        iounmap(dev->local);
+fail_remap:
 fail_request:
        release_mem_region(regs_res->start, len);
        kfree(dev);
@@ -497,6 +587,7 @@ static int vlynq_remove(struct platform_device *pdev)
        struct vlynq_device *dev = platform_get_drvdata(pdev);
 
        device_unregister(&dev->dev);
+       iounmap(dev->local);
        release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start);
 
        kfree(dev);
@@ -520,7 +611,7 @@ EXPORT_SYMBOL(vlynq_bus_type);
 #ifdef CONFIG_PCI
 extern void vlynq_pci_init(void);
 #endif
-static int __init vlynq_init(void)
+int __init vlynq_init(void)
 {
        int res = 0;
 
@@ -544,13 +635,13 @@ fail_bus:
        return res;
 }
 
-/*
+/* Add this back when vlynq-pci crap is gone */
+#if 0
 void __devexit vlynq_exit(void)
 {
        platform_driver_unregister(&vlynq_driver);
        bus_unregister(&vlynq_bus_type);
 }
-*/
-
+#endif
 
 subsys_initcall(vlynq_init);
index 34b9407..12865be 100644 (file)
@@ -16,7 +16,6 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-
 #ifndef __VLYNQ_H__
 #define __VLYNQ_H__
 
@@ -29,13 +28,34 @@ struct vlynq_device_id {
        u32 id;
 };
 
+enum vlynq_divisor {
+       vlynq_div_auto = 0,
+       vlynq_ldiv1,
+       vlynq_ldiv2,
+       vlynq_ldiv3,
+       vlynq_ldiv4,
+       vlynq_ldiv5,
+       vlynq_ldiv6,
+       vlynq_ldiv7,
+       vlynq_ldiv8,
+       vlynq_rdiv1,
+       vlynq_rdiv2,
+       vlynq_rdiv3,
+       vlynq_rdiv4,
+       vlynq_rdiv5,
+       vlynq_rdiv6,
+       vlynq_rdiv7,
+       vlynq_rdiv8,
+       vlynq_div_external
+};
+
 struct vlynq_regs;
 struct vlynq_device {
        u32 id;
        int irq;
        int local_irq;
        int remote_irq;
-       int clock_div;
+       enum vlynq_divisor divisor;
        u32 regs_start, regs_end;
        u32 mem_start, mem_end;
        u32 irq_start, irq_end;