mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-12-26 01:33:20 +02:00
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
This commit is contained in:
parent
769621dec9
commit
fc58f2fd75
@ -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;
|
||||||
@ -194,7 +195,7 @@ static int vlynq_config_read(struct pci_bus *bus, unsigned int devfn, int where,
|
|||||||
case PCI_BASE_ADDRESS_3:
|
case PCI_BASE_ADDRESS_3:
|
||||||
resno = (where - PCI_BASE_ADDRESS_0) >> 2;
|
resno = (where - PCI_BASE_ADDRESS_0) >> 2;
|
||||||
if (priv->sz_mask & (1 << resno)) {
|
if (priv->sz_mask & (1 << resno)) {
|
||||||
priv->sz_mask &= ~(1 << resno);
|
priv->sz_mask &= ~(1 << resno);
|
||||||
*val = priv->config->rx_mapping[resno].size;
|
*val = priv->config->rx_mapping[resno].size;
|
||||||
} else {
|
} else {
|
||||||
*val = vlynq_get_mapped(dev, resno);
|
*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;
|
*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;
|
||||||
|
@ -20,42 +20,40 @@
|
|||||||
#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
|
||||||
|
|
||||||
#define VLYNQ_CTRL_PM_ENABLE 0x80000000
|
#define VLYNQ_CTRL_PM_ENABLE 0x80000000
|
||||||
#define VLYNQ_CTRL_CLOCK_INT 0x00008000
|
#define VLYNQ_CTRL_CLOCK_INT 0x00008000
|
||||||
#define VLYNQ_CTRL_CLOCK_DIV(x) (((x) & 7) << 16)
|
#define VLYNQ_CTRL_CLOCK_DIV(x) (((x) & 7) << 16)
|
||||||
#define VLYNQ_CTRL_INT_LOCAL 0x00004000
|
#define VLYNQ_CTRL_INT_LOCAL 0x00004000
|
||||||
#define VLYNQ_CTRL_INT_ENABLE 0x00002000
|
#define VLYNQ_CTRL_INT_ENABLE 0x00002000
|
||||||
#define VLYNQ_CTRL_INT_VECTOR(x) (((x) & 0x1f) << 8)
|
#define VLYNQ_CTRL_INT_VECTOR(x) (((x) & 0x1f) << 8)
|
||||||
#define VLYNQ_CTRL_INT2CFG 0x00000080
|
#define VLYNQ_CTRL_INT2CFG 0x00000080
|
||||||
#define VLYNQ_CTRL_RESET 0x00000001
|
#define VLYNQ_CTRL_RESET 0x00000001
|
||||||
|
|
||||||
#define VLYNQ_STATUS_LINK 0x00000001
|
#define VLYNQ_INT_OFFSET 0x00000014
|
||||||
#define VLYNQ_STATUS_LERROR 0x00000080
|
#define VLYNQ_REMOTE_OFFSET 0x00000080
|
||||||
#define VLYNQ_STATUS_RERROR 0x00000100
|
|
||||||
|
|
||||||
#define VINT_ENABLE 0x00000100
|
#define VLYNQ_STATUS_LINK 0x00000001
|
||||||
#define VINT_TYPE_EDGE 0x00000080
|
#define VLYNQ_STATUS_LERROR 0x00000080
|
||||||
#define VINT_LEVEL_LOW 0x00000040
|
#define VLYNQ_STATUS_RERROR 0x00000100
|
||||||
#define VINT_VECTOR(x) ((x) & 0x1f)
|
|
||||||
#define VINT_OFFSET(irq) (8 * ((irq) % 4))
|
|
||||||
|
|
||||||
#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 {
|
struct vlynq_regs {
|
||||||
u32 revision;
|
u32 revision;
|
||||||
@ -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,
|
||||||
set_irq_chip(dev->irq_start + i, &vlynq_irq_chip);
|
&vlynq_local_chip,
|
||||||
set_irq_chip_data(dev->irq_start + i, dev);
|
handle_level_irq);
|
||||||
vlynq_reg_write(dev->remote->int_device[i >> 2], 0);
|
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);
|
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) {
|
||||||
vlynq_reg_write(dev->remote->control, 0);
|
case vlynq_div_auto:
|
||||||
|
/* First try locally supplied clock */
|
||||||
/*
|
vlynq_reg_write(dev->remote->control, 0);
|
||||||
if (vlynq_linked(dev)) {
|
for (i = vlynq_ldiv1; i <= vlynq_ldiv8; i++) {
|
||||||
printk(KERN_INFO "%s: linked (using external clock)\n",
|
vlynq_reg_write(dev->local->control,
|
||||||
dev->dev.bus_id);
|
VLYNQ_CTRL_CLOCK_INT |
|
||||||
return vlynq_setup_irq(dev);
|
VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1));
|
||||||
}
|
if (vlynq_linked(dev)) {
|
||||||
*/
|
printk(KERN_DEBUG
|
||||||
|
"%s: using local clock divisor %d\n",
|
||||||
for (div = 1; div <= 8; div++) {
|
dev->dev.bus_id, i - vlynq_ldiv1 + 1);
|
||||||
mdelay(20);
|
return vlynq_setup_irq(dev);
|
||||||
vlynq_reg_write(dev->local->control, VLYNQ_CTRL_CLOCK_INT |
|
}
|
||||||
VLYNQ_CTRL_CLOCK_DIV(div - 1));
|
}
|
||||||
|
/* 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);
|
vlynq_reg_write(dev->remote->control, 0);
|
||||||
if (vlynq_linked(dev)) {
|
if (vlynq_linked(dev)) {
|
||||||
printk(KERN_INFO "%s: linked (using internal clock, div: %d)\n",
|
printk(KERN_DEBUG "%s: using external clock\n",
|
||||||
dev->dev.bus_id, div);
|
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:
|
||||||
|
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_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;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,20 +541,21 @@ static int vlynq_probe(struct platform_device *pdev)
|
|||||||
len = regs_res->end - regs_res->start;
|
len = regs_res->end - regs_res->start;
|
||||||
if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) {
|
if (!request_mem_region(regs_res->start, len, dev->dev.bus_id)) {
|
||||||
printk(KERN_ERR "%s: Can't request vlynq registers\n",
|
printk(KERN_ERR "%s: Can't request vlynq registers\n",
|
||||||
dev->dev.bus_id);
|
dev->dev.bus_id);
|
||||||
result = -ENXIO;
|
result = -ENXIO;
|
||||||
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);
|
||||||
result = -ENXIO;
|
result = -ENXIO;
|
||||||
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);
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user