From fc58f2fd75ca18ab42813e6392c536c35c8e157f Mon Sep 17 00:00:00 2001 From: ejka Date: Wed, 12 Sep 2007 12:23:56 +0000 Subject: vlynq cleanups: * 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 | 29 +-- target/linux/ar7/files/arch/mips/ar7/vlynq.c | 257 ++++++++++++++------- .../linux/ar7/files/include/asm-mips/ar7/vlynq.h | 24 +- 3 files changed, 212 insertions(+), 98 deletions(-) diff --git a/target/linux/ar7/files/arch/mips/ar7/vlynq-pci.c b/target/linux/ar7/files/arch/mips/ar7/vlynq-pci.c index 9f3b63d691..ffcfa62a17 100644 --- a/target/linux/ar7/files/arch/mips/ar7/vlynq-pci.c +++ b/target/linux/ar7/files/arch/mips/ar7/vlynq-pci.c @@ -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); diff --git a/target/linux/ar7/files/arch/mips/ar7/vlynq.c b/target/linux/ar7/files/arch/mips/ar7/vlynq.c index 88e974653f..76dbd55ef4 100644 --- a/target/linux/ar7/files/arch/mips/ar7/vlynq.c +++ b/target/linux/ar7/files/arch/mips/ar7/vlynq.c @@ -20,42 +20,40 @@ #include #include #include -#include #include -#include #include #include -#include #include #include #include -#include -#include #include -#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); diff --git a/target/linux/ar7/files/include/asm-mips/ar7/vlynq.h b/target/linux/ar7/files/include/asm-mips/ar7/vlynq.h index 34b940759a..12865bebba 100644 --- a/target/linux/ar7/files/include/asm-mips/ar7/vlynq.h +++ b/target/linux/ar7/files/include/asm-mips/ar7/vlynq.h @@ -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; -- cgit v1.2.3