vlynq cleanups: * drivers can now set a clock divisor * irq handling cleanup, drivers now can handle error irqs themselves * style cleanup

SVN-Revision: 8759
lede-17.01
Eugene Konev 2007-09-12 12:23:56 +00:00
parent 09b45caf09
commit db5e143756
3 changed files with 212 additions and 98 deletions

View File

@ -143,7 +143,8 @@ static inline u32 vlynq_read(u32 val, int size) {
return val; 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_device *dev;
struct vlynq_pci_private *priv; struct vlynq_pci_private *priv;
@ -214,14 +215,15 @@ static int vlynq_config_read(struct pci_bus *bus, unsigned int devfn, int where,
*val = 1; *val = 1;
break; break;
default: default:
printk("%s: Read of unknown register 0x%x (size %d)\n", printk(KERN_NOTICE "%s: Read of unknown register 0x%x "
dev->dev.bus_id, where, size); "(size %d)\n", dev->dev.bus_id, where, size);
return PCIBIOS_BAD_REGISTER_NUMBER; return PCIBIOS_BAD_REGISTER_NUMBER;
} }
return PCIBIOS_SUCCESSFUL; 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_device *dev;
struct vlynq_pci_private *priv; 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: case PCI_ROM_ADDRESS:
break; break;
default: default:
printk("%s: Write to unknown register 0x%x (size %d) value=0x%x\n", printk(KERN_NOTICE "%s: Write to unknown register 0x%x "
dev->dev.bus_id, where, size, val); "(size %d) value=0x%x\n", dev->dev.bus_id, where, size,
val);
return PCIBIOS_BAD_REGISTER_NUMBER; return PCIBIOS_BAD_REGISTER_NUMBER;
} }
return PCIBIOS_SUCCESSFUL; return PCIBIOS_SUCCESSFUL;
@ -309,6 +312,7 @@ static int vlynq_pci_probe(struct vlynq_device *dev)
if (result) if (result)
return result; return result;
dev->divisor = vlynq_ldiv4;
result = vlynq_device_enable(dev); result = vlynq_device_enable(dev);
if (result) if (result)
return result; return result;
@ -319,17 +323,17 @@ static int vlynq_pci_probe(struct vlynq_device *dev)
config = &known_devices[i]; config = &known_devices[i];
if (!config) { if (!config) {
printk("vlynq-pci: skipping unknown device " printk(KERN_DEBUG "vlynq-pci: skipping unknown device "
"%04x:%04x at %s\n", chip_id >> 16, "%04x:%04x at %s\n", chip_id >> 16,
chip_id & 0xffff, dev->dev.bus_id); chip_id & 0xffff, dev->dev.bus_id);
result = -ENODEV; result = -ENODEV;
goto fail; 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); config->name, dev->dev.bus_id);
priv = kmalloc(sizeof(struct vlynq_pci_private), GFP_KERNEL); priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) { if (!priv) {
printk(KERN_ERR "%s: failed to allocate private data\n", printk(KERN_ERR "%s: failed to allocate private data\n",
dev->dev.bus_id); dev->dev.bus_id);
@ -337,7 +341,6 @@ static int vlynq_pci_probe(struct vlynq_device *dev)
goto fail; goto fail;
} }
memset(priv, 0, sizeof(struct vlynq_pci_private));
priv->latency = 64; priv->latency = 64;
priv->cache_line = 32; priv->cache_line = 32;
priv->config = config; priv->config = config;

View File

@ -20,18 +20,13 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/delay.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/ioport.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
#include <asm/addrspace.h>
#include <asm/ar7/ar7.h>
#include <asm/ar7/vlynq.h> #include <asm/ar7/vlynq.h>
#define PER_DEVICE_IRQS 32 #define PER_DEVICE_IRQS 32
@ -45,6 +40,9 @@
#define VLYNQ_CTRL_INT2CFG 0x00000080 #define VLYNQ_CTRL_INT2CFG 0x00000080
#define VLYNQ_CTRL_RESET 0x00000001 #define VLYNQ_CTRL_RESET 0x00000001
#define VLYNQ_INT_OFFSET 0x00000014
#define VLYNQ_REMOTE_OFFSET 0x00000080
#define VLYNQ_STATUS_LINK 0x00000001 #define VLYNQ_STATUS_LINK 0x00000001
#define VLYNQ_STATUS_LERROR 0x00000080 #define VLYNQ_STATUS_LERROR 0x00000080
#define VLYNQ_STATUS_RERROR 0x00000100 #define VLYNQ_STATUS_RERROR 0x00000100
@ -105,11 +103,11 @@ int vlynq_linked(struct vlynq_device *dev)
{ {
int i; int i;
for (i = 0; i < 10; i++) for (i = 0; i < 100; i++)
if (vlynq_reg_read(dev->local->status) & VLYNQ_STATUS_LINK) if (vlynq_reg_read(dev->local->status) & VLYNQ_STATUS_LINK)
return 1; return 1;
else else
mdelay(1); cpu_relax();
return 0; 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) static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
{ {
u32 val; u32 val;
struct vlynq_device *dev = irq_desc[irq].chip_data; struct vlynq_device *dev = get_irq_chip_data(irq);
int virq; int virq;
BUG_ON(!dev); BUG_ON(!dev);
@ -171,28 +169,41 @@ static int vlynq_irq_type(unsigned int irq, unsigned int flow_type)
return 0; 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) static irqreturn_t vlynq_irq(int irq, void *dev_id)
{ {
struct vlynq_device *dev = dev_id; struct vlynq_device *dev = dev_id;
u32 status, ack; u32 status;
int virq = 0; int virq = 0;
status = vlynq_reg_read(dev->local->int_status); status = vlynq_reg_read(dev->local->int_status);
vlynq_reg_write(dev->local->int_status, status); vlynq_reg_write(dev->local->int_status, status);
if (status & (1 << dev->local_irq)) { /* Local vlynq IRQ. Ack */ if (unlikely(!status))
ack = vlynq_reg_read(dev->local->status); spurious_interrupt();
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);
}
status &= ~((1 << dev->local_irq) | (1 << dev->remote_irq));
while (status) { while (status) {
if (status & 1) /* Remote device IRQ. Pass. */ if (status & 1)
do_IRQ(dev->irq_start + virq); do_IRQ(dev->irq_start + virq);
status >>= 1; status >>= 1;
virq++; virq++;
@ -208,40 +219,70 @@ static struct irq_chip vlynq_irq_chip = {
.set_type = vlynq_irq_type, .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) static int vlynq_setup_irq(struct vlynq_device *dev)
{ {
u32 val; u32 val;
int i; int i;
if (dev->local_irq == dev->remote_irq) { if (dev->local_irq == dev->remote_irq) {
printk(KERN_WARNING printk(KERN_ERR
"%s: local vlynq irq should be different from remote\n", "%s: local vlynq irq should be different from remote\n",
dev->dev.bus_id); dev->dev.bus_id);
return -EINVAL; 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_VECTOR(dev->local_irq);
val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL | val |= VLYNQ_CTRL_INT_ENABLE | VLYNQ_CTRL_INT_LOCAL |
VLYNQ_CTRL_INT2CFG; VLYNQ_CTRL_INT2CFG;
val |= vlynq_reg_read(dev->local->control); 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); vlynq_reg_write(dev->local->control, val);
val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq); val = VLYNQ_CTRL_INT_VECTOR(dev->remote_irq);
val |= VLYNQ_CTRL_INT_ENABLE; val |= VLYNQ_CTRL_INT_ENABLE;
val |= vlynq_reg_read(dev->remote->control); 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); vlynq_reg_write(dev->remote->control, val);
for (i = 0; i < PER_DEVICE_IRQS; i++) { for (i = 0; i < PER_DEVICE_IRQS; i++) {
if ((i == dev->local_irq) || (i == dev->remote_irq)) if (i == dev->local_irq) {
continue; 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(dev->irq_start + i, &vlynq_irq_chip);
set_irq_chip_data(dev->irq_start + i, dev); set_irq_chip_data(dev->irq_start + i, dev);
vlynq_reg_write(dev->remote->int_device[i >> 2], 0); 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); printk(KERN_ERR "%s: request_irq failed\n", dev->dev.bus_id);
return -EAGAIN; return -EAGAIN;
} }
@ -280,7 +321,6 @@ int __vlynq_register_driver(struct vlynq_driver *driver, struct module *owner)
{ {
driver->driver.name = driver->name; driver->driver.name = driver->name;
driver->driver.bus = &vlynq_bus_type; driver->driver.bus = &vlynq_bus_type;
/* driver->driver.owner = owner;*/
return driver_register(&driver->driver); return driver_register(&driver->driver);
} }
EXPORT_SYMBOL(__vlynq_register_driver); EXPORT_SYMBOL(__vlynq_register_driver);
@ -293,35 +333,86 @@ EXPORT_SYMBOL(vlynq_unregister_driver);
int vlynq_device_enable(struct vlynq_device *dev) int vlynq_device_enable(struct vlynq_device *dev)
{ {
u32 div; int i, result;
int result;
struct plat_vlynq_ops *ops = dev->dev.platform_data; struct plat_vlynq_ops *ops = dev->dev.platform_data;
result = ops->on(dev); result = ops->on(dev);
if (result) if (result)
return result; return result;
vlynq_reg_write(dev->local->control, 0); switch (dev->divisor) {
case vlynq_div_auto:
/* First try locally supplied clock */
vlynq_reg_write(dev->remote->control, 0); 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)) { if (vlynq_linked(dev)) {
printk(KERN_INFO "%s: linked (using external clock)\n", 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); dev->dev.bus_id);
return vlynq_setup_irq(dev); return vlynq_setup_irq(dev);
} }
*/ break;
case vlynq_ldiv1: case vlynq_ldiv2: case vlynq_ldiv3: case vlynq_ldiv4:
for (div = 1; div <= 8; div++) { case vlynq_ldiv5: case vlynq_ldiv6: case vlynq_ldiv7: case vlynq_ldiv8:
mdelay(20);
vlynq_reg_write(dev->local->control, VLYNQ_CTRL_CLOCK_INT |
VLYNQ_CTRL_CLOCK_DIV(div - 1));
vlynq_reg_write(dev->remote->control, 0); 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)) { if (vlynq_linked(dev)) {
printk(KERN_INFO "%s: linked (using internal clock, div: %d)\n", printk(KERN_DEBUG
dev->dev.bus_id, div); "%s: using local clock divisor %d\n",
dev->dev.bus_id, dev->divisor - vlynq_ldiv1 + 1);
return vlynq_setup_irq(dev); 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; return -ENODEV;
@ -369,9 +460,6 @@ int vlynq_virq_to_irq(struct vlynq_device *dev, int virq)
if ((virq < 0) || (virq >= PER_DEVICE_IRQS)) if ((virq < 0) || (virq >= PER_DEVICE_IRQS))
return -EINVAL; return -EINVAL;
if ((virq == dev->local_irq) || (virq == dev->remote_irq))
return -EINVAL;
return dev->irq_start + virq; return dev->irq_start + virq;
} }
@ -430,9 +518,10 @@ static int vlynq_probe(struct platform_device *pdev)
if (!irq_res) if (!irq_res)
return -ENODEV; return -ENODEV;
dev = kzalloc(sizeof(struct vlynq_device), GFP_KERNEL); dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) { if (!dev) {
printk(KERN_ERR "vlynq: failed to allocate device structure\n"); printk(KERN_ERR
"vlynq: failed to allocate device structure\n");
return -ENOMEM; return -ENOMEM;
} }
@ -457,7 +546,7 @@ static int vlynq_probe(struct platform_device *pdev)
goto fail_request; goto fail_request;
} }
dev->local = ioremap_nocache(regs_res->start, len); dev->local = ioremap(regs_res->start, len);
if (!dev->local) { if (!dev->local) {
printk(KERN_ERR "%s: Can't remap vlynq registers\n", printk(KERN_ERR "%s: Can't remap vlynq registers\n",
dev->dev.bus_id); dev->dev.bus_id);
@ -465,7 +554,8 @@ static int vlynq_probe(struct platform_device *pdev)
goto fail_remap; 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 = platform_get_irq_byname(pdev, "irq");
dev->irq_start = irq_res->start; dev->irq_start = irq_res->start;
@ -484,8 +574,8 @@ static int vlynq_probe(struct platform_device *pdev)
return 0; return 0;
fail_register: fail_register:
fail_remap:
iounmap(dev->local); iounmap(dev->local);
fail_remap:
fail_request: fail_request:
release_mem_region(regs_res->start, len); release_mem_region(regs_res->start, len);
kfree(dev); kfree(dev);
@ -497,6 +587,7 @@ static int vlynq_remove(struct platform_device *pdev)
struct vlynq_device *dev = platform_get_drvdata(pdev); struct vlynq_device *dev = platform_get_drvdata(pdev);
device_unregister(&dev->dev); device_unregister(&dev->dev);
iounmap(dev->local);
release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start); release_mem_region(dev->regs_start, dev->regs_end - dev->regs_start);
kfree(dev); kfree(dev);
@ -520,7 +611,7 @@ EXPORT_SYMBOL(vlynq_bus_type);
#ifdef CONFIG_PCI #ifdef CONFIG_PCI
extern void vlynq_pci_init(void); extern void vlynq_pci_init(void);
#endif #endif
static int __init vlynq_init(void) int __init vlynq_init(void)
{ {
int res = 0; int res = 0;
@ -544,13 +635,13 @@ fail_bus:
return res; return res;
} }
/* /* Add this back when vlynq-pci crap is gone */
#if 0
void __devexit vlynq_exit(void) void __devexit vlynq_exit(void)
{ {
platform_driver_unregister(&vlynq_driver); platform_driver_unregister(&vlynq_driver);
bus_unregister(&vlynq_bus_type); bus_unregister(&vlynq_bus_type);
} }
*/ #endif
subsys_initcall(vlynq_init); subsys_initcall(vlynq_init);

View File

@ -16,7 +16,6 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#ifndef __VLYNQ_H__ #ifndef __VLYNQ_H__
#define __VLYNQ_H__ #define __VLYNQ_H__
@ -29,13 +28,34 @@ struct vlynq_device_id {
u32 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_regs;
struct vlynq_device { struct vlynq_device {
u32 id; u32 id;
int irq; int irq;
int local_irq; int local_irq;
int remote_irq; int remote_irq;
int clock_div; enum vlynq_divisor divisor;
u32 regs_start, regs_end; u32 regs_start, regs_end;
u32 mem_start, mem_end; u32 mem_start, mem_end;
u32 irq_start, irq_end; u32 irq_start, irq_end;