diff options
Diffstat (limited to 'drivers')
155 files changed, 3232 insertions, 3978 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 5c91d6afb11..aeb5ab2391e 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -68,8 +68,6 @@ source "drivers/leds/Kconfig" source "drivers/infiniband/Kconfig" -source "drivers/sn/Kconfig" - source "drivers/edac/Kconfig" source "drivers/rtc/Kconfig" diff --git a/drivers/base/class.c b/drivers/base/class.c index 0e71dff327c..b1ea4df85c7 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -456,6 +456,35 @@ static void class_device_remove_attrs(struct class_device * cd) } } +static int class_device_add_groups(struct class_device * cd) +{ + int i; + int error = 0; + + if (cd->groups) { + for (i = 0; cd->groups[i]; i++) { + error = sysfs_create_group(&cd->kobj, cd->groups[i]); + if (error) { + while (--i >= 0) + sysfs_remove_group(&cd->kobj, cd->groups[i]); + goto out; + } + } + } +out: + return error; +} + +static void class_device_remove_groups(struct class_device * cd) +{ + int i; + if (cd->groups) { + for (i = 0; cd->groups[i]; i++) { + sysfs_remove_group(&cd->kobj, cd->groups[i]); + } + } +} + static ssize_t show_dev(struct class_device *class_dev, char *buf) { return print_dev_t(buf, class_dev->devt); @@ -559,6 +588,8 @@ int class_device_add(struct class_device *class_dev) class_name); } + class_device_add_groups(class_dev); + kobject_uevent(&class_dev->kobj, KOBJ_ADD); /* notify any interfaces this device is now here */ @@ -672,6 +703,7 @@ void class_device_del(struct class_device *class_dev) if (class_dev->devt_attr) class_device_remove_file(class_dev, class_dev->devt_attr); class_device_remove_attrs(class_dev); + class_device_remove_groups(class_dev); kobject_uevent(&class_dev->kobj, KOBJ_REMOVE); kobject_del(&class_dev->kobj); diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index bedb689b051..dff1e67b1dd 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4301,7 +4301,7 @@ static int __init floppy_init(void) } use_virtual_dma = can_use_virtual_dma & 1; -#if defined(CONFIG_PPC64) +#if defined(CONFIG_PPC_MERGE) if (check_legacy_ioport(FDC1)) { del_timer(&fd_timeout); err = -ENODEV; diff --git a/drivers/char/genrtc.c b/drivers/char/genrtc.c index d3a2bc36129..588fca542a9 100644 --- a/drivers/char/genrtc.c +++ b/drivers/char/genrtc.c @@ -200,13 +200,13 @@ static ssize_t gen_rtc_read(struct file *file, char __user *buf, /* first test allows optimizer to nuke this case for 32-bit machines */ if (sizeof (int) != sizeof (long) && count == sizeof (unsigned int)) { unsigned int uidata = data; - retval = put_user(uidata, (unsigned long __user *)buf); + retval = put_user(uidata, (unsigned int __user *)buf) ?: + sizeof(unsigned int); } else { - retval = put_user(data, (unsigned long __user *)buf); + retval = put_user(data, (unsigned long __user *)buf) ?: + sizeof(unsigned long); } - if (!retval) - retval = sizeof(unsigned long); out: current->state = TASK_RUNNING; remove_wait_queue(&gen_rtc_wait, &wait); diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 935670a3cd9..5755b7e5f18 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -860,9 +860,32 @@ static void k_slock(struct vc_data *vc, unsigned char value, char up_flag, struc } /* by default, 300ms interval for combination release */ -static long brl_timeout = 300; -MODULE_PARM_DESC(brl_timeout, "Braille keys release delay in ms (0 for combination on first release, < 0 for dead characters)"); -module_param(brl_timeout, long, 0644); +static unsigned brl_timeout = 300; +MODULE_PARM_DESC(brl_timeout, "Braille keys release delay in ms (0 for commit on first key release)"); +module_param(brl_timeout, uint, 0644); + +static unsigned brl_nbchords = 1; +MODULE_PARM_DESC(brl_nbchords, "Number of chords that produce a braille pattern (0 for dead chords)"); +module_param(brl_nbchords, uint, 0644); + +static void k_brlcommit(struct vc_data *vc, unsigned int pattern, char up_flag, struct pt_regs *regs) +{ + static unsigned long chords; + static unsigned committed; + + if (!brl_nbchords) + k_deadunicode(vc, BRL_UC_ROW | pattern, up_flag, regs); + else { + committed |= pattern; + chords++; + if (chords == brl_nbchords) { + k_unicode(vc, BRL_UC_ROW | committed, up_flag, regs); + chords = 0; + committed = 0; + } + } +} + static void k_brl(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs) { static unsigned pressed,committing; @@ -882,11 +905,6 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag, struct if (value > 8) return; - if (brl_timeout < 0) { - k_deadunicode(vc, BRL_UC_ROW | (1 << (value - 1)), up_flag, regs); - return; - } - if (up_flag) { if (brl_timeout) { if (!committing || @@ -897,13 +915,13 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag, struct pressed &= ~(1 << (value - 1)); if (!pressed) { if (committing) { - k_unicode(vc, BRL_UC_ROW | committing, 0, regs); + k_brlcommit(vc, committing, 0, regs); committing = 0; } } } else { if (committing) { - k_unicode(vc, BRL_UC_ROW | committing, 0, regs); + k_brlcommit(vc, committing, 0, regs); committing = 0; } pressed &= ~(1 << (value - 1)); diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 8666171e187..d3ba2f860ef 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -271,7 +271,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file, ipcnum, pDrvData->IPCs[ipcnum].usIntCount); - if (ipcnum > ARRAY_SIZE(pDrvData->IPCs)) { + if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" " IOCTL_MW_REGISTER_IPC:" diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 02114a0bd0d..128b2632512 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1981,10 +1981,6 @@ static int __init cmm_init(void) if (!cmm_class) return -1; - rc = pcmcia_register_driver(&cm4000_driver); - if (rc < 0) - return rc; - major = register_chrdev(0, DEVICE_NAME, &cm4000_fops); if (major < 0) { printk(KERN_WARNING MODULE_NAME @@ -1992,6 +1988,12 @@ static int __init cmm_init(void) return -1; } + rc = pcmcia_register_driver(&cm4000_driver); + if (rc < 0) { + unregister_chrdev(major, DEVICE_NAME); + return rc; + } + return 0; } diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index 29efa64580a..47a8465bf95 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -724,16 +724,19 @@ static int __init cm4040_init(void) if (!cmx_class) return -1; - rc = pcmcia_register_driver(&reader_driver); - if (rc < 0) - return rc; - major = register_chrdev(0, DEVICE_NAME, &reader_fops); if (major < 0) { printk(KERN_WARNING MODULE_NAME ": could not get major number\n"); return -1; } + + rc = pcmcia_register_driver(&reader_driver); + if (rc < 0) { + unregister_chrdev(major, DEVICE_NAME); + return rc; + } + return 0; } diff --git a/drivers/char/tipar.c b/drivers/char/tipar.c index eb2eb3e12d6..079db5a935a 100644 --- a/drivers/char/tipar.c +++ b/drivers/char/tipar.c @@ -515,7 +515,7 @@ tipar_init_module(void) err = PTR_ERR(tipar_class); goto out_chrdev; } - if (parport_register_driver(&tipar_driver) || tp_count == 0) { + if (parport_register_driver(&tipar_driver)) { printk(KERN_ERR "tipar: unable to register with parport\n"); err = -EIO; goto out_class; diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index 956d121cb16..3e6ffcaa5af 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -74,6 +74,8 @@ static unsigned int dbs_enable; /* number of CPUs using this policy */ static DEFINE_MUTEX (dbs_mutex); static DECLARE_WORK (dbs_work, do_dbs_timer, NULL); +static struct workqueue_struct *dbs_workq; + struct dbs_tuners { unsigned int sampling_rate; unsigned int sampling_down_factor; @@ -364,23 +366,29 @@ static void do_dbs_timer(void *data) mutex_lock(&dbs_mutex); for_each_online_cpu(i) dbs_check_cpu(i); - schedule_delayed_work(&dbs_work, - usecs_to_jiffies(dbs_tuners_ins.sampling_rate)); + queue_delayed_work(dbs_workq, &dbs_work, + usecs_to_jiffies(dbs_tuners_ins.sampling_rate)); mutex_unlock(&dbs_mutex); } static inline void dbs_timer_init(void) { INIT_WORK(&dbs_work, do_dbs_timer, NULL); - schedule_delayed_work(&dbs_work, - usecs_to_jiffies(dbs_tuners_ins.sampling_rate)); + if (!dbs_workq) + dbs_workq = create_singlethread_workqueue("ondemand"); + if (!dbs_workq) { + printk(KERN_ERR "ondemand: Cannot initialize kernel thread\n"); + return; + } + queue_delayed_work(dbs_workq, &dbs_work, + usecs_to_jiffies(dbs_tuners_ins.sampling_rate)); return; } static inline void dbs_timer_exit(void) { - cancel_delayed_work(&dbs_work); - return; + if (dbs_workq) + cancel_rearming_delayed_workqueue(dbs_workq, &dbs_work); } static int cpufreq_governor_dbs(struct cpufreq_policy *policy, @@ -489,8 +497,12 @@ static int __init cpufreq_gov_dbs_init(void) static void __exit cpufreq_gov_dbs_exit(void) { - /* Make sure that the scheduled work is indeed not running */ - flush_scheduled_work(); + /* Make sure that the scheduled work is indeed not running. + Assumes the timer has been cancelled first. */ + if (dbs_workq) { + flush_workqueue(dbs_workq); + destroy_workqueue(dbs_workq); + } cpufreq_unregister_governor(&cpufreq_gov_dbs); } diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c index 66572c5323a..fce31936e6d 100644 --- a/drivers/edac/e752x_edac.c +++ b/drivers/edac/e752x_edac.c @@ -25,6 +25,8 @@ #include <linux/slab.h> #include "edac_mc.h" +static int force_function_unhide; + #define e752x_printk(level, fmt, arg...) \ edac_printk(level, "e752x", fmt, ##arg) @@ -782,8 +784,16 @@ static int e752x_probe1(struct pci_dev *pdev, int dev_idx) debugf0("%s(): mci\n", __func__); debugf0("Starting Probe1\n"); - /* enable device 0 function 1 */ + /* check to see if device 0 function 1 is enabled; if it isn't, we + * assume the BIOS has reserved it for a reason and is expecting + * exclusive access, we take care not to violate that assumption and + * fail the probe. */ pci_read_config_byte(pdev, E752X_DEVPRES1, &stat8); + if (!force_function_unhide && !(stat8 & (1 << 5))) { + printk(KERN_INFO "Contact your BIOS vendor to see if the " + "E752x error registers can be safely un-hidden\n"); + goto fail; + } stat8 |= (1 << 5); pci_write_config_byte(pdev, E752X_DEVPRES1, stat8); @@ -1063,3 +1073,8 @@ module_exit(e752x_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux Networx (http://lnxi.com) Tom Zimmerman\n"); MODULE_DESCRIPTION("MC support for Intel e752x memory controllers"); + +module_param(force_function_unhide, int, 0444); +MODULE_PARM_DESC(force_function_unhide, "if BIOS sets Dev0:Fun1 up as hidden:" +" 1=force unhide and hope BIOS doesn't fight driver for Dev0:Fun1 access"); + diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 15121cb5a1f..21f9282c1b2 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -336,7 +336,7 @@ static ssize_t show_pma_counter(struct ib_port *p, struct port_attribute *attr, switch (width) { case 4: ret = sprintf(buf, "%u\n", (out_mad->data[40 + offset / 8] >> - (offset % 4)) & 0xf); + (4 - (offset % 8))) & 0xf); break; case 8: ret = sprintf(buf, "%u\n", out_mad->data[40 + offset / 8]); diff --git a/drivers/infiniband/hw/ipath/ipath_debug.h b/drivers/infiniband/hw/ipath/ipath_debug.h index 593e28969c6..46762387f5f 100644 --- a/drivers/infiniband/hw/ipath/ipath_debug.h +++ b/drivers/infiniband/hw/ipath/ipath_debug.h @@ -60,11 +60,11 @@ #define __IPATH_KERNEL_SEND 0x2000 /* use kernel mode send */ #define __IPATH_EPKTDBG 0x4000 /* print ethernet packet data */ #define __IPATH_SMADBG 0x8000 /* sma packet debug */ -#define __IPATH_IPATHDBG 0x10000 /* Ethernet (IPATH) general debug on */ -#define __IPATH_IPATHWARN 0x20000 /* Ethernet (IPATH) warnings on */ -#define __IPATH_IPATHERR 0x40000 /* Ethernet (IPATH) errors on */ -#define __IPATH_IPATHPD 0x80000 /* Ethernet (IPATH) packet dump on */ -#define __IPATH_IPATHTABLE 0x100000 /* Ethernet (IPATH) table dump on */ +#define __IPATH_IPATHDBG 0x10000 /* Ethernet (IPATH) gen debug */ +#define __IPATH_IPATHWARN 0x20000 /* Ethernet (IPATH) warnings */ +#define __IPATH_IPATHERR 0x40000 /* Ethernet (IPATH) errors */ +#define __IPATH_IPATHPD 0x80000 /* Ethernet (IPATH) packet dump */ +#define __IPATH_IPATHTABLE 0x100000 /* Ethernet (IPATH) table dump */ #else /* _IPATH_DEBUGGING */ @@ -79,11 +79,12 @@ #define __IPATH_TRSAMPLE 0x0 /* generate trace buffer sample entries */ #define __IPATH_VERBDBG 0x0 /* very verbose debug */ #define __IPATH_PKTDBG 0x0 /* print packet data */ -#define __IPATH_PROCDBG 0x0 /* print process startup (init)/exit messages */ +#define __IPATH_PROCDBG 0x0 /* process startup (init)/exit messages */ /* print mmap/nopage stuff, not using VDBG any more */ #define __IPATH_MMDBG 0x0 #define __IPATH_EPKTDBG 0x0 /* print ethernet packet data */ -#define __IPATH_SMADBG 0x0 /* print process startup (init)/exit messages */#define __IPATH_IPATHDBG 0x0 /* Ethernet (IPATH) table dump on */ +#define __IPATH_SMADBG 0x0 /* process startup (init)/exit messages */ +#define __IPATH_IPATHDBG 0x0 /* Ethernet (IPATH) table dump on */ #define __IPATH_IPATHWARN 0x0 /* Ethernet (IPATH) warnings on */ #define __IPATH_IPATHERR 0x0 /* Ethernet (IPATH) errors on */ #define __IPATH_IPATHPD 0x0 /* Ethernet (IPATH) packet dump on */ diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c index 7d3fb6996b4..28ddceb260e 100644 --- a/drivers/infiniband/hw/ipath/ipath_diag.c +++ b/drivers/infiniband/hw/ipath/ipath_diag.c @@ -277,13 +277,14 @@ static int ipath_diag_open(struct inode *in, struct file *fp) bail: spin_unlock_irqrestore(&ipath_devs_lock, flags); - mutex_unlock(&ipath_mutex); /* Only expose a way to reset the device if we make it into diag mode. */ if (ret == 0) ipath_expose_reset(&dd->pcidev->dev); + mutex_unlock(&ipath_mutex); + return ret; } diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index e7617c3982e..398add4d4cb 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -418,9 +418,19 @@ static int __devinit ipath_init_one(struct pci_dev *pdev, ret = pci_set_dma_mask(pdev, DMA_64BIT_MASK); if (ret) { - dev_info(&pdev->dev, "pci_set_dma_mask unit %u " - "fails: %d\n", dd->ipath_unit, ret); - goto bail_regions; + /* + * if the 64 bit setup fails, try 32 bit. Some systems + * do not setup 64 bit maps on systems with 2GB or less + * memory installed. + */ + ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (ret) { + dev_info(&pdev->dev, "pci_set_dma_mask unit %u " + "fails: %d\n", dd->ipath_unit, ret); + goto bail_regions; + } + else + ipath_dbg("No 64bit DMA mask, used 32 bit mask\n"); } pci_set_master(pdev); @@ -1949,7 +1959,7 @@ int ipath_reset_device(int unit) } if (dd->ipath_pd) - for (i = 1; i < dd->ipath_portcnt; i++) { + for (i = 1; i < dd->ipath_cfgports; i++) { if (dd->ipath_pd[i] && dd->ipath_pd[i]->port_cnt) { ipath_dbg("unit %u port %d is in use " "(PID %u cmd %s), can't reset\n", diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c index 2823ff9c0c6..16f640e1c16 100644 --- a/drivers/infiniband/hw/ipath/ipath_init_chip.c +++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c @@ -53,13 +53,19 @@ MODULE_PARM_DESC(cfgports, "Set max number of ports to use"); /* * Number of buffers reserved for driver (layered drivers and SMA - * send). Reserved at end of buffer list. + * send). Reserved at end of buffer list. Initialized based on + * number of PIO buffers if not set via module interface. + * The problem with this is that it's global, but we'll use different + * numbers for different chip types. So the default value is not + * very useful. I've redefined it for the 1.3 release so that it's + * zero unless set by the user to something else, in which case we + * try to respect it. */ -static ushort ipath_kpiobufs = 32; +static ushort ipath_kpiobufs; static int ipath_set_kpiobufs(const char *val, struct kernel_param *kp); -module_param_call(kpiobufs, ipath_set_kpiobufs, param_get_uint, +module_param_call(kpiobufs, ipath_set_kpiobufs, param_get_ushort, &ipath_kpiobufs, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(kpiobufs, "Set number of PIO buffers for driver"); @@ -531,8 +537,11 @@ static int init_housekeeping(struct ipath_devdata *dd, * Don't clear ipath_flags as 8bit mode was set before * entering this func. However, we do set the linkstate to * unknown, so we can watch for a transition. + * PRESENT is set because we want register reads to work, + * and the kernel infrastructure saw it in config space; + * We clear it if we have failures. */ - dd->ipath_flags |= IPATH_LINKUNK; + dd->ipath_flags |= IPATH_LINKUNK | IPATH_PRESENT; dd->ipath_flags &= ~(IPATH_LINKACTIVE | IPATH_LINKARMED | IPATH_LINKDOWN | IPATH_LINKINIT); @@ -560,6 +569,7 @@ static int init_housekeeping(struct ipath_devdata *dd, || (dd->ipath_uregbase & 0xffffffff) == 0xffffffff) { ipath_dev_err(dd, "Register read failures from chip, " "giving up initialization\n"); + dd->ipath_flags &= ~IPATH_PRESENT; ret = -ENODEV; goto done; } @@ -682,16 +692,14 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit) */ dd->ipath_pioavregs = ALIGN(val, sizeof(u64) * BITS_PER_BYTE / 2) / (sizeof(u64) * BITS_PER_BYTE / 2); - if (!ipath_kpiobufs) /* have to have at least 1, for SMA */ - kpiobufs = ipath_kpiobufs = 1; - else if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) < - (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT)) { - dev_info(&dd->pcidev->dev, "Too few PIO buffers (%u) " - "for %u ports to have %u each!\n", - dd->ipath_piobcnt2k + dd->ipath_piobcnt4k, - dd->ipath_cfgports, IPATH_MIN_USER_PORT_BUFCNT); - kpiobufs = 1; /* reserve just the minimum for SMA/ether */ - } else + if (ipath_kpiobufs == 0) { + /* not set by user, or set explictly to default */ + if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) > 128) + kpiobufs = 32; + else + kpiobufs = 16; + } + else kpiobufs = ipath_kpiobufs; if (kpiobufs > diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c index 0bcb428041f..3e72a1fe3d7 100644 --- a/drivers/infiniband/hw/ipath/ipath_intr.c +++ b/drivers/infiniband/hw/ipath/ipath_intr.c @@ -665,14 +665,14 @@ static void handle_layer_pioavail(struct ipath_devdata *dd) ret = __ipath_layer_intr(dd, IPATH_LAYER_INT_SEND_CONTINUE); if (ret > 0) - goto clear; + goto set; ret = __ipath_verbs_piobufavail(dd); if (ret > 0) - goto clear; + goto set; return; -clear: +set: set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl); ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl); @@ -719,11 +719,24 @@ static void handle_rcv(struct ipath_devdata *dd, u32 istat) irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) { struct ipath_devdata *dd = data; - u32 istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus); + u32 istat; ipath_err_t estat = 0; static unsigned unexpected = 0; irqreturn_t ret; + if(!(dd->ipath_flags & IPATH_PRESENT)) { + /* this is mostly so we don't try to touch the chip while + * it is being reset */ + /* + * This return value is perhaps odd, but we do not want the + * interrupt core code to remove our interrupt handler + * because we don't appear to be handling an interrupt + * during a chip reset. + */ + return IRQ_HANDLED; + } + + istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus); if (unlikely(!istat)) { ipath_stats.sps_nullintr++; ret = IRQ_NONE; /* not our interrupt, or already handled */ diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h index 0ce5f19c9d6..e6507f8115b 100644 --- a/drivers/infiniband/hw/ipath/ipath_kernel.h +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h @@ -731,7 +731,7 @@ u64 ipath_read_kreg64_port(const struct ipath_devdata *, ipath_kreg, static inline u32 ipath_read_ureg32(const struct ipath_devdata *dd, ipath_ureg regno, int port) { - if (!dd->ipath_kregbase) + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) return 0; return readl(regno + (u64 __iomem *) @@ -762,7 +762,7 @@ static inline void ipath_write_ureg(const struct ipath_devdata *dd, static inline u32 ipath_read_kreg32(const struct ipath_devdata *dd, ipath_kreg regno) { - if (!dd->ipath_kregbase) + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) return -1; return readl((u32 __iomem *) & dd->ipath_kregbase[regno]); } @@ -770,7 +770,7 @@ static inline u32 ipath_read_kreg32(const struct ipath_devdata *dd, static inline u64 ipath_read_kreg64(const struct ipath_devdata *dd, ipath_kreg regno) { - if (!dd->ipath_kregbase) + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) return -1; return readq(&dd->ipath_kregbase[regno]); @@ -786,7 +786,7 @@ static inline void ipath_write_kreg(const struct ipath_devdata *dd, static inline u64 ipath_read_creg(const struct ipath_devdata *dd, ipath_sreg regno) { - if (!dd->ipath_kregbase) + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) return 0; return readq(regno + (u64 __iomem *) @@ -797,7 +797,7 @@ static inline u64 ipath_read_creg(const struct ipath_devdata *dd, static inline u32 ipath_read_creg32(const struct ipath_devdata *dd, ipath_sreg regno) { - if (!dd->ipath_kregbase) + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) return 0; return readl(regno + (u64 __iomem *) (dd->ipath_cregbase + diff --git a/drivers/infiniband/hw/ipath/ipath_layer.c b/drivers/infiniband/hw/ipath/ipath_layer.c index 69ed1100701..9cb5258ffed 100644 --- a/drivers/infiniband/hw/ipath/ipath_layer.c +++ b/drivers/infiniband/hw/ipath/ipath_layer.c @@ -46,13 +46,15 @@ /* Acquire before ipath_devs_lock. */ static DEFINE_MUTEX(ipath_layer_mutex); +static int ipath_verbs_registered; + u16 ipath_layer_rcv_opcode; + static int (*layer_intr)(void *, u32); static int (*layer_rcv)(void *, void *, struct sk_buff *); static int (*layer_rcv_lid)(void *, void *); static int (*verbs_piobufavail)(void *); static void (*verbs_rcv)(void *, void *, void *, u32); -static int ipath_verbs_registered; static void *(*layer_add_one)(int, struct ipath_devdata *); static void (*layer_remove_one)(void *); @@ -586,6 +588,8 @@ void ipath_verbs_unregister(void) verbs_rcv = NULL; verbs_timer_cb = NULL; + ipath_verbs_registered = 0; + mutex_unlock(&ipath_layer_mutex); } diff --git a/drivers/infiniband/hw/ipath/ipath_pe800.c b/drivers/infiniband/hw/ipath/ipath_pe800.c index e1dc4f75706..6318067ab5e 100644 --- a/drivers/infiniband/hw/ipath/ipath_pe800.c +++ b/drivers/infiniband/hw/ipath/ipath_pe800.c @@ -972,6 +972,8 @@ static int ipath_setup_pe_reset(struct ipath_devdata *dd) /* Use ERROR so it shows up in logs, etc. */ ipath_dev_err(dd, "Resetting PE-800 unit %u\n", dd->ipath_unit); + /* keep chip from being accessed in a few places */ + dd->ipath_flags &= ~(IPATH_INITTED|IPATH_PRESENT); val = dd->ipath_control | INFINIPATH_C_RESET; ipath_write_kreg(dd, dd->ipath_kregs->kr_control, val); mb(); @@ -997,6 +999,8 @@ static int ipath_setup_pe_reset(struct ipath_devdata *dd) if ((r = pci_enable_device(dd->pcidev))) ipath_dev_err(dd, "pci_enable_device failed after " "reset: %d\n", r); + /* whether it worked or not, mark as present, again */ + dd->ipath_flags |= IPATH_PRESENT; val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_revision); if (val == dd->ipath_revision) { ipath_cdbg(VERBOSE, "Got matching revision " diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h index 1e59750c5f6..402126eb79c 100644 --- a/drivers/infiniband/hw/ipath/ipath_registers.h +++ b/drivers/infiniband/hw/ipath/ipath_registers.h @@ -34,8 +34,9 @@ #define _IPATH_REGISTERS_H /* - * This file should only be included by kernel source, and by the diags. - * It defines the registers, and their contents, for the InfiniPath HT-400 chip + * This file should only be included by kernel source, and by the diags. It + * defines the registers, and their contents, for the InfiniPath HT-400 + * chip. */ /* @@ -156,8 +157,10 @@ #define INFINIPATH_IBCC_FLOWCTRLWATERMARK_SHIFT 8 #define INFINIPATH_IBCC_LINKINITCMD_MASK 0x3ULL #define INFINIPATH_IBCC_LINKINITCMD_DISABLE 1 -#define INFINIPATH_IBCC_LINKINITCMD_POLL 2 /* cycle through TS1/TS2 till OK */ -#define INFINIPATH_IBCC_LINKINITCMD_SLEEP 3 /* wait for TS1, then go on */ +/* cycle through TS1/TS2 till OK */ +#define INFINIPATH_IBCC_LINKINITCMD_POLL 2 +/* wait for TS1, then go on */ +#define INFINIPATH_IBCC_LINKINITCMD_SLEEP 3 #define INFINIPATH_IBCC_LINKINITCMD_SHIFT 16 #define INFINIPATH_IBCC_LINKCMD_MASK 0x3ULL #define INFINIPATH_IBCC_LINKCMD_INIT 1 /* move to 0x11 */ @@ -182,7 +185,8 @@ #define INFINIPATH_IBCS_LINKSTATE_SHIFT 4 #define INFINIPATH_IBCS_TXREADY 0x40000000 #define INFINIPATH_IBCS_TXCREDITOK 0x80000000 -/* link training states (shift by INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) */ +/* link training states (shift by + INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) */ #define INFINIPATH_IBCS_LT_STATE_DISABLED 0x00 #define INFINIPATH_IBCS_LT_STATE_LINKUP 0x01 #define INFINIPATH_IBCS_LT_STATE_POLLACTIVE 0x02 @@ -267,10 +271,12 @@ /* kr_serdesconfig0 bits */ #define INFINIPATH_SERDC0_RESET_MASK 0xfULL /* overal reset bits */ #define INFINIPATH_SERDC0_RESET_PLL 0x10000000ULL /* pll reset */ -#define INFINIPATH_SERDC0_TXIDLE 0xF000ULL /* tx idle enables (per lane) */ -#define INFINIPATH_SERDC0_RXDETECT_EN 0xF0000ULL /* rx detect enables (per lane) */ -#define INFINIPATH_SERDC0_L1PWR_DN 0xF0ULL /* L1 Power down; use with RXDETECT, - Otherwise not used on IB side */ +/* tx idle enables (per lane) */ +#define INFINIPATH_SERDC0_TXIDLE 0xF000ULL +/* rx detect enables (per lane) */ +#define INFINIPATH_SERDC0_RXDETECT_EN 0xF0000ULL +/* L1 Power down; use with RXDETECT, Otherwise not used on IB side */ +#define INFINIPATH_SERDC0_L1PWR_DN 0xF0ULL /* kr_xgxsconfig bits */ #define INFINIPATH_XGXS_RESET 0x7ULL @@ -390,12 +396,13 @@ struct ipath_kregs { ipath_kreg kr_txintmemsize; ipath_kreg kr_xgxsconfig; ipath_kreg kr_ibpllcfg; - /* use these two (and the following N ports) only with ipath_k*_kreg64_port(); - * not *kreg64() */ + /* use these two (and the following N ports) only with + * ipath_k*_kreg64_port(); not *kreg64() */ ipath_kreg kr_rcvhdraddr; ipath_kreg kr_rcvhdrtailaddr; - /* remaining registers are not present on all types of infinipath chips */ + /* remaining registers are not present on all types of infinipath + chips */ ipath_kreg kr_rcvpktledcnt; ipath_kreg kr_pcierbuftestreg0; ipath_kreg kr_pcierbuftestreg1; diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index f232e77b78e..eb81424b3c5 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -531,19 +531,12 @@ int ipath_post_rc_send(struct ipath_qp *qp, struct ib_send_wr *wr) } wqe->wr.num_sge = j; qp->s_head = next; - /* - * Wake up the send tasklet if the QP is not waiting - * for an RNR timeout. - */ - next = qp->s_rnr_timeout; spin_unlock_irqrestore(&qp->s_lock, flags); - if (next == 0) { - if (qp->ibqp.qp_type == IB_QPT_UC) - ipath_do_uc_send((unsigned long) qp); - else - ipath_do_rc_send((unsigned long) qp); - } + if (qp->ibqp.qp_type == IB_QPT_UC) + ipath_do_uc_send((unsigned long) qp); + else + ipath_do_rc_send((unsigned long) qp); ret = 0; diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c index 32acd8048b4..f323791cc49 100644 --- a/drivers/infiniband/hw/ipath/ipath_sysfs.c +++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c @@ -711,10 +711,22 @@ static struct attribute_group dev_attr_group = { * enters diag mode. A device reset is quite likely to crash the * machine entirely, so we don't want to normally make it * available. + * + * Called with ipath_mutex held. */ int ipath_expose_reset(struct device *dev) { - return device_create_file(dev, &dev_attr_reset); + static int exposed; + int ret; + + if (!exposed) { + ret = device_create_file(dev, &dev_attr_reset); + exposed = 1; + } + else + ret = 0; + + return ret; } int ipath_driver_create_group(struct device_driver *drv) diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c index 01cfb30ee16..e606daf8321 100644 --- a/drivers/infiniband/hw/ipath/ipath_ud.c +++ b/drivers/infiniband/hw/ipath/ipath_ud.c @@ -46,8 +46,10 @@ * This is called from ipath_post_ud_send() to forward a WQE addressed * to the same HCA. */ -static void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_sge_state *ss, - u32 length, struct ib_send_wr *wr, struct ib_wc *wc) +static void ipath_ud_loopback(struct ipath_qp *sqp, + struct ipath_sge_state *ss, + u32 length, struct ib_send_wr *wr, + struct ib_wc *wc) { struct ipath_ibdev *dev = to_idev(sqp->ibqp.device); struct ipath_qp *qp; diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c index 8d2558a01f3..cb9e387c301 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c @@ -449,7 +449,6 @@ static void ipath_ib_timer(void *arg) { struct ipath_ibdev *dev = (struct ipath_ibdev *) arg; struct ipath_qp *resend = NULL; - struct ipath_qp *rnr = NULL; struct list_head *last; struct ipath_qp *qp; unsigned long flags; @@ -465,32 +464,18 @@ static void ipath_ib_timer(void *arg) last = &dev->pending[dev->pending_index]; while (!list_empty(last)) { qp = list_entry(last->next, struct ipath_qp, timerwait); - if (last->next == LIST_POISON1 || - last->next != &qp->timerwait || - qp->timerwait.prev != last) { - INIT_LIST_HEAD(last); - } else { - list_del(&qp->timerwait); - qp->timerwait.prev = (struct list_head *) resend; - resend = qp; - atomic_inc(&qp->refcount); - } + list_del(&qp->timerwait); + qp->timer_next = resend; + resend = qp; + atomic_inc(&qp->refcount); } last = &dev->rnrwait; if (!list_empty(last)) { qp = list_entry(last->next, struct ipath_qp, timerwait); if (--qp->s_rnr_timeout == 0) { do { - if (last->next == LIST_POISON1 || - last->next != &qp->timerwait || - qp->timerwait.prev != last) { - INIT_LIST_HEAD(last); - break; - } list_del(&qp->timerwait); - qp->timerwait.prev = - (struct list_head *) rnr; - rnr = qp; + tasklet_hi_schedule(&qp->s_task); if (list_empty(last)) break; qp = list_entry(last->next, struct ipath_qp, @@ -530,8 +515,7 @@ static void ipath_ib_timer(void *arg) spin_unlock_irqrestore(&dev->pending_lock, flags); /* XXX What if timer fires again while this is running? */ - for (qp = resend; qp != NULL; - qp = (struct ipath_qp *) qp->timerwait.prev) { + for (qp = resend; qp != NULL; qp = qp->timer_next) { struct ib_wc wc; spin_lock_irqsave(&qp->s_lock, flags); @@ -545,9 +529,6 @@ static void ipath_ib_timer(void *arg) if (atomic_dec_and_test(&qp->refcount)) wake_up(&qp->wait); } - for (qp = rnr; qp != NULL; - qp = (struct ipath_qp *) qp->timerwait.prev) - tasklet_hi_schedule(&qp->s_task); } /** @@ -556,9 +537,9 @@ static void ipath_ib_timer(void *arg) * * This is called from ipath_intr() at interrupt level when a PIO buffer is * available after ipath_verbs_send() returned an error that no buffers were - * available. Return 0 if we consumed all the PIO buffers and we still have + * available. Return 1 if we consumed all the PIO buffers and we still have * QPs waiting for buffers (for now, just do a tasklet_hi_schedule and - * return one). + * return zero). */ static int ipath_ib_piobufavail(void *arg) { @@ -579,7 +560,7 @@ static int ipath_ib_piobufavail(void *arg) spin_unlock_irqrestore(&dev->pending_lock, flags); bail: - return 1; + return 0; } static int ipath_query_device(struct ib_device *ibdev, @@ -1159,7 +1140,7 @@ static ssize_t show_stats(struct class_device *cdev, char *buf) len = sprintf(buf, "RC resends %d\n" - "RC QACKs %d\n" + "RC no QACK %d\n" "RC ACKs %d\n" "RC SEQ NAKs %d\n" "RC RDMA seq %d\n" diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h index fcafbc7c9e7..4f8d59300e9 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.h +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h @@ -282,7 +282,8 @@ struct ipath_srq { */ struct ipath_qp { struct ib_qp ibqp; - struct ipath_qp *next; /* link list for QPN hash table */ + struct ipath_qp *next; /* link list for QPN hash table */ + struct ipath_qp *timer_next; /* link list for ipath_ib_timer() */ struct list_head piowait; /* link for wait PIO buf */ struct list_head timerwait; /* link for waiting for timeouts */ struct ib_ah_attr remote_ah_attr; diff --git a/drivers/infiniband/hw/ipath/ips_common.h b/drivers/infiniband/hw/ipath/ips_common.h index 410a764dfce..ab7cbbbfd03 100644 --- a/drivers/infiniband/hw/ipath/ips_common.h +++ b/drivers/infiniband/hw/ipath/ips_common.h @@ -95,7 +95,7 @@ struct ether_header { __u8 seq_num; __le32 len; /* MUST be of word size due to PIO write requirements */ - __u32 csum; + __le32 csum; __le16 csum_offset; __le16 flags; __u16 first_2_bytes; diff --git a/drivers/infiniband/hw/mthca/mthca_cq.c b/drivers/infiniband/hw/mthca/mthca_cq.c index 312cf90731e..205854e9c66 100644 --- a/drivers/infiniband/hw/mthca/mthca_cq.c +++ b/drivers/infiniband/hw/mthca/mthca_cq.c @@ -238,9 +238,9 @@ void mthca_cq_event(struct mthca_dev *dev, u32 cqn, spin_lock(&dev->cq_table.lock); cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1)); - if (cq) - atomic_inc(&cq->refcount); + ++cq->refcount; + spin_unlock(&dev->cq_table.lock); if (!cq) { @@ -254,8 +254,10 @@ void mthca_cq_event(struct mthca_dev *dev, u32 cqn, if (cq->ibcq.event_handler) cq->ibcq.event_handler(&event, cq->ibcq.cq_context); - if (atomic_dec_and_test(&cq->refcount)) + spin_lock(&dev->cq_table.lock); + if (!--cq->refcount) wake_up(&cq->wait); + spin_unlock(&dev->cq_table.lock); } static inline int is_recv_cqe(struct mthca_cqe *cqe) @@ -267,23 +269,13 @@ static inline int is_recv_cqe(struct mthca_cqe *cqe) return !(cqe->is_send & 0x80); } -void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn, +void mthca_cq_clean(struct mthca_dev *dev, struct mthca_cq *cq, u32 qpn, struct mthca_srq *srq) { - struct mthca_cq *cq; struct mthca_cqe *cqe; u32 prod_index; int nfreed = 0; - spin_lock_irq(&dev->cq_table.lock); - cq = mthca_array_get(&dev->cq_table.cq, cqn & (dev->limits.num_cqs - 1)); - if (cq) - atomic_inc(&cq->refcount); - spin_unlock_irq(&dev->cq_table.lock); - - if (!cq) - return; - spin_lock_irq(&cq->lock); /* @@ -301,7 +293,7 @@ void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn, if (0) mthca_dbg(dev, "Cleaning QPN %06x from CQN %06x; ci %d, pi %d\n", - qpn, cqn, cq->cons_index, prod_index); + qpn, cq->cqn, cq->cons_index, prod_index); /* * Now sweep backwards through the CQ, removing CQ entries @@ -325,8 +317,6 @@ void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn, } spin_unlock_irq(&cq->lock); - if (atomic_dec_and_test(&cq->refcount)) - wake_up(&cq->wait); } void mthca_cq_resize_copy_cqes(struct mthca_cq *cq) @@ -821,7 +811,7 @@ int mthca_init_cq(struct mthca_dev *dev, int nent, } spin_lock_init(&cq->lock); - atomic_set(&cq->refcount, 1); + cq->refcount = 1; init_waitqueue_head(&cq->wait); memset(cq_context, 0, sizeof *cq_context); @@ -896,6 +886,17 @@ err_out: return err; } +static inline int get_cq_refcount(struct mthca_dev *dev, struct mthca_cq *cq) +{ + int c; + + spin_lock_irq(&dev->cq_table.lock); + c = cq->refcount; + spin_unlock_irq(&dev->cq_table.lock); + + return c; +} + void mthca_free_cq(struct mthca_dev *dev, struct mthca_cq *cq) { @@ -929,6 +930,7 @@ void mthca_free_cq(struct mthca_dev *dev, spin_lock_irq(&dev->cq_table.lock); mthca_array_clear(&dev->cq_table.cq, cq->cqn & (dev->limits.num_cqs - 1)); + --cq->refcount; spin_unlock_irq(&dev->cq_table.lock); if (dev->mthca_flags & MTHCA_FLAG_MSI_X) @@ -936,8 +938,7 @@ void mthca_free_cq(struct mthca_dev *dev, else synchronize_irq(dev->pdev->irq); - atomic_dec(&cq->refcount); - wait_event(cq->wait, !atomic_read(&cq->refcount)); + wait_event(cq->wait, !get_cq_refcount(dev, cq)); if (cq->is_kernel) { mthca_free_cq_buf(dev, &cq->buf, cq->ibcq.cqe); diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h index 4c1dcb4c182..f8160b8de09 100644 --- a/drivers/infiniband/hw/mthca/mthca_dev.h +++ b/drivers/infiniband/hw/mthca/mthca_dev.h @@ -496,7 +496,7 @@ void mthca_free_cq(struct mthca_dev *dev, void mthca_cq_completion(struct mthca_dev *dev, u32 cqn); void mthca_cq_event(struct mthca_dev *dev, u32 cqn, enum ib_event_type event_type); -void mthca_cq_clean(struct mthca_dev *dev, u32 cqn, u32 qpn, +void mthca_cq_clean(struct mthca_dev *dev, struct mthca_cq *cq, u32 qpn, struct mthca_srq *srq); void mthca_cq_resize_copy_cqes(struct mthca_cq *cq); int mthca_alloc_cq_buf(struct mthca_dev *dev, struct mthca_cq_buf *buf, int nent); diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c index 25e1c1db9a4..a486dec1707 100644 --- a/drivers/infiniband/hw/mthca/mthca_mr.c +++ b/drivers/infiniband/hw/mthca/mthca_mr.c @@ -761,6 +761,7 @@ void mthca_arbel_fmr_unmap(struct mthca_dev *dev, struct mthca_fmr *fmr) int __devinit mthca_init_mr_table(struct mthca_dev *dev) { + unsigned long addr; int err, i; err = mthca_alloc_init(&dev->mr_table.mpt_alloc, @@ -796,9 +797,12 @@ int __devinit mthca_init_mr_table(struct mthca_dev *dev) goto err_fmr_mpt; } + addr = pci_resource_start(dev->pdev, 4) + + ((pci_resource_len(dev->pdev, 4) - 1) & + dev->mr_table.mpt_base); + dev->mr_table.tavor_fmr.mpt_base = - ioremap(dev->mr_table.mpt_base, - (1 << i) * sizeof (struct mthca_mpt_entry)); + ioremap(addr, (1 << i) * sizeof(struct mthca_mpt_entry)); if (!dev->mr_table.tavor_fmr.mpt_base) { mthca_warn(dev, "MPT ioremap for FMR failed.\n"); @@ -806,9 +810,12 @@ int __devinit mthca_init_mr_table(struct mthca_dev *dev) goto err_fmr_mpt; } + addr = pci_resource_start(dev->pdev, 4) + + ((pci_resource_len(dev->pdev, 4) - 1) & + dev->mr_table.mtt_base); + dev->mr_table.tavor_fmr.mtt_base = - ioremap(dev->mr_table.mtt_base, - (1 << i) * MTHCA_MTT_SEG_SIZE); + ioremap(addr, (1 << i) * MTHCA_MTT_SEG_SIZE); if (!dev->mr_table.tavor_fmr.mtt_base) { mthca_warn(dev, "MTT ioremap for FMR failed.\n"); err = -ENOMEM; diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 565a24b1756..a2eae8a3016 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -306,7 +306,7 @@ static int mthca_query_gid(struct ib_device *ibdev, u8 port, goto out; } - memcpy(gid->raw + 8, out_mad->data + (index % 8) * 16, 8); + memcpy(gid->raw + 8, out_mad->data + (index % 8) * 8, 8); out: kfree(in_mad); diff --git a/drivers/infiniband/hw/mthca/mthca_provider.h b/drivers/infiniband/hw/mthca/mthca_provider.h index 6676a786d69..179a8f610d0 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.h +++ b/drivers/infiniband/hw/mthca/mthca_provider.h @@ -139,11 +139,12 @@ struct mthca_ah { * a qp may be locked, with the send cq locked first. No other * nesting should be done. * - * Each struct mthca_cq/qp also has an atomic_t ref count. The - * pointer from the cq/qp_table to the struct counts as one reference. - * This reference also is good for access through the consumer API, so - * modifying the CQ/QP etc doesn't need to take another reference. - * Access because of a completion being polled does need a reference. + * Each struct mthca_cq/qp also has an ref count, protected by the + * corresponding table lock. The pointer from the cq/qp_table to the + * struct counts as one reference. This reference also is good for + * access through the consumer API, so modifying the CQ/QP etc doesn't + * need to take another reference. Access to a QP because of a + * completion being polled does not need a reference either. * * Finally, each struct mthca_cq/qp has a wait_queue_head_t for the * destroy function to sleep on. @@ -159,8 +160,9 @@ struct mthca_ah { * - decrement ref count; if zero, wake up waiters * * To destroy a CQ/QP, we can do the following: - * - lock cq/qp_table, remove pointer, unlock cq/qp_table lock - * - decrement ref count + * - lock cq/qp_table + * - remove pointer and decrement ref count + * - unlock cq/qp_table lock * - wait_event until ref count is zero * * It is the consumer's responsibilty to make sure that no QP @@ -197,7 +199,7 @@ struct mthca_cq_resize { struct mthca_cq { struct ib_cq ibcq; spinlock_t lock; - atomic_t refcount; + int refcount; int cqn; u32 cons_index; struct mthca_cq_buf buf; @@ -217,7 +219,7 @@ struct mthca_cq { struct mthca_srq { struct ib_srq ibsrq; spinlock_t lock; - atomic_t refcount; + int refcount; int srqn; int max; int max_gs; @@ -254,7 +256,7 @@ struct mthca_wq { struct mthca_qp { struct ib_qp ibqp; - atomic_t refcount; + int refcount; u32 qpn; int is_direct; u8 port; /* for SQP and memfree use only */ diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index f37b0e36732..19765f6f8d5 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -240,7 +240,7 @@ void mthca_qp_event(struct mthca_dev *dev, u32 qpn, spin_lock(&dev->qp_table.lock); qp = mthca_array_get(&dev->qp_table.qp, qpn & (dev->limits.num_qps - 1)); if (qp) - atomic_inc(&qp->refcount); + ++qp->refcount; spin_unlock(&dev->qp_table.lock); if (!qp) { @@ -257,8 +257,10 @@ void mthca_qp_event(struct mthca_dev *dev, u32 qpn, if (qp->ibqp.event_handler) qp->ibqp.event_handler(&event, qp->ibqp.qp_context); - if (atomic_dec_and_test(&qp->refcount)) + spin_lock(&dev->qp_table.lock); + if (!--qp->refcount) wake_up(&qp->wait); + spin_unlock(&dev->qp_table.lock); } static int to_mthca_state(enum ib_qp_state ib_state) @@ -833,10 +835,10 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask) * entries and reinitialize the QP. */ if (new_state == IB_QPS_RESET && !qp->ibqp.uobject) { - mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq)->cqn, qp->qpn, + mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq), qp->qpn, qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); if (qp->ibqp.send_cq != qp->ibqp.recv_cq) - mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq)->cqn, qp->qpn, + mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn, qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); mthca_wq_init(&qp->sq); @@ -1096,7 +1098,7 @@ static int mthca_alloc_qp_common(struct mthca_dev *dev, int ret; int i; - atomic_set(&qp->refcount, 1); + qp->refcount = 1; init_waitqueue_head(&qp->wait); qp->state = IB_QPS_RESET; qp->atomic_rd_en = 0; @@ -1318,6 +1320,17 @@ int mthca_alloc_sqp(struct mthca_dev *dev, return err; } +static inline int get_qp_refcount(struct mthca_dev *dev, struct mthca_qp *qp) +{ + int c; + + spin_lock_irq(&dev->qp_table.lock); + c = qp->refcount; + spin_unlock_irq(&dev->qp_table.lock); + + return c; +} + void mthca_free_qp(struct mthca_dev *dev, struct mthca_qp *qp) { @@ -1339,14 +1352,14 @@ void mthca_free_qp(struct mthca_dev *dev, spin_lock(&dev->qp_table.lock); mthca_array_clear(&dev->qp_table.qp, qp->qpn & (dev->limits.num_qps - 1)); + --qp->refcount; spin_unlock(&dev->qp_table.lock); if (send_cq != recv_cq) spin_unlock(&recv_cq->lock); spin_unlock_irq(&send_cq->lock); - atomic_dec(&qp->refcount); - wait_event(qp->wait, !atomic_read(&qp->refcount)); + wait_event(qp->wait, !get_qp_refcount(dev, qp)); if (qp->state != IB_QPS_RESET) mthca_MODIFY_QP(dev, qp->state, IB_QPS_RESET, qp->qpn, 0, @@ -1358,10 +1371,10 @@ void mthca_free_qp(struct mthca_dev *dev, * unref the mem-free tables and free the QPN in our table. */ if (!qp->ibqp.uobject) { - mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq)->cqn, qp->qpn, + mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq), qp->qpn, qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); if (qp->ibqp.send_cq != qp->ibqp.recv_cq) - mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq)->cqn, qp->qpn, + mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn, qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); mthca_free_memfree(dev, qp); diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c index adcaf85355a..1ea433291fa 100644 --- a/drivers/infiniband/hw/mthca/mthca_srq.c +++ b/drivers/infiniband/hw/mthca/mthca_srq.c @@ -241,7 +241,7 @@ int mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd, goto err_out_mailbox; spin_lock_init(&srq->lock); - atomic_set(&srq->refcount, 1); + srq->refcount = 1; init_waitqueue_head(&srq->wait); if (mthca_is_memfree(dev)) @@ -308,6 +308,17 @@ err_out: return err; } +static inline int get_srq_refcount(struct mthca_dev *dev, struct mthca_srq *srq) +{ + int c; + + spin_lock_irq(&dev->srq_table.lock); + c = srq->refcount; + spin_unlock_irq(&dev->srq_table.lock); + + return c; +} + void mthca_free_srq(struct mthca_dev *dev, struct mthca_srq *srq) { struct mthca_mailbox *mailbox; @@ -329,10 +340,10 @@ void mthca_free_srq(struct mthca_dev *dev, struct mthca_srq *srq) spin_lock_irq(&dev->srq_table.lock); mthca_array_clear(&dev->srq_table.srq, srq->srqn & (dev->limits.num_srqs - 1)); + --srq->refcount; spin_unlock_irq(&dev->srq_table.lock); - atomic_dec(&srq->refcount); - wait_event(srq->wait, !atomic_read(&srq->refcount)); + wait_event(srq->wait, !get_srq_refcount(dev, srq)); if (!srq->ibsrq.uobject) { mthca_free_srq_buf(dev, srq); @@ -414,7 +425,7 @@ void mthca_srq_event(struct mthca_dev *dev, u32 srqn, spin_lock(&dev->srq_table.lock); srq = mthca_array_get(&dev->srq_table.srq, srqn & (dev->limits.num_srqs - 1)); if (srq) - atomic_inc(&srq->refcount); + ++srq->refcount; spin_unlock(&dev->srq_table.lock); if (!srq) { @@ -431,8 +442,10 @@ void mthca_srq_event(struct mthca_dev *dev, u32 srqn, srq->ibsrq.event_handler(&event, srq->ibsrq.srq_context); out: - if (atomic_dec_and_test(&srq->refcount)) + spin_lock(&dev->srq_table.lock); + if (!--srq->refcount) wake_up(&srq->wait); + spin_unlock(&dev->srq_table.lock); } /* diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index 4ca175553f9..f887780e809 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -158,10 +158,8 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey) if (priv->pkey == pkey) { unregister_netdev(priv->dev); ipoib_dev_cleanup(priv->dev); - list_del(&priv->list); - - kfree(priv); + free_netdev(priv->dev); ret = 0; break; diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 5bb55742ada..c32ce4348e1 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -409,6 +409,34 @@ static int srp_connect_target(struct srp_target_port *target) } } +static void srp_unmap_data(struct scsi_cmnd *scmnd, + struct srp_target_port *target, + struct srp_request *req) +{ + struct scatterlist *scat; + int nents; + + if (!scmnd->request_buffer || + (scmnd->sc_data_direction != DMA_TO_DEVICE && + scmnd->sc_data_direction != DMA_FROM_DEVICE)) + return; + + /* + * This handling of non-SG commands can be killed when the + * SCSI midlayer no longer generates non-SG commands. + */ + if (likely(scmnd->use_sg)) { + nents = scmnd->use_sg; + scat = scmnd->request_buffer; + } else { + nents = 1; + scat = &req->fake_sg; + } + + dma_unmap_sg(target->srp_host->dev->dma_device, scat, nents, + scmnd->sc_data_direction); +} + static int srp_reconnect_target(struct srp_target_port *target) { struct ib_cm_id *new_cm_id; @@ -455,16 +483,16 @@ static int srp_reconnect_target(struct srp_target_port *target) list_for_each_entry(req, &target->req_queue, list) { req->scmnd->result = DID_RESET << 16; req->scmnd->scsi_done(req->scmnd); + srp_unmap_data(req->scmnd, target, req); } target->rx_head = 0; target->tx_head = 0; target->tx_tail = 0; - target->req_head = 0; - for (i = 0; i < SRP_SQ_SIZE - 1; ++i) - target->req_ring[i].next = i + 1; - target->req_ring[SRP_SQ_SIZE - 1].next = -1; + INIT_LIST_HEAD(&target->free_reqs); INIT_LIST_HEAD(&target->req_queue); + for (i = 0; i < SRP_SQ_SIZE; ++i) + list_add_tail(&target->req_ring[i].list, &target->free_reqs); ret = srp_connect_target(target); if (ret) @@ -589,40 +617,10 @@ static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target, return len; } -static void srp_unmap_data(struct scsi_cmnd *scmnd, - struct srp_target_port *target, - struct srp_request *req) -{ - struct scatterlist *scat; - int nents; - - if (!scmnd->request_buffer || - (scmnd->sc_data_direction != DMA_TO_DEVICE && - scmnd->sc_data_direction != DMA_FROM_DEVICE)) - return; - - /* - * This handling of non-SG commands can be killed when the - * SCSI midlayer no longer generates non-SG commands. - */ - if (likely(scmnd->use_sg)) { - nents = scmnd->use_sg; - scat = scmnd->request_buffer; - } else { - nents = 1; - scat = &req->fake_sg; - } - - dma_unmap_sg(target->srp_host->dev->dma_device, scat, nents, - scmnd->sc_data_direction); -} - -static void srp_remove_req(struct srp_target_port *target, struct srp_request *req, - int index) +static void srp_remove_req(struct srp_target_port *target, struct srp_request *req) { - list_del(&req->list); - req->next = target->req_head; - target->req_head = index; + srp_unmap_data(req->scmnd, target, req); + list_move_tail(&req->list, &target->free_reqs); } static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) @@ -647,7 +645,7 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) req->tsk_status = rsp->data[3]; complete(&req->done); } else { - scmnd = req->scmnd; + scmnd = req->scmnd; if (!scmnd) printk(KERN_ERR "Null scmnd for RSP w/tag %016llx\n", (unsigned long long) rsp->tag); @@ -665,14 +663,11 @@ static void srp_process_rsp(struct srp_target_port *target, struct srp_rsp *rsp) else if (rsp->flags & (SRP_RSP_FLAG_DIOVER | SRP_RSP_FLAG_DIUNDER)) scmnd->resid = be32_to_cpu(rsp->data_in_res_cnt); - srp_unmap_data(scmnd, target, req); - if (!req->tsk_mgmt) { - req->scmnd = NULL; scmnd->host_scribble = (void *) -1L; scmnd->scsi_done(scmnd); - srp_remove_req(target, req, rsp->tag & ~SRP_TAG_TSK_MGMT); + srp_remove_req(target, req); } else req->cmd_done = 1; } @@ -859,7 +854,6 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd, struct srp_request *req; struct srp_iu *iu; struct srp_cmd *cmd; - long req_index; int len; if (target->state == SRP_TARGET_CONNECTING) @@ -879,22 +873,20 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd, dma_sync_single_for_cpu(target->srp_host->dev->dma_device, iu->dma, SRP_MAX_IU_LEN, DMA_TO_DEVICE); - req_index = target->req_head; + req = list_entry(target->free_reqs.next, struct srp_request, list); scmnd->scsi_done = done; scmnd->result = 0; - scmnd->host_scribble = (void *) req_index; + scmnd->host_scribble = (void *) (long) req->index; cmd = iu->buf; memset(cmd, 0, sizeof *cmd); cmd->opcode = SRP_CMD; cmd->lun = cpu_to_be64((u64) scmnd->device->lun << 48); - cmd->tag = req_index; + cmd->tag = req->index; memcpy(cmd->cdb, scmnd->cmnd, scmnd->cmd_len); - req = &target->req_ring[req_index]; - req->scmnd = scmnd; req->cmd = iu; req->cmd_done = 0; @@ -919,8 +911,7 @@ static int srp_queuecommand(struct scsi_cmnd *scmnd, goto err_unmap; } - target->req_head = req->next; - list_add_tail(&req->list, &target->req_queue); + list_move_tail(&req->list, &target->req_queue); return 0; @@ -1143,30 +1134,20 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event) return 0; } -static int srp_send_tsk_mgmt(struct scsi_cmnd *scmnd, u8 func) +static int srp_send_tsk_mgmt(struct srp_target_port *target, + struct srp_request *req, u8 func) { - struct srp_target_port *target = host_to_target(scmnd->device->host); - struct srp_request *req; struct srp_iu *iu; struct srp_tsk_mgmt *tsk_mgmt; - int req_index; - int ret = FAILED; spin_lock_irq(target->scsi_host->host_lock); if (target->state == SRP_TARGET_DEAD || target->state == SRP_TARGET_REMOVED) { - scmnd->result = DID_BAD_TARGET << 16; + req->scmnd->result = DID_BAD_TARGET << 16; goto out; } - if (scmnd->host_scribble == (void *) -1L) - goto out; - - req_index = (long) scmnd->host_scribble; - printk(KERN_ERR "Abort for req_index %d\n", req_index); - - req = &target->req_ring[req_index]; init_completion(&req->done); iu = __srp_get_tx_iu(target); @@ -1177,10 +1158,10 @@ static int srp_send_tsk_mgmt(struct scsi_cmnd *scmnd, u8 func) memset(tsk_mgmt, 0, sizeof *tsk_mgmt); tsk_mgmt->opcode = SRP_TSK_MGMT; - tsk_mgmt->lun = cpu_to_be64((u64) scmnd->device->lun << 48); - tsk_mgmt->tag = req_index | SRP_TAG_TSK_MGMT; + tsk_mgmt->lun = cpu_to_be64((u64) req->scmnd->device->lun << 48); + tsk_mgmt->tag = req->index | SRP_TAG_TSK_MGMT; tsk_mgmt->tsk_mgmt_func = func; - tsk_mgmt->task_tag = req_index; + tsk_mgmt->task_tag = req->index; if (__srp_post_send(target, iu, sizeof *tsk_mgmt)) goto out; @@ -1188,37 +1169,85 @@ static int srp_send_tsk_mgmt(struct scsi_cmnd *scmnd, u8 func) req->tsk_mgmt = iu; spin_unlock_irq(target->scsi_host->host_lock); + if (!wait_for_completion_timeout(&req->done, msecs_to_jiffies(SRP_ABORT_TIMEOUT_MS))) - return FAILED; - spin_lock_irq(target->scsi_host->host_lock); + return -1; - if (req->cmd_done) { - srp_remove_req(target, req, req_index); - scmnd->scsi_done(scmnd); - } else if (!req->tsk_status) { - srp_remove_req(target, req, req_index); - scmnd->result = DID_ABORT << 16; - ret = SUCCESS; - } + return 0; out: spin_unlock_irq(target->scsi_host->host_lock); - return ret; + return -1; +} + +static int srp_find_req(struct srp_target_port *target, + struct scsi_cmnd *scmnd, + struct srp_request **req) +{ + if (scmnd->host_scribble == (void *) -1L) + return -1; + + *req = &target->req_ring[(long) scmnd->host_scribble]; + + return 0; } static int srp_abort(struct scsi_cmnd *scmnd) { + struct srp_target_port *target = host_to_target(scmnd->device->host); + struct srp_request *req; + int ret = SUCCESS; + printk(KERN_ERR "SRP abort called\n"); - return srp_send_tsk_mgmt(scmnd, SRP_TSK_ABORT_TASK); + if (srp_find_req(target, scmnd, &req)) + return FAILED; + if (srp_send_tsk_mgmt(target, req, SRP_TSK_ABORT_TASK)) + return FAILED; + + spin_lock_irq(target->scsi_host->host_lock); + + if (req->cmd_done) { + srp_remove_req(target, req); + scmnd->scsi_done(scmnd); + } else if (!req->tsk_status) { + srp_remove_req(target, req); + scmnd->result = DID_ABORT << 16; + } else + ret = FAILED; + + spin_unlock_irq(target->scsi_host->host_lock); + + return ret; } static int srp_reset_device(struct scsi_cmnd *scmnd) { + struct srp_target_port *target = host_to_target(scmnd->device->host); + struct srp_request *req, *tmp; + printk(KERN_ERR "SRP reset_device called\n"); - return srp_send_tsk_mgmt(scmnd, SRP_TSK_LUN_RESET); + if (srp_find_req(target, scmnd, &req)) + return FAILED; + if (srp_send_tsk_mgmt(target, req, SRP_TSK_LUN_RESET)) + return FAILED; + if (req->tsk_status) + return FAILED; + + spin_lock_irq(target->scsi_host->host_lock); + + list_for_each_entry_safe(req, tmp, &target->req_queue, list) + if (req->scmnd->device == scmnd->device) { + req->scmnd->result = DID_RESET << 16; + scmnd->scsi_done(scmnd); + srp_remove_req(target, req); + } + + spin_unlock_irq(target->scsi_host->host_lock); + + return SUCCESS; } static int srp_reset_host(struct scsi_cmnd *scmnd) @@ -1518,10 +1547,12 @@ static ssize_t srp_create_target(struct class_device *class_dev, INIT_WORK(&target->work, srp_reconnect_work, target); - for (i = 0; i < SRP_SQ_SIZE - 1; ++i) - target->req_ring[i].next = i + 1; - target->req_ring[SRP_SQ_SIZE - 1].next = -1; + INIT_LIST_HEAD(&target->free_reqs); INIT_LIST_HEAD(&target->req_queue); + for (i = 0; i < SRP_SQ_SIZE; ++i) { + target->req_ring[i].index = i; + list_add_tail(&target->req_ring[i].list, &target->free_reqs); + } ret = srp_parse_options(buf, target); if (ret) diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h index bd7f7c3115d..c5cd43aae86 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.h +++ b/drivers/infiniband/ulp/srp/ib_srp.h @@ -101,7 +101,7 @@ struct srp_request { */ struct scatterlist fake_sg; struct completion done; - short next; + short index; u8 cmd_done; u8 tsk_status; }; @@ -133,7 +133,7 @@ struct srp_target_port { unsigned tx_tail; struct srp_iu *tx_ring[SRP_SQ_SIZE + 1]; - int req_head; + struct list_head free_reqs; struct list_head req_queue; struct srp_request req_ring[SRP_SQ_SIZE]; diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a34e3d91d9e..ba325f16d07 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -403,6 +403,27 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, case EVIOCGID: if (copy_to_user(p, &dev->id, sizeof(struct input_id))) return -EFAULT; + return 0; + + case EVIOCGREP: + if (!test_bit(EV_REP, dev->evbit)) + return -ENOSYS; + if (put_user(dev->rep[REP_DELAY], ip)) + return -EFAULT; + if (put_user(dev->rep[REP_PERIOD], ip + 1)) + return -EFAULT; + return 0; + + case EVIOCSREP: + if (!test_bit(EV_REP, dev->evbit)) + return -ENOSYS; + if (get_user(u, ip)) + return -EFAULT; + if (get_user(v, ip + 1)) + return -EFAULT; + + input_event(dev, EV_REP, REP_DELAY, u); + input_event(dev, EV_REP, REP_PERIOD, v); return 0; diff --git a/drivers/input/input.c b/drivers/input/input.c index a935abeffff..3038c268917 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -155,6 +155,9 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in if (code > SND_MAX || !test_bit(code, dev->sndbit)) return; + if (!!test_bit(code, dev->snd) != !!value) + change_bit(code, dev->snd); + if (dev->event) dev->event(dev, type, code, value); break; @@ -286,19 +289,19 @@ static struct input_device_id *input_match_device(struct input_device_id *id, st for (; id->flags || id->driver_info; id++) { if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) - if (id->id.bustype != dev->id.bustype) + if (id->bustype != dev->id.bustype) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) - if (id->id.vendor != dev->id.vendor) + if (id->vendor != dev->id.vendor) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) - if (id->id.product != dev->id.product) + if (id->product != dev->id.product) continue; if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) - if (id->id.version != dev->id.version) + if (id->version != dev->id.version) continue; MATCH_BIT(evbit, EV_MAX); diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c index bc61cf8cfc6..1d238a9d52d 100644 --- a/drivers/input/keyboard/spitzkbd.c +++ b/drivers/input/keyboard/spitzkbd.c @@ -53,8 +53,8 @@ static unsigned char spitzkbd_keycode[NR_SCANCODES] = { KEY_LEFTCTRL, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, SPITZ_KEY_EXOK, SPITZ_KEY_EXCANCEL, 0, 0, 0, 0, 0, /* 1-16 */ 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, SPITZ_KEY_EXJOGDOWN, SPITZ_KEY_EXJOGUP, 0, 0, 0, 0, 0, /* 17-32 */ KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */ - SPITZ_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */ - SPITZ_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 65-80 */ + SPITZ_KEY_ADDRESS, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */ + SPITZ_KEY_CALENDER, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 65-80 */ SPITZ_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, SPITZ_KEY_FN, 0, 0, 0, 0, 0, /* 81-96 */ KEY_SYSRQ, SPITZ_KEY_JAP1, SPITZ_KEY_JAP2, SPITZ_KEY_CANCEL, SPITZ_KEY_OK, SPITZ_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0 /* 97-112 */ }; diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 4b415d9b012..36cd2e07fce 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -273,6 +273,18 @@ static struct key_entry keymap_fs_amilo_pro_v2000[] = { { KE_END, 0 } }; +static struct key_entry keymap_fujitsu_n3510[] = { + { KE_KEY, 0x11, KEY_PROG1 }, + { KE_KEY, 0x12, KEY_PROG2 }, + { KE_KEY, 0x36, KEY_WWW }, + { KE_KEY, 0x31, KEY_MAIL }, + { KE_KEY, 0x71, KEY_STOPCD }, + { KE_KEY, 0x72, KEY_PLAYPAUSE }, + { KE_KEY, 0x74, KEY_REWIND }, + { KE_KEY, 0x78, KEY_FORWARD }, + { KE_END, 0 } +}; + static struct key_entry keymap_wistron_ms2141[] = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, @@ -323,6 +335,24 @@ static struct dmi_system_id dmi_ids[] = { }, { .callback = dmi_matched, + .ident = "Fujitsu-Siemens Amilo M7400", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "), + }, + .driver_data = keymap_fs_amilo_pro_v2000 + }, + { + .callback = dmi_matched, + .ident = "Fujitsu N3510", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "N3510"), + }, + .driver_data = keymap_fujitsu_n3510 + }, + { + .callback = dmi_matched, .ident = "Acer Aspire 1500", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Acer"), diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 32d70ed8f41..136321a2cfd 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -302,8 +302,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio, * Check if this is a new device announcement (0xAA 0x00) */ if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) { - if (psmouse->pktcnt == 1) + if (psmouse->pktcnt == 1) { + psmouse->last = jiffies; goto out; + } if (psmouse->packet[1] == PSMOUSE_RET_ID) { __psmouse_set_state(psmouse, PSMOUSE_IGNORE); diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index 9a922164425..cc21914fbc7 100644 --- a/drivers/input/serio/i8042-io.h +++ b/drivers/input/serio/i8042-io.h @@ -67,14 +67,14 @@ static inline int i8042_platform_init(void) * On some platforms touching the i8042 data register region can do really * bad things. Because of this the region is always reserved on such boxes. */ -#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) && !defined(CONFIG_PPC64) +#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) && !defined(CONFIG_PPC_MERGE) if (!request_region(I8042_DATA_REG, 16, "i8042")) return -EBUSY; #endif i8042_reset = 1; -#if defined(CONFIG_PPC64) +#if defined(CONFIG_PPC_MERGE) if (check_legacy_ioport(I8042_DATA_REG)) return -EBUSY; if (!request_region(I8042_DATA_REG, 16, "i8042")) diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 46d1fec2cfd..1494175ac6f 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -2,6 +2,8 @@ * ADS7846 based touchscreen and sensor driver * * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * Various changes: Imre Deak <imre.deak@nokia.com> * * Using code from: * - corgi_ts.c @@ -34,17 +36,25 @@ /* - * This code has been lightly tested on an ads7846. + * This code has been tested on an ads7846 / N770 device. * Support for ads7843 and ads7845 has only been stubbed in. * - * Not yet done: investigate the values reported. Are x/y/pressure - * event values sane enough for X11? How accurate are the temperature - * and voltage readings? (System-specific calibration should support + * Not yet done: How accurate are the temperature and voltage + * readings? (System-specific calibration should support * accuracy of 0.3 degrees C; otherwise it's 2.0 degrees.) * + * IRQ handling needs a workaround because of a shortcoming in handling + * edge triggered IRQs on some platforms like the OMAP1/2. These + * platforms don't handle the ARM lazy IRQ disabling properly, thus we + * have to maintain our own SW IRQ disabled status. This should be + * removed as soon as the affected platform's IRQ handling is fixed. + * * app note sbaa036 talks in more detail about accurate sampling... * that ought to help in situations like LCDs inducing noise (which * can also be helped by using synch signals) and more generally. + * This driver tries to utilize the measures described in the app + * note. The strength of filtering can be set in the board-* specific + * files. */ #define TS_POLL_PERIOD msecs_to_jiffies(10) @@ -61,6 +71,7 @@ struct ts_event { __be16 x; __be16 y; __be16 z1, z2; + int ignore; }; struct ads7846 { @@ -71,12 +82,23 @@ struct ads7846 { u16 model; u16 vref_delay_usecs; u16 x_plate_ohms; + u16 pressure_max; - u8 read_x, read_y, read_z1, read_z2; + u8 read_x, read_y, read_z1, read_z2, pwrdown; + u16 dummy; /* for the pwrdown read */ struct ts_event tc; - struct spi_transfer xfer[8]; - struct spi_message msg; + struct spi_transfer xfer[10]; + struct spi_message msg[5]; + struct spi_message *last_msg; + int msg_idx; + int read_cnt; + int read_rep; + int last_read; + + u16 debounce_max; + u16 debounce_tol; + u16 debounce_rep; spinlock_t lock; struct timer_list timer; /* P: lock */ @@ -84,6 +106,9 @@ struct ads7846 { unsigned pending:1; /* P: lock */ // FIXME remove "irq_disabled" unsigned irq_disabled:1; /* P: lock */ + unsigned disabled:1; + + int (*get_pendown_state)(void); }; /* leave chip selected when we're done, for quicker re-select? */ @@ -125,7 +150,9 @@ struct ads7846 { #define READ_Y (READ_12BIT_DFR(y) | ADS_PD10_ADC_ON) #define READ_Z1 (READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON) #define READ_Z2 (READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON) -#define READ_X (READ_12BIT_DFR(x) | ADS_PD10_PDOWN) /* LAST */ + +#define READ_X (READ_12BIT_DFR(x) | ADS_PD10_ADC_ON) +#define PWRDOWN (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) /* LAST */ /* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered @@ -152,6 +179,15 @@ struct ser_req { struct spi_transfer xfer[6]; }; +static void ads7846_enable(struct ads7846 *ts); +static void ads7846_disable(struct ads7846 *ts); + +static int device_suspended(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + return dev->power.power_state.event != PM_EVENT_ON || ts->disabled; +} + static int ads7846_read12_ser(struct device *dev, unsigned command) { struct spi_device *spi = to_spi_device(dev); @@ -164,7 +200,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) if (!req) return -ENOMEM; - INIT_LIST_HEAD(&req->msg.transfers); + spi_message_init(&req->msg); /* activate reference, so it has time to settle; */ req->ref_on = REF_ON; @@ -204,8 +240,10 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) for (i = 0; i < 6; i++) spi_message_add_tail(&req->xfer[i], &req->msg); + ts->irq_disabled = 1; disable_irq(spi->irq); status = spi_sync(spi, &req->msg); + ts->irq_disabled = 0; enable_irq(spi->irq); if (req->msg.status) @@ -233,6 +271,52 @@ SHOW(temp1) SHOW(vaux) SHOW(vbatt) +static int is_pen_down(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + return ts->pendown; +} + +static ssize_t ads7846_pen_down_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", is_pen_down(dev)); +} + +static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); + +static ssize_t ads7846_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ts->disabled); +} + +static ssize_t ads7846_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + char *endp; + int i; + + i = simple_strtoul(buf, &endp, 10); + spin_lock_irq(&ts->lock); + + if (i) + ads7846_disable(ts); + else + ads7846_enable(ts); + + spin_unlock_irq(&ts->lock); + + return count; +} + +static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); + /*--------------------------------------------------------------------------*/ /* @@ -264,7 +348,7 @@ static void ads7846_rx(void *ads) if (x == MAX_12BIT) x = 0; - if (x && z1 && ts->spi->dev.power.power_state.event == PM_EVENT_ON) { + if (likely(x && z1 && !device_suspended(&ts->spi->dev))) { /* compute touch pressure resistance using equation #2 */ Rt = z2; Rt -= z1; @@ -275,6 +359,14 @@ static void ads7846_rx(void *ads) } else Rt = 0; + /* Sample found inconsistent by debouncing or pressure is beyond + * the maximum. Don't report it to user space, repeat at least + * once more the measurement */ + if (ts->tc.ignore || Rt > ts->pressure_max) { + mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); + return; + } + /* NOTE: "pendown" is inferred from pressure; we don't rely on * being able to check nPENIRQ status, or "friendly" trigger modes * (both-edges is much better than just-falling or low-level). @@ -296,11 +388,13 @@ static void ads7846_rx(void *ads) if (Rt) { input_report_abs(input_dev, ABS_X, x); input_report_abs(input_dev, ABS_Y, y); - input_report_abs(input_dev, ABS_PRESSURE, Rt); sync = 1; } - if (sync) + + if (sync) { + input_report_abs(input_dev, ABS_PRESSURE, Rt); input_sync(input_dev); + } #ifdef VERBOSE if (Rt || ts->pendown) @@ -308,80 +402,138 @@ static void ads7846_rx(void *ads) x, y, Rt, Rt ? "" : " UP"); #endif - /* don't retrigger while we're suspended */ spin_lock_irqsave(&ts->lock, flags); ts->pendown = (Rt != 0); - ts->pending = 0; + mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); - if (ts->spi->dev.power.power_state.event == PM_EVENT_ON) { - if (ts->pendown) - mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); - else if (ts->irq_disabled) { - ts->irq_disabled = 0; - enable_irq(ts->spi->irq); + spin_unlock_irqrestore(&ts->lock, flags); +} + +static void ads7846_debounce(void *ads) +{ + struct ads7846 *ts = ads; + struct spi_message *m; + struct spi_transfer *t; + int val; + int status; + + m = &ts->msg[ts->msg_idx]; + t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + val = (*(u16 *)t->rx_buf) >> 3; + if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol)) { + /* Repeat it, if this was the first read or the read + * wasn't consistent enough. */ + if (ts->read_cnt < ts->debounce_max) { + ts->last_read = val; + ts->read_cnt++; + } else { + /* Maximum number of debouncing reached and still + * not enough number of consistent readings. Abort + * the whole sample, repeat it in the next sampling + * period. + */ + ts->tc.ignore = 1; + ts->read_cnt = 0; + /* Last message will contain ads7846_rx() as the + * completion function. + */ + m = ts->last_msg; } + /* Start over collecting consistent readings. */ + ts->read_rep = 0; + } else { + if (++ts->read_rep > ts->debounce_rep) { + /* Got a good reading for this coordinate, + * go for the next one. */ + ts->tc.ignore = 0; + ts->msg_idx++; + ts->read_cnt = 0; + ts->read_rep = 0; + m++; + } else + /* Read more values that are consistent. */ + ts->read_cnt++; } - - spin_unlock_irqrestore(&ts->lock, flags); + status = spi_async(ts->spi, m); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", + status); } static void ads7846_timer(unsigned long handle) { struct ads7846 *ts = (void *)handle; int status = 0; - unsigned long flags; + + spin_lock_irq(&ts->lock); + + if (unlikely(ts->msg_idx && !ts->pendown)) { + /* measurment cycle ended */ + if (!device_suspended(&ts->spi->dev)) { + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); + } + ts->pending = 0; + ts->msg_idx = 0; + } else { + /* pen is still down, continue with the measurement */ + ts->msg_idx = 0; + status = spi_async(ts->spi, &ts->msg[0]); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", status); + } + + spin_unlock_irq(&ts->lock); +} + +static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) +{ + struct ads7846 *ts = handle; + unsigned long flags; spin_lock_irqsave(&ts->lock, flags); - if (!ts->pending) { - ts->pending = 1; + if (likely(ts->get_pendown_state())) { if (!ts->irq_disabled) { + /* REVISIT irq logic for many ARM chips has cloned a + * bug wherein disabling an irq in its handler won't + * work;(it's disabled lazily, and too late to work. + * until all their irq logic is fixed, we must shadow + * that state here. + */ ts->irq_disabled = 1; disable_irq(ts->spi->irq); + ts->pending = 1; + mod_timer(&ts->timer, jiffies); } - status = spi_async(ts->spi, &ts->msg); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", - status); } spin_unlock_irqrestore(&ts->lock, flags); -} -static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) -{ - ads7846_timer((unsigned long) handle); return IRQ_HANDLED; } /*--------------------------------------------------------------------------*/ -static int -ads7846_suspend(struct spi_device *spi, pm_message_t message) +/* Must be called with ts->lock held */ +static void ads7846_disable(struct ads7846 *ts) { - struct ads7846 *ts = dev_get_drvdata(&spi->dev); - unsigned long flags; + if (ts->disabled) + return; - spin_lock_irqsave(&ts->lock, flags); - - spi->dev.power.power_state = message; + ts->disabled = 1; /* are we waiting for IRQ, or polling? */ - if (!ts->pendown) { - if (!ts->irq_disabled) { - ts->irq_disabled = 1; - disable_irq(ts->spi->irq); - } + if (!ts->pending) { + ts->irq_disabled = 1; + disable_irq(ts->spi->irq); } else { - /* polling; force a final SPI completion; - * that will clean things up neatly + /* the timer will run at least once more, and + * leave everything in a clean state, IRQ disabled */ - if (!ts->pending) - mod_timer(&ts->timer, jiffies); - - while (ts->pendown || ts->pending) { - spin_unlock_irqrestore(&ts->lock, flags); - udelay(10); - spin_lock_irqsave(&ts->lock, flags); + while (ts->pending) { + spin_unlock_irq(&ts->lock); + msleep(1); + spin_lock_irq(&ts->lock); } } @@ -389,17 +541,45 @@ ads7846_suspend(struct spi_device *spi, pm_message_t message) * leave it that way after every request */ - spin_unlock_irqrestore(&ts->lock, flags); +} + +/* Must be called with ts->lock held */ +static void ads7846_enable(struct ads7846 *ts) +{ + if (!ts->disabled) + return; + + ts->disabled = 0; + ts->irq_disabled = 0; + enable_irq(ts->spi->irq); +} + +static int ads7846_suspend(struct spi_device *spi, pm_message_t message) +{ + struct ads7846 *ts = dev_get_drvdata(&spi->dev); + + spin_lock_irq(&ts->lock); + + spi->dev.power.power_state = message; + ads7846_disable(ts); + + spin_unlock_irq(&ts->lock); + return 0; + } static int ads7846_resume(struct spi_device *spi) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); - ts->irq_disabled = 0; - enable_irq(ts->spi->irq); + spin_lock_irq(&ts->lock); + spi->dev.power.power_state = PMSG_ON; + ads7846_enable(ts); + + spin_unlock_irq(&ts->lock); + return 0; } @@ -408,6 +588,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) struct ads7846 *ts; struct input_dev *input_dev; struct ads7846_platform_data *pdata = spi->dev.platform_data; + struct spi_message *m; struct spi_transfer *x; int err; @@ -428,6 +609,11 @@ static int __devinit ads7846_probe(struct spi_device *spi) return -EINVAL; } + if (pdata->get_pendown_state == NULL) { + dev_dbg(&spi->dev, "no get_pendown_state function?\n"); + return -EINVAL; + } + /* We'd set the wordsize to 12 bits ... except that some controllers * will then treat the 8 bit command words as 12 bits (and drop the * four MSBs of the 12 bit result). Result: inputs must be shifted @@ -451,9 +637,21 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->timer.data = (unsigned long) ts; ts->timer.function = ads7846_timer; + spin_lock_init(&ts->lock); + ts->model = pdata->model ? : 7846; ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->pressure_max = pdata->pressure_max ? : ~0; + if (pdata->debounce_max) { + ts->debounce_max = pdata->debounce_max; + ts->debounce_tol = pdata->debounce_tol; + ts->debounce_rep = pdata->debounce_rep; + if (ts->debounce_rep > ts->debounce_max + 1) + ts->debounce_rep = ts->debounce_max - 1; + } else + ts->debounce_tol = ~0; + ts->get_pendown_state = pdata->get_pendown_state; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); @@ -477,60 +675,100 @@ static int __devinit ads7846_probe(struct spi_device *spi) /* set up the transfers to read touchscreen state; this assumes we * use formula #2 for pressure, not #3. */ - INIT_LIST_HEAD(&ts->msg.transfers); + m = &ts->msg[0]; x = ts->xfer; + spi_message_init(m); + /* y- still on; turn on only y+ (and ADC) */ ts->read_y = READ_Y; x->tx_buf = &ts->read_y; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.y; x->len = 2; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; + + m++; + spi_message_init(m); + + /* turn y- off, x+ on, then leave in lowpower */ + x++; + ts->read_x = READ_X; + x->tx_buf = &ts->read_x; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &ts->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; /* turn y+ off, x- on; we'll use formula #2 */ if (ts->model == 7846) { + m++; + spi_message_init(m); + x++; ts->read_z1 = READ_Z1; x->tx_buf = &ts->read_z1; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.z1; x->len = 2; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; + + m++; + spi_message_init(m); x++; ts->read_z2 = READ_Z2; x->tx_buf = &ts->read_z2; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.z2; x->len = 2; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; } - /* turn y- off, x+ on, then leave in lowpower */ + /* power down */ + m++; + spi_message_init(m); + x++; - ts->read_x = READ_X; - x->tx_buf = &ts->read_x; + ts->pwrdown = PWRDOWN; + x->tx_buf = &ts->pwrdown; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.x; + x->rx_buf = &ts->dummy; x->len = 2; CS_CHANGE(*x); - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); - ts->msg.complete = ads7846_rx; - ts->msg.context = ts; + m->complete = ads7846_rx; + m->context = ts; + + ts->last_msg = m; if (request_irq(spi->irq, ads7846_irq, SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING, @@ -559,13 +797,27 @@ static int __devinit ads7846_probe(struct spi_device *spi) device_create_file(&spi->dev, &dev_attr_vbatt); device_create_file(&spi->dev, &dev_attr_vaux); + device_create_file(&spi->dev, &dev_attr_pen_down); + + device_create_file(&spi->dev, &dev_attr_disable); + err = input_register_device(input_dev); if (err) - goto err_free_irq; + goto err_remove_attr; return 0; - err_free_irq: + err_remove_attr: + device_remove_file(&spi->dev, &dev_attr_disable); + device_remove_file(&spi->dev, &dev_attr_pen_down); + if (ts->model == 7846) { + device_remove_file(&spi->dev, &dev_attr_temp1); + device_remove_file(&spi->dev, &dev_attr_temp0); + } + if (ts->model != 7845) + device_remove_file(&spi->dev, &dev_attr_vbatt); + device_remove_file(&spi->dev, &dev_attr_vaux); + free_irq(spi->irq, ts); err_free_mem: input_free_device(input_dev); @@ -577,20 +829,24 @@ static int __devexit ads7846_remove(struct spi_device *spi) { struct ads7846 *ts = dev_get_drvdata(&spi->dev); + input_unregister_device(ts->input); + ads7846_suspend(spi, PMSG_SUSPEND); - free_irq(ts->spi->irq, ts); - if (ts->irq_disabled) - enable_irq(ts->spi->irq); + device_remove_file(&spi->dev, &dev_attr_disable); + device_remove_file(&spi->dev, &dev_attr_pen_down); if (ts->model == 7846) { - device_remove_file(&spi->dev, &dev_attr_temp0); device_remove_file(&spi->dev, &dev_attr_temp1); + device_remove_file(&spi->dev, &dev_attr_temp0); } if (ts->model != 7845) device_remove_file(&spi->dev, &dev_attr_vbatt); device_remove_file(&spi->dev, &dev_attr_vaux); - input_unregister_device(ts->input); + free_irq(ts->spi->irq, ts); + /* suspend left the IRQ disabled */ + enable_irq(ts->spi->irq); + kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); diff --git a/drivers/input/touchscreen/corgi_ts.c b/drivers/input/touchscreen/corgi_ts.c index 1042987856f..5013703db0e 100644 --- a/drivers/input/touchscreen/corgi_ts.c +++ b/drivers/input/touchscreen/corgi_ts.c @@ -17,7 +17,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/slab.h> -#include <asm/irq.h> +//#include <asm/irq.h> #include <asm/arch/sharpsl.h> #include <asm/arch/hardware.h> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 6081941de1b..4070eff6f0f 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -315,10 +315,11 @@ static int raid1_end_write_request(struct bio *bio, unsigned int bytes_done, int if (r1_bio->bios[mirror] == bio) break; - if (error == -ENOTSUPP && test_bit(R1BIO_Barrier, &r1_bio->state)) { + if (error == -EOPNOTSUPP && test_bit(R1BIO_Barrier, &r1_bio->state)) { set_bit(BarriersNotsupp, &conf->mirrors[mirror].rdev->flags); set_bit(R1BIO_BarrierRetry, &r1_bio->state); r1_bio->mddev->barriers_work = 0; + /* Don't rdev_dec_pending in this branch - keep it for the retry */ } else { /* * this branch is our 'one mirror IO has finished' event handler: @@ -365,6 +366,7 @@ static int raid1_end_write_request(struct bio *bio, unsigned int bytes_done, int } } } + rdev_dec_pending(conf->mirrors[mirror].rdev, conf->mddev); } /* * @@ -374,11 +376,9 @@ static int raid1_end_write_request(struct bio *bio, unsigned int bytes_done, int if (atomic_dec_and_test(&r1_bio->remaining)) { if (test_bit(R1BIO_BarrierRetry, &r1_bio->state)) { reschedule_retry(r1_bio); - /* Don't dec_pending yet, we want to hold - * the reference over the retry - */ goto out; } + /* it really is the end of this request */ if (test_bit(R1BIO_BehindIO, &r1_bio->state)) { /* free extra copy of the data pages */ int i = bio->bi_vcnt; @@ -393,8 +393,6 @@ static int raid1_end_write_request(struct bio *bio, unsigned int bytes_done, int md_write_end(r1_bio->mddev); raid_end_bio_io(r1_bio); } - - rdev_dec_pending(conf->mirrors[mirror].rdev, conf->mddev); out: if (to_put) bio_put(to_put); @@ -753,18 +751,24 @@ static int make_request(request_queue_t *q, struct bio * bio) const int rw = bio_data_dir(bio); int do_barriers; - if (unlikely(!mddev->barriers_work && bio_barrier(bio))) { - bio_endio(bio, bio->bi_size, -EOPNOTSUPP); - return 0; - } - /* * Register the new request and wait if the reconstruction * thread has put up a bar for new requests. * Continue immediately if no resync is active currently. + * We test barriers_work *after* md_write_start as md_write_start + * may cause the first superblock write, and that will check out + * if barriers work. */ + md_write_start(mddev, bio); /* wait on superblock update early */ + if (unlikely(!mddev->barriers_work && bio_barrier(bio))) { + if (rw == WRITE) + md_write_end(mddev); + bio_endio(bio, bio->bi_size, -EOPNOTSUPP); + return 0; + } + wait_barrier(conf); disk_stat_inc(mddev->gendisk, ios[rw]); @@ -1404,10 +1408,11 @@ static void raid1d(mddev_t *mddev) unplug = 1; } else if (test_bit(R1BIO_BarrierRetry, &r1_bio->state)) { /* some requests in the r1bio were BIO_RW_BARRIER - * requests which failed with -ENOTSUPP. Hohumm.. + * requests which failed with -EOPNOTSUPP. Hohumm.. * Better resubmit without the barrier. * We know which devices to resubmit for, because * all others have had their bios[] entry cleared. + * We already have a nr_pending reference on these rdevs. */ int i; clear_bit(R1BIO_BarrierRetry, &r1_bio->state); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 617012bc107..1440935414e 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1407,43 +1407,54 @@ static void raid10d(mddev_t *mddev) if (s > (PAGE_SIZE>>9)) s = PAGE_SIZE >> 9; + rcu_read_lock(); do { int d = r10_bio->devs[sl].devnum; - rdev = conf->mirrors[d].rdev; + rdev = rcu_dereference(conf->mirrors[d].rdev); if (rdev && - test_bit(In_sync, &rdev->flags) && - sync_page_io(rdev->bdev, - r10_bio->devs[sl].addr + - sect + rdev->data_offset, - s<<9, - conf->tmppage, READ)) - success = 1; - else { - sl++; - if (sl == conf->copies) - sl = 0; + test_bit(In_sync, &rdev->flags)) { + atomic_inc(&rdev->nr_pending); + rcu_read_unlock(); + success = sync_page_io(rdev->bdev, + r10_bio->devs[sl].addr + + sect + rdev->data_offset, + s<<9, + conf->tmppage, READ); + rdev_dec_pending(rdev, mddev); + rcu_read_lock(); + if (success) + break; } + sl++; + if (sl == conf->copies) + sl = 0; } while (!success && sl != r10_bio->read_slot); + rcu_read_unlock(); if (success) { int start = sl; /* write it back and re-read */ + rcu_read_lock(); while (sl != r10_bio->read_slot) { int d; if (sl==0) sl = conf->copies; sl--; d = r10_bio->devs[sl].devnum; - rdev = conf->mirrors[d].rdev; - atomic_add(s, &rdev->corrected_errors); + rdev = rcu_dereference(conf->mirrors[d].rdev); if (rdev && test_bit(In_sync, &rdev->flags)) { + atomic_inc(&rdev->nr_pending); + rcu_read_unlock(); + atomic_add(s, &rdev->corrected_errors); if (sync_page_io(rdev->bdev, r10_bio->devs[sl].addr + sect + rdev->data_offset, s<<9, conf->tmppage, WRITE) == 0) /* Well, this device is dead */ md_error(mddev, rdev); + rdev_dec_pending(rdev, mddev); + rcu_read_lock(); } } sl = start; @@ -1453,17 +1464,22 @@ static void raid10d(mddev_t *mddev) sl = conf->copies; sl--; d = r10_bio->devs[sl].devnum; - rdev = conf->mirrors[d].rdev; + rdev = rcu_dereference(conf->mirrors[d].rdev); if (rdev && test_bit(In_sync, &rdev->flags)) { + atomic_inc(&rdev->nr_pending); + rcu_read_unlock(); if (sync_page_io(rdev->bdev, r10_bio->devs[sl].addr + sect + rdev->data_offset, s<<9, conf->tmppage, READ) == 0) /* Well, this device is dead */ md_error(mddev, rdev); + rdev_dec_pending(rdev, mddev); + rcu_read_lock(); } } + rcu_read_unlock(); } else { /* Cannot read from anywhere -- bye bye array */ md_error(mddev, conf->mirrors[r10_bio->devs[r10_bio->read_slot].devnum].rdev); diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index f9d87b86492..320b3d9384b 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -616,7 +616,7 @@ static struct snd_kcontrol_new snd_cx88_capture_volume = { * Only boards with eeprom and byte 1 at eeprom=1 have it */ -static struct pci_device_id cx88_audio_pci_tbl[] = { +static struct pci_device_id cx88_audio_pci_tbl[] __devinitdata = { {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, {0, } diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 266414ca281..9080853fe28 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -1189,7 +1189,6 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) ioc->diagPending = 0; spin_lock_init(&ioc->diagLock); spin_lock_init(&ioc->fc_rescan_work_lock); - spin_lock_init(&ioc->fc_rport_lock); spin_lock_init(&ioc->initializing_hba_lock); /* Initialize the event logging. @@ -5736,11 +5735,13 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag) return rc; } +# define EVENT_DESCR_STR_SZ 100 + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ static void EventDescriptionStr(u8 event, u32 evData0, char *evStr) { - char *ds; + char *ds = NULL; switch(event) { case MPI_EVENT_NONE: @@ -5777,9 +5778,9 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr) if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LIP) ds = "Loop State(LIP) Change"; else if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LPE) - ds = "Loop State(LPE) Change"; /* ??? */ + ds = "Loop State(LPE) Change"; /* ??? */ else - ds = "Loop State(LPB) Change"; /* ??? */ + ds = "Loop State(LPB) Change"; /* ??? */ break; case MPI_EVENT_LOGOUT: ds = "Logout"; @@ -5841,27 +5842,32 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr) break; case MPI_EVENT_SAS_DEVICE_STATUS_CHANGE: { - char buf[50]; u8 id = (u8)(evData0); u8 ReasonCode = (u8)(evData0 >> 16); switch (ReasonCode) { case MPI_EVENT_SAS_DEV_STAT_RC_ADDED: - sprintf(buf,"SAS Device Status Change: Added: id=%d", id); + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS Device Status Change: Added: id=%d", id); break; case MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING: - sprintf(buf,"SAS Device Status Change: Deleted: id=%d", id); + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS Device Status Change: Deleted: id=%d", id); break; case MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA: - sprintf(buf,"SAS Device Status Change: SMART Data: id=%d", id); + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS Device Status Change: SMART Data: id=%d", + id); break; case MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED: - sprintf(buf,"SAS Device Status Change: No Persistancy Added: id=%d", id); + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS Device Status Change: No Persistancy " + "Added: id=%d", id); break; default: - sprintf(buf,"SAS Device Status Change: Unknown: id=%d", id); - break; + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS Device Status Change: Unknown: id=%d", id); + break; } - ds = buf; break; } case MPI_EVENT_ON_BUS_TIMER_EXPIRED: @@ -5878,41 +5884,46 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr) break; case MPI_EVENT_SAS_PHY_LINK_STATUS: { - char buf[50]; u8 LinkRates = (u8)(evData0 >> 8); u8 PhyNumber = (u8)(evData0); LinkRates = (LinkRates & MPI_EVENT_SAS_PLS_LR_CURRENT_MASK) >> MPI_EVENT_SAS_PLS_LR_CURRENT_SHIFT; switch (LinkRates) { case MPI_EVENT_SAS_PLS_LR_RATE_UNKNOWN: - sprintf(buf,"SAS PHY Link Status: Phy=%d:" + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS PHY Link Status: Phy=%d:" " Rate Unknown",PhyNumber); break; case MPI_EVENT_SAS_PLS_LR_RATE_PHY_DISABLED: - sprintf(buf,"SAS PHY Link Status: Phy=%d:" + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS PHY Link Status: Phy=%d:" " Phy Disabled",PhyNumber); break; case MPI_EVENT_SAS_PLS_LR_RATE_FAILED_SPEED_NEGOTIATION: - sprintf(buf,"SAS PHY Link Status: Phy=%d:" + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS PHY Link Status: Phy=%d:" " Failed Speed Nego",PhyNumber); break; case MPI_EVENT_SAS_PLS_LR_RATE_SATA_OOB_COMPLETE: - sprintf(buf,"SAS PHY Link Status: Phy=%d:" + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS PHY Link Status: Phy=%d:" " Sata OOB Completed",PhyNumber); break; case MPI_EVENT_SAS_PLS_LR_RATE_1_5: - sprintf(buf,"SAS PHY Link Status: Phy=%d:" + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS PHY Link Status: Phy=%d:" " Rate 1.5 Gbps",PhyNumber); break; case MPI_EVENT_SAS_PLS_LR_RATE_3_0: - sprintf(buf,"SAS PHY Link Status: Phy=%d:" + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS PHY Link Status: Phy=%d:" " Rate 3.0 Gpbs",PhyNumber); break; default: - sprintf(buf,"SAS PHY Link Status: Phy=%d", PhyNumber); + snprintf(evStr, EVENT_DESCR_STR_SZ, + "SAS PHY Link Status: Phy=%d", PhyNumber); break; } - ds = buf; break; } case MPI_EVENT_SAS_DISCOVERY_ERROR: @@ -5921,9 +5932,8 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr) case MPI_EVENT_IR_RESYNC_UPDATE: { u8 resync_complete = (u8)(evData0 >> 16); - char buf[40]; - sprintf(buf,"IR Resync Update: Complete = %d:",resync_complete); - ds = buf; + snprintf(evStr, EVENT_DESCR_STR_SZ, + "IR Resync Update: Complete = %d:",resync_complete); break; } case MPI_EVENT_IR2: @@ -5976,7 +5986,8 @@ EventDescriptionStr(u8 event, u32 evData0, char *evStr) ds = "Unknown"; break; } - strcpy(evStr,ds); + if (ds) + strncpy(evStr, ds, EVENT_DESCR_STR_SZ); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -5998,7 +6009,7 @@ ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply int ii; int r = 0; int handlers = 0; - char evStr[100]; + char evStr[EVENT_DESCR_STR_SZ]; u8 event; /* diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index be7e8501b53..f673cca507e 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -76,8 +76,8 @@ #define COPYRIGHT "Copyright (c) 1999-2005 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "3.03.08" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.03.08" +#define MPT_LINUX_VERSION_COMMON "3.03.09" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.03.09" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ @@ -489,7 +489,6 @@ typedef struct _RaidCfgData { #define MPT_RPORT_INFO_FLAGS_REGISTERED 0x01 /* rport registered */ #define MPT_RPORT_INFO_FLAGS_MISSING 0x02 /* missing from DevPage0 scan */ -#define MPT_RPORT_INFO_FLAGS_MAPPED_VDEV 0x04 /* target mapped in vdev */ /* * data allocated for each fc rport device @@ -501,7 +500,6 @@ struct mptfc_rport_info struct scsi_target *starget; FCDevicePage0_t pg0; u8 flags; - u8 remap_needed; }; /* @@ -628,11 +626,11 @@ typedef struct _MPT_ADAPTER struct work_struct mptscsih_persistTask; struct list_head fc_rports; - spinlock_t fc_rport_lock; /* list and ri flags */ spinlock_t fc_rescan_work_lock; int fc_rescan_work_count; struct work_struct fc_rescan_work; - + char fc_rescan_work_q_name[KOBJ_NAME_LEN]; + struct workqueue_struct *fc_rescan_work_q; } MPT_ADAPTER; /* diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c index b343f2a68b1..856487741ef 100644 --- a/drivers/message/fusion/mptfc.c +++ b/drivers/message/fusion/mptfc.c @@ -341,9 +341,6 @@ mptfc_generate_rport_ids(FCDevicePage0_t *pg0, struct fc_rport_identifiers *rid) rid->port_name = ((u64)pg0->WWPN.High) << 32 | (u64)pg0->WWPN.Low; rid->port_id = pg0->PortIdentifier; rid->roles = FC_RPORT_ROLE_UNKNOWN; - rid->roles |= FC_RPORT_ROLE_FCP_TARGET; - if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR) - rid->roles |= FC_RPORT_ROLE_FCP_INITIATOR; return 0; } @@ -355,15 +352,18 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) struct fc_rport *rport; struct mptfc_rport_info *ri; int new_ri = 1; - u64 pn; - unsigned long flags; + u64 pn, nn; VirtTarget *vtarget; + u32 roles = FC_RPORT_ROLE_UNKNOWN; if (mptfc_generate_rport_ids(pg0, &rport_ids) < 0) return; + roles |= FC_RPORT_ROLE_FCP_TARGET; + if (pg0->Protocol & MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR) + roles |= FC_RPORT_ROLE_FCP_INITIATOR; + /* scan list looking for a match */ - spin_lock_irqsave(&ioc->fc_rport_lock, flags); list_for_each_entry(ri, &ioc->fc_rports, list) { pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; if (pn == rport_ids.port_name) { /* match */ @@ -373,11 +373,9 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) } } if (new_ri) { /* allocate one */ - spin_unlock_irqrestore(&ioc->fc_rport_lock, flags); ri = kzalloc(sizeof(struct mptfc_rport_info), GFP_KERNEL); if (!ri) return; - spin_lock_irqsave(&ioc->fc_rport_lock, flags); list_add_tail(&ri->list, &ioc->fc_rports); } @@ -387,14 +385,11 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) /* MPT_RPORT_INFO_FLAGS_REGISTERED - rport not previously deleted */ if (!(ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED)) { ri->flags |= MPT_RPORT_INFO_FLAGS_REGISTERED; - spin_unlock_irqrestore(&ioc->fc_rport_lock, flags); rport = fc_remote_port_add(ioc->sh, channel, &rport_ids); - spin_lock_irqsave(&ioc->fc_rport_lock, flags); if (rport) { ri->rport = rport; if (new_ri) /* may have been reset by user */ rport->dev_loss_tmo = mptfc_dev_loss_tmo; - *((struct mptfc_rport_info **)rport->dd_data) = ri; /* * if already mapped, remap here. If not mapped, * target_alloc will allocate vtarget and map, @@ -406,16 +401,21 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) vtarget->target_id = pg0->CurrentTargetID; vtarget->bus_id = pg0->CurrentBus; } - ri->remap_needed = 0; } + *((struct mptfc_rport_info **)rport->dd_data) = ri; + /* scan will be scheduled once rport becomes a target */ + fc_remote_port_rolechg(rport,roles); + + pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; + nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low; dfcprintk ((MYIOC_s_INFO_FMT "mptfc_reg_dev.%d: %x, %llx / %llx, tid %d, " "rport tid %d, tmo %d\n", ioc->name, ioc->sh->host_no, pg0->PortIdentifier, - pg0->WWNN, - pg0->WWPN, + (unsigned long long)nn, + (unsigned long long)pn, pg0->CurrentTargetID, ri->rport->scsi_target_id, ri->rport->dev_loss_tmo)); @@ -425,8 +425,6 @@ mptfc_register_dev(MPT_ADAPTER *ioc, int channel, FCDevicePage0_t *pg0) ri = NULL; } } - spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); - } /* @@ -476,7 +474,6 @@ mptfc_target_alloc(struct scsi_target *starget) vtarget->target_id = ri->pg0.CurrentTargetID; vtarget->bus_id = ri->pg0.CurrentBus; ri->starget = starget; - ri->remap_needed = 0; rc = 0; } } @@ -502,10 +499,10 @@ mptfc_slave_alloc(struct scsi_device *sdev) VirtDevice *vdev; struct scsi_target *starget; struct fc_rport *rport; - unsigned long flags; - rport = starget_to_rport(scsi_target(sdev)); + starget = scsi_target(sdev); + rport = starget_to_rport(starget); if (!rport || fc_remote_port_chkready(rport)) return -ENXIO; @@ -519,10 +516,8 @@ mptfc_slave_alloc(struct scsi_device *sdev) return -ENOMEM; } - spin_lock_irqsave(&hd->ioc->fc_rport_lock,flags); sdev->hostdata = vdev; - starget = scsi_target(sdev); vtarget = starget->hostdata; if (vtarget->num_luns == 0) { @@ -535,14 +530,16 @@ mptfc_slave_alloc(struct scsi_device *sdev) vdev->vtarget = vtarget; vdev->lun = sdev->lun; - spin_unlock_irqrestore(&hd->ioc->fc_rport_lock,flags); - vtarget->num_luns++; + #ifdef DMPT_DEBUG_FC - { + { + u64 nn, pn; struct mptfc_rport_info *ri; ri = *((struct mptfc_rport_info **)rport->dd_data); + pn = (u64)ri->pg0.WWPN.High << 32 | (u64)ri->pg0.WWPN.Low; + nn = (u64)ri->pg0.WWNN.High << 32 | (u64)ri->pg0.WWNN.Low; dfcprintk ((MYIOC_s_INFO_FMT "mptfc_slv_alloc.%d: num_luns %d, sdev.id %d, " "CurrentTargetID %d, %x %llx %llx\n", @@ -550,7 +547,9 @@ mptfc_slave_alloc(struct scsi_device *sdev) sdev->host->host_no, vtarget->num_luns, sdev->id, ri->pg0.CurrentTargetID, - ri->pg0.PortIdentifier, ri->pg0.WWPN, ri->pg0.WWNN)); + ri->pg0.PortIdentifier, + (unsigned long long)pn, + (unsigned long long)nn)); } #endif @@ -570,11 +569,31 @@ mptfc_qcmd(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) done(SCpnt); return 0; } + + /* dd_data is null until finished adding target */ ri = *((struct mptfc_rport_info **)rport->dd_data); - if (unlikely(ri->remap_needed)) - return SCSI_MLQUEUE_HOST_BUSY; + if (unlikely(!ri)) { + dfcprintk ((MYIOC_s_INFO_FMT + "mptfc_qcmd.%d: %d:%d, dd_data is null.\n", + ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->name, + ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->sh->host_no, + SCpnt->device->id,SCpnt->device->lun)); + SCpnt->result = DID_IMM_RETRY << 16; + done(SCpnt); + return 0; + } - return mptscsih_qcmd(SCpnt,done); + err = mptscsih_qcmd(SCpnt,done); +#ifdef DMPT_DEBUG_FC + if (unlikely(err)) { + dfcprintk ((MYIOC_s_INFO_FMT + "mptfc_qcmd.%d: %d:%d, mptscsih_qcmd returns non-zero.\n", + ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->name, + ((MPT_SCSI_HOST *) SCpnt->device->host->hostdata)->ioc->sh->host_no, + SCpnt->device->id,SCpnt->device->lun)); + } +#endif + return err; } static void @@ -615,18 +634,17 @@ mptfc_rescan_devices(void *arg) MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg; int ii; int work_to_do; + u64 pn; unsigned long flags; struct mptfc_rport_info *ri; do { /* start by tagging all ports as missing */ - spin_lock_irqsave(&ioc->fc_rport_lock,flags); list_for_each_entry(ri, &ioc->fc_rports, list) { if (ri->flags & MPT_RPORT_INFO_FLAGS_REGISTERED) { ri->flags |= MPT_RPORT_INFO_FLAGS_MISSING; } } - spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); /* * now rescan devices known to adapter, @@ -639,33 +657,24 @@ mptfc_rescan_devices(void *arg) } /* delete devices still missing */ - spin_lock_irqsave(&ioc->fc_rport_lock, flags); list_for_each_entry(ri, &ioc->fc_rports, list) { /* if newly missing, delete it */ - if ((ri->flags & (MPT_RPORT_INFO_FLAGS_REGISTERED | - MPT_RPORT_INFO_FLAGS_MISSING)) - == (MPT_RPORT_INFO_FLAGS_REGISTERED | - MPT_RPORT_INFO_FLAGS_MISSING)) { + if (ri->flags & MPT_RPORT_INFO_FLAGS_MISSING) { ri->flags &= ~(MPT_RPORT_INFO_FLAGS_REGISTERED| MPT_RPORT_INFO_FLAGS_MISSING); - ri->remap_needed = 1; - fc_remote_port_delete(ri->rport); - /* - * remote port not really deleted 'cause - * binding is by WWPN and driver only - * registers FCP_TARGETs but cannot trust - * data structures. - */ + fc_remote_port_delete(ri->rport); /* won't sleep */ ri->rport = NULL; + + pn = (u64)ri->pg0.WWPN.High << 32 | + (u64)ri->pg0.WWPN.Low; dfcprintk ((MYIOC_s_INFO_FMT "mptfc_rescan.%d: %llx deleted\n", ioc->name, ioc->sh->host_no, - ri->pg0.WWPN)); + (unsigned long long)pn)); } } - spin_unlock_irqrestore(&ioc->fc_rport_lock,flags); /* * allow multiple passes as target state @@ -870,10 +879,23 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto out_mptfc_probe; } - for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { - mptfc_init_host_attr(ioc,ii); - mptfc_GetFcDevPage0(ioc,ii,mptfc_register_dev); - } + /* initialize workqueue */ + + snprintf(ioc->fc_rescan_work_q_name, KOBJ_NAME_LEN, "mptfc_wq_%d", + sh->host_no); + ioc->fc_rescan_work_q = + create_singlethread_workqueue(ioc->fc_rescan_work_q_name); + if (!ioc->fc_rescan_work_q) + goto out_mptfc_probe; + + /* + * scan for rports - + * by doing it via the workqueue, some locking is eliminated + */ + + ioc->fc_rescan_work_count = 1; + queue_work(ioc->fc_rescan_work_q, &ioc->fc_rescan_work); + flush_workqueue(ioc->fc_rescan_work_q); return 0; @@ -949,8 +971,18 @@ mptfc_init(void) static void __devexit mptfc_remove(struct pci_dev *pdev) { - MPT_ADAPTER *ioc = pci_get_drvdata(pdev); - struct mptfc_rport_info *p, *n; + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + struct mptfc_rport_info *p, *n; + struct workqueue_struct *work_q; + unsigned long flags; + + /* destroy workqueue */ + if ((work_q=ioc->fc_rescan_work_q)) { + spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); + ioc->fc_rescan_work_q = NULL; + spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); + destroy_workqueue(work_q); + } fc_remove_host(ioc->sh); diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index e9716b10ace..af6ec553ff7 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -91,6 +91,7 @@ enum mptsas_hotplug_action { MPTSAS_DEL_DEVICE, MPTSAS_ADD_RAID, MPTSAS_DEL_RAID, + MPTSAS_IGNORE_EVENT, }; struct mptsas_hotplug_event { @@ -298,6 +299,26 @@ mptsas_find_portinfo_by_handle(MPT_ADAPTER *ioc, u16 handle) return rc; } +/* + * Returns true if there is a scsi end device + */ +static inline int +mptsas_is_end_device(struct mptsas_devinfo * attached) +{ + if ((attached->handle) && + (attached->device_info & + MPI_SAS_DEVICE_INFO_END_DEVICE) && + ((attached->device_info & + MPI_SAS_DEVICE_INFO_SSP_TARGET) | + (attached->device_info & + MPI_SAS_DEVICE_INFO_STP_TARGET) | + (attached->device_info & + MPI_SAS_DEVICE_INFO_SATA_DEVICE))) + return 1; + else + return 0; +} + static int mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure, u32 form, u32 form_specific) @@ -872,7 +893,11 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info, SasDevicePage0_t *buffer; dma_addr_t dma_handle; __le64 sas_address; - int error; + int error=0; + + if (ioc->sas_discovery_runtime && + mptsas_is_end_device(device_info)) + goto out; hdr.PageVersion = MPI_SASDEVICE0_PAGEVERSION; hdr.ExtPageLength = 0; @@ -1009,7 +1034,11 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, CONFIGPARMS cfg; SasExpanderPage1_t *buffer; dma_addr_t dma_handle; - int error; + int error=0; + + if (ioc->sas_discovery_runtime && + mptsas_is_end_device(&phy_info->attached)) + goto out; hdr.PageVersion = MPI_SASEXPANDER0_PAGEVERSION; hdr.ExtPageLength = 0; @@ -1068,26 +1097,6 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info, return error; } -/* - * Returns true if there is a scsi end device - */ -static inline int -mptsas_is_end_device(struct mptsas_devinfo * attached) -{ - if ((attached->handle) && - (attached->device_info & - MPI_SAS_DEVICE_INFO_END_DEVICE) && - ((attached->device_info & - MPI_SAS_DEVICE_INFO_SSP_TARGET) | - (attached->device_info & - MPI_SAS_DEVICE_INFO_STP_TARGET) | - (attached->device_info & - MPI_SAS_DEVICE_INFO_SATA_DEVICE))) - return 1; - else - return 0; -} - static void mptsas_parse_device_info(struct sas_identify *identify, struct mptsas_devinfo *device_info) @@ -1737,6 +1746,9 @@ mptsas_hotplug_work(void *arg) break; case MPTSAS_ADD_DEVICE: + if (ev->phys_disk_num_valid) + mpt_findImVolumes(ioc); + /* * Refresh sas device pg0 data */ @@ -1868,6 +1880,9 @@ mptsas_hotplug_work(void *arg) scsi_device_put(sdev); mpt_findImVolumes(ioc); break; + case MPTSAS_IGNORE_EVENT: + default: + break; } kfree(ev); @@ -1940,7 +1955,8 @@ mptscsih_send_raid_event(MPT_ADAPTER *ioc, EVENT_DATA_RAID *raid_event_data) { struct mptsas_hotplug_event *ev; - RAID_VOL0_STATUS * volumeStatus; + int status = le32_to_cpu(raid_event_data->SettingsStatus); + int state = (status >> 8) & 0xff; if (ioc->bus_type != SAS) return; @@ -1955,6 +1971,7 @@ mptscsih_send_raid_event(MPT_ADAPTER *ioc, INIT_WORK(&ev->work, mptsas_hotplug_work, ev); ev->ioc = ioc; ev->id = raid_event_data->VolumeID; + ev->event_type = MPTSAS_IGNORE_EVENT; switch (raid_event_data->ReasonCode) { case MPI_EVENT_RAID_RC_PHYSDISK_DELETED: @@ -1966,6 +1983,25 @@ mptscsih_send_raid_event(MPT_ADAPTER *ioc, ev->phys_disk_num = raid_event_data->PhysDiskNum; ev->event_type = MPTSAS_DEL_DEVICE; break; + case MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED: + switch (state) { + case MPI_PD_STATE_ONLINE: + ioc->raid_data.isRaid = 1; + ev->phys_disk_num_valid = 1; + ev->phys_disk_num = raid_event_data->PhysDiskNum; + ev->event_type = MPTSAS_ADD_DEVICE; + break; + case MPI_PD_STATE_MISSING: + case MPI_PD_STATE_NOT_COMPATIBLE: + case MPI_PD_STATE_OFFLINE_AT_HOST_REQUEST: + case MPI_PD_STATE_FAILED_AT_HOST_REQUEST: + case MPI_PD_STATE_OFFLINE_FOR_ANOTHER_REASON: + ev->event_type = MPTSAS_DEL_DEVICE; + break; + default: + break; + } + break; case MPI_EVENT_RAID_RC_VOLUME_DELETED: ev->event_type = MPTSAS_DEL_RAID; break; @@ -1973,11 +2009,18 @@ mptscsih_send_raid_event(MPT_ADAPTER *ioc, ev->event_type = MPTSAS_ADD_RAID; break; case MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED: - volumeStatus = (RAID_VOL0_STATUS *) & - raid_event_data->SettingsStatus; - ev->event_type = (volumeStatus->State == - MPI_RAIDVOL0_STATUS_STATE_FAILED) ? - MPTSAS_DEL_RAID : MPTSAS_ADD_RAID; + switch (state) { + case MPI_RAIDVOL0_STATUS_STATE_FAILED: + case MPI_RAIDVOL0_STATUS_STATE_MISSING: + ev->event_type = MPTSAS_DEL_RAID; + break; + case MPI_RAIDVOL0_STATUS_STATE_OPTIMAL: + case MPI_RAIDVOL0_STATUS_STATE_DEGRADED: + ev->event_type = MPTSAS_ADD_RAID; + break; + default: + break; + } break; default: break; diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c index 3729062db31..84fa271eb8f 100644 --- a/drivers/message/fusion/mptscsih.c +++ b/drivers/message/fusion/mptscsih.c @@ -632,7 +632,11 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */ /* Spoof to SCSI Selection Timeout! */ - sc->result = DID_NO_CONNECT << 16; + if (ioc->bus_type != FC) + sc->result = DID_NO_CONNECT << 16; + /* else fibre, just stall until rescan event */ + else + sc->result = DID_REQUEUE << 16; if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF) hd->sel_timeout[pScsiReq->TargetID]++; @@ -877,7 +881,7 @@ mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, VirtDevice *vdevice) struct scsi_cmnd *sc; dsprintk((KERN_INFO MYNAM ": search_running target %d lun %d max %d\n", - vdevice->target_id, vdevice->lun, max)); + vdevice->vtarget->target_id, vdevice->lun, max)); for (ii=0; ii < max; ii++) { if ((sc = hd->ScsiLookup[ii]) != NULL) { @@ -1645,7 +1649,6 @@ int mptscsih_abort(struct scsi_cmnd * SCpnt) { MPT_SCSI_HOST *hd; - MPT_ADAPTER *ioc; MPT_FRAME_HDR *mf; u32 ctx2abort; int scpnt_idx; @@ -1663,14 +1666,6 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) return FAILED; } - ioc = hd->ioc; - if (hd->resetPending) { - return FAILED; - } - - if (hd->timeouts < -1) - hd->timeouts++; - /* Find this command */ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) { @@ -1684,6 +1679,13 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) return SUCCESS; } + if (hd->resetPending) { + return FAILED; + } + + if (hd->timeouts < -1) + hd->timeouts++; + printk(KERN_WARNING MYNAM ": %s: attempting task abort! (sc=%p)\n", hd->ioc->name, SCpnt); scsi_print_command(SCpnt); @@ -1703,7 +1705,7 @@ mptscsih_abort(struct scsi_cmnd * SCpnt) vdev = SCpnt->device->hostdata; retval = mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK, vdev->vtarget->bus_id, vdev->vtarget->target_id, vdev->lun, - ctx2abort, mptscsih_get_tm_timeout(ioc)); + ctx2abort, mptscsih_get_tm_timeout(hd->ioc)); printk (KERN_WARNING MYNAM ": %s: task abort: %s (sc=%p)\n", hd->ioc->name, @@ -2521,15 +2523,15 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) /* 7. FC: Rescan for blocked rports which might have returned. */ - else if (ioc->bus_type == FC) { - int work_count; - unsigned long flags; - + if (ioc->bus_type == FC) { spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); - work_count = ++ioc->fc_rescan_work_count; + if (ioc->fc_rescan_work_q) { + if (ioc->fc_rescan_work_count++ == 0) { + queue_work(ioc->fc_rescan_work_q, + &ioc->fc_rescan_work); + } + } spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); - if (work_count == 1) - schedule_work(&ioc->fc_rescan_work); } dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name)); @@ -2544,7 +2546,6 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) { MPT_SCSI_HOST *hd; u8 event = le32_to_cpu(pEvReply->Event) & 0xFF; - int work_count; unsigned long flags; devtverboseprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", @@ -2569,10 +2570,13 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) case MPI_EVENT_RESCAN: /* 06 */ spin_lock_irqsave(&ioc->fc_rescan_work_lock, flags); - work_count = ++ioc->fc_rescan_work_count; + if (ioc->fc_rescan_work_q) { + if (ioc->fc_rescan_work_count++ == 0) { + queue_work(ioc->fc_rescan_work_q, + &ioc->fc_rescan_work); + } + } spin_unlock_irqrestore(&ioc->fc_rescan_work_lock, flags); - if (work_count == 1) - schedule_work(&ioc->fc_rescan_work); break; /* diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c index 09c745b19cc..f2a4d382ea1 100644 --- a/drivers/message/fusion/mptspi.c +++ b/drivers/message/fusion/mptspi.c @@ -783,6 +783,70 @@ static struct pci_device_id mptspi_pci_table[] = { }; MODULE_DEVICE_TABLE(pci, mptspi_pci_table); + +/* + * renegotiate for a given target + */ +static void +mptspi_dv_renegotiate_work(void *data) +{ + struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data; + struct _MPT_SCSI_HOST *hd = wqw->hd; + struct scsi_device *sdev; + + kfree(wqw); + + shost_for_each_device(sdev, hd->ioc->sh) + mptspi_dv_device(hd, sdev); +} + +static void +mptspi_dv_renegotiate(struct _MPT_SCSI_HOST *hd) +{ + struct work_queue_wrapper *wqw = kmalloc(sizeof(*wqw), GFP_ATOMIC); + + if (!wqw) + return; + + INIT_WORK(&wqw->work, mptspi_dv_renegotiate_work, wqw); + wqw->hd = hd; + + schedule_work(&wqw->work); +} + +/* + * spi module reset handler + */ +static int +mptspi_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) +{ + struct _MPT_SCSI_HOST *hd = (struct _MPT_SCSI_HOST *)ioc->sh->hostdata; + int rc; + + rc = mptscsih_ioc_reset(ioc, reset_phase); + + if (reset_phase == MPT_IOC_POST_RESET) + mptspi_dv_renegotiate(hd); + + return rc; +} + +/* + * spi module resume handler + */ +static int +mptspi_resume(struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc = pci_get_drvdata(pdev); + struct _MPT_SCSI_HOST *hd = (struct _MPT_SCSI_HOST *)ioc->sh->hostdata; + int rc; + + rc = mptscsih_resume(pdev); + mptspi_dv_renegotiate(hd); + + return rc; +} + /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* @@ -1032,7 +1096,7 @@ static struct pci_driver mptspi_driver = { .shutdown = mptscsih_shutdown, #ifdef CONFIG_PM .suspend = mptscsih_suspend, - .resume = mptscsih_resume, + .resume = mptspi_resume, #endif }; @@ -1061,7 +1125,7 @@ mptspi_init(void) ": Registered for IOC event notifications\n")); } - if (mpt_reset_register(mptspiDoneCtx, mptscsih_ioc_reset) == 0) { + if (mpt_reset_register(mptspiDoneCtx, mptspi_ioc_reset) == 0) { dprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n")); } diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c index 6061c2d101a..88f0eef9cf3 100644 --- a/drivers/mmc/at91_mci.c +++ b/drivers/mmc/at91_mci.c @@ -621,9 +621,6 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct at91mci_host *host = mmc_priv(mmc); unsigned long at91_master_clock = clk_get_rate(mci_clk); - DBG("Clock %uHz, busmode %u, powermode %u, Vdd %u\n", - ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); - if (host) host->bus_mode = ios->bus_mode; else diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index c0326bbc5f2..914d62b2406 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -720,10 +720,6 @@ static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) { struct au1xmmc_host *host = mmc_priv(mmc); - DBG("set_ios (power=%u, clock=%uHz, vdd=%u, mode=%u)\n", - host->id, ios->power_mode, ios->clock, ios->vdd, - ios->bus_mode); - if (ios->power_mode == MMC_POWER_OFF) au1xmmc_set_power(host, 0); else if (ios->power_mode == MMC_POWER_ON) { diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index ffb7f55d346..79358e223f5 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -102,6 +102,7 @@ struct imxmci_host { #define IMXMCI_PEND_CPU_DATA_b 5 #define IMXMCI_PEND_CARD_XCHG_b 6 #define IMXMCI_PEND_SET_INIT_b 7 +#define IMXMCI_PEND_STARTED_b 8 #define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b) #define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b) @@ -111,6 +112,7 @@ struct imxmci_host { #define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b) #define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b) #define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b) +#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b) static void imxmci_stop_clock(struct imxmci_host *host) { @@ -131,23 +133,52 @@ static void imxmci_stop_clock(struct imxmci_host *host) dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n"); } -static void imxmci_start_clock(struct imxmci_host *host) +static int imxmci_start_clock(struct imxmci_host *host) { - int i = 0; + unsigned int trials = 0; + unsigned int delay_limit = 128; + unsigned long flags; + MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK; - while(i < 0x1000) { - if(!(i & 0x7f)) - MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK; - if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) { - /* Check twice before cut */ + clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events); + + /* + * Command start of the clock, this usually succeeds in less + * then 6 delay loops, but during card detection (low clockrate) + * it takes up to 5000 delay loops and sometimes fails for the first time + */ + MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK; + + do { + unsigned int delay = delay_limit; + + while(delay--){ if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) - return; + /* Check twice before cut */ + if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) + return 0; + + if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) + return 0; } - i++; - } - dev_dbg(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n"); + local_irq_save(flags); + /* + * Ensure, that request is not doubled under all possible circumstances. + * It is possible, that cock running state is missed, because some other + * IRQ or schedule delays this function execution and the clocks has + * been already stopped by other means (response processing, SDHC HW) + */ + if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) + MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK; + local_irq_restore(flags); + + } while(++trials<256); + + dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n"); + + return -1; } static void imxmci_softreset(void) @@ -498,7 +529,7 @@ static int imxmci_data_done(struct imxmci_host *host, unsigned int stat) data_error = imxmci_finish_data(host, stat); - if (host->req->stop && (data_error == MMC_ERR_NONE)) { + if (host->req->stop) { imxmci_stop_clock(host); imxmci_start_cmd(host, host->req->stop, 0); } else { @@ -622,6 +653,7 @@ static irqreturn_t imxmci_irq(int irq, void *devid, struct pt_regs *regs) atomic_set(&host->stuck_timeout, 0); host->status_reg = stat; set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); + set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events); tasklet_schedule(&host->tasklet); return IRQ_RETVAL(handled);; @@ -775,10 +807,6 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct imxmci_host *host = mmc_priv(mmc); int prescaler; - dev_dbg(mmc_dev(host->mmc), "clock %u power %u vdd %u width %u\n", - ios->clock, ios->power_mode, ios->vdd, - (ios->bus_width==MMC_BUS_WIDTH_4)?4:1); - if( ios->bus_width==MMC_BUS_WIDTH_4 ) { host->actual_bus_width = MMC_BUS_WIDTH_4; imx_gpio_mode(PB11_PF_SD_DAT3); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index da6ddd910fc..1ca2c8b9c9b 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -59,21 +59,23 @@ static const unsigned int tacc_mant[] = { /** - * mmc_request_done - finish processing an MMC command - * @host: MMC host which completed command - * @mrq: MMC request which completed + * mmc_request_done - finish processing an MMC request + * @host: MMC host which completed request + * @mrq: MMC request which request * * MMC drivers should call this function when they have completed - * their processing of a command. This should be called before the - * data part of the command has completed. + * their processing of a request. */ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) { struct mmc_command *cmd = mrq->cmd; - int err = mrq->cmd->error; - pr_debug("MMC: req done (%02x): %d: %08x %08x %08x %08x\n", - cmd->opcode, err, cmd->resp[0], cmd->resp[1], - cmd->resp[2], cmd->resp[3]); + int err = cmd->error; + + pr_debug("%s: req done (CMD%u): %d/%d/%d: %08x %08x %08x %08x\n", + mmc_hostname(host), cmd->opcode, err, + mrq->data ? mrq->data->error : 0, + mrq->stop ? mrq->stop->error : 0, + cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); if (err && cmd->retries) { cmd->retries--; @@ -97,8 +99,9 @@ EXPORT_SYMBOL(mmc_request_done); void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) { - pr_debug("MMC: starting cmd %02x arg %08x flags %08x\n", - mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); + pr_debug("%s: starting CMD%u arg %08x flags %08x\n", + mmc_hostname(host), mrq->cmd->opcode, + mrq->cmd->arg, mrq->cmd->flags); WARN_ON(host->card_busy == NULL); @@ -312,6 +315,18 @@ void mmc_release_host(struct mmc_host *host) EXPORT_SYMBOL(mmc_release_host); +static inline void mmc_set_ios(struct mmc_host *host) +{ + struct mmc_ios *ios = &host->ios; + + pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n", + mmc_hostname(host), ios->clock, ios->bus_mode, + ios->power_mode, ios->chip_select, ios->vdd, + ios->bus_width); + + host->ops->set_ios(host, ios); +} + static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) { int err; @@ -364,7 +379,7 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) } } - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); return MMC_ERR_NONE; } @@ -415,7 +430,7 @@ static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) ocr = 3 << bit; host->ios.vdd = bit; - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); } else { ocr = 0; } @@ -549,6 +564,7 @@ static void mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); } else { @@ -583,6 +599,7 @@ static void mmc_decode_csd(struct mmc_card *card) csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); } @@ -666,7 +683,7 @@ static void mmc_idle_cards(struct mmc_host *host) struct mmc_command cmd; host->ios.chip_select = MMC_CS_HIGH; - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); mmc_delay(1); @@ -679,7 +696,7 @@ static void mmc_idle_cards(struct mmc_host *host) mmc_delay(1); host->ios.chip_select = MMC_CS_DONTCARE; - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); mmc_delay(1); } @@ -704,13 +721,13 @@ static void mmc_power_up(struct mmc_host *host) host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_UP; host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); mmc_delay(1); host->ios.clock = host->f_min; host->ios.power_mode = MMC_POWER_ON; - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); mmc_delay(2); } @@ -723,7 +740,7 @@ static void mmc_power_off(struct mmc_host *host) host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_OFF; host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); } static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) @@ -971,7 +988,8 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host) if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr) max_dtr = card->csd.max_dtr; - pr_debug("MMC: selected %d.%03dMHz transfer rate\n", + pr_debug("%s: selected %d.%03dMHz transfer rate\n", + mmc_hostname(host), max_dtr / 1000000, (max_dtr / 1000) % 1000); return max_dtr; @@ -1046,7 +1064,7 @@ static void mmc_setup(struct mmc_host *host) } else { host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.clock = host->f_min; - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); /* * We should remember the OCR mask from the existing @@ -1082,7 +1100,7 @@ static void mmc_setup(struct mmc_host *host) * Ok, now switch to push-pull mode. */ host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); mmc_read_csds(host); @@ -1128,7 +1146,7 @@ static void mmc_rescan(void *data) * attached cards and the host support. */ host->ios.clock = mmc_calculate_clock(host); - host->ops->set_ios(host, &host->ios); + mmc_set_ios(host); } mmc_release_host(host); diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 8eb2a2ede64..06bd1f4cb9b 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -187,6 +187,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.cmd.opcode = MMC_WRITE_BLOCK; brq.data.flags |= MMC_DATA_WRITE; brq.data.blocks = 1; + + /* + * Scale up the timeout by the r2w factor + */ + brq.data.timeout_ns <<= card->csd.r2w_factor; + brq.data.timeout_clks <<= card->csd.r2w_factor; } if (brq.data.blocks > 1) { diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c index df7e861e2fc..da8e4d7339c 100644 --- a/drivers/mmc/mmci.c +++ b/drivers/mmc/mmci.c @@ -402,9 +402,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct mmci_host *host = mmc_priv(mmc); u32 clk = 0, pwr = 0; - DBG(host, "clock %uHz busmode %u powermode %u Vdd %u\n", - ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); - if (ios->clock) { if (ios->clock >= host->mclk) { clk = MCI_CLK_BYPASS; diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index eb42cb34942..f97b472085c 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -198,7 +198,6 @@ static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq) { - pr_debug("PXAMCI: request done\n"); host->mrq = NULL; host->cmd = NULL; host->data = NULL; @@ -291,7 +290,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) pxamci_disable_irq(host, DATA_TRAN_DONE); host->data = NULL; - if (host->mrq->stop && data->error == MMC_ERR_NONE) { + if (host->mrq->stop) { pxamci_stop_clock(host); pxamci_start_cmd(host, host->mrq->stop, 0); } else { @@ -309,12 +308,10 @@ static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs) ireg = readl(host->base + MMC_I_REG); - pr_debug("PXAMCI: irq %08x\n", ireg); - if (ireg) { unsigned stat = readl(host->base + MMC_STAT); - pr_debug("PXAMCI: stat %08x\n", stat); + pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat); if (ireg & END_CMD_RES) handled |= pxamci_cmd_done(host, stat); @@ -368,10 +365,6 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct pxamci_host *host = mmc_priv(mmc); - pr_debug("pxamci_set_ios: clock %u power %u vdd %u.%02u\n", - ios->clock, ios->power_mode, ios->vdd / 100, - ios->vdd % 100); - if (ios->clock) { unsigned int clk = CLOCKRATE / ios->clock; if (CLOCKRATE / clk > ios->clock) @@ -397,7 +390,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->cmdat |= CMDAT_INIT; } - pr_debug("pxamci_set_ios: clkrt = %x cmdat = %x\n", + pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", host->clkrt, host->cmdat); } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index bdbfca05002..b0053280ff2 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -570,10 +570,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_lock_irqsave(&host->lock, flags); - DBG("clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n", - ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, - ios->vdd, ios->bus_width); - /* * Reset the chip on each power off. * Should clear out any weird states. diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 511f7b0b31d..39b3d97f891 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -931,10 +931,6 @@ static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct wbsd_host *host = mmc_priv(mmc); u8 clk, setup, pwr; - DBGF("clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n", - ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, - ios->vdd, ios->bus_width); - spin_lock_bh(&host->lock); /* diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 1363083b4d8..14dbad14afb 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -52,6 +52,7 @@ #include <linux/mii.h> #include <linux/skbuff.h> #include <linux/delay.h> +#include <linux/crc32.h> #include <asm/mipsregs.h> #include <asm/irq.h> #include <asm/io.h> @@ -2070,23 +2071,6 @@ static void au1000_tx_timeout(struct net_device *dev) netif_wake_queue(dev); } - -static unsigned const ethernet_polynomial = 0x04c11db7U; -static inline u32 ether_crc(int length, unsigned char *data) -{ - int crc = -1; - - while(--length >= 0) { - unsigned char current_octet = *data++; - int bit; - for (bit = 0; bit < 8; bit++, current_octet >>= 1) - crc = (crc << 1) ^ - ((crc < 0) ^ (current_octet & 1) ? - ethernet_polynomial : 0); - } - return crc; -} - static void set_rx_mode(struct net_device *dev) { struct au1000_private *aup = (struct au1000_private *) dev->priv; diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 1f3627470c9..1ddefd28121 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -765,7 +765,7 @@ rio_free_tx (struct net_device *dev, int irq) break; skb = np->tx_skbuff[entry]; pci_unmap_single (np->pdev, - np->tx_ring[entry].fraginfo & 0xffffffffffff, + np->tx_ring[entry].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_TODEVICE); if (irq) dev_kfree_skb_irq (skb); @@ -893,7 +893,7 @@ receive_packet (struct net_device *dev) /* Small skbuffs for short packets */ if (pkt_len > copy_thresh) { pci_unmap_single (np->pdev, - desc->fraginfo & 0xffffffffffff, + desc->fraginfo & DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb_put (skb = np->rx_skbuff[entry], pkt_len); @@ -901,7 +901,7 @@ receive_packet (struct net_device *dev) } else if ((skb = dev_alloc_skb (pkt_len + 2)) != NULL) { pci_dma_sync_single_for_cpu(np->pdev, desc->fraginfo & - 0xffffffffffff, + DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); skb->dev = dev; @@ -913,7 +913,7 @@ receive_packet (struct net_device *dev) skb_put (skb, pkt_len); pci_dma_sync_single_for_device(np->pdev, desc->fraginfo & - 0xffffffffffff, + DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); } @@ -1800,7 +1800,7 @@ rio_close (struct net_device *dev) skb = np->rx_skbuff[i]; if (skb) { pci_unmap_single(np->pdev, - np->rx_ring[i].fraginfo & 0xffffffffffff, + np->rx_ring[i].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_FROMDEVICE); dev_kfree_skb (skb); np->rx_skbuff[i] = NULL; @@ -1810,7 +1810,7 @@ rio_close (struct net_device *dev) skb = np->tx_skbuff[i]; if (skb) { pci_unmap_single(np->pdev, - np->tx_ring[i].fraginfo & 0xffffffffffff, + np->tx_ring[i].fraginfo & DMA_48BIT_MASK, skb->len, PCI_DMA_TODEVICE); dev_kfree_skb (skb); np->tx_skbuff[i] = NULL; diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 9788b1ef2e7..f7235c9bc42 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -106,6 +106,7 @@ * 0.51: 20 Jan 2006: Add 64bit consistent memory allocation for rings. * 0.52: 20 Jan 2006: Add MSI/MSIX support. * 0.53: 19 Mar 2006: Fix init from low power mode and add hw reset. + * 0.54: 21 Mar 2006: Fix spin locks for multi irqs and cleanup. * * Known bugs: * We suspect that on some hardware no TX done interrupts are generated. @@ -117,7 +118,7 @@ * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few * superfluous timer interrupts from the nic. */ -#define FORCEDETH_VERSION "0.53" +#define FORCEDETH_VERSION "0.54" #define DRV_NAME "forcedeth" #include <linux/module.h> @@ -710,6 +711,72 @@ static void setup_hw_rings(struct net_device *dev, int rxtx_flags) } } +static int using_multi_irqs(struct net_device *dev) +{ + struct fe_priv *np = get_nvpriv(dev); + + if (!(np->msi_flags & NV_MSI_X_ENABLED) || + ((np->msi_flags & NV_MSI_X_ENABLED) && + ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1))) + return 0; + else + return 1; +} + +static void nv_enable_irq(struct net_device *dev) +{ + struct fe_priv *np = get_nvpriv(dev); + + if (!using_multi_irqs(dev)) { + if (np->msi_flags & NV_MSI_X_ENABLED) + enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector); + else + enable_irq(dev->irq); + } else { + enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector); + enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector); + enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector); + } +} + +static void nv_disable_irq(struct net_device *dev) +{ + struct fe_priv *np = get_nvpriv(dev); + + if (!using_multi_irqs(dev)) { + if (np->msi_flags & NV_MSI_X_ENABLED) + disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector); + else + disable_irq(dev->irq); + } else { + disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector); + disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector); + disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector); + } +} + +/* In MSIX mode, a write to irqmask behaves as XOR */ +static void nv_enable_hw_interrupts(struct net_device *dev, u32 mask) +{ + u8 __iomem *base = get_hwbase(dev); + + writel(mask, base + NvRegIrqMask); +} + +static void nv_disable_hw_interrupts(struct net_device *dev, u32 mask) +{ + struct fe_priv *np = get_nvpriv(dev); + u8 __iomem *base = get_hwbase(dev); + + if (np->msi_flags & NV_MSI_X_ENABLED) { + writel(mask, base + NvRegIrqMask); + } else { + if (np->msi_flags & NV_MSI_ENABLED) + writel(0, base + NvRegMSIIrqMask); + writel(0, base + NvRegIrqMask); + } +} + #define MII_READ (-1) /* mii_rw: read/write a register on the PHY. * @@ -1019,24 +1086,25 @@ static void nv_do_rx_refill(unsigned long data) struct net_device *dev = (struct net_device *) data; struct fe_priv *np = netdev_priv(dev); - - if (!(np->msi_flags & NV_MSI_X_ENABLED) || - ((np->msi_flags & NV_MSI_X_ENABLED) && - ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1))) { - disable_irq(dev->irq); + if (!using_multi_irqs(dev)) { + if (np->msi_flags & NV_MSI_X_ENABLED) + disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector); + else + disable_irq(dev->irq); } else { disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector); } if (nv_alloc_rx(dev)) { - spin_lock(&np->lock); + spin_lock_irq(&np->lock); if (!np->in_shutdown) mod_timer(&np->oom_kick, jiffies + OOM_REFILL); - spin_unlock(&np->lock); + spin_unlock_irq(&np->lock); } - if (!(np->msi_flags & NV_MSI_X_ENABLED) || - ((np->msi_flags & NV_MSI_X_ENABLED) && - ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1))) { - enable_irq(dev->irq); + if (!using_multi_irqs(dev)) { + if (np->msi_flags & NV_MSI_X_ENABLED) + enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector); + else + enable_irq(dev->irq); } else { enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector); } @@ -1668,15 +1736,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu) * guessed, there is probably a simpler approach. * Changing the MTU is a rare event, it shouldn't matter. */ - if (!(np->msi_flags & NV_MSI_X_ENABLED) || - ((np->msi_flags & NV_MSI_X_ENABLED) && - ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1))) { - disable_irq(dev->irq); - } else { - disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector); - disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector); - disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector); - } + nv_disable_irq(dev); spin_lock_bh(&dev->xmit_lock); spin_lock(&np->lock); /* stop engines */ @@ -1709,15 +1769,7 @@ static int nv_change_mtu(struct net_device *dev, int new_mtu) nv_start_tx(dev); spin_unlock(&np->lock); spin_unlock_bh(&dev->xmit_lock); - if (!(np->msi_flags & NV_MSI_X_ENABLED) || - ((np->msi_flags & NV_MSI_X_ENABLED) && - ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1))) { - enable_irq(dev->irq); - } else { - enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector); - enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector); - enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector); - } + nv_enable_irq(dev); } return 0; } @@ -2108,16 +2160,16 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data, struct pt_regs *regs) if (!(events & np->irqmask)) break; - spin_lock(&np->lock); + spin_lock_irq(&np->lock); nv_tx_done(dev); - spin_unlock(&np->lock); + spin_unlock_irq(&np->lock); if (events & (NVREG_IRQ_TX_ERR)) { dprintk(KERN_DEBUG "%s: received irq with events 0x%x. Probably TX fail.\n", dev->name, events); } if (i > max_interrupt_work) { - spin_lock(&np->lock); + spin_lock_irq(&np->lock); /* disable interrupts on the nic */ writel(NVREG_IRQ_TX_ALL, base + NvRegIrqMask); pci_push(base); @@ -2127,7 +2179,7 @@ static irqreturn_t nv_nic_irq_tx(int foo, void *data, struct pt_regs *regs) mod_timer(&np->nic_poll, jiffies + POLL_WAIT); } printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq_tx.\n", dev->name, i); - spin_unlock(&np->lock); + spin_unlock_irq(&np->lock); break; } @@ -2157,14 +2209,14 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs) nv_rx_process(dev); if (nv_alloc_rx(dev)) { - spin_lock(&np->lock); + spin_lock_irq(&np->lock); if (!np->in_shutdown) mod_timer(&np->oom_kick, jiffies + OOM_REFILL); - spin_unlock(&np->lock); + spin_unlock_irq(&np->lock); } if (i > max_interrupt_work) { - spin_lock(&np->lock); + spin_lock_irq(&np->lock); /* disable interrupts on the nic */ writel(NVREG_IRQ_RX_ALL, base + NvRegIrqMask); pci_push(base); @@ -2174,7 +2226,7 @@ static irqreturn_t nv_nic_irq_rx(int foo, void *data, struct pt_regs *regs) mod_timer(&np->nic_poll, jiffies + POLL_WAIT); } printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq_rx.\n", dev->name, i); - spin_unlock(&np->lock); + spin_unlock_irq(&np->lock); break; } @@ -2203,14 +2255,14 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data, struct pt_regs *regs) break; if (events & NVREG_IRQ_LINK) { - spin_lock(&np->lock); + spin_lock_irq(&np->lock); nv_link_irq(dev); - spin_unlock(&np->lock); + spin_unlock_irq(&np->lock); } if (np->need_linktimer && time_after(jiffies, np->link_timeout)) { - spin_lock(&np->lock); + spin_lock_irq(&np->lock); nv_linkchange(dev); - spin_unlock(&np->lock); + spin_unlock_irq(&np->lock); np->link_timeout = jiffies + LINK_TIMEOUT; } if (events & (NVREG_IRQ_UNKNOWN)) { @@ -2218,7 +2270,7 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data, struct pt_regs *regs) dev->name, events); } if (i > max_interrupt_work) { - spin_lock(&np->lock); + spin_lock_irq(&np->lock); /* disable interrupts on the nic */ writel(NVREG_IRQ_OTHER, base + NvRegIrqMask); pci_push(base); @@ -2228,7 +2280,7 @@ static irqreturn_t nv_nic_irq_other(int foo, void *data, struct pt_regs *regs) mod_timer(&np->nic_poll, jiffies + POLL_WAIT); } printk(KERN_DEBUG "%s: too many iterations (%d) in nv_nic_irq_other.\n", dev->name, i); - spin_unlock(&np->lock); + spin_unlock_irq(&np->lock); break; } @@ -2251,10 +2303,11 @@ static void nv_do_nic_poll(unsigned long data) * nv_nic_irq because that may decide to do otherwise */ - if (!(np->msi_flags & NV_MSI_X_ENABLED) || - ((np->msi_flags & NV_MSI_X_ENABLED) && - ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1))) { - disable_irq(dev->irq); + if (!using_multi_irqs(dev)) { + if (np->msi_flags & NV_MSI_X_ENABLED) + disable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector); + else + disable_irq(dev->irq); mask = np->irqmask; } else { if (np->nic_poll_irq & NVREG_IRQ_RX_ALL) { @@ -2277,11 +2330,12 @@ static void nv_do_nic_poll(unsigned long data) writel(mask, base + NvRegIrqMask); pci_push(base); - if (!(np->msi_flags & NV_MSI_X_ENABLED) || - ((np->msi_flags & NV_MSI_X_ENABLED) && - ((np->msi_flags & NV_MSI_X_VECTORS_MASK) == 0x1))) { + if (!using_multi_irqs(dev)) { nv_nic_irq((int) 0, (void *) data, (struct pt_regs *) NULL); - enable_irq(dev->irq); + if (np->msi_flags & NV_MSI_X_ENABLED) + enable_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector); + else + enable_irq(dev->irq); } else { if (np->nic_poll_irq & NVREG_IRQ_RX_ALL) { nv_nic_irq_rx((int) 0, (void *) data, (struct pt_regs *) NULL); @@ -2628,6 +2682,113 @@ static void set_msix_vector_map(struct net_device *dev, u32 vector, u32 irqmask) writel(readl(base + NvRegMSIXMap1) | msixmap, base + NvRegMSIXMap1); } +static int nv_request_irq(struct net_device *dev) +{ + struct fe_priv *np = get_nvpriv(dev); + u8 __iomem *base = get_hwbase(dev); + int ret = 1; + int i; + + if (np->msi_flags & NV_MSI_X_CAPABLE) { + for (i = 0; i < (np->msi_flags & NV_MSI_X_VECTORS_MASK); i++) { + np->msi_x_entry[i].entry = i; + } + if ((ret = pci_enable_msix(np->pci_dev, np->msi_x_entry, (np->msi_flags & NV_MSI_X_VECTORS_MASK))) == 0) { + np->msi_flags |= NV_MSI_X_ENABLED; + if (optimization_mode == NV_OPTIMIZATION_MODE_THROUGHPUT) { + /* Request irq for rx handling */ + if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector, &nv_nic_irq_rx, SA_SHIRQ, dev->name, dev) != 0) { + printk(KERN_INFO "forcedeth: request_irq failed for rx %d\n", ret); + pci_disable_msix(np->pci_dev); + np->msi_flags &= ~NV_MSI_X_ENABLED; + goto out_err; + } + /* Request irq for tx handling */ + if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector, &nv_nic_irq_tx, SA_SHIRQ, dev->name, dev) != 0) { + printk(KERN_INFO "forcedeth: request_irq failed for tx %d\n", ret); + pci_disable_msix(np->pci_dev); + np->msi_flags &= ~NV_MSI_X_ENABLED; + goto out_free_rx; + } + /* Request irq for link and timer handling */ + if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_OTHER].vector, &nv_nic_irq_other, SA_SHIRQ, dev->name, dev) != 0) { + printk(KERN_INFO "forcedeth: request_irq failed for link %d\n", ret); + pci_disable_msix(np->pci_dev); + np->msi_flags &= ~NV_MSI_X_ENABLED; + goto out_free_tx; + } + /* map interrupts to their respective vector */ + writel(0, base + NvRegMSIXMap0); + writel(0, base + NvRegMSIXMap1); + set_msix_vector_map(dev, NV_MSI_X_VECTOR_RX, NVREG_IRQ_RX_ALL); + set_msix_vector_map(dev, NV_MSI_X_VECTOR_TX, NVREG_IRQ_TX_ALL); + set_msix_vector_map(dev, NV_MSI_X_VECTOR_OTHER, NVREG_IRQ_OTHER); + } else { + /* Request irq for all interrupts */ + if (request_irq(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector, &nv_nic_irq, SA_SHIRQ, dev->name, dev) != 0) { + printk(KERN_INFO "forcedeth: request_irq failed %d\n", ret); + pci_disable_msix(np->pci_dev); + np->msi_flags &= ~NV_MSI_X_ENABLED; + goto out_err; + } + + /* map interrupts to vector 0 */ + writel(0, base + NvRegMSIXMap0); + writel(0, base + NvRegMSIXMap1); + } + } + } + if (ret != 0 && np->msi_flags & NV_MSI_CAPABLE) { + if ((ret = pci_enable_msi(np->pci_dev)) == 0) { + np->msi_flags |= NV_MSI_ENABLED; + if (request_irq(np->pci_dev->irq, &nv_nic_irq, SA_SHIRQ, dev->name, dev) != 0) { + printk(KERN_INFO "forcedeth: request_irq failed %d\n", ret); + pci_disable_msi(np->pci_dev); + np->msi_flags &= ~NV_MSI_ENABLED; + goto out_err; + } + + /* map interrupts to vector 0 */ + writel(0, base + NvRegMSIMap0); + writel(0, base + NvRegMSIMap1); + /* enable msi vector 0 */ + writel(NVREG_MSI_VECTOR_0_ENABLED, base + NvRegMSIIrqMask); + } + } + if (ret != 0) { + if (request_irq(np->pci_dev->irq, &nv_nic_irq, SA_SHIRQ, dev->name, dev) != 0) + goto out_err; + } + + return 0; +out_free_tx: + free_irq(np->msi_x_entry[NV_MSI_X_VECTOR_TX].vector, dev); +out_free_rx: + free_irq(np->msi_x_entry[NV_MSI_X_VECTOR_RX].vector, dev); +out_err: + return 1; +} + +static void nv_free_irq(struct net_device *dev) +{ + struct fe_priv *np = get_nvpriv(dev); + int i; + + if (np->msi_flags & NV_MSI_X_ENABLED) { + for (i = 0; i < (np->msi_flags & NV_MSI_X_VECTORS_MASK); i++) { + free_irq(np->msi_x_entry[i].vector, dev); + } + pci_disable_msix(np->pci_dev); + np->msi_flags &= ~NV_MSI_X_ENABLED; + } else { + free_irq(np->pci_dev->irq, dev); + if (np->msi_flags & NV_MSI_ENABLED) { + pci_disable_msi(np->pci_dev); + np->msi_flags &= ~NV_MSI_ENABLED; + } + } +} + static int nv_open(struct net_device *dev) { struct fe_priv *np = netdev_priv(dev); @@ -2720,12 +2881,16 @@ static int nv_open(struct net_device *dev) udelay(10); writel(readl(base + NvRegPowerState) | NVREG_POWERSTATE_VALID, base + NvRegPowerState); - writel(0, base + NvRegIrqMask); + nv_disable_hw_interrupts(dev, np->irqmask); pci_push(base); writel(NVREG_MIISTAT_MASK2, base + NvRegMIIStatus); writel(NVREG_IRQSTAT_MASK, base + NvRegIrqStatus); pci_push(base); + if (nv_request_irq(dev)) { + goto out_drain; + } + if (np->msi_flags & NV_MSI_X_CAPABLE) { for (i = 0; i < (np->msi_flags & NV_MSI_X_VECTORS_MASK); i++) { np->msi_x_entry[i].entry = i; @@ -2799,7 +2964,7 @@ static int nv_open(struct net_device *dev) } /* ask for interrupts */ - writel(np->irqmask, base + NvRegIrqMask); + nv_enable_hw_interrupts(dev, np->irqmask); spin_lock_irq(&np->lock); writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA); @@ -2843,7 +3008,6 @@ static int nv_close(struct net_device *dev) { struct fe_priv *np = netdev_priv(dev); u8 __iomem *base; - int i; spin_lock_irq(&np->lock); np->in_shutdown = 1; @@ -2861,31 +3025,13 @@ static int nv_close(struct net_device *dev) /* disable interrupts on the nic or we will lock up */ base = get_hwbase(dev); - if (np->msi_flags & NV_MSI_X_ENABLED) { - writel(np->irqmask, base + NvRegIrqMask); - } else { - if (np->msi_flags & NV_MSI_ENABLED) - writel(0, base + NvRegMSIIrqMask); - writel(0, base + NvRegIrqMask); - } + nv_disable_hw_interrupts(dev, np->irqmask); pci_push(base); dprintk(KERN_INFO "%s: Irqmask is zero again\n", dev->name); spin_unlock_irq(&np->lock); - if (np->msi_flags & NV_MSI_X_ENABLED) { - for (i = 0; i < (np->msi_flags & NV_MSI_X_VECTORS_MASK); i++) { - free_irq(np->msi_x_entry[i].vector, dev); - } - pci_disable_msix(np->pci_dev); - np->msi_flags &= ~NV_MSI_X_ENABLED; - } else { - free_irq(np->pci_dev->irq, dev); - if (np->msi_flags & NV_MSI_ENABLED) { - pci_disable_msi(np->pci_dev); - np->msi_flags &= ~NV_MSI_ENABLED; - } - } + nv_free_irq(dev); drain_ring(dev); @@ -2974,20 +3120,18 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i if (id->driver_data & DEV_HAS_HIGH_DMA) { /* packet format 3: supports 40-bit addressing */ np->desc_ver = DESC_VER_3; + np->txrxctl_bits = NVREG_TXRXCTL_DESC_3; if (pci_set_dma_mask(pci_dev, DMA_39BIT_MASK)) { printk(KERN_INFO "forcedeth: 64-bit DMA failed, using 32-bit addressing for device %s.\n", pci_name(pci_dev)); } else { - if (pci_set_consistent_dma_mask(pci_dev, 0x0000007fffffffffULL)) { - printk(KERN_INFO "forcedeth: 64-bit DMA (consistent) failed for device %s.\n", - pci_name(pci_dev)); - goto out_relreg; - } else { - dev->features |= NETIF_F_HIGHDMA; - printk(KERN_INFO "forcedeth: using HIGHDMA\n"); - } + dev->features |= NETIF_F_HIGHDMA; + printk(KERN_INFO "forcedeth: using HIGHDMA\n"); + } + if (pci_set_consistent_dma_mask(pci_dev, 0x0000007fffffffffULL)) { + printk(KERN_INFO "forcedeth: 64-bit DMA (consistent) failed for device %s.\n", + pci_name(pci_dev)); } - np->txrxctl_bits = NVREG_TXRXCTL_DESC_3; } else if (id->driver_data & DEV_HAS_LARGEDESC) { /* packet format 2: supports jumbo frames */ np->desc_ver = DESC_VER_2; diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 79a8fbcf5f9..0d5fccc984b 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -582,7 +582,6 @@ static int __init setup_adapter(int card_base, int type, int n) INIT_WORK(&priv->rx_work, rx_bh, priv); dev->priv = priv; sprintf(dev->name, "dmascc%i", 2 * n + i); - SET_MODULE_OWNER(dev); dev->base_addr = card_base; dev->irq = irq; dev->open = scc_open; diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index 6ace0e914fd..5927784df3f 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1550,7 +1550,6 @@ static unsigned char ax25_nocall[AX25_ADDR_LEN] = static void scc_net_setup(struct net_device *dev) { - SET_MODULE_OWNER(dev); dev->tx_queue_len = 16; /* should be enough... */ dev->open = scc_net_open; diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index fe22479eb20..b49884048ca 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -1098,7 +1098,6 @@ static void yam_setup(struct net_device *dev) dev->base_addr = yp->iobase; dev->irq = yp->irq; - SET_MODULE_OWNER(dev); dev->open = yam_open; dev->stop = yam_close; diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile index 27ab75f2079..c1ce2398efe 100644 --- a/drivers/net/irda/Makefile +++ b/drivers/net/irda/Makefile @@ -46,4 +46,4 @@ obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o # The SIR helper module -sir-dev-objs := sir_dev.o sir_dongle.o sir_kthread.o +sir-dev-objs := sir_dev.o sir_dongle.o diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 96bdb73c228..cd87593e4e8 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -1778,7 +1778,7 @@ static int irda_usb_probe(struct usb_interface *intf, if (self->needspatch) { ret = usb_control_msg (self->usbdev, usb_sndctrlpipe (self->usbdev, 0), - 0x02, 0x40, 0, 0, 0, 0, msecs_to_jiffies(500)); + 0x02, 0x40, 0, 0, NULL, 0, 500); if (ret < 0) { IRDA_DEBUG (0, "usb_control_msg failed %d\n", ret); goto err_out_3; diff --git a/drivers/net/irda/sir-dev.h b/drivers/net/irda/sir-dev.h index f69fb4cec76..9fa294a546d 100644 --- a/drivers/net/irda/sir-dev.h +++ b/drivers/net/irda/sir-dev.h @@ -15,23 +15,14 @@ #define IRDA_SIR_H #include <linux/netdevice.h> +#include <linux/workqueue.h> #include <net/irda/irda.h> #include <net/irda/irda_device.h> // iobuff_t -/* FIXME: unify irda_request with sir_fsm! */ - -struct irda_request { - struct list_head lh_request; - unsigned long pending; - void (*func)(void *); - void *data; - struct timer_list timer; -}; - struct sir_fsm { struct semaphore sem; - struct irda_request rq; + struct work_struct work; unsigned state, substate; int param; int result; diff --git a/drivers/net/irda/sir_dev.c b/drivers/net/irda/sir_dev.c index ea7c9464d46..3b5854d10c1 100644 --- a/drivers/net/irda/sir_dev.c +++ b/drivers/net/irda/sir_dev.c @@ -23,6 +23,298 @@ #include "sir-dev.h" + +static struct workqueue_struct *irda_sir_wq; + +/* STATE MACHINE */ + +/* substate handler of the config-fsm to handle the cases where we want + * to wait for transmit completion before changing the port configuration + */ + +static int sirdev_tx_complete_fsm(struct sir_dev *dev) +{ + struct sir_fsm *fsm = &dev->fsm; + unsigned next_state, delay; + unsigned bytes_left; + + do { + next_state = fsm->substate; /* default: stay in current substate */ + delay = 0; + + switch(fsm->substate) { + + case SIRDEV_STATE_WAIT_XMIT: + if (dev->drv->chars_in_buffer) + bytes_left = dev->drv->chars_in_buffer(dev); + else + bytes_left = 0; + if (!bytes_left) { + next_state = SIRDEV_STATE_WAIT_UNTIL_SENT; + break; + } + + if (dev->speed > 115200) + delay = (bytes_left*8*10000) / (dev->speed/100); + else if (dev->speed > 0) + delay = (bytes_left*10*10000) / (dev->speed/100); + else + delay = 0; + /* expected delay (usec) until remaining bytes are sent */ + if (delay < 100) { + udelay(delay); + delay = 0; + break; + } + /* sleep some longer delay (msec) */ + delay = (delay+999) / 1000; + break; + + case SIRDEV_STATE_WAIT_UNTIL_SENT: + /* block until underlaying hardware buffer are empty */ + if (dev->drv->wait_until_sent) + dev->drv->wait_until_sent(dev); + next_state = SIRDEV_STATE_TX_DONE; + break; + + case SIRDEV_STATE_TX_DONE: + return 0; + + default: + IRDA_ERROR("%s - undefined state\n", __FUNCTION__); + return -EINVAL; + } + fsm->substate = next_state; + } while (delay == 0); + return delay; +} + +/* + * Function sirdev_config_fsm + * + * State machine to handle the configuration of the device (and attached dongle, if any). + * This handler is scheduled for execution in kIrDAd context, so we can sleep. + * however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too + * long. Instead, for longer delays we start a timer to reschedule us later. + * On entry, fsm->sem is always locked and the netdev xmit queue stopped. + * Both must be unlocked/restarted on completion - but only on final exit. + */ + +static void sirdev_config_fsm(void *data) +{ + struct sir_dev *dev = data; + struct sir_fsm *fsm = &dev->fsm; + int next_state; + int ret = -1; + unsigned delay; + + IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies); + + do { + IRDA_DEBUG(3, "%s - state=0x%04x / substate=0x%04x\n", + __FUNCTION__, fsm->state, fsm->substate); + + next_state = fsm->state; + delay = 0; + + switch(fsm->state) { + + case SIRDEV_STATE_DONGLE_OPEN: + if (dev->dongle_drv != NULL) { + ret = sirdev_put_dongle(dev); + if (ret) { + fsm->result = -EINVAL; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + + /* Initialize dongle */ + ret = sirdev_get_dongle(dev, fsm->param); + if (ret) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + + /* Dongles are powered through the modem control lines which + * were just set during open. Before resetting, let's wait for + * the power to stabilize. This is what some dongle drivers did + * in open before, while others didn't - should be safe anyway. + */ + + delay = 50; + fsm->substate = SIRDEV_STATE_DONGLE_RESET; + next_state = SIRDEV_STATE_DONGLE_RESET; + + fsm->param = 9600; + + break; + + case SIRDEV_STATE_DONGLE_CLOSE: + /* shouldn't we just treat this as success=? */ + if (dev->dongle_drv == NULL) { + fsm->result = -EINVAL; + next_state = SIRDEV_STATE_ERROR; + break; + } + + ret = sirdev_put_dongle(dev); + if (ret) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_SET_DTR_RTS: + ret = sirdev_set_dtr_rts(dev, + (fsm->param&0x02) ? TRUE : FALSE, + (fsm->param&0x01) ? TRUE : FALSE); + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_SET_SPEED: + fsm->substate = SIRDEV_STATE_WAIT_XMIT; + next_state = SIRDEV_STATE_DONGLE_CHECK; + break; + + case SIRDEV_STATE_DONGLE_CHECK: + ret = sirdev_tx_complete_fsm(dev); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + if ((delay=ret) != 0) + break; + + if (dev->dongle_drv) { + fsm->substate = SIRDEV_STATE_DONGLE_RESET; + next_state = SIRDEV_STATE_DONGLE_RESET; + } + else { + dev->speed = fsm->param; + next_state = SIRDEV_STATE_PORT_SPEED; + } + break; + + case SIRDEV_STATE_DONGLE_RESET: + if (dev->dongle_drv->reset) { + ret = dev->dongle_drv->reset(dev); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + else + ret = 0; + if ((delay=ret) == 0) { + /* set serial port according to dongle default speed */ + if (dev->drv->set_speed) + dev->drv->set_speed(dev, dev->speed); + fsm->substate = SIRDEV_STATE_DONGLE_SPEED; + next_state = SIRDEV_STATE_DONGLE_SPEED; + } + break; + + case SIRDEV_STATE_DONGLE_SPEED: + if (dev->dongle_drv->reset) { + ret = dev->dongle_drv->set_speed(dev, fsm->param); + if (ret < 0) { + fsm->result = ret; + next_state = SIRDEV_STATE_ERROR; + break; + } + } + else + ret = 0; + if ((delay=ret) == 0) + next_state = SIRDEV_STATE_PORT_SPEED; + break; + + case SIRDEV_STATE_PORT_SPEED: + /* Finally we are ready to change the serial port speed */ + if (dev->drv->set_speed) + dev->drv->set_speed(dev, dev->speed); + dev->new_speed = 0; + next_state = SIRDEV_STATE_DONE; + break; + + case SIRDEV_STATE_DONE: + /* Signal network layer so it can send more frames */ + netif_wake_queue(dev->netdev); + next_state = SIRDEV_STATE_COMPLETE; + break; + + default: + IRDA_ERROR("%s - undefined state\n", __FUNCTION__); + fsm->result = -EINVAL; + /* fall thru */ + + case SIRDEV_STATE_ERROR: + IRDA_ERROR("%s - error: %d\n", __FUNCTION__, fsm->result); + +#if 0 /* don't enable this before we have netdev->tx_timeout to recover */ + netif_stop_queue(dev->netdev); +#else + netif_wake_queue(dev->netdev); +#endif + /* fall thru */ + + case SIRDEV_STATE_COMPLETE: + /* config change finished, so we are not busy any longer */ + sirdev_enable_rx(dev); + up(&fsm->sem); + return; + } + fsm->state = next_state; + } while(!delay); + + queue_delayed_work(irda_sir_wq, &fsm->work, msecs_to_jiffies(delay)); +} + +/* schedule some device configuration task for execution by kIrDAd + * on behalf of the above state machine. + * can be called from process or interrupt/tasklet context. + */ + +int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param) +{ + struct sir_fsm *fsm = &dev->fsm; + + IRDA_DEBUG(2, "%s - state=0x%04x / param=%u\n", __FUNCTION__, initial_state, param); + + if (down_trylock(&fsm->sem)) { + if (in_interrupt() || in_atomic() || irqs_disabled()) { + IRDA_DEBUG(1, "%s(), state machine busy!\n", __FUNCTION__); + return -EWOULDBLOCK; + } else + down(&fsm->sem); + } + + if (fsm->state == SIRDEV_STATE_DEAD) { + /* race with sirdev_close should never happen */ + IRDA_ERROR("%s(), instance staled!\n", __FUNCTION__); + up(&fsm->sem); + return -ESTALE; /* or better EPIPE? */ + } + + netif_stop_queue(dev->netdev); + atomic_set(&dev->enable_rx, 0); + + fsm->state = initial_state; + fsm->param = param; + fsm->result = 0; + + INIT_WORK(&fsm->work, sirdev_config_fsm, dev); + queue_work(irda_sir_wq, &fsm->work); + return 0; +} + + /***************************************************************************/ void sirdev_enable_rx(struct sir_dev *dev) @@ -619,10 +911,6 @@ struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *n spin_lock_init(&dev->tx_lock); init_MUTEX(&dev->fsm.sem); - INIT_LIST_HEAD(&dev->fsm.rq.lh_request); - dev->fsm.rq.pending = 0; - init_timer(&dev->fsm.rq.timer); - dev->drv = drv; dev->netdev = ndev; @@ -682,3 +970,22 @@ int sirdev_put_instance(struct sir_dev *dev) } EXPORT_SYMBOL(sirdev_put_instance); +static int __init sir_wq_init(void) +{ + irda_sir_wq = create_singlethread_workqueue("irda_sir_wq"); + if (!irda_sir_wq) + return -ENOMEM; + return 0; +} + +static void __exit sir_wq_exit(void) +{ + destroy_workqueue(irda_sir_wq); +} + +module_init(sir_wq_init); +module_exit(sir_wq_exit); + +MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>"); +MODULE_DESCRIPTION("IrDA SIR core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/sir_kthread.c b/drivers/net/irda/sir_kthread.c deleted file mode 100644 index e3904d6bfec..00000000000 --- a/drivers/net/irda/sir_kthread.c +++ /dev/null @@ -1,508 +0,0 @@ -/********************************************************************* - * - * sir_kthread.c: dedicated thread to process scheduled - * sir device setup requests - * - * Copyright (c) 2002 Martin Diehl - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - ********************************************************************/ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/version.h> -#include <linux/init.h> -#include <linux/smp_lock.h> -#include <linux/completion.h> -#include <linux/delay.h> - -#include <net/irda/irda.h> - -#include "sir-dev.h" - -/************************************************************************** - * - * kIrDAd kernel thread and config state machine - * - */ - -struct irda_request_queue { - struct list_head request_list; - spinlock_t lock; - task_t *thread; - struct completion exit; - wait_queue_head_t kick, done; - atomic_t num_pending; -}; - -static struct irda_request_queue irda_rq_queue; - -static int irda_queue_request(struct irda_request *rq) -{ - int ret = 0; - unsigned long flags; - - if (!test_and_set_bit(0, &rq->pending)) { - spin_lock_irqsave(&irda_rq_queue.lock, flags); - list_add_tail(&rq->lh_request, &irda_rq_queue.request_list); - wake_up(&irda_rq_queue.kick); - atomic_inc(&irda_rq_queue.num_pending); - spin_unlock_irqrestore(&irda_rq_queue.lock, flags); - ret = 1; - } - return ret; -} - -static void irda_request_timer(unsigned long data) -{ - struct irda_request *rq = (struct irda_request *)data; - unsigned long flags; - - spin_lock_irqsave(&irda_rq_queue.lock, flags); - list_add_tail(&rq->lh_request, &irda_rq_queue.request_list); - wake_up(&irda_rq_queue.kick); - spin_unlock_irqrestore(&irda_rq_queue.lock, flags); -} - -static int irda_queue_delayed_request(struct irda_request *rq, unsigned long delay) -{ - int ret = 0; - struct timer_list *timer = &rq->timer; - - if (!test_and_set_bit(0, &rq->pending)) { - timer->expires = jiffies + delay; - timer->function = irda_request_timer; - timer->data = (unsigned long)rq; - atomic_inc(&irda_rq_queue.num_pending); - add_timer(timer); - ret = 1; - } - return ret; -} - -static void run_irda_queue(void) -{ - unsigned long flags; - struct list_head *entry, *tmp; - struct irda_request *rq; - - spin_lock_irqsave(&irda_rq_queue.lock, flags); - list_for_each_safe(entry, tmp, &irda_rq_queue.request_list) { - rq = list_entry(entry, struct irda_request, lh_request); - list_del_init(entry); - spin_unlock_irqrestore(&irda_rq_queue.lock, flags); - - clear_bit(0, &rq->pending); - rq->func(rq->data); - - if (atomic_dec_and_test(&irda_rq_queue.num_pending)) - wake_up(&irda_rq_queue.done); - - spin_lock_irqsave(&irda_rq_queue.lock, flags); - } - spin_unlock_irqrestore(&irda_rq_queue.lock, flags); -} - -static int irda_thread(void *startup) -{ - DECLARE_WAITQUEUE(wait, current); - - daemonize("kIrDAd"); - - irda_rq_queue.thread = current; - - complete((struct completion *)startup); - - while (irda_rq_queue.thread != NULL) { - - /* We use TASK_INTERRUPTIBLE, rather than - * TASK_UNINTERRUPTIBLE. Andrew Morton made this - * change ; he told me that it is safe, because "signal - * blocking is now handled in daemonize()", he added - * that the problem is that "uninterruptible sleep - * contributes to load average", making user worry. - * Jean II */ - set_task_state(current, TASK_INTERRUPTIBLE); - add_wait_queue(&irda_rq_queue.kick, &wait); - if (list_empty(&irda_rq_queue.request_list)) - schedule(); - else - __set_task_state(current, TASK_RUNNING); - remove_wait_queue(&irda_rq_queue.kick, &wait); - - /* make swsusp happy with our thread */ - try_to_freeze(); - - run_irda_queue(); - } - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,35) - reparent_to_init(); -#endif - complete_and_exit(&irda_rq_queue.exit, 0); - /* never reached */ - return 0; -} - - -static void flush_irda_queue(void) -{ - if (atomic_read(&irda_rq_queue.num_pending)) { - - DECLARE_WAITQUEUE(wait, current); - - if (!list_empty(&irda_rq_queue.request_list)) - run_irda_queue(); - - set_task_state(current, TASK_UNINTERRUPTIBLE); - add_wait_queue(&irda_rq_queue.done, &wait); - if (atomic_read(&irda_rq_queue.num_pending)) - schedule(); - else - __set_task_state(current, TASK_RUNNING); - remove_wait_queue(&irda_rq_queue.done, &wait); - } -} - -/* substate handler of the config-fsm to handle the cases where we want - * to wait for transmit completion before changing the port configuration - */ - -static int irda_tx_complete_fsm(struct sir_dev *dev) -{ - struct sir_fsm *fsm = &dev->fsm; - unsigned next_state, delay; - unsigned bytes_left; - - do { - next_state = fsm->substate; /* default: stay in current substate */ - delay = 0; - - switch(fsm->substate) { - - case SIRDEV_STATE_WAIT_XMIT: - if (dev->drv->chars_in_buffer) - bytes_left = dev->drv->chars_in_buffer(dev); - else - bytes_left = 0; - if (!bytes_left) { - next_state = SIRDEV_STATE_WAIT_UNTIL_SENT; - break; - } - - if (dev->speed > 115200) - delay = (bytes_left*8*10000) / (dev->speed/100); - else if (dev->speed > 0) - delay = (bytes_left*10*10000) / (dev->speed/100); - else - delay = 0; - /* expected delay (usec) until remaining bytes are sent */ - if (delay < 100) { - udelay(delay); - delay = 0; - break; - } - /* sleep some longer delay (msec) */ - delay = (delay+999) / 1000; - break; - - case SIRDEV_STATE_WAIT_UNTIL_SENT: - /* block until underlaying hardware buffer are empty */ - if (dev->drv->wait_until_sent) - dev->drv->wait_until_sent(dev); - next_state = SIRDEV_STATE_TX_DONE; - break; - - case SIRDEV_STATE_TX_DONE: - return 0; - - default: - IRDA_ERROR("%s - undefined state\n", __FUNCTION__); - return -EINVAL; - } - fsm->substate = next_state; - } while (delay == 0); - return delay; -} - -/* - * Function irda_config_fsm - * - * State machine to handle the configuration of the device (and attached dongle, if any). - * This handler is scheduled for execution in kIrDAd context, so we can sleep. - * however, kIrDAd is shared by all sir_dev devices so we better don't sleep there too - * long. Instead, for longer delays we start a timer to reschedule us later. - * On entry, fsm->sem is always locked and the netdev xmit queue stopped. - * Both must be unlocked/restarted on completion - but only on final exit. - */ - -static void irda_config_fsm(void *data) -{ - struct sir_dev *dev = data; - struct sir_fsm *fsm = &dev->fsm; - int next_state; - int ret = -1; - unsigned delay; - - IRDA_DEBUG(2, "%s(), <%ld>\n", __FUNCTION__, jiffies); - - do { - IRDA_DEBUG(3, "%s - state=0x%04x / substate=0x%04x\n", - __FUNCTION__, fsm->state, fsm->substate); - - next_state = fsm->state; - delay = 0; - - switch(fsm->state) { - - case SIRDEV_STATE_DONGLE_OPEN: - if (dev->dongle_drv != NULL) { - ret = sirdev_put_dongle(dev); - if (ret) { - fsm->result = -EINVAL; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - - /* Initialize dongle */ - ret = sirdev_get_dongle(dev, fsm->param); - if (ret) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - - /* Dongles are powered through the modem control lines which - * were just set during open. Before resetting, let's wait for - * the power to stabilize. This is what some dongle drivers did - * in open before, while others didn't - should be safe anyway. - */ - - delay = 50; - fsm->substate = SIRDEV_STATE_DONGLE_RESET; - next_state = SIRDEV_STATE_DONGLE_RESET; - - fsm->param = 9600; - - break; - - case SIRDEV_STATE_DONGLE_CLOSE: - /* shouldn't we just treat this as success=? */ - if (dev->dongle_drv == NULL) { - fsm->result = -EINVAL; - next_state = SIRDEV_STATE_ERROR; - break; - } - - ret = sirdev_put_dongle(dev); - if (ret) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_SET_DTR_RTS: - ret = sirdev_set_dtr_rts(dev, - (fsm->param&0x02) ? TRUE : FALSE, - (fsm->param&0x01) ? TRUE : FALSE); - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_SET_SPEED: - fsm->substate = SIRDEV_STATE_WAIT_XMIT; - next_state = SIRDEV_STATE_DONGLE_CHECK; - break; - - case SIRDEV_STATE_DONGLE_CHECK: - ret = irda_tx_complete_fsm(dev); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - if ((delay=ret) != 0) - break; - - if (dev->dongle_drv) { - fsm->substate = SIRDEV_STATE_DONGLE_RESET; - next_state = SIRDEV_STATE_DONGLE_RESET; - } - else { - dev->speed = fsm->param; - next_state = SIRDEV_STATE_PORT_SPEED; - } - break; - - case SIRDEV_STATE_DONGLE_RESET: - if (dev->dongle_drv->reset) { - ret = dev->dongle_drv->reset(dev); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - else - ret = 0; - if ((delay=ret) == 0) { - /* set serial port according to dongle default speed */ - if (dev->drv->set_speed) - dev->drv->set_speed(dev, dev->speed); - fsm->substate = SIRDEV_STATE_DONGLE_SPEED; - next_state = SIRDEV_STATE_DONGLE_SPEED; - } - break; - - case SIRDEV_STATE_DONGLE_SPEED: - if (dev->dongle_drv->reset) { - ret = dev->dongle_drv->set_speed(dev, fsm->param); - if (ret < 0) { - fsm->result = ret; - next_state = SIRDEV_STATE_ERROR; - break; - } - } - else - ret = 0; - if ((delay=ret) == 0) - next_state = SIRDEV_STATE_PORT_SPEED; - break; - - case SIRDEV_STATE_PORT_SPEED: - /* Finally we are ready to change the serial port speed */ - if (dev->drv->set_speed) - dev->drv->set_speed(dev, dev->speed); - dev->new_speed = 0; - next_state = SIRDEV_STATE_DONE; - break; - - case SIRDEV_STATE_DONE: - /* Signal network layer so it can send more frames */ - netif_wake_queue(dev->netdev); - next_state = SIRDEV_STATE_COMPLETE; - break; - - default: - IRDA_ERROR("%s - undefined state\n", __FUNCTION__); - fsm->result = -EINVAL; - /* fall thru */ - - case SIRDEV_STATE_ERROR: - IRDA_ERROR("%s - error: %d\n", __FUNCTION__, fsm->result); - -#if 0 /* don't enable this before we have netdev->tx_timeout to recover */ - netif_stop_queue(dev->netdev); -#else - netif_wake_queue(dev->netdev); -#endif - /* fall thru */ - - case SIRDEV_STATE_COMPLETE: - /* config change finished, so we are not busy any longer */ - sirdev_enable_rx(dev); - up(&fsm->sem); - return; - } - fsm->state = next_state; - } while(!delay); - - irda_queue_delayed_request(&fsm->rq, msecs_to_jiffies(delay)); -} - -/* schedule some device configuration task for execution by kIrDAd - * on behalf of the above state machine. - * can be called from process or interrupt/tasklet context. - */ - -int sirdev_schedule_request(struct sir_dev *dev, int initial_state, unsigned param) -{ - struct sir_fsm *fsm = &dev->fsm; - int xmit_was_down; - - IRDA_DEBUG(2, "%s - state=0x%04x / param=%u\n", __FUNCTION__, initial_state, param); - - if (down_trylock(&fsm->sem)) { - if (in_interrupt() || in_atomic() || irqs_disabled()) { - IRDA_DEBUG(1, "%s(), state machine busy!\n", __FUNCTION__); - return -EWOULDBLOCK; - } else - down(&fsm->sem); - } - - if (fsm->state == SIRDEV_STATE_DEAD) { - /* race with sirdev_close should never happen */ - IRDA_ERROR("%s(), instance staled!\n", __FUNCTION__); - up(&fsm->sem); - return -ESTALE; /* or better EPIPE? */ - } - - xmit_was_down = netif_queue_stopped(dev->netdev); - netif_stop_queue(dev->netdev); - atomic_set(&dev->enable_rx, 0); - - fsm->state = initial_state; - fsm->param = param; - fsm->result = 0; - - INIT_LIST_HEAD(&fsm->rq.lh_request); - fsm->rq.pending = 0; - fsm->rq.func = irda_config_fsm; - fsm->rq.data = dev; - - if (!irda_queue_request(&fsm->rq)) { /* returns 0 on error! */ - atomic_set(&dev->enable_rx, 1); - if (!xmit_was_down) - netif_wake_queue(dev->netdev); - up(&fsm->sem); - return -EAGAIN; - } - return 0; -} - -static int __init irda_thread_create(void) -{ - struct completion startup; - int pid; - - spin_lock_init(&irda_rq_queue.lock); - irda_rq_queue.thread = NULL; - INIT_LIST_HEAD(&irda_rq_queue.request_list); - init_waitqueue_head(&irda_rq_queue.kick); - init_waitqueue_head(&irda_rq_queue.done); - atomic_set(&irda_rq_queue.num_pending, 0); - - init_completion(&startup); - pid = kernel_thread(irda_thread, &startup, CLONE_FS|CLONE_FILES); - if (pid <= 0) - return -EAGAIN; - else - wait_for_completion(&startup); - - return 0; -} - -static void __exit irda_thread_join(void) -{ - if (irda_rq_queue.thread) { - flush_irda_queue(); - init_completion(&irda_rq_queue.exit); - irda_rq_queue.thread = NULL; - wake_up(&irda_rq_queue.kick); - wait_for_completion(&irda_rq_queue.exit); - } -} - -module_init(irda_thread_create); -module_exit(irda_thread_join); - -MODULE_AUTHOR("Martin Diehl <info@mdiehl.de>"); -MODULE_DESCRIPTION("IrDA SIR core"); -MODULE_LICENSE("GPL"); - diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index 58f76cefbc8..a4674044bd6 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -54,6 +54,7 @@ #include <linux/rtnetlink.h> #include <linux/serial_reg.h> #include <linux/dma-mapping.h> +#include <linux/pnp.h> #include <linux/platform_device.h> #include <asm/io.h> @@ -358,6 +359,16 @@ static inline void register_bank(int iobase, int bank) iobase + IRCC_MASTER); } +#ifdef CONFIG_PNP +/* PNP hotplug support */ +static const struct pnp_device_id smsc_ircc_pnp_table[] = { + { .id = "SMCf010", .driver_data = 0 }, + /* and presumably others */ + { } +}; +MODULE_DEVICE_TABLE(pnp, smsc_ircc_pnp_table); +#endif + /******************************************************************************* * @@ -2072,7 +2083,8 @@ static void smsc_ircc_sir_wait_hw_transmitter_finish(struct smsc_ircc_cb *self) /* PROBING * - * + * REVISIT we can be told about the device by PNP, and should use that info + * instead of probing hardware and creating a platform_device ... */ static int __init smsc_ircc_look_for_chips(void) diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index ea62a3e7d58..411f4d809c4 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1419,6 +1419,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev) mv643xx_eth_update_pscr(dev, &cmd); mv643xx_set_settings(dev, &cmd); + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); err = register_netdev(dev); if (err) goto out; diff --git a/drivers/net/ne.c b/drivers/net/ne.c index 93c494bcd18..b32765215f7 100644 --- a/drivers/net/ne.c +++ b/drivers/net/ne.c @@ -139,8 +139,9 @@ bad_clone_list[] __initdata = { #if defined(CONFIG_PLAT_MAPPI) # define DCR_VAL 0x4b -#elif defined(CONFIG_PLAT_OAKS32R) -# define DCR_VAL 0x48 +#elif defined(CONFIG_PLAT_OAKS32R) || \ + defined(CONFIG_TOSHIBA_RBTX4927) || defined(CONFIG_TOSHIBA_RBTX4938) +# define DCR_VAL 0x48 /* 8-bit mode */ #else # define DCR_VAL 0x49 #endif @@ -396,10 +397,22 @@ static int __init ne_probe1(struct net_device *dev, int ioaddr) /* We must set the 8390 for word mode. */ outb_p(DCR_VAL, ioaddr + EN0_DCFG); start_page = NESM_START_PG; - stop_page = NESM_STOP_PG; + + /* + * Realtek RTL8019AS datasheet says that the PSTOP register + * shouldn't exceed 0x60 in 8-bit mode. + * This chip can be identified by reading the signature from + * the remote byte count registers (otherwise write-only)... + */ + if ((DCR_VAL & 0x01) == 0 && /* 8-bit mode */ + inb(ioaddr + EN0_RCNTLO) == 0x50 && + inb(ioaddr + EN0_RCNTHI) == 0x70) + stop_page = 0x60; + else + stop_page = NESM_STOP_PG; } else { start_page = NE1SM_START_PG; - stop_page = NE1SM_STOP_PG; + stop_page = NE1SM_STOP_PG; } #if defined(CONFIG_PLAT_MAPPI) || defined(CONFIG_PLAT_OAKS32R) @@ -509,15 +522,9 @@ static int __init ne_probe1(struct net_device *dev, int ioaddr) ei_status.name = name; ei_status.tx_start_page = start_page; ei_status.stop_page = stop_page; -#if defined(CONFIG_TOSHIBA_RBTX4927) || defined(CONFIG_TOSHIBA_RBTX4938) - wordlength = 1; -#endif -#ifdef CONFIG_PLAT_OAKS32R - ei_status.word16 = 0; -#else - ei_status.word16 = (wordlength == 2); -#endif + /* Use 16-bit mode only if this wasn't overridden by DCR_VAL */ + ei_status.word16 = (wordlength == 2 && (DCR_VAL & 0x01)); ei_status.rx_start_page = start_page + TX_PAGES; #ifdef PACKETBUF_MEMSIZE diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 459443b572c..1b236bdf6b9 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -60,8 +60,10 @@ int mdiobus_register(struct mii_bus *bus) for (i = 0; i < PHY_MAX_ADDR; i++) { struct phy_device *phydev; - if (bus->phy_mask & (1 << i)) + if (bus->phy_mask & (1 << i)) { + bus->phy_map[i] = NULL; continue; + } phydev = get_phy_device(bus, i); diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index b82191d2bee..f5a3bf4d959 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -127,6 +127,7 @@ static const struct mii_chip_info { } mii_chip_table[] = { { "SiS 900 Internal MII PHY", 0x001d, 0x8000, LAN }, { "SiS 7014 Physical Layer Solution", 0x0016, 0xf830, LAN }, + { "SiS 900 on Foxconn 661 7MI", 0x0143, 0xBC70, LAN }, { "Altimata AC101LF PHY", 0x0022, 0x5520, LAN }, { "ADM 7001 LAN PHY", 0x002e, 0xcc60, LAN }, { "AMD 79C901 10BASE-T PHY", 0x0000, 0x6B70, LAN }, diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 227df9876a2..ffd267fab21 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -51,7 +51,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.2" +#define DRV_VERSION "1.3" #define PFX DRV_NAME " " /* @@ -79,6 +79,8 @@ #define NAPI_WEIGHT 64 #define PHY_RETRIES 1000 +#define RING_NEXT(x,s) (((x)+1) & ((s)-1)) + static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_TIMER | NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR @@ -96,6 +98,10 @@ static int disable_msi = 0; module_param(disable_msi, int, 0); MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); +static int idle_timeout = 100; +module_param(idle_timeout, int, 0); +MODULE_PARM_DESC(idle_timeout, "Idle timeout workaround for lost interrupts (ms)"); + static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, @@ -122,6 +128,7 @@ MODULE_DEVICE_TABLE(pci, sky2_id_table); /* Avoid conditionals by using array */ static const unsigned txqaddr[] = { Q_XA1, Q_XA2 }; static const unsigned rxqaddr[] = { Q_R1, Q_R2 }; +static const u32 portirq_msk[] = { Y2_IS_PORT_1, Y2_IS_PORT_2 }; /* This driver supports yukon2 chipset only */ static const char *yukon2_name[] = { @@ -298,7 +305,8 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) struct sky2_port *sky2 = netdev_priv(hw->dev[port]); u16 ctrl, ct1000, adv, pg, ledctrl, ledover; - if (sky2->autoneg == AUTONEG_ENABLE && hw->chip_id != CHIP_ID_YUKON_XL) { + if (sky2->autoneg == AUTONEG_ENABLE && + (hw->chip_id != CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)) { u16 ectrl = gm_phy_read(hw, port, PHY_MARV_EXT_CTRL); ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK | @@ -326,7 +334,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ctrl |= PHY_M_PC_MDI_XMODE(PHY_M_PC_ENA_AUTO); if (sky2->autoneg == AUTONEG_ENABLE && - hw->chip_id == CHIP_ID_YUKON_XL) { + (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)) { ctrl &= ~PHY_M_PC_DSC_MSK; ctrl |= PHY_M_PC_DSC(2) | PHY_M_PC_DOWN_S_ENA; } @@ -442,10 +450,11 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); /* set LED Function Control register */ - gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ - PHY_M_LEDC_INIT_CTRL(7) | /* 10 Mbps */ - PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ - PHY_M_LEDC_STA0_CTRL(7))); /* 1000 Mbps */ + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, + (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ + PHY_M_LEDC_INIT_CTRL(7) | /* 10 Mbps */ + PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ + PHY_M_LEDC_STA0_CTRL(7))); /* 1000 Mbps */ /* set Polarity Control register */ gm_phy_write(hw, port, PHY_MARV_PHY_STAT, @@ -459,6 +468,25 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) /* restore page register */ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); break; + case CHIP_ID_YUKON_EC_U: + pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + + /* select page 3 to access LED control register */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); + + /* set LED Function Control register */ + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, + (PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ + PHY_M_LEDC_INIT_CTRL(8) | /* 10 Mbps */ + PHY_M_LEDC_STA1_CTRL(7) | /* 100 Mbps */ + PHY_M_LEDC_STA0_CTRL(7)));/* 1000 Mbps */ + + /* set Blink Rate in LED Timer Control Register */ + gm_phy_write(hw, port, PHY_MARV_INT_MASK, + ledctrl | PHY_M_LED_BLINK_RT(BLINK_84MS)); + /* restore page register */ + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); + break; default: /* set Tx LED (LED_TX) to blink mode on Rx OR Tx activity */ @@ -467,19 +495,21 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ledover |= PHY_M_LED_MO_RX(MO_LED_OFF); } - if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev >= 2) { + if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev == CHIP_REV_YU_EC_A1) { /* apply fixes in PHY AFE */ - gm_phy_write(hw, port, 22, 255); + pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 255); + /* increase differential signal amplitude in 10BASE-T */ - gm_phy_write(hw, port, 24, 0xaa99); - gm_phy_write(hw, port, 23, 0x2011); + gm_phy_write(hw, port, 0x18, 0xaa99); + gm_phy_write(hw, port, 0x17, 0x2011); /* fix for IEEE A/B Symmetry failure in 1000BASE-T */ - gm_phy_write(hw, port, 24, 0xa204); - gm_phy_write(hw, port, 23, 0x2002); + gm_phy_write(hw, port, 0x18, 0xa204); + gm_phy_write(hw, port, 0x17, 0x2002); /* set page register to 0 */ - gm_phy_write(hw, port, 22, 0); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); } else { gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); @@ -553,6 +583,11 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) if (sky2->duplex == DUPLEX_FULL) reg |= GM_GPCR_DUP_FULL; + + /* turn off pause in 10/100mbps half duplex */ + else if (sky2->speed != SPEED_1000 && + hw->chip_id != CHIP_ID_YUKON_EC_U) + sky2->tx_pause = sky2->rx_pause = 0; } else reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL; @@ -719,7 +754,7 @@ static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2) { struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod; - sky2->tx_prod = (sky2->tx_prod + 1) % TX_RING_SIZE; + sky2->tx_prod = RING_NEXT(sky2->tx_prod, TX_RING_SIZE); return le; } @@ -735,7 +770,7 @@ static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx) static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2) { struct sky2_rx_le *le = sky2->rx_le + sky2->rx_put; - sky2->rx_put = (sky2->rx_put + 1) % RX_LE_SIZE; + sky2->rx_put = RING_NEXT(sky2->rx_put, RX_LE_SIZE); return le; } @@ -1050,7 +1085,7 @@ static int sky2_up(struct net_device *dev) /* Enable interrupts from phy/mac for port */ imask = sky2_read32(hw, B0_IMSK); - imask |= (port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; + imask |= portirq_msk[port]; sky2_write32(hw, B0_IMSK, imask); return 0; @@ -1078,7 +1113,7 @@ err_out: /* Modular subtraction in ring */ static inline int tx_dist(unsigned tail, unsigned head) { - return (head - tail) % TX_RING_SIZE; + return (head - tail) & (TX_RING_SIZE - 1); } /* Number of list elements available for next tx */ @@ -1255,7 +1290,7 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) le->opcode = OP_BUFFER | HW_OWNER; fre = sky2->tx_ring - + ((re - sky2->tx_ring) + i + 1) % TX_RING_SIZE; + + RING_NEXT((re - sky2->tx_ring) + i, TX_RING_SIZE); pci_unmap_addr_set(fre, mapaddr, mapping); } @@ -1315,7 +1350,7 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done) for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { struct tx_ring_info *fre; - fre = sky2->tx_ring + (put + i + 1) % TX_RING_SIZE; + fre = sky2->tx_ring + RING_NEXT(put + i, TX_RING_SIZE); pci_unmap_page(pdev, pci_unmap_addr(fre, mapaddr), skb_shinfo(skb)->frags[i].size, PCI_DMA_TODEVICE); @@ -1401,7 +1436,7 @@ static int sky2_down(struct net_device *dev) /* Disable port IRQ */ imask = sky2_read32(hw, B0_IMSK); - imask &= ~(sky2->port == 0) ? Y2_IS_PORT_1 : Y2_IS_PORT_2; + imask &= ~portirq_msk[port]; sky2_write32(hw, B0_IMSK, imask); /* turn off LED's */ @@ -1498,17 +1533,26 @@ static void sky2_link_up(struct sky2_port *sky2) sky2_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_ON | LINKLED_BLINK_OFF | LINKLED_LINKSYNC_OFF); - if (hw->chip_id == CHIP_ID_YUKON_XL) { + if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U) { u16 pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); + u16 led = PHY_M_LEDC_LOS_CTRL(1); /* link active */ + + switch(sky2->speed) { + case SPEED_10: + led |= PHY_M_LEDC_INIT_CTRL(7); + break; + + case SPEED_100: + led |= PHY_M_LEDC_STA1_CTRL(7); + break; + + case SPEED_1000: + led |= PHY_M_LEDC_STA0_CTRL(7); + break; + } gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 3); - gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, PHY_M_LEDC_LOS_CTRL(1) | /* LINK/ACT */ - PHY_M_LEDC_INIT_CTRL(sky2->speed == - SPEED_10 ? 7 : 0) | - PHY_M_LEDC_STA1_CTRL(sky2->speed == - SPEED_100 ? 7 : 0) | - PHY_M_LEDC_STA0_CTRL(sky2->speed == - SPEED_1000 ? 7 : 0)); + gm_phy_write(hw, port, PHY_MARV_PHY_CTRL, led); gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); } @@ -1583,7 +1627,7 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) sky2->speed = sky2_phy_speed(hw, aux); /* Pause bits are offset (9..8) */ - if (hw->chip_id == CHIP_ID_YUKON_XL) + if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U) aux >>= 6; sky2->rx_pause = (aux & PHY_M_PS_RX_P_EN) != 0; @@ -1859,35 +1903,28 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last) static int sky2_status_intr(struct sky2_hw *hw, int to_do) { int work_done = 0; + u16 hwidx = sky2_read16(hw, STAT_PUT_IDX); rmb(); - for(;;) { + while (hw->st_idx != hwidx) { struct sky2_status_le *le = hw->st_le + hw->st_idx; struct net_device *dev; struct sky2_port *sky2; struct sk_buff *skb; u32 status; u16 length; - u8 link, opcode; - opcode = le->opcode; - if (!opcode) - break; - opcode &= ~HW_OWNER; - - hw->st_idx = (hw->st_idx + 1) % STATUS_RING_SIZE; - le->opcode = 0; + hw->st_idx = RING_NEXT(hw->st_idx, STATUS_RING_SIZE); - link = le->link; - BUG_ON(link >= 2); - dev = hw->dev[link]; + BUG_ON(le->link >= 2); + dev = hw->dev[le->link]; sky2 = netdev_priv(dev); length = le->length; status = le->status; - switch (opcode) { + switch (le->opcode & ~HW_OWNER) { case OP_RXSTAT: skb = sky2_receive(sky2, length, status); if (!skb) @@ -1927,7 +1964,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do) case OP_TXINDEXLE: /* TX index reports status for both ports */ - sky2_tx_done(hw->dev[0], status & 0xffff); + BUILD_BUG_ON(TX_RING_SIZE > 0x1000); + sky2_tx_done(hw->dev[0], status & 0xfff); if (hw->dev[1]) sky2_tx_done(hw->dev[1], ((status >> 24) & 0xff) @@ -1937,8 +1975,8 @@ static int sky2_status_intr(struct sky2_hw *hw, int to_do) default: if (net_ratelimit()) printk(KERN_WARNING PFX - "unknown status opcode 0x%x\n", opcode); - break; + "unknown status opcode 0x%x\n", le->opcode); + goto exit_loop; } } @@ -2089,12 +2127,13 @@ static void sky2_descriptor_error(struct sky2_hw *hw, unsigned port, */ static void sky2_idle(unsigned long arg) { - struct net_device *dev = (struct net_device *) arg; + struct sky2_hw *hw = (struct sky2_hw *) arg; + struct net_device *dev = hw->dev[0]; - local_irq_disable(); if (__netif_rx_schedule_prep(dev)) __netif_rx_schedule(dev); - local_irq_enable(); + + mod_timer(&hw->idle_timer, jiffies + msecs_to_jiffies(idle_timeout)); } @@ -2105,65 +2144,46 @@ static int sky2_poll(struct net_device *dev0, int *budget) int work_done = 0; u32 status = sky2_read32(hw, B0_Y2_SP_EISR); - restart_poll: - if (unlikely(status & ~Y2_IS_STAT_BMU)) { - if (status & Y2_IS_HW_ERR) - sky2_hw_intr(hw); - - if (status & Y2_IS_IRQ_PHY1) - sky2_phy_intr(hw, 0); - - if (status & Y2_IS_IRQ_PHY2) - sky2_phy_intr(hw, 1); + if (status & Y2_IS_HW_ERR) + sky2_hw_intr(hw); - if (status & Y2_IS_IRQ_MAC1) - sky2_mac_intr(hw, 0); + if (status & Y2_IS_IRQ_PHY1) + sky2_phy_intr(hw, 0); - if (status & Y2_IS_IRQ_MAC2) - sky2_mac_intr(hw, 1); + if (status & Y2_IS_IRQ_PHY2) + sky2_phy_intr(hw, 1); - if (status & Y2_IS_CHK_RX1) - sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1); + if (status & Y2_IS_IRQ_MAC1) + sky2_mac_intr(hw, 0); - if (status & Y2_IS_CHK_RX2) - sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2); + if (status & Y2_IS_IRQ_MAC2) + sky2_mac_intr(hw, 1); - if (status & Y2_IS_CHK_TXA1) - sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1); + if (status & Y2_IS_CHK_RX1) + sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1); - if (status & Y2_IS_CHK_TXA2) - sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2); - } + if (status & Y2_IS_CHK_RX2) + sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2); - if (status & Y2_IS_STAT_BMU) { - work_done += sky2_status_intr(hw, work_limit - work_done); - *budget -= work_done; - dev0->quota -= work_done; + if (status & Y2_IS_CHK_TXA1) + sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1); - if (work_done >= work_limit) - return 1; + if (status & Y2_IS_CHK_TXA2) + sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2); + if (status & Y2_IS_STAT_BMU) sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); - } - - mod_timer(&hw->idle_timer, jiffies + HZ); - local_irq_disable(); - __netif_rx_complete(dev0); + work_done = sky2_status_intr(hw, work_limit); + *budget -= work_done; + dev0->quota -= work_done; - status = sky2_read32(hw, B0_Y2_SP_LISR); + if (work_done >= work_limit) + return 1; - if (unlikely(status)) { - /* More work pending, try and keep going */ - if (__netif_rx_schedule_prep(dev0)) { - __netif_rx_reschedule(dev0, work_done); - status = sky2_read32(hw, B0_Y2_SP_EISR); - local_irq_enable(); - goto restart_poll; - } - } + netif_rx_complete(dev0); - local_irq_enable(); + status = sky2_read32(hw, B0_Y2_SP_LISR); return 0; } @@ -2244,13 +2264,6 @@ static int __devinit sky2_reset(struct sky2_hw *hw) return -EOPNOTSUPP; } - /* This chip is new and not tested yet */ - if (hw->chip_id == CHIP_ID_YUKON_EC_U) { - pr_info(PFX "%s: is a version of Yukon 2 chipset that has not been tested yet.\n", - pci_name(hw->pdev)); - pr_info("Please report success/failure to maintainer <shemminger@osdl.org>\n"); - } - /* disable ASF */ if (hw->chip_id <= CHIP_ID_YUKON_EC) { sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET); @@ -3302,7 +3315,10 @@ static int __devinit sky2_probe(struct pci_dev *pdev, sky2_write32(hw, B0_IMSK, Y2_IS_BASE); - setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) dev); + setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw); + if (idle_timeout > 0) + mod_timer(&hw->idle_timer, + jiffies + msecs_to_jiffies(idle_timeout)); pci_set_drvdata(pdev, hw); @@ -3342,6 +3358,8 @@ static void __devexit sky2_remove(struct pci_dev *pdev) del_timer_sync(&hw->idle_timer); sky2_write32(hw, B0_IMSK, 0); + synchronize_irq(hw->pdev->irq); + dev0 = hw->dev[0]; dev1 = hw->dev[1]; if (dev1) diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index b026f5653f0..8012994c9b9 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -378,6 +378,9 @@ enum { CHIP_REV_YU_EC_A1 = 0, /* Chip Rev. for Yukon-EC A1/A0 */ CHIP_REV_YU_EC_A2 = 1, /* Chip Rev. for Yukon-EC A2 */ CHIP_REV_YU_EC_A3 = 2, /* Chip Rev. for Yukon-EC A3 */ + + CHIP_REV_YU_EC_U_A0 = 0, + CHIP_REV_YU_EC_U_A1 = 1, }; /* B2_Y2_CLK_GATE 8 bit Clock Gating (Yukon-2 only) */ diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 43f5e86fc55..394339d5e87 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -1652,6 +1652,8 @@ spider_net_enable_card(struct spider_net_card *card) { SPIDER_NET_GFTRESTRT, SPIDER_NET_RESTART_VALUE }, { SPIDER_NET_GMRWOLCTRL, 0 }, + { SPIDER_NET_GTESTMD, 0x10000000 }, + { SPIDER_NET_GTTQMSK, 0x00400040 }, { SPIDER_NET_GTESTMD, 0 }, { SPIDER_NET_GMACINTEN, 0 }, @@ -1792,15 +1794,7 @@ spider_net_setup_phy(struct spider_net_card *card) if (phy->def->ops->setup_forced) phy->def->ops->setup_forced(phy, SPEED_1000, DUPLEX_FULL); - /* the following two writes could be moved to sungem_phy.c */ - /* enable fiber mode */ - spider_net_write_phy(card->netdev, 1, MII_NCONFIG, 0x9020); - /* LEDs active in both modes, autosense prio = fiber */ - spider_net_write_phy(card->netdev, 1, MII_NCONFIG, 0x945f); - - /* switch off fibre autoneg */ - spider_net_write_phy(card->netdev, 1, MII_NCONFIG, 0xfc01); - spider_net_write_phy(card->netdev, 1, 0x0b, 0x0004); + phy->def->ops->enable_fiber(phy); phy->def->ops->read_link(phy); pr_info("Found %s with %i Mbps, %s-duplex.\n", phy->def->name, diff --git a/drivers/net/spider_net.h b/drivers/net/spider_net.h index 5922b529a04..3b8d951cf73 100644 --- a/drivers/net/spider_net.h +++ b/drivers/net/spider_net.h @@ -120,6 +120,8 @@ extern char spider_net_driver_name[]; #define SPIDER_NET_GMRUAFILnR 0x00000500 #define SPIDER_NET_GMRUA0FIL15R 0x00000578 +#define SPIDER_NET_GTTQMSK 0x00000934 + /* RX DMA controller registers, all 0x00000a.. are for DMA controller A, * 0x00000b.. for DMA controller B, etc. */ #define SPIDER_NET_GDADCHA 0x00000a00 diff --git a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c index 046371ee5bb..b2ddd5e7930 100644 --- a/drivers/net/sungem_phy.c +++ b/drivers/net/sungem_phy.c @@ -329,6 +329,30 @@ static int bcm5421_init(struct mii_phy* phy) return 0; } +static int bcm5421_enable_fiber(struct mii_phy* phy) +{ + /* enable fiber mode */ + phy_write(phy, MII_NCONFIG, 0x9020); + /* LEDs active in both modes, autosense prio = fiber */ + phy_write(phy, MII_NCONFIG, 0x945f); + + /* switch off fibre autoneg */ + phy_write(phy, MII_NCONFIG, 0xfc01); + phy_write(phy, 0x0b, 0x0004); + + return 0; +} + +static int bcm5461_enable_fiber(struct mii_phy* phy) +{ + phy_write(phy, MII_NCONFIG, 0xfc0c); + phy_write(phy, MII_BMCR, 0x4140); + phy_write(phy, MII_NCONFIG, 0xfc0b); + phy_write(phy, MII_BMCR, 0x0140); + + return 0; +} + static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise) { u16 ctl, adv; @@ -762,6 +786,7 @@ static struct mii_phy_ops bcm5421_phy_ops = { .setup_forced = bcm54xx_setup_forced, .poll_link = genmii_poll_link, .read_link = bcm54xx_read_link, + .enable_fiber = bcm5421_enable_fiber, }; static struct mii_phy_def bcm5421_phy_def = { @@ -792,6 +817,25 @@ static struct mii_phy_def bcm5421k2_phy_def = { .ops = &bcm5421k2_phy_ops }; +static struct mii_phy_ops bcm5461_phy_ops = { + .init = bcm5421_init, + .suspend = generic_suspend, + .setup_aneg = bcm54xx_setup_aneg, + .setup_forced = bcm54xx_setup_forced, + .poll_link = genmii_poll_link, + .read_link = bcm54xx_read_link, + .enable_fiber = bcm5461_enable_fiber, +}; + +static struct mii_phy_def bcm5461_phy_def = { + .phy_id = 0x002060c0, + .phy_id_mask = 0xfffffff0, + .name = "BCM5461", + .features = MII_GBIT_FEATURES, + .magic_aneg = 1, + .ops = &bcm5461_phy_ops +}; + /* Broadcom BCM 5462 built-in Vesta */ static struct mii_phy_ops bcm5462V_phy_ops = { .init = bcm5421_init, @@ -857,6 +901,7 @@ static struct mii_phy_def* mii_phy_table[] = { &bcm5411_phy_def, &bcm5421_phy_def, &bcm5421k2_phy_def, + &bcm5461_phy_def, &bcm5462V_phy_def, &marvell_phy_def, &genmii_phy_def, diff --git a/drivers/net/sungem_phy.h b/drivers/net/sungem_phy.h index 430544496c5..69e125197fc 100644 --- a/drivers/net/sungem_phy.h +++ b/drivers/net/sungem_phy.h @@ -12,6 +12,7 @@ struct mii_phy_ops int (*setup_forced)(struct mii_phy *phy, int speed, int fd); int (*poll_link)(struct mii_phy *phy); int (*read_link)(struct mii_phy *phy); + int (*enable_fiber)(struct mii_phy *phy); }; /* Structure used to statically define an mii/gii based PHY */ diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 73e271e59c6..2bd9592b75c 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -69,8 +69,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "3.56" -#define DRV_MODULE_RELDATE "Apr 1, 2006" +#define DRV_MODULE_VERSION "3.57" +#define DRV_MODULE_RELDATE "Apr 28, 2006" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 @@ -974,6 +974,8 @@ static int tg3_phy_reset_5703_4_5(struct tg3 *tp) return err; } +static void tg3_link_report(struct tg3 *); + /* This will reset the tigon3 PHY if there is no valid * link unless the FORCE argument is non-zero. */ @@ -987,6 +989,11 @@ static int tg3_phy_reset(struct tg3 *tp) if (err != 0) return -EBUSY; + if (netif_running(tp->dev) && netif_carrier_ok(tp->dev)) { + netif_carrier_off(tp->dev); + tg3_link_report(tp); + } + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { @@ -1023,6 +1030,12 @@ out: tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x14e2); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); } + else if (tp->tg3_flags2 & TG3_FLG2_PHY_JITTER_BUG) { + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x010b); + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); + } /* Set Extended packet length bit (bit 14) on all chips that */ /* support jumbo frames */ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { @@ -3531,7 +3544,7 @@ static irqreturn_t tg3_test_isr(int irq, void *dev_id, return IRQ_RETVAL(0); } -static int tg3_init_hw(struct tg3 *); +static int tg3_init_hw(struct tg3 *, int); static int tg3_halt(struct tg3 *, int, int); #ifdef CONFIG_NET_POLL_CONTROLLER @@ -3567,7 +3580,7 @@ static void tg3_reset_task(void *_data) tp->tg3_flags2 &= ~TG3_FLG2_RESTART_TIMER; tg3_halt(tp, RESET_KIND_SHUTDOWN, 0); - tg3_init_hw(tp); + tg3_init_hw(tp, 1); tg3_netif_start(tp); @@ -4042,7 +4055,7 @@ static int tg3_change_mtu(struct net_device *dev, int new_mtu) tg3_set_mtu(dev, tp, new_mtu); - tg3_init_hw(tp); + tg3_init_hw(tp, 0); tg3_netif_start(tp); @@ -5719,9 +5732,23 @@ static int tg3_set_mac_addr(struct net_device *dev, void *p) if (!netif_running(dev)) return 0; - spin_lock_bh(&tp->lock); - __tg3_set_mac_addr(tp); - spin_unlock_bh(&tp->lock); + if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { + /* Reset chip so that ASF can re-init any MAC addresses it + * needs. + */ + tg3_netif_stop(tp); + tg3_full_lock(tp, 1); + + tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); + tg3_init_hw(tp, 0); + + tg3_netif_start(tp); + tg3_full_unlock(tp); + } else { + spin_lock_bh(&tp->lock); + __tg3_set_mac_addr(tp); + spin_unlock_bh(&tp->lock); + } return 0; } @@ -5771,7 +5798,7 @@ static void __tg3_set_coalesce(struct tg3 *tp, struct ethtool_coalesce *ec) } /* tp->lock is held. */ -static int tg3_reset_hw(struct tg3 *tp) +static int tg3_reset_hw(struct tg3 *tp, int reset_phy) { u32 val, rdmac_mode; int i, err, limit; @@ -5786,7 +5813,7 @@ static int tg3_reset_hw(struct tg3 *tp) tg3_abort_hw(tp, 1); } - if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) + if ((tp->tg3_flags2 & TG3_FLG2_MII_SERDES) && reset_phy) tg3_phy_reset(tp); err = tg3_chip_reset(tp); @@ -6327,7 +6354,7 @@ static int tg3_reset_hw(struct tg3 *tp) tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl); } - err = tg3_setup_phy(tp, 1); + err = tg3_setup_phy(tp, reset_phy); if (err) return err; @@ -6400,7 +6427,7 @@ static int tg3_reset_hw(struct tg3 *tp) /* Called at device open time to get the chip ready for * packet processing. Invoked with tp->lock held. */ -static int tg3_init_hw(struct tg3 *tp) +static int tg3_init_hw(struct tg3 *tp, int reset_phy) { int err; @@ -6413,7 +6440,7 @@ static int tg3_init_hw(struct tg3 *tp) tw32(TG3PCI_MEM_WIN_BASE_ADDR, 0); - err = tg3_reset_hw(tp); + err = tg3_reset_hw(tp, reset_phy); out: return err; @@ -6683,7 +6710,7 @@ static int tg3_test_msi(struct tg3 *tp) tg3_full_lock(tp, 1); tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); - err = tg3_init_hw(tp); + err = tg3_init_hw(tp, 1); tg3_full_unlock(tp); @@ -6748,7 +6775,7 @@ static int tg3_open(struct net_device *dev) tg3_full_lock(tp, 0); - err = tg3_init_hw(tp); + err = tg3_init_hw(tp, 1); if (err) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); tg3_free_rings(tp); @@ -7839,7 +7866,7 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e if (netif_running(dev)) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); - tg3_init_hw(tp); + tg3_init_hw(tp, 1); tg3_netif_start(tp); } @@ -7884,7 +7911,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam if (netif_running(dev)) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); - tg3_init_hw(tp); + tg3_init_hw(tp, 1); tg3_netif_start(tp); } @@ -8427,6 +8454,9 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode) tx_len = 1514; skb = dev_alloc_skb(tx_len); + if (!skb) + return -ENOMEM; + tx_data = skb_put(skb, tx_len); memcpy(tx_data, tp->dev->dev_addr, 6); memset(tx_data + 6, 0x0, 8); @@ -8522,7 +8552,7 @@ static int tg3_test_loopback(struct tg3 *tp) if (!netif_running(tp->dev)) return TG3_LOOPBACK_FAILED; - tg3_reset_hw(tp); + tg3_reset_hw(tp, 1); if (tg3_run_loopback(tp, TG3_MAC_LOOPBACK)) err |= TG3_MAC_LOOPBACK_FAILED; @@ -8596,7 +8626,7 @@ static void tg3_self_test(struct net_device *dev, struct ethtool_test *etest, tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); if (netif_running(dev)) { tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; - tg3_init_hw(tp); + tg3_init_hw(tp, 1); tg3_netif_start(tp); } @@ -9377,7 +9407,7 @@ static int tg3_nvram_write_block_buffered(struct tg3 *tp, u32 offset, u32 len, if ((page_off == 0) || (i == 0)) nvram_cmd |= NVRAM_CMD_FIRST; - else if (page_off == (tp->nvram_pagesize - 4)) + if (page_off == (tp->nvram_pagesize - 4)) nvram_cmd |= NVRAM_CMD_LAST; if (i == (len - 4)) @@ -10353,10 +10383,13 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) tp->tg3_flags2 |= TG3_FLG2_PHY_5704_A0_BUG; - if ((tp->tg3_flags2 & TG3_FLG2_5705_PLUS) && - (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5755) && - (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787)) - tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG; + if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) + tp->tg3_flags2 |= TG3_FLG2_PHY_JITTER_BUG; + else + tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG; + } tp->coalesce_mode = 0; if (GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_AX && @@ -11569,7 +11602,7 @@ static int tg3_suspend(struct pci_dev *pdev, pm_message_t state) tg3_full_lock(tp, 0); tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; - tg3_init_hw(tp); + tg3_init_hw(tp, 1); tp->timer.expires = jiffies + tp->timer_offset; add_timer(&tp->timer); @@ -11603,7 +11636,7 @@ static int tg3_resume(struct pci_dev *pdev) tg3_full_lock(tp, 0); tp->tg3_flags |= TG3_FLAG_INIT_COMPLETE; - tg3_init_hw(tp); + tg3_init_hw(tp, 1); tp->timer.expires = jiffies + tp->timer_offset; add_timer(&tp->timer); diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 8c8b987d125..0e29b885d44 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2215,6 +2215,7 @@ struct tg3 { #define TG3_FLG2_HW_TSO_2 0x08000000 #define TG3_FLG2_HW_TSO (TG3_FLG2_HW_TSO_1 | TG3_FLG2_HW_TSO_2) #define TG3_FLG2_1SHOT_MSI 0x10000000 +#define TG3_FLG2_PHY_JITTER_BUG 0x20000000 u32 split_mode_max_reqs; #define SPLIT_MODE_5704_MAX_REQ 3 diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 6a23964c131..a6dc53b4250 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -129,6 +129,7 @@ - Massive clean-up - Rewrite PHY, media handling (remove options, full_duplex, backoff) - Fix Tx engine race for good + - Craig Brind: Zero padded aligned buffers for short packets. */ @@ -1326,7 +1327,12 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev) rp->stats.tx_dropped++; return 0; } + + /* Padding is not copied and so must be redone. */ skb_copy_and_csum_dev(skb, rp->tx_buf[entry]); + if (skb->len < ETH_ZLEN) + memset(rp->tx_buf[entry] + skb->len, 0, + ETH_ZLEN - skb->len); rp->tx_skbuff_dma[entry] = 0; rp->tx_ring[entry].addr = cpu_to_le32(rp->tx_bufs_dma + (rp->tx_buf[entry] - diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index 9a06e61df0a..e2982a83ae4 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -939,9 +939,9 @@ static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm) return 0; } -static void bcm43xx_geo_init(struct bcm43xx_private *bcm) +static int bcm43xx_geo_init(struct bcm43xx_private *bcm) { - struct ieee80211_geo geo; + struct ieee80211_geo *geo; struct ieee80211_channel *chan; int have_a = 0, have_bg = 0; int i; @@ -949,7 +949,10 @@ static void bcm43xx_geo_init(struct bcm43xx_private *bcm) struct bcm43xx_phyinfo *phy; const char *iso_country; - memset(&geo, 0, sizeof(geo)); + geo = kzalloc(sizeof(*geo), GFP_KERNEL); + if (!geo) + return -ENOMEM; + for (i = 0; i < bcm->nr_80211_available; i++) { phy = &(bcm->core_80211_ext[i].phy); switch (phy->type) { @@ -967,31 +970,36 @@ static void bcm43xx_geo_init(struct bcm43xx_private *bcm) iso_country = bcm43xx_locale_iso(bcm->sprom.locale); if (have_a) { - for (i = 0, channel = 0; channel < 201; channel++) { - chan = &geo.a[i++]; + for (i = 0, channel = IEEE80211_52GHZ_MIN_CHANNEL; + channel <= IEEE80211_52GHZ_MAX_CHANNEL; channel++) { + chan = &geo->a[i++]; chan->freq = bcm43xx_channel_to_freq_a(channel); chan->channel = channel; } - geo.a_channels = i; + geo->a_channels = i; } if (have_bg) { - for (i = 0, channel = 1; channel < 15; channel++) { - chan = &geo.bg[i++]; + for (i = 0, channel = IEEE80211_24GHZ_MIN_CHANNEL; + channel <= IEEE80211_24GHZ_MAX_CHANNEL; channel++) { + chan = &geo->bg[i++]; chan->freq = bcm43xx_channel_to_freq_bg(channel); chan->channel = channel; } - geo.bg_channels = i; + geo->bg_channels = i; } - memcpy(geo.name, iso_country, 2); + memcpy(geo->name, iso_country, 2); if (0 /*TODO: Outdoor use only */) - geo.name[2] = 'O'; + geo->name[2] = 'O'; else if (0 /*TODO: Indoor use only */) - geo.name[2] = 'I'; + geo->name[2] = 'I'; else - geo.name[2] = ' '; - geo.name[3] = '\0'; + geo->name[2] = ' '; + geo->name[3] = '\0'; + + ieee80211_set_geo(bcm->ieee, geo); + kfree(geo); - ieee80211_set_geo(bcm->ieee, &geo); + return 0; } /* DummyTransmission function, as documented on @@ -3479,16 +3487,17 @@ static int bcm43xx_attach_board(struct bcm43xx_private *bcm) goto err_80211_unwind; bcm43xx_wireless_core_disable(bcm); } + err = bcm43xx_geo_init(bcm); + if (err) + goto err_80211_unwind; bcm43xx_pctl_set_crystal(bcm, 0); /* Set the MAC address in the networking subsystem */ - if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) + if (is_valid_ether_addr(bcm->sprom.et1macaddr)) memcpy(bcm->net_dev->dev_addr, bcm->sprom.et1macaddr, 6); else memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6); - bcm43xx_geo_init(bcm); - snprintf(bcm->nick, IW_ESSID_MAX_SIZE, "Broadcom %04X", bcm->chip_id); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h index eca79a38594..30a202b258b 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h @@ -118,12 +118,14 @@ int bcm43xx_channel_to_freq(struct bcm43xx_private *bcm, static inline int bcm43xx_is_valid_channel_a(u8 channel) { - return (channel <= 200); + return (channel >= IEEE80211_52GHZ_MIN_CHANNEL + && channel <= IEEE80211_52GHZ_MAX_CHANNEL); } static inline int bcm43xx_is_valid_channel_bg(u8 channel) { - return (channel >= 1 && channel <= 14); + return (channel >= IEEE80211_24GHZ_MIN_CHANNEL + && channel <= IEEE80211_24GHZ_MAX_CHANNEL); } static inline int bcm43xx_is_valid_channel(struct bcm43xx_private *bcm, diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c index 33137165727..b0abac51553 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c @@ -1287,7 +1287,7 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm) if (radio->revision == 8) bcm43xx_phy_write(bcm, 0x0805, 0x3230); bcm43xx_phy_init_pctl(bcm); - if (bcm->chip_id == 0x4306 && bcm->chip_package != 2) { + if (bcm->chip_id == 0x4306 && bcm->chip_package == 2) { bcm43xx_phy_write(bcm, 0x0429, bcm43xx_phy_read(bcm, 0x0429) & 0xBFFF); bcm43xx_phy_write(bcm, 0x04C3, diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c index 3edbb481a0a..b45063974ae 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c @@ -182,8 +182,11 @@ static int bcm43xx_wx_set_mode(struct net_device *net_dev, mode = BCM43xx_INITIAL_IWMODE; bcm43xx_lock_mmio(bcm, flags); - if (bcm->ieee->iw_mode != mode) - bcm43xx_set_iwmode(bcm, mode); + if (bcm->initialized) { + if (bcm->ieee->iw_mode != mode) + bcm43xx_set_iwmode(bcm, mode); + } else + bcm->ieee->iw_mode = mode; bcm43xx_unlock_mmio(bcm, flags); return 0; diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 2087a397ef1..9855c4c920b 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -793,8 +793,10 @@ static int msix_capability_init(struct pci_dev *dev, if (!entry) break; vector = get_msi_vector(dev); - if (vector < 0) + if (vector < 0) { + kmem_cache_free(msi_cachep, entry); break; + } j = entries[i].entry; entries[i].vector = vector; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index c42ae2cf8d6..19e2b174d33 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -642,13 +642,15 @@ static void quirk_via_irq(struct pci_dev *dev) new_irq = dev->irq & 0xf; pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); if (new_irq != irq) { - printk(KERN_INFO "PCI: Via IRQ fixup for %s, from %d to %d\n", + printk(KERN_INFO "PCI: VIA IRQ fixup for %s, from %d to %d\n", pci_name(dev), irq, new_irq); udelay(15); /* unknown if delay really needed */ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, new_irq); } } -DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_via_irq); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_irq); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_via_irq); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, quirk_via_irq); /* * VIA VT82C598 has its device ID settable and many BIOSes diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index bd0308e8981..a2f05f48515 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -509,7 +509,8 @@ static irqreturn_t i365_count_irq(int irq, void *dev, struct pt_regs *regs) static u_int __init test_irq(u_short sock, int irq) { debug(2, " testing ISA irq %d\n", irq); - if (request_irq(irq, i365_count_irq, 0, "scan", i365_count_irq) != 0) + if (request_irq(irq, i365_count_irq, SA_PROBEIRQ, "scan", + i365_count_irq) != 0) return 1; irq_hits = 0; irq_sock = sock; msleep(10); @@ -561,7 +562,7 @@ static u_int __init isa_scan(u_short sock, u_int mask0) } else { /* Fallback: just find interrupts that aren't in use */ for (i = 0; i < 16; i++) - if ((mask0 & (1 << i)) && (_check_irq(i, 0) == 0)) + if ((mask0 & (1 << i)) && (_check_irq(i, SA_PROBEIRQ) == 0)) mask1 |= (1 << i); printk("default"); /* If scan failed, default to polled status */ @@ -725,7 +726,7 @@ static void __init add_pcic(int ns, int type) u_int cs_mask = mask & ((cs_irq) ? (1<<cs_irq) : ~(1<<12)); for (cs_irq = 15; cs_irq > 0; cs_irq--) if ((cs_mask & (1 << cs_irq)) && - (_check_irq(cs_irq, 0) == 0)) + (_check_irq(cs_irq, SA_PROBEIRQ) == 0)) break; if (cs_irq) { grab_irq = 1; diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index b1e3e6179e5..6c9ad92747f 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -58,7 +58,7 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) unsigned long data; ssize_t ret; - if (count < sizeof(unsigned long)) + if (count != sizeof(unsigned int) && count < sizeof(unsigned long)) return -EINVAL; add_wait_queue(&rtc->irq_queue, &wait); @@ -90,11 +90,16 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) if (ret == 0) { /* Check for any data updates */ if (rtc->ops->read_callback) - data = rtc->ops->read_callback(rtc->class_dev.dev, data); - - ret = put_user(data, (unsigned long __user *)buf); - if (ret == 0) - ret = sizeof(unsigned long); + data = rtc->ops->read_callback(rtc->class_dev.dev, + data); + + if (sizeof(int) != sizeof(long) && + count == sizeof(unsigned int)) + ret = put_user(data, (unsigned int __user *)buf) ?: + sizeof(unsigned int); + else + ret = put_user(data, (unsigned long __user *)buf) ?: + sizeof(unsigned long); } return ret; } diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index a23ec54989f..2bc8aad4721 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -178,9 +178,9 @@ static int sa1100_rtc_open(struct device *dev) return 0; fail_pi: - free_irq(IRQ_RTCAlrm, NULL); + free_irq(IRQ_RTCAlrm, dev); fail_ai: - free_irq(IRQ_RTC1Hz, NULL); + free_irq(IRQ_RTC1Hz, dev); fail_ui: return ret; } @@ -295,7 +295,7 @@ static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) { - seq_printf(seq, "trim/divider\t: 0x%08x\n", RTTR); + seq_printf(seq, "trim/divider\t: 0x%08lx\n", RTTR); seq_printf(seq, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" ); seq_printf(seq, "update_IRQ\t: %s\n", diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index a3bfebcf31e..cfb1fff3787 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -315,6 +315,11 @@ dasd_increase_state(struct dasd_device *device) rc = dasd_state_basic_to_ready(device); if (!rc && + device->state == DASD_STATE_UNFMT && + device->target > DASD_STATE_UNFMT) + rc = -EPERM; + + if (!rc && device->state == DASD_STATE_READY && device->target >= DASD_STATE_ONLINE) rc = dasd_state_ready_to_online(device); diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index c1c6f138115..216bc4fba19 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -45,6 +45,7 @@ struct dasd_devmap { unsigned int devindex; unsigned short features; struct dasd_device *device; + struct dasd_uid uid; }; /* @@ -716,6 +717,68 @@ dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *bu static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL); +static ssize_t +dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_devmap *devmap; + int alias; + + devmap = dasd_find_busid(dev->bus_id); + spin_lock(&dasd_devmap_lock); + if (!IS_ERR(devmap)) + alias = devmap->uid.alias; + else + alias = 0; + spin_unlock(&dasd_devmap_lock); + + return sprintf(buf, alias ? "1\n" : "0\n"); +} + +static DEVICE_ATTR(alias, 0444, dasd_alias_show, NULL); + +static ssize_t +dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_devmap *devmap; + char *vendor; + + devmap = dasd_find_busid(dev->bus_id); + spin_lock(&dasd_devmap_lock); + if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0) + vendor = devmap->uid.vendor; + else + vendor = ""; + spin_unlock(&dasd_devmap_lock); + + return snprintf(buf, PAGE_SIZE, "%s\n", vendor); +} + +static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL); + +#define UID_STRLEN ( /* vendor */ 3 + 1 + /* serial */ 14 + 1 +\ + /* SSID */ 4 + 1 + /* unit addr */ 2 + 1) + +static ssize_t +dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_devmap *devmap; + char uid[UID_STRLEN]; + + devmap = dasd_find_busid(dev->bus_id); + spin_lock(&dasd_devmap_lock); + if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0) + snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x", + devmap->uid.vendor, devmap->uid.serial, + devmap->uid.ssid, devmap->uid.unit_addr); + else + uid[0] = 0; + spin_unlock(&dasd_devmap_lock); + + return snprintf(buf, PAGE_SIZE, "%s\n", uid); +} + +static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL); + /* * extended error-reporting */ @@ -759,6 +822,9 @@ static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store); static struct attribute * dasd_attrs[] = { &dev_attr_readonly.attr, &dev_attr_discipline.attr, + &dev_attr_alias.attr, + &dev_attr_vendor.attr, + &dev_attr_uid.attr, &dev_attr_use_diag.attr, &dev_attr_eer_enabled.attr, NULL, @@ -768,6 +834,42 @@ static struct attribute_group dasd_attr_group = { .attrs = dasd_attrs, }; + +/* + * Return copy of the device unique identifier. + */ +int +dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid) +{ + struct dasd_devmap *devmap; + + devmap = dasd_find_busid(cdev->dev.bus_id); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); + spin_lock(&dasd_devmap_lock); + *uid = devmap->uid; + spin_unlock(&dasd_devmap_lock); + return 0; +} + +/* + * Register the given device unique identifier into devmap struct. + */ +int +dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid) +{ + struct dasd_devmap *devmap; + + devmap = dasd_find_busid(cdev->dev.bus_id); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); + spin_lock(&dasd_devmap_lock); + devmap->uid = *uid; + spin_unlock(&dasd_devmap_lock); + return 0; +} +EXPORT_SYMBOL(dasd_set_uid); + /* * Return value of the specified feature. */ diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index ee09ef33d08..7d5a6cee4bd 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -446,6 +446,39 @@ dasd_eckd_cdl_reclen(int recid) return LABEL_SIZE; } +/* + * Generate device unique id that specifies the physical device. + */ +static int +dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid) +{ + struct dasd_eckd_private *private; + struct dasd_eckd_confdata *confdata; + + private = (struct dasd_eckd_private *) device->private; + if (!private) + return -ENODEV; + confdata = &private->conf_data; + if (!confdata) + return -ENODEV; + + memset(uid, 0, sizeof(struct dasd_uid)); + strncpy(uid->vendor, confdata->ned1.HDA_manufacturer, + sizeof(uid->vendor) - 1); + EBCASC(uid->vendor, sizeof(uid->vendor) - 1); + strncpy(uid->serial, confdata->ned1.HDA_location, + sizeof(uid->serial) - 1); + EBCASC(uid->serial, sizeof(uid->serial) - 1); + uid->ssid = confdata->neq.subsystemID; + if (confdata->ned2.sneq.flags == 0x40) { + uid->alias = 1; + uid->unit_addr = confdata->ned2.sneq.base_unit_addr; + } else + uid->unit_addr = confdata->ned1.unit_addr; + + return 0; +} + static int dasd_eckd_read_conf(struct dasd_device *device) { @@ -507,11 +540,15 @@ dasd_eckd_read_conf(struct dasd_device *device) return 0; } - +/* + * Check device characteristics. + * If the device is accessible using ECKD discipline, the device is enabled. + */ static int dasd_eckd_check_characteristics(struct dasd_device *device) { struct dasd_eckd_private *private; + struct dasd_uid uid; void *rdc_data; int rc; @@ -536,6 +573,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* Read Device Characteristics */ rdc_data = (void *) &(private->rdc_data); + memset(rdc_data, 0, sizeof(rdc_data)); rc = read_dev_chars(device->cdev, &rdc_data, 64); if (rc) { DEV_MESSAGE(KERN_WARNING, device, @@ -556,8 +594,17 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* Read Configuration Data */ rc = dasd_eckd_read_conf (device); - return rc; + if (rc) + return rc; + + /* Generate device unique id and register in devmap */ + rc = dasd_eckd_generate_uid(device, &uid); + if (rc) + return rc; + rc = dasd_set_uid(device->cdev, &uid); + + return rc; } static struct dasd_ccw_req * diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index ad8524bb7bb..d5734e976e1 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -228,26 +228,36 @@ struct dasd_eckd_confdata { unsigned char HDA_manufacturer[3]; unsigned char HDA_location[2]; unsigned char HDA_seqno[12]; - __u16 ID; + __u8 ID; + __u8 unit_addr; } __attribute__ ((packed)) ned1; - struct { + union { struct { - unsigned char identifier:2; - unsigned char token_id:1; - unsigned char sno_valid:1; - unsigned char subst_sno:1; - unsigned char recNED:1; - unsigned char emuNED:1; - unsigned char reserved:1; - } __attribute__ ((packed)) flags; - __u8 descriptor; - __u8 reserved[2]; - unsigned char dev_type[6]; - unsigned char dev_model[3]; - unsigned char DASD_manufacturer[3]; - unsigned char DASD_location[2]; - unsigned char DASD_seqno[12]; - __u16 ID; + struct { + unsigned char identifier:2; + unsigned char token_id:1; + unsigned char sno_valid:1; + unsigned char subst_sno:1; + unsigned char recNED:1; + unsigned char emuNED:1; + unsigned char reserved:1; + } __attribute__ ((packed)) flags; + __u8 descriptor; + __u8 reserved[2]; + unsigned char dev_type[6]; + unsigned char dev_model[3]; + unsigned char DASD_manufacturer[3]; + unsigned char DASD_location[2]; + unsigned char DASD_seqno[12]; + __u16 ID; + } __attribute__ ((packed)) ned; + struct { + unsigned char flags; /* byte 0 */ + unsigned char res2[7]; /* byte 1- 7 */ + unsigned char sua_flags; /* byte 8 */ + __u8 base_unit_addr; /* byte 9 */ + unsigned char res3[22]; /* byte 10-31 */ + } __attribute__ ((packed)) sneq; } __attribute__ ((packed)) ned2; struct { struct { diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 4293ba82752..d4b13e300a7 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -268,6 +268,16 @@ struct dasd_discipline { extern struct dasd_discipline *dasd_diag_discipline_pointer; +/* + * Unique identifier for dasd device. + */ +struct dasd_uid { + __u8 alias; + char vendor[4]; + char serial[15]; + __u16 ssid; + __u8 unit_addr; +}; /* * Notification numbers for extended error reporting notifications: @@ -516,6 +526,8 @@ void dasd_devmap_exit(void); struct dasd_device *dasd_create_device(struct ccw_device *); void dasd_delete_device(struct dasd_device *); +int dasd_get_uid(struct ccw_device *, struct dasd_uid *); +int dasd_set_uid(struct ccw_device *, struct dasd_uid *); int dasd_get_feature(struct ccw_device *, int); int dasd_set_feature(struct ccw_device *, int, int); diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c index c3915f60a3a..d71ef1adea5 100644 --- a/drivers/s390/char/tape_3590.c +++ b/drivers/s390/char/tape_3590.c @@ -230,14 +230,16 @@ tape_3590_read_attmsg(struct tape_device *device) * These functions are used to schedule follow-up actions from within an * interrupt context (like unsolicited interrupts). */ +struct work_handler_data { + struct tape_device *device; + enum tape_op op; + struct work_struct work; +}; + static void tape_3590_work_handler(void *data) { - struct { - struct tape_device *device; - enum tape_op op; - struct work_struct work; - } *p = data; + struct work_handler_data *p = data; switch (p->op) { case TO_MSEN: @@ -257,11 +259,7 @@ tape_3590_work_handler(void *data) static int tape_3590_schedule_work(struct tape_device *device, enum tape_op op) { - struct { - struct tape_device *device; - enum tape_op op; - struct work_struct work; - } *p; + struct work_handler_data *p; if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL) return -ENOMEM; @@ -316,7 +314,7 @@ tape_3590_bread(struct tape_device *device, struct request *req) rq_for_each_bio(bio, req) { bio_for_each_segment(bv, bio, i) { - dst = kmap(bv->bv_page) + bv->bv_offset; + dst = page_address(bv->bv_page) + bv->bv_offset; for (off = 0; off < bv->bv_len; off += TAPEBLOCK_HSEC_SIZE) { ccw->flags = CCW_FLAG_CC; @@ -1168,6 +1166,7 @@ tape_3590_setup_device(struct tape_device *device) static void tape_3590_cleanup_device(struct tape_device *device) { + flush_scheduled_work(); tape_std_unassign(device); kfree(device->discdata); @@ -1234,6 +1233,7 @@ static struct tape_discipline tape_discipline_3590 = { static struct ccw_device_id tape_3590_ids[] = { {CCW_DEVICE_DEVTYPE(0x3590, 0, 0x3590, 0), .driver_info = tape_3590}, + {CCW_DEVICE_DEVTYPE(0x3592, 0, 0x3592, 0), .driver_info = tape_3592}, { /* end of list */ } }; diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h index 2d311798edf..1fc95235934 100644 --- a/drivers/s390/char/tape_std.h +++ b/drivers/s390/char/tape_std.h @@ -153,6 +153,7 @@ enum s390_tape_type { tape_3480, tape_3490, tape_3590, + tape_3592, }; #endif // _TAPE_STD_H diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 6412b2c3edd..72187e54dca 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -242,28 +242,10 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) if (sch->vpm == mask) goto out_unreg; - if ((sch->schib.scsw.actl & (SCSW_ACTL_CLEAR_PEND | - SCSW_ACTL_HALT_PEND | - SCSW_ACTL_START_PEND | - SCSW_ACTL_RESUME_PEND)) && - (sch->schib.pmcw.lpum == mask)) { - int cc = cio_cancel(sch); - - if (cc == -ENODEV) - goto out_unreg; - - if (cc == -EINVAL) { - cc = cio_clear(sch); - if (cc == -ENODEV) - goto out_unreg; - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); - goto out_unlock; - } - } else if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && - (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && - (sch->schib.pmcw.lpum == mask)) { + if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && + (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && + (sch->schib.pmcw.lpum == mask) && + (sch->vpm == 0)) { int cc; cc = cio_clear(sch); @@ -653,13 +635,13 @@ __chp_add(struct subchannel_id schid, void *data) if (sch->schib.pmcw.chpid[i] == chp->id) { if (stsch(sch->schid, &sch->schib) != 0) { /* Endgame. */ - spin_unlock(&sch->lock); + spin_unlock_irq(&sch->lock); return -ENXIO; } break; } if (i==8) { - spin_unlock(&sch->lock); + spin_unlock_irq(&sch->lock); return 0; } sch->lpm = ((sch->schib.pmcw.pim & diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 814f9258ce0..96f519281d9 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -38,6 +38,7 @@ #include <linux/kernel.h> #include <linux/proc_fs.h> #include <linux/timer.h> +#include <linux/mempool.h> #include <asm/ccwdev.h> #include <asm/io.h> @@ -80,6 +81,8 @@ static int indicator_used[INDICATORS_PER_CACHELINE]; static __u32 * volatile indicators; static __u32 volatile spare_indicator; static atomic_t spare_indicator_usecount; +#define QDIO_MEMPOOL_SCSSC_ELEMENTS 2 +static mempool_t *qdio_mempool_scssc; static debug_info_t *qdio_dbf_setup; static debug_info_t *qdio_dbf_sbal; @@ -1637,7 +1640,7 @@ next: } kfree(irq_ptr->qdr); - kfree(irq_ptr); + free_page((unsigned long) irq_ptr); } static void @@ -2304,7 +2307,7 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr) QDIO_DBF_TEXT0(0,setup,"getssqd"); qdioac = 0; - ssqd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + ssqd_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC); if (!ssqd_area) { QDIO_PRINT_WARN("Could not get memory for chsc. Using all " \ "SIGAs for sch x%x.\n", irq_ptr->schid.sch_no); @@ -2364,7 +2367,7 @@ qdio_get_ssqd_information(struct qdio_irq *irq_ptr) out: qdio_check_subchannel_qebsm(irq_ptr, qdioac, ssqd_area->sch_token); - free_page ((unsigned long) ssqd_area); + mempool_free(ssqd_area, qdio_mempool_scssc); irq_ptr->qdioac = qdioac; } @@ -2458,7 +2461,7 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); } - scssc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + scssc_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC); if (!scssc_area) { QDIO_PRINT_WARN("No memory for setting indicators on " \ "subchannel 0.%x.%x.\n", @@ -2514,7 +2517,7 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero) QDIO_DBF_HEX2(0,setup,&real_addr_dev_st_chg_ind,sizeof(unsigned long)); result = 0; out: - free_page ((unsigned long) scssc_area); + mempool_free(scssc_area, qdio_mempool_scssc); return result; } @@ -2543,7 +2546,7 @@ tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target) if (!irq_ptr->is_thinint_irq) return -ENODEV; - scsscf_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); + scsscf_area = mempool_alloc(qdio_mempool_scssc, GFP_ATOMIC); if (!scsscf_area) { QDIO_PRINT_WARN("No memory for setting delay target on " \ "subchannel 0.%x.%x.\n", @@ -2581,7 +2584,7 @@ tiqdio_set_delay_target(struct qdio_irq *irq_ptr, unsigned long delay_target) QDIO_DBF_HEX2(0,trace,&delay_target,sizeof(unsigned long)); result = 0; /* not critical */ out: - free_page ((unsigned long) scsscf_area); + mempool_free(scsscf_area, qdio_mempool_scssc); return result; } @@ -2980,7 +2983,7 @@ qdio_allocate(struct qdio_initialize *init_data) qdio_allocate_do_dbf(init_data); /* create irq */ - irq_ptr = kzalloc(sizeof(struct qdio_irq), GFP_KERNEL | GFP_DMA); + irq_ptr = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); QDIO_DBF_TEXT0(0,setup,"irq_ptr:"); QDIO_DBF_HEX0(0,setup,&irq_ptr,sizeof(void*)); @@ -2995,7 +2998,7 @@ qdio_allocate(struct qdio_initialize *init_data) /* QDR must be in DMA area since CCW data address is only 32 bit */ irq_ptr->qdr=kmalloc(sizeof(struct qdr), GFP_KERNEL | GFP_DMA); if (!(irq_ptr->qdr)) { - kfree(irq_ptr); + free_page((unsigned long) irq_ptr); QDIO_PRINT_ERR("kmalloc of irq_ptr->qdr failed!\n"); return -ENOMEM; } @@ -3780,6 +3783,16 @@ oom: return -ENOMEM; } +static void *qdio_mempool_alloc(gfp_t gfp_mask, void *size) +{ + return (void *) get_zeroed_page(gfp_mask|GFP_DMA); +} + +static void qdio_mempool_free(void *element, void *size) +{ + free_page((unsigned long) element); +} + static int __init init_QDIO(void) { @@ -3809,6 +3822,10 @@ init_QDIO(void) qdio_add_procfs_entry(); + qdio_mempool_scssc = mempool_create(QDIO_MEMPOOL_SCSSC_ELEMENTS, + qdio_mempool_alloc, + qdio_mempool_free, NULL); + if (tiqdio_check_chsc_availability()) QDIO_PRINT_ERR("Not all CHSCs supported. Continuing.\n"); @@ -3824,6 +3841,7 @@ cleanup_QDIO(void) qdio_remove_procfs_entry(); qdio_release_qdio_memory(); qdio_unregister_dbf_views(); + mempool_destroy(qdio_mempool_scssc); printk("qdio: %s: module removed\n",version); } diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index b3c6e790779..cb14642d97a 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -8014,7 +8014,6 @@ static int (*qeth_old_arp_constructor) (struct neighbour *); static struct neigh_ops arp_direct_ops_template = { .family = AF_INET, - .destructor = NULL, .solicit = NULL, .error_report = NULL, .output = dev_queue_xmit, diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index 3bf46660351..f99e55308b3 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -13,6 +13,7 @@ #include <linux/sched.h> #include <linux/errno.h> #include <linux/workqueue.h> +#include <linux/time.h> #include <asm/lowcore.h> @@ -362,12 +363,19 @@ s390_revalidate_registers(struct mci *mci) return kill_task; } +#define MAX_IPD_COUNT 29 +#define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */ + /* * machine check handler. */ void s390_do_machine_check(struct pt_regs *regs) { + static DEFINE_SPINLOCK(ipd_lock); + static unsigned long long last_ipd; + static int ipd_count; + unsigned long long tmp; struct mci *mci; struct mcck_struct *mcck; int umode; @@ -404,11 +412,27 @@ s390_do_machine_check(struct pt_regs *regs) s390_handle_damage("processing backup machine " "check with damage."); } - if (!umode) - s390_handle_damage("processing backup machine " - "check in kernel mode."); - mcck->kill_task = 1; - mcck->mcck_code = *(unsigned long long *) mci; + + /* + * Nullifying exigent condition, therefore we might + * retry this instruction. + */ + + spin_lock(&ipd_lock); + + tmp = get_clock(); + + if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME) + ipd_count++; + else + ipd_count = 1; + + last_ipd = tmp; + + if (ipd_count == MAX_IPD_COUNT) + s390_handle_damage("too many ipd retries."); + + spin_unlock(&ipd_lock); } else { /* Processing damage -> stopping machine */ diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 3e7302692db..a480a3742d4 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -446,7 +446,9 @@ config SCSI_DPT_I2O config SCSI_ADVANSYS tristate "AdvanSys SCSI support" - depends on (ISA || EISA || PCI) && SCSI && BROKEN + depends on SCSI + depends on ISA || EISA || PCI + depends on BROKEN || X86_32 help This is a driver for all SCSI host adapters manufactured by AdvanSys. It is documented in the kernel source in diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index 28b93057b60..2a419634b25 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -2051,7 +2051,7 @@ STATIC ASC_DCNT AscGetMaxDmaCount(ushort); #define ADV_VADDR_TO_U32 virt_to_bus #define ADV_U32_TO_VADDR bus_to_virt -#define AdvPortAddr ulong /* Virtual memory address size */ +#define AdvPortAddr void __iomem * /* Virtual memory address size */ /* * Define Adv Library required memory access macros. diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c index cb30d9c1153..0c9c2f400bf 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c @@ -219,6 +219,7 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ahc->flags |= AHC_39BIT_ADDRESSING; } else { if (dma_set_mask(dev, DMA_32BIT_MASK)) { + ahc_free(ahc); printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n"); return (-ENODEV); } diff --git a/drivers/scsi/aic7xxx/aic7xxx_pci.c b/drivers/scsi/aic7xxx/aic7xxx_pci.c index 5f586140e05..3adecef2178 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_pci.c @@ -2036,12 +2036,12 @@ ahc_pci_resume(struct ahc_softc *ahc) * that the OS doesn't know about and rely on our chip * reset handler to handle the rest. */ - ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4, - ahc->bus_softc.pci_softc.devconfig); - ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1, - ahc->bus_softc.pci_softc.command); - ahc_pci_write_config(ahc->dev_softc, CSIZE_LATTIME, /*bytes*/1, - ahc->bus_softc.pci_softc.csize_lattime); + ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, + ahc->bus_softc.pci_softc.devconfig, /*bytes*/4); + ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, + ahc->bus_softc.pci_softc.command, /*bytes*/1); + ahc_pci_write_config(ahc->dev_softc, CSIZE_LATTIME, + ahc->bus_softc.pci_softc.csize_lattime, /*bytes*/1); if ((ahc->flags & AHC_HAS_TERM_LOGIC) != 0) { struct seeprom_descriptor sd; u_int sxfrctl1; diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index 0a8ad37ae89..2e9be83a697 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -739,7 +739,8 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) { struct viosrp_adapter_info *req; struct srp_event_struct *evt_struct; - + dma_addr_t addr; + evt_struct = get_event_struct(&hostdata->pool); if (!evt_struct) { printk(KERN_ERR "ibmvscsi: couldn't allocate an event " @@ -757,10 +758,10 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) req->common.type = VIOSRP_ADAPTER_INFO_TYPE; req->common.length = sizeof(hostdata->madapter_info); - req->buffer = dma_map_single(hostdata->dev, - &hostdata->madapter_info, - sizeof(hostdata->madapter_info), - DMA_BIDIRECTIONAL); + req->buffer = addr = dma_map_single(hostdata->dev, + &hostdata->madapter_info, + sizeof(hostdata->madapter_info), + DMA_BIDIRECTIONAL); if (dma_mapping_error(req->buffer)) { printk(KERN_ERR @@ -770,8 +771,13 @@ static void send_mad_adapter_info(struct ibmvscsi_host_data *hostdata) return; } - if (ibmvscsi_send_srp_event(evt_struct, hostdata)) + if (ibmvscsi_send_srp_event(evt_struct, hostdata)) { printk(KERN_ERR "ibmvscsi: couldn't send ADAPTER_INFO_REQ!\n"); + dma_unmap_single(hostdata->dev, + addr, + sizeof(hostdata->madapter_info), + DMA_BIDIRECTIONAL); + } }; /** @@ -1259,6 +1265,7 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, { struct viosrp_host_config *host_config; struct srp_event_struct *evt_struct; + dma_addr_t addr; int rc; evt_struct = get_event_struct(&hostdata->pool); @@ -1279,8 +1286,9 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, memset(host_config, 0x00, sizeof(*host_config)); host_config->common.type = VIOSRP_HOST_CONFIG_TYPE; host_config->common.length = length; - host_config->buffer = dma_map_single(hostdata->dev, buffer, length, - DMA_BIDIRECTIONAL); + host_config->buffer = addr = dma_map_single(hostdata->dev, buffer, + length, + DMA_BIDIRECTIONAL); if (dma_mapping_error(host_config->buffer)) { printk(KERN_ERR @@ -1291,11 +1299,9 @@ static int ibmvscsi_do_host_config(struct ibmvscsi_host_data *hostdata, init_completion(&evt_struct->comp); rc = ibmvscsi_send_srp_event(evt_struct, hostdata); - if (rc == 0) { + if (rc == 0) wait_for_completion(&evt_struct->comp); - dma_unmap_single(hostdata->dev, host_config->buffer, - length, DMA_BIDIRECTIONAL); - } + dma_unmap_single(hostdata->dev, addr, length, DMA_BIDIRECTIONAL); return rc; } diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index fad607b2e6f..ee22173fce4 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -27,7 +27,6 @@ void lpfc_config_link(struct lpfc_hba *, LPFC_MBOXQ_t *); int lpfc_read_sparam(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_read_config(struct lpfc_hba *, LPFC_MBOXQ_t *); void lpfc_read_lnk_stat(struct lpfc_hba *, LPFC_MBOXQ_t *); -void lpfc_set_slim(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t, uint32_t); int lpfc_reg_login(struct lpfc_hba *, uint32_t, uint8_t *, LPFC_MBOXQ_t *, uint32_t); void lpfc_unreg_login(struct lpfc_hba *, uint32_t, LPFC_MBOXQ_t *); diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h index 8932b1be2b6..41cf5d3ea6c 100644 --- a/drivers/scsi/lpfc/lpfc_disc.h +++ b/drivers/scsi/lpfc/lpfc_disc.h @@ -113,6 +113,7 @@ struct lpfc_nodelist { #define NLP_NPR_ADISC 0x2000000 /* Issue ADISC when dq'ed from NPR list */ #define NLP_DELAY_REMOVE 0x4000000 /* Defer removal till end of DSM */ +#define NLP_NODEV_REMOVE 0x8000000 /* Defer removal till discovery ends */ /* Defines for list searchs */ #define NLP_SEARCH_MAPPED 0x1 /* search mapped */ diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 4813beaaca8..283b7d824c3 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -302,10 +302,6 @@ lpfc_cmpl_els_flogi_fabric(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp, if (lpfc_reg_login(phba, Fabric_DID, (uint8_t *) sp, mbox, 0)) goto fail_free_mbox; - /* - * set_slim mailbox command needs to execute first, - * queue this command to be processed later. - */ mbox->mbox_cmpl = lpfc_mbx_cmpl_fabric_reg_login; mbox->context2 = ndlp; @@ -781,25 +777,26 @@ lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, if (disc && phba->num_disc_nodes) { /* Check to see if there are more PLOGIs to be sent */ lpfc_more_plogi(phba); - } - if (phba->num_disc_nodes == 0) { - spin_lock_irq(phba->host->host_lock); - phba->fc_flag &= ~FC_NDISC_ACTIVE; - spin_unlock_irq(phba->host->host_lock); + if (phba->num_disc_nodes == 0) { + spin_lock_irq(phba->host->host_lock); + phba->fc_flag &= ~FC_NDISC_ACTIVE; + spin_unlock_irq(phba->host->host_lock); - lpfc_can_disctmo(phba); - if (phba->fc_flag & FC_RSCN_MODE) { - /* Check to see if more RSCNs came in while we were - * processing this one. - */ - if ((phba->fc_rscn_id_cnt == 0) && - (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { - spin_lock_irq(phba->host->host_lock); - phba->fc_flag &= ~FC_RSCN_MODE; - spin_unlock_irq(phba->host->host_lock); - } else { - lpfc_els_handle_rscn(phba); + lpfc_can_disctmo(phba); + if (phba->fc_flag & FC_RSCN_MODE) { + /* + * Check to see if more RSCNs came in while + * we were processing this one. + */ + if ((phba->fc_rscn_id_cnt == 0) && + (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { + spin_lock_irq(phba->host->host_lock); + phba->fc_flag &= ~FC_RSCN_MODE; + spin_unlock_irq(phba->host->host_lock); + } else { + lpfc_els_handle_rscn(phba); + } } } } @@ -1263,7 +1260,7 @@ lpfc_issue_els_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, psli = &phba->sli; pring = &psli->ring[LPFC_ELS_RING]; - cmdsize = 2 * (sizeof (uint32_t) + sizeof (struct lpfc_name)); + cmdsize = (2 * sizeof (uint32_t)) + sizeof (struct lpfc_name); elsiocb = lpfc_prep_els_iocb(phba, 1, cmdsize, retry, ndlp, ndlp->nlp_DID, ELS_CMD_LOGO); if (!elsiocb) @@ -1451,22 +1448,23 @@ lpfc_cancel_retry_delay_tmo(struct lpfc_hba *phba, struct lpfc_nodelist * nlp) * PLOGIs to be sent */ lpfc_more_plogi(phba); - } - if (phba->num_disc_nodes == 0) { - phba->fc_flag &= ~FC_NDISC_ACTIVE; - lpfc_can_disctmo(phba); - if (phba->fc_flag & FC_RSCN_MODE) { - /* Check to see if more RSCNs - * came in while we were - * processing this one. - */ - if((phba->fc_rscn_id_cnt==0) && - (!(phba->fc_flag & FC_RSCN_DISCOVERY))) { - phba->fc_flag &= ~FC_RSCN_MODE; - } - else { - lpfc_els_handle_rscn(phba); + if (phba->num_disc_nodes == 0) { + phba->fc_flag &= ~FC_NDISC_ACTIVE; + lpfc_can_disctmo(phba); + if (phba->fc_flag & FC_RSCN_MODE) { + /* + * Check to see if more RSCNs + * came in while we were + * processing this one. + */ + if((phba->fc_rscn_id_cnt==0) && + !(phba->fc_flag & FC_RSCN_DISCOVERY)) { + phba->fc_flag &= ~FC_RSCN_MODE; + } + else { + lpfc_els_handle_rscn(phba); + } } } } @@ -1872,9 +1870,6 @@ lpfc_cmpl_els_acc(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, if (mbox) { if ((rspiocb->iocb.ulpStatus == 0) && (ndlp->nlp_flag & NLP_ACC_REGLOGIN)) { - /* set_slim mailbox command needs to execute first, - * queue this command to be processed later. - */ lpfc_unreg_rpi(phba, ndlp); mbox->mbox_cmpl = lpfc_mbx_cmpl_reg_login; mbox->context2 = ndlp; @@ -1920,6 +1915,7 @@ lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, uint8_t *pcmd; uint16_t cmdsize; int rc; + ELS_PKT *els_pkt_ptr; psli = &phba->sli; pring = &psli->ring[LPFC_ELS_RING]; /* ELS ring */ @@ -1958,6 +1954,23 @@ lpfc_els_rsp_acc(struct lpfc_hba * phba, uint32_t flag, pcmd += sizeof (uint32_t); memcpy(pcmd, &phba->fc_sparam, sizeof (struct serv_parm)); break; + case ELS_CMD_PRLO: + cmdsize = sizeof (uint32_t) + sizeof (PRLO); + elsiocb = lpfc_prep_els_iocb(phba, 0, cmdsize, oldiocb->retry, + ndlp, ndlp->nlp_DID, ELS_CMD_PRLO); + if (!elsiocb) + return 1; + + icmd = &elsiocb->iocb; + icmd->ulpContext = oldcmd->ulpContext; /* Xri */ + pcmd = (((struct lpfc_dmabuf *) elsiocb->context2)->virt); + + memcpy(pcmd, ((struct lpfc_dmabuf *) oldiocb->context2)->virt, + sizeof (uint32_t) + sizeof (PRLO)); + *((uint32_t *) (pcmd)) = ELS_CMD_PRLO_ACC; + els_pkt_ptr = (ELS_PKT *) pcmd; + els_pkt_ptr->un.prlo.acceptRspCode = PRLO_REQ_EXECUTED; + break; default: return 1; } @@ -2498,7 +2511,7 @@ lpfc_els_rcv_rscn(struct lpfc_hba * phba, /* If we are about to begin discovery, just ACC the RSCN. * Discovery processing will satisfy it. */ - if (phba->hba_state < LPFC_NS_QRY) { + if (phba->hba_state <= LPFC_NS_QRY) { lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, newnode); return 0; diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 6721e679df6..adb086009ae 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -311,8 +311,8 @@ lpfc_workq_post_event(struct lpfc_hba * phba, void *arg1, void *arg2, evtp->evt_arg2 = arg2; evtp->evt = evt; - list_add_tail(&evtp->evt_listp, &phba->work_list); spin_lock_irq(phba->host->host_lock); + list_add_tail(&evtp->evt_listp, &phba->work_list); if (phba->work_wait) wake_up(phba->work_wait); spin_unlock_irq(phba->host->host_lock); @@ -1071,10 +1071,6 @@ lpfc_register_remote_port(struct lpfc_hba * phba, /* initialize static port data */ rport->maxframe_size = ndlp->nlp_maxframe; rport->supported_classes = ndlp->nlp_class_sup; - if ((rport->scsi_target_id != -1) && - (rport->scsi_target_id < MAX_FCP_TARGET)) { - ndlp->nlp_sid = rport->scsi_target_id; - } rdata = rport->dd_data; rdata->pnode = ndlp; @@ -1087,6 +1083,10 @@ lpfc_register_remote_port(struct lpfc_hba * phba, if (rport_ids.roles != FC_RPORT_ROLE_UNKNOWN) fc_remote_port_rolechg(rport, rport_ids.roles); + if ((rport->scsi_target_id != -1) && + (rport->scsi_target_id < MAX_FCP_TARGET)) { + ndlp->nlp_sid = rport->scsi_target_id; + } return; } @@ -1238,6 +1238,7 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) evt_listp); } + nlp->nlp_flag &= ~NLP_NODEV_REMOVE; nlp->nlp_type |= NLP_FC_NODE; break; case NLP_MAPPED_LIST: @@ -1258,6 +1259,7 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) evt_listp); } + nlp->nlp_flag &= ~NLP_NODEV_REMOVE; break; case NLP_NPR_LIST: nlp->nlp_flag |= list; @@ -1402,6 +1404,8 @@ lpfc_check_sli_ndlp(struct lpfc_hba * phba, if (icmd->ulpContext == (volatile ushort)ndlp->nlp_rpi) return 1; case CMD_ELS_REQUEST64_CR: + if (icmd->un.elsreq64.remoteID == ndlp->nlp_DID) + return 1; case CMD_XMIT_ELS_RSP64_CX: if (iocb->context1 == (uint8_t *) ndlp) return 1; @@ -1901,10 +1905,8 @@ lpfc_setup_disc_node(struct lpfc_hba * phba, uint32_t did) */ if (ndlp->nlp_flag & NLP_DELAY_TMO) lpfc_cancel_retry_delay_tmo(phba, ndlp); - } else { - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + } else ndlp = NULL; - } } else { flg = ndlp->nlp_flag & NLP_LIST_MASK; if ((flg == NLP_ADISC_LIST) || (flg == NLP_PLOGI_LIST)) diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 54d04188f7c..eedf9880136 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -449,6 +449,7 @@ struct serv_parm { /* Structure is in Big Endian format */ #define ELS_CMD_RRQ 0x12000000 #define ELS_CMD_PRLI 0x20100014 #define ELS_CMD_PRLO 0x21100014 +#define ELS_CMD_PRLO_ACC 0x02100014 #define ELS_CMD_PDISC 0x50000000 #define ELS_CMD_FDISC 0x51000000 #define ELS_CMD_ADISC 0x52000000 @@ -484,6 +485,7 @@ struct serv_parm { /* Structure is in Big Endian format */ #define ELS_CMD_RRQ 0x12 #define ELS_CMD_PRLI 0x14001020 #define ELS_CMD_PRLO 0x14001021 +#define ELS_CMD_PRLO_ACC 0x14001002 #define ELS_CMD_PDISC 0x50 #define ELS_CMD_FDISC 0x51 #define ELS_CMD_ADISC 0x52 @@ -1539,6 +1541,7 @@ typedef struct { #define FLAGS_TOPOLOGY_FAILOVER 0x0400 /* Bit 10 */ #define FLAGS_LINK_SPEED 0x0800 /* Bit 11 */ +#define FLAGS_IMED_ABORT 0x04000 /* Bit 14 */ uint32_t link_speed; #define LINK_SPEED_AUTO 0 /* Auto selection */ diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 66d5d003555..908d0f27706 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -294,15 +294,6 @@ lpfc_config_port_post(struct lpfc_hba * phba) } } - /* This should turn on DELAYED ABTS for ELS timeouts */ - lpfc_set_slim(phba, pmb, 0x052198, 0x1); - if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { - phba->hba_state = LPFC_HBA_ERROR; - mempool_free( pmb, phba->mbox_mem_pool); - return -EIO; - } - - lpfc_read_config(phba, pmb); if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { lpfc_printf_log(phba, @@ -804,7 +795,7 @@ lpfc_get_hba_model_desc(struct lpfc_hba * phba, uint8_t * mdp, uint8_t * descp) int max_speed; char * ports; char * bus; - } m; + } m = {"<Unknown>", 0, "", ""}; pci_read_config_byte(phba->pcidev, PCI_HEADER_TYPE, &hdrtype); ports = (hdrtype == 0x80) ? "2-port " : ""; @@ -1627,7 +1618,7 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) error = lpfc_alloc_sysfs_attr(phba); if (error) - goto out_kthread_stop; + goto out_remove_host; error = request_irq(phba->pcidev->irq, lpfc_intr_handler, SA_SHIRQ, LPFC_DRIVER_NAME, phba); @@ -1644,8 +1635,10 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) phba->HCregaddr = phba->ctrl_regs_memmap_p + HC_REG_OFFSET; error = lpfc_sli_hba_setup(phba); - if (error) + if (error) { + error = -ENODEV; goto out_free_irq; + } if (phba->cfg_poll & DISABLE_FCP_RING_INT) { spin_lock_irq(phba->host->host_lock); @@ -1700,6 +1693,9 @@ out_free_irq: free_irq(phba->pcidev->irq, phba); out_free_sysfs_attr: lpfc_free_sysfs_attr(phba); +out_remove_host: + fc_remove_host(phba->host); + scsi_remove_host(phba->host); out_kthread_stop: kthread_stop(phba->worker_thread); out_free_iocbq: @@ -1721,12 +1717,14 @@ out_iounmap_slim: out_idr_remove: idr_remove(&lpfc_hba_index, phba->brd_no); out_put_host: + phba->host = NULL; scsi_host_put(host); out_release_regions: pci_release_regions(pdev); out_disable_device: pci_disable_device(pdev); out: + pci_set_drvdata(pdev, NULL); return error; } diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index c585e2b2e58..e42f22aaf71 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -200,6 +200,9 @@ lpfc_init_link(struct lpfc_hba * phba, break; } + /* Enable asynchronous ABTS responses from firmware */ + mb->un.varInitLnk.link_flags |= FLAGS_IMED_ABORT; + /* NEW_FEATURE * Setting up the link speed */ @@ -292,36 +295,6 @@ lpfc_unreg_did(struct lpfc_hba * phba, uint32_t did, LPFC_MBOXQ_t * pmb) return; } -/***********************************************/ - -/* command to write slim */ -/***********************************************/ -void -lpfc_set_slim(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint32_t addr, - uint32_t value) -{ - MAILBOX_t *mb; - - mb = &pmb->mb; - memset(pmb, 0, sizeof (LPFC_MBOXQ_t)); - - /* addr = 0x090597 is AUTO ABTS disable for ELS commands */ - /* addr = 0x052198 is DELAYED ABTS enable for ELS commands */ - - /* - * Always turn on DELAYED ABTS for ELS timeouts - */ - if ((addr == 0x052198) && (value == 0)) - value = 1; - - mb->un.varWords[0] = addr; - mb->un.varWords[1] = value; - - mb->mbxCommand = MBX_SET_SLIM; - mb->mbxOwner = OWN_HOST; - return; -} - /**********************************************/ /* lpfc_read_nv Issue a READ CONFIG */ /* mailbox command */ diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 3d77bd999b7..27d60ad897c 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -465,14 +465,18 @@ lpfc_rcv_padisc(struct lpfc_hba * phba, static int lpfc_rcv_logo(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, - struct lpfc_iocbq *cmdiocb) + struct lpfc_iocbq *cmdiocb, + uint32_t els_cmd) { /* Put ndlp on NPR list with 1 sec timeout for plogi, ACC logo */ /* Only call LOGO ACC for first LOGO, this avoids sending unnecessary * PLOGIs during LOGO storms from a device. */ ndlp->nlp_flag |= NLP_LOGO_ACC; - lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + if (els_cmd == ELS_CMD_PRLO) + lpfc_els_rsp_acc(phba, ELS_CMD_PRLO, cmdiocb, ndlp, NULL, 0); + else + lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); if (!(ndlp->nlp_type & NLP_FABRIC) || (ndlp->nlp_state == NLP_STE_ADISC_ISSUE)) { @@ -681,7 +685,7 @@ lpfc_rcv_logo_plogi_issue(struct lpfc_hba * phba, /* software abort outstanding PLOGI */ lpfc_els_abort(phba, ndlp, 1); - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO); return ndlp->nlp_state; } @@ -788,10 +792,6 @@ lpfc_cmpl_plogi_plogi_issue(struct lpfc_hba * phba, if (lpfc_reg_login (phba, irsp->un.elsreq64.remoteID, (uint8_t *) sp, mbox, 0) == 0) { - /* set_slim mailbox command needs to - * execute first, queue this command to - * be processed later. - */ switch (ndlp->nlp_DID) { case NameServer_DID: mbox->mbox_cmpl = @@ -832,11 +832,17 @@ static uint32_t lpfc_device_rm_plogi_issue(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) { - /* software abort outstanding PLOGI */ - lpfc_els_abort(phba, ndlp, 1); + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + ndlp->nlp_flag |= NLP_NODEV_REMOVE; + return ndlp->nlp_state; + } + else { + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); - lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); - return NLP_STE_FREED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return NLP_STE_FREED_NODE; + } } static uint32_t @@ -851,7 +857,7 @@ lpfc_device_recov_plogi_issue(struct lpfc_hba * phba, ndlp->nlp_state = NLP_STE_NPR_NODE; lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); spin_unlock_irq(phba->host->host_lock); return ndlp->nlp_state; @@ -905,7 +911,7 @@ lpfc_rcv_logo_adisc_issue(struct lpfc_hba * phba, /* software abort outstanding ADISC */ lpfc_els_abort(phba, ndlp, 0); - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO); return ndlp->nlp_state; } @@ -932,7 +938,7 @@ lpfc_rcv_prlo_adisc_issue(struct lpfc_hba * phba, cmdiocb = (struct lpfc_iocbq *) arg; /* Treat like rcv logo */ - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_PRLO); return ndlp->nlp_state; } @@ -987,11 +993,17 @@ lpfc_device_rm_adisc_issue(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) { - /* software abort outstanding ADISC */ - lpfc_els_abort(phba, ndlp, 1); + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + ndlp->nlp_flag |= NLP_NODEV_REMOVE; + return ndlp->nlp_state; + } + else { + /* software abort outstanding ADISC */ + lpfc_els_abort(phba, ndlp, 1); - lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); - return NLP_STE_FREED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return NLP_STE_FREED_NODE; + } } static uint32_t @@ -1006,7 +1018,7 @@ lpfc_device_recov_adisc_issue(struct lpfc_hba * phba, ndlp->nlp_state = NLP_STE_NPR_NODE; lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); ndlp->nlp_flag |= NLP_NPR_ADISC; spin_unlock_irq(phba->host->host_lock); @@ -1048,7 +1060,7 @@ lpfc_rcv_logo_reglogin_issue(struct lpfc_hba * phba, cmdiocb = (struct lpfc_iocbq *) arg; - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO); return ndlp->nlp_state; } @@ -1073,7 +1085,7 @@ lpfc_rcv_prlo_reglogin_issue(struct lpfc_hba * phba, struct lpfc_iocbq *cmdiocb; cmdiocb = (struct lpfc_iocbq *) arg; - lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + lpfc_els_rsp_acc(phba, ELS_CMD_PRLO, cmdiocb, ndlp, NULL, 0); return ndlp->nlp_state; } @@ -1133,8 +1145,14 @@ lpfc_device_rm_reglogin_issue(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) { - lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); - return NLP_STE_FREED_NODE; + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + ndlp->nlp_flag |= NLP_NODEV_REMOVE; + return ndlp->nlp_state; + } + else { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return NLP_STE_FREED_NODE; + } } static uint32_t @@ -1146,7 +1164,7 @@ lpfc_device_recov_reglogin_issue(struct lpfc_hba * phba, ndlp->nlp_state = NLP_STE_NPR_NODE; lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); spin_unlock_irq(phba->host->host_lock); return ndlp->nlp_state; } @@ -1186,7 +1204,7 @@ lpfc_rcv_logo_prli_issue(struct lpfc_hba * phba, /* Software abort outstanding PRLI before sending acc */ lpfc_els_abort(phba, ndlp, 1); - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO); return ndlp->nlp_state; } @@ -1214,7 +1232,7 @@ lpfc_rcv_prlo_prli_issue(struct lpfc_hba * phba, struct lpfc_iocbq *cmdiocb; cmdiocb = (struct lpfc_iocbq *) arg; - lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + lpfc_els_rsp_acc(phba, ELS_CMD_PRLO, cmdiocb, ndlp, NULL, 0); return ndlp->nlp_state; } @@ -1278,11 +1296,17 @@ static uint32_t lpfc_device_rm_prli_issue(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) { - /* software abort outstanding PRLI */ - lpfc_els_abort(phba, ndlp, 1); + if(ndlp->nlp_flag & NLP_NPR_2B_DISC) { + ndlp->nlp_flag |= NLP_NODEV_REMOVE; + return ndlp->nlp_state; + } + else { + /* software abort outstanding PLOGI */ + lpfc_els_abort(phba, ndlp, 1); - lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); - return NLP_STE_FREED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return NLP_STE_FREED_NODE; + } } @@ -1313,7 +1337,7 @@ lpfc_device_recov_prli_issue(struct lpfc_hba * phba, ndlp->nlp_state = NLP_STE_NPR_NODE; lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); spin_unlock_irq(phba->host->host_lock); return ndlp->nlp_state; } @@ -1351,7 +1375,7 @@ lpfc_rcv_logo_unmap_node(struct lpfc_hba * phba, cmdiocb = (struct lpfc_iocbq *) arg; - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO); return ndlp->nlp_state; } @@ -1375,7 +1399,7 @@ lpfc_rcv_prlo_unmap_node(struct lpfc_hba * phba, cmdiocb = (struct lpfc_iocbq *) arg; - lpfc_els_rsp_acc(phba, ELS_CMD_ACC, cmdiocb, ndlp, NULL, 0); + lpfc_els_rsp_acc(phba, ELS_CMD_PRLO, cmdiocb, ndlp, NULL, 0); return ndlp->nlp_state; } @@ -1386,7 +1410,7 @@ lpfc_device_recov_unmap_node(struct lpfc_hba * phba, ndlp->nlp_prev_state = NLP_STE_UNMAPPED_NODE; ndlp->nlp_state = NLP_STE_NPR_NODE; lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); lpfc_disc_set_adisc(phba, ndlp); return ndlp->nlp_state; @@ -1424,7 +1448,7 @@ lpfc_rcv_logo_mapped_node(struct lpfc_hba * phba, cmdiocb = (struct lpfc_iocbq *) arg; - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO); return ndlp->nlp_state; } @@ -1456,7 +1480,7 @@ lpfc_rcv_prlo_mapped_node(struct lpfc_hba * phba, spin_unlock_irq(phba->host->host_lock); /* Treat like rcv logo */ - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_PRLO); return ndlp->nlp_state; } @@ -1469,7 +1493,7 @@ lpfc_device_recov_mapped_node(struct lpfc_hba * phba, ndlp->nlp_state = NLP_STE_NPR_NODE; lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); spin_unlock_irq(phba->host->host_lock); lpfc_disc_set_adisc(phba, ndlp); return ndlp->nlp_state; @@ -1551,7 +1575,7 @@ lpfc_rcv_logo_npr_node(struct lpfc_hba * phba, cmdiocb = (struct lpfc_iocbq *) arg; - lpfc_rcv_logo(phba, ndlp, cmdiocb); + lpfc_rcv_logo(phba, ndlp, cmdiocb, ELS_CMD_LOGO); return ndlp->nlp_state; } @@ -1617,9 +1641,16 @@ lpfc_cmpl_plogi_npr_node(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) { struct lpfc_iocbq *cmdiocb, *rspiocb; + IOCB_t *irsp; cmdiocb = (struct lpfc_iocbq *) arg; rspiocb = cmdiocb->context_un.rsp_iocb; + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return NLP_STE_FREED_NODE; + } return ndlp->nlp_state; } @@ -1628,9 +1659,16 @@ lpfc_cmpl_prli_npr_node(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) { struct lpfc_iocbq *cmdiocb, *rspiocb; + IOCB_t *irsp; cmdiocb = (struct lpfc_iocbq *) arg; rspiocb = cmdiocb->context_un.rsp_iocb; + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus && (ndlp->nlp_flag & NLP_NODEV_REMOVE)) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return NLP_STE_FREED_NODE; + } return ndlp->nlp_state; } @@ -1649,9 +1687,16 @@ lpfc_cmpl_adisc_npr_node(struct lpfc_hba * phba, uint32_t evt) { struct lpfc_iocbq *cmdiocb, *rspiocb; + IOCB_t *irsp; cmdiocb = (struct lpfc_iocbq *) arg; rspiocb = cmdiocb->context_un.rsp_iocb; + + irsp = &rspiocb->iocb; + if (irsp->ulpStatus && (ndlp->nlp_flag & NLP_NODEV_REMOVE)) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return NLP_STE_FREED_NODE; + } return ndlp->nlp_state; } @@ -1668,7 +1713,12 @@ lpfc_cmpl_reglogin_npr_node(struct lpfc_hba * phba, if (!mb->mbxStatus) ndlp->nlp_rpi = mb->un.varWords[0]; - + else { + if (ndlp->nlp_flag & NLP_NODEV_REMOVE) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + return NLP_STE_FREED_NODE; + } + } return ndlp->nlp_state; } @@ -1677,6 +1727,10 @@ lpfc_device_rm_npr_node(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, void *arg, uint32_t evt) { + if (ndlp->nlp_flag & NLP_NPR_2B_DISC) { + ndlp->nlp_flag |= NLP_NODEV_REMOVE; + return ndlp->nlp_state; + } lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); return NLP_STE_FREED_NODE; } @@ -1687,7 +1741,7 @@ lpfc_device_recov_npr_node(struct lpfc_hba * phba, uint32_t evt) { spin_lock_irq(phba->host->host_lock); - ndlp->nlp_flag &= ~NLP_NPR_2B_DISC; + ndlp->nlp_flag &= ~(NLP_NODEV_REMOVE | NLP_NPR_2B_DISC); spin_unlock_irq(phba->host->host_lock); if (ndlp->nlp_flag & NLP_DELAY_TMO) { lpfc_cancel_retry_delay_tmo(phba, ndlp); diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index f9379987372..7dc4c2e6bed 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -629,8 +629,7 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq; IOCB_t *piocb; struct fcp_cmnd *fcp_cmnd; - struct scsi_device *scsi_dev = lpfc_cmd->pCmd->device; - struct lpfc_rport_data *rdata = scsi_dev->hostdata; + struct lpfc_rport_data *rdata = lpfc_cmd->rdata; struct lpfc_nodelist *ndlp = rdata->pnode; if ((ndlp == NULL) || (ndlp->nlp_state != NLP_STE_MAPPED_NODE)) { @@ -665,56 +664,18 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba, piocb->ulpTimeout = lpfc_cmd->timeout; } - lpfc_cmd->rdata = rdata; - - switch (task_mgmt_cmd) { - case FCP_LUN_RESET: - /* Issue LUN Reset to TGT <num> LUN <num> */ - lpfc_printf_log(phba, - KERN_INFO, - LOG_FCP, - "%d:0703 Issue LUN Reset to TGT %d LUN %d " - "Data: x%x x%x\n", - phba->brd_no, - scsi_dev->id, scsi_dev->lun, - ndlp->nlp_rpi, ndlp->nlp_flag); - - break; - case FCP_ABORT_TASK_SET: - /* Issue Abort Task Set to TGT <num> LUN <num> */ - lpfc_printf_log(phba, - KERN_INFO, - LOG_FCP, - "%d:0701 Issue Abort Task Set to TGT %d LUN %d " - "Data: x%x x%x\n", - phba->brd_no, - scsi_dev->id, scsi_dev->lun, - ndlp->nlp_rpi, ndlp->nlp_flag); - - break; - case FCP_TARGET_RESET: - /* Issue Target Reset to TGT <num> */ - lpfc_printf_log(phba, - KERN_INFO, - LOG_FCP, - "%d:0702 Issue Target Reset to TGT %d " - "Data: x%x x%x\n", - phba->brd_no, - scsi_dev->id, ndlp->nlp_rpi, - ndlp->nlp_flag); - break; - } - return (1); } static int -lpfc_scsi_tgt_reset(struct lpfc_scsi_buf * lpfc_cmd, struct lpfc_hba * phba) +lpfc_scsi_tgt_reset(struct lpfc_scsi_buf * lpfc_cmd, struct lpfc_hba * phba, + unsigned tgt_id, struct lpfc_rport_data *rdata) { struct lpfc_iocbq *iocbq; struct lpfc_iocbq *iocbqrsp; int ret; + lpfc_cmd->rdata = rdata; ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_TARGET_RESET); if (!ret) return FAILED; @@ -726,6 +687,13 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf * lpfc_cmd, struct lpfc_hba * phba) if (!iocbqrsp) return FAILED; + /* Issue Target Reset to TGT <num> */ + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0702 Issue Target Reset to TGT %d " + "Data: x%x x%x\n", + phba->brd_no, tgt_id, rdata->pnode->nlp_rpi, + rdata->pnode->nlp_flag); + ret = lpfc_sli_issue_iocb_wait(phba, &phba->sli.ring[phba->sli.fcp_ring], iocbq, iocbqrsp, lpfc_cmd->timeout); @@ -1021,6 +989,7 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) lpfc_cmd->pCmd = cmnd; lpfc_cmd->timeout = 60; lpfc_cmd->scsi_hba = phba; + lpfc_cmd->rdata = rdata; ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_LUN_RESET); if (!ret) @@ -1033,6 +1002,11 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) if (iocbqrsp == NULL) goto out_free_scsi_buf; + lpfc_printf_log(phba, KERN_INFO, LOG_FCP, + "%d:0703 Issue LUN Reset to TGT %d LUN %d " + "Data: x%x x%x\n", phba->brd_no, cmnd->device->id, + cmnd->device->lun, pnode->nlp_rpi, pnode->nlp_flag); + ret = lpfc_sli_issue_iocb_wait(phba, &phba->sli.ring[phba->sli.fcp_ring], iocbq, iocbqrsp, lpfc_cmd->timeout); @@ -1104,7 +1078,6 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) int match; int ret = FAILED, i, err_count = 0; int cnt, loopcnt; - unsigned int midlayer_id = 0; struct lpfc_scsi_buf * lpfc_cmd; lpfc_block_requests(phba); @@ -1124,7 +1097,6 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) * targets known to the driver. Should any target reset * fail, this routine returns failure to the midlayer. */ - midlayer_id = cmnd->device->id; for (i = 0; i < MAX_FCP_TARGET; i++) { /* Search the mapped list for this target ID */ match = 0; @@ -1137,9 +1109,8 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) if (!match) continue; - lpfc_cmd->pCmd->device->id = i; - lpfc_cmd->pCmd->device->hostdata = ndlp->rport->dd_data; - ret = lpfc_scsi_tgt_reset(lpfc_cmd, phba); + ret = lpfc_scsi_tgt_reset(lpfc_cmd, phba, + i, ndlp->rport->dd_data); if (ret != SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_FCP, "%d:0713 Bus Reset on target %d failed\n", @@ -1158,7 +1129,6 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) * the targets. Unfortunately, some targets do not abide by * this forcing the driver to double check. */ - cmnd->device->id = midlayer_id; cnt = lpfc_sli_sum_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring], 0, 0, LPFC_CTX_HOST); if (cnt) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 4cf1366108b..6b737568b83 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.1.4" +#define LPFC_DRIVER_VERSION "8.1.6" #define LPFC_DRIVER_NAME "lpfc" diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 80b68a2481b..de35ffe2f79 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -4471,7 +4471,6 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru) { Scsi_Cmnd *scmd; struct scsi_device *sdev; - unsigned long flags = 0; scb_t *scb; int rval; diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index c11e5ce6865..bec1424eda8 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -10,7 +10,7 @@ * 2 of the License, or (at your option) any later version. * * FILE : megaraid_mbox.c - * Version : v2.20.4.7 (Nov 14 2005) + * Version : v2.20.4.8 (Apr 11 2006) * * Authors: * Atul Mukker <Atul.Mukker@lsil.com> @@ -2278,6 +2278,7 @@ megaraid_mbox_dpc(unsigned long devp) unsigned long flags; uint8_t c; int status; + uioc_t *kioc; if (!adapter) return; @@ -2320,6 +2321,9 @@ megaraid_mbox_dpc(unsigned long devp) // remove from local clist list_del_init(&scb->list); + kioc = (uioc_t *)scb->gp; + kioc->status = 0; + megaraid_mbox_mm_done(adapter, scb); continue; @@ -2636,6 +2640,7 @@ megaraid_reset_handler(struct scsi_cmnd *scp) int recovery_window; int recovering; int i; + uioc_t *kioc; adapter = SCP2ADAPTER(scp); raid_dev = ADAP2RAIDDEV(adapter); @@ -2655,32 +2660,51 @@ megaraid_reset_handler(struct scsi_cmnd *scp) // Also, reset all the commands currently owned by the driver spin_lock_irqsave(PENDING_LIST_LOCK(adapter), flags); list_for_each_entry_safe(scb, tmp, &adapter->pend_list, list) { - list_del_init(&scb->list); // from pending list - con_log(CL_ANN, (KERN_WARNING - "megaraid: %ld:%d[%d:%d], reset from pending list\n", - scp->serial_number, scb->sno, - scb->dev_channel, scb->dev_target)); + if (scb->sno >= MBOX_MAX_SCSI_CMDS) { + con_log(CL_ANN, (KERN_WARNING + "megaraid: IOCTL packet with %d[%d:%d] being reset\n", + scb->sno, scb->dev_channel, scb->dev_target)); - scp->result = (DID_RESET << 16); - scp->scsi_done(scp); + scb->status = -1; - megaraid_dealloc_scb(adapter, scb); + kioc = (uioc_t *)scb->gp; + kioc->status = -EFAULT; + + megaraid_mbox_mm_done(adapter, scb); + } else { + if (scb->scp == scp) { // Found command + con_log(CL_ANN, (KERN_WARNING + "megaraid: %ld:%d[%d:%d], reset from pending list\n", + scp->serial_number, scb->sno, + scb->dev_channel, scb->dev_target)); + } else { + con_log(CL_ANN, (KERN_WARNING + "megaraid: IO packet with %d[%d:%d] being reset\n", + scb->sno, scb->dev_channel, scb->dev_target)); + } + + scb->scp->result = (DID_RESET << 16); + scb->scp->scsi_done(scb->scp); + + megaraid_dealloc_scb(adapter, scb); + } } spin_unlock_irqrestore(PENDING_LIST_LOCK(adapter), flags); if (adapter->outstanding_cmds) { con_log(CL_ANN, (KERN_NOTICE "megaraid: %d outstanding commands. Max wait %d sec\n", - adapter->outstanding_cmds, MBOX_RESET_WAIT)); + adapter->outstanding_cmds, + (MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT))); } recovery_window = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT; recovering = adapter->outstanding_cmds; - for (i = 0; i < recovery_window && adapter->outstanding_cmds; i++) { + for (i = 0; i < recovery_window; i++) { megaraid_ack_sequence(adapter); @@ -2689,12 +2713,11 @@ megaraid_reset_handler(struct scsi_cmnd *scp) con_log(CL_ANN, ( "megaraid mbox: Wait for %d commands to complete:%d\n", adapter->outstanding_cmds, - MBOX_RESET_WAIT - i)); + (MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT) - i)); } // bailout if no recovery happended in reset time - if ((i == MBOX_RESET_WAIT) && - (recovering == adapter->outstanding_cmds)) { + if (adapter->outstanding_cmds == 0) { break; } @@ -2918,12 +2941,13 @@ mbox_post_sync_cmd_fast(adapter_t *adapter, uint8_t raw_mbox[]) wmb(); WRINDOOR(raid_dev, raid_dev->mbox_dma | 0x1); - for (i = 0; i < 0xFFFFF; i++) { + for (i = 0; i < MBOX_SYNC_WAIT_CNT; i++) { if (mbox->numstatus != 0xFF) break; rmb(); + udelay(MBOX_SYNC_DELAY_200); } - if (i == 0xFFFFF) { + if (i == MBOX_SYNC_WAIT_CNT) { // We may need to re-calibrate the counter con_log(CL_ANN, (KERN_CRIT "megaraid: fast sync command timed out\n")); @@ -3475,7 +3499,7 @@ megaraid_cmm_register(adapter_t *adapter) adp.drvr_data = (unsigned long)adapter; adp.pdev = adapter->pdev; adp.issue_uioc = megaraid_mbox_mm_handler; - adp.timeout = 300; + adp.timeout = MBOX_RESET_WAIT + MBOX_RESET_EXT_WAIT; adp.max_kioc = MBOX_MAX_USER_CMDS; if ((rval = mraid_mm_register_adp(&adp)) != 0) { @@ -3702,7 +3726,6 @@ megaraid_mbox_mm_done(adapter_t *adapter, scb_t *scb) unsigned long flags; kioc = (uioc_t *)scb->gp; - kioc->status = 0; mbox64 = (mbox64_t *)(unsigned long)kioc->cmdbuf; mbox64->mbox32.status = scb->status; raw_mbox = (uint8_t *)&mbox64->mbox32; diff --git a/drivers/scsi/megaraid/megaraid_mbox.h b/drivers/scsi/megaraid/megaraid_mbox.h index 882fb1a0b57..868fb0ec93e 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.h +++ b/drivers/scsi/megaraid/megaraid_mbox.h @@ -21,8 +21,8 @@ #include "megaraid_ioctl.h" -#define MEGARAID_VERSION "2.20.4.7" -#define MEGARAID_EXT_VERSION "(Release Date: Mon Nov 14 12:27:22 EST 2005)" +#define MEGARAID_VERSION "2.20.4.8" +#define MEGARAID_EXT_VERSION "(Release Date: Mon Apr 11 12:27:22 EST 2006)" /* @@ -100,6 +100,9 @@ #define MBOX_BUSY_WAIT 10 // max usec to wait for busy mailbox #define MBOX_RESET_WAIT 180 // wait these many seconds in reset #define MBOX_RESET_EXT_WAIT 120 // extended wait reset +#define MBOX_SYNC_WAIT_CNT 0xFFFF // wait loop index for synchronous mode + +#define MBOX_SYNC_DELAY_200 200 // 200 micro-seconds /* * maximum transfer that can happen through the firmware commands issued diff --git a/drivers/scsi/megaraid/megaraid_mm.c b/drivers/scsi/megaraid/megaraid_mm.c index 8f3ce043229..e8f534fb336 100644 --- a/drivers/scsi/megaraid/megaraid_mm.c +++ b/drivers/scsi/megaraid/megaraid_mm.c @@ -898,10 +898,8 @@ mraid_mm_register_adp(mraid_mmadp_t *lld_adp) adapter = kmalloc(sizeof(mraid_mmadp_t), GFP_KERNEL); - if (!adapter) { - rval = -ENOMEM; - goto memalloc_error; - } + if (!adapter) + return -ENOMEM; memset(adapter, 0, sizeof(mraid_mmadp_t)); diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 017729c59a4..584fe5d8e50 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -599,6 +599,7 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha) * Either SUCCESS or FAILED. * * Note: +* Only return FAILED if command not returned by firmware. **************************************************************************/ int qla2xxx_eh_abort(struct scsi_cmnd *cmd) @@ -609,11 +610,12 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) unsigned int id, lun; unsigned long serial; unsigned long flags; + int wait = 0; if (!CMD_SP(cmd)) - return FAILED; + return SUCCESS; - ret = FAILED; + ret = SUCCESS; id = cmd->device->id; lun = cmd->device->lun; @@ -642,7 +644,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) } else { DEBUG3(printk("%s(%ld): abort_command " "mbx success.\n", __func__, ha->host_no)); - ret = SUCCESS; + wait = 1; } spin_lock_irqsave(&ha->hardware_lock, flags); @@ -651,17 +653,18 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) spin_unlock_irqrestore(&ha->hardware_lock, flags); /* Wait for the command to be returned. */ - if (ret == SUCCESS) { + if (wait) { if (qla2x00_eh_wait_on_command(ha, cmd) != QLA_SUCCESS) { qla_printk(KERN_ERR, ha, "scsi(%ld:%d:%d): Abort handler timed out -- %lx " "%x.\n", ha->host_no, id, lun, serial, ret); + ret = FAILED; } } qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): Abort command issued -- %lx %x.\n", ha->host_no, - id, lun, serial, ret); + "scsi(%ld:%d:%d): Abort command issued -- %d %lx %x.\n", + ha->host_no, id, lun, wait, serial, ret); return ret; } @@ -1700,8 +1703,8 @@ qla2x00_free_device(scsi_qla_host_t *ha) ha->flags.online = 0; /* Detach interrupts */ - if (ha->pdev->irq) - free_irq(ha->pdev->irq, ha); + if (ha->host->irq) + free_irq(ha->host->irq, ha); /* release io space registers */ if (ha->iobase) diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index c750d3399a9..941c1e15c89 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -56,6 +56,8 @@ static struct { {"DENON", "DRD-25X", "V", BLIST_NOLUN}, /* locks up */ {"HITACHI", "DK312C", "CM81", BLIST_NOLUN}, /* responds to all lun */ {"HITACHI", "DK314C", "CR21", BLIST_NOLUN}, /* responds to all lun */ + {"IBM", "2104-DU3", NULL, BLIST_NOLUN}, /* locks up */ + {"IBM", "2104-TU3", NULL, BLIST_NOLUN}, /* locks up */ {"IMS", "CDD521/10", "2.06", BLIST_NOLUN}, /* locks up */ {"MAXTOR", "XT-3280", "PR02", BLIST_NOLUN}, /* locks up */ {"MAXTOR", "XT-4380S", "B3C", BLIST_NOLUN}, /* locks up */ diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 7b0f9a3810d..764a8b375ea 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1067,16 +1067,29 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, break; case NOT_READY: /* - * If the device is in the process of becoming ready, - * retry. + * If the device is in the process of becoming + * ready, or has a temporary blockage, retry. */ - if (sshdr.asc == 0x04 && sshdr.ascq == 0x01) { - scsi_requeue_command(q, cmd); - return; + if (sshdr.asc == 0x04) { + switch (sshdr.ascq) { + case 0x01: /* becoming ready */ + case 0x04: /* format in progress */ + case 0x05: /* rebuild in progress */ + case 0x06: /* recalculation in progress */ + case 0x07: /* operation in progress */ + case 0x08: /* Long write in progress */ + case 0x09: /* self test in progress */ + scsi_requeue_command(q, cmd); + return; + default: + break; + } } - if (!(req->flags & REQ_QUIET)) + if (!(req->flags & REQ_QUIET)) { scmd_printk(KERN_INFO, cmd, - "Device not ready.\n"); + "Device not ready: "); + scsi_print_sense_hdr("", &sshdr); + } scsi_end_request(cmd, 0, this_count, 1); return; case VOLUME_OVERFLOW: diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c index 3274ab76c8d..255886a9ac5 100644 --- a/drivers/scsi/sim710.c +++ b/drivers/scsi/sim710.c @@ -75,7 +75,7 @@ param_setup(char *str) else if(!strncmp(pos, "id:", 3)) { if(slot == -1) { printk(KERN_WARNING "sim710: Must specify slot for id parameter\n"); - } else if(slot > MAX_SLOTS) { + } else if(slot >= MAX_SLOTS) { printk(KERN_WARNING "sim710: Illegal slot %d for id %d\n", slot, val); } else { id_array[slot] = val; diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 674b15c78f6..bbf78aaf9e0 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -362,6 +362,40 @@ serial_out(struct uart_8250_port *up, int offset, int value) #define serial_inp(up, offset) serial_in(up, offset) #define serial_outp(up, offset, value) serial_out(up, offset, value) +/* Uart divisor latch read */ +static inline int _serial_dl_read(struct uart_8250_port *up) +{ + return serial_inp(up, UART_DLL) | serial_inp(up, UART_DLM) << 8; +} + +/* Uart divisor latch write */ +static inline void _serial_dl_write(struct uart_8250_port *up, int value) +{ + serial_outp(up, UART_DLL, value & 0xff); + serial_outp(up, UART_DLM, value >> 8 & 0xff); +} + +#ifdef CONFIG_SERIAL_8250_AU1X00 +/* Au1x00 haven't got a standard divisor latch */ +static int serial_dl_read(struct uart_8250_port *up) +{ + if (up->port.iotype == UPIO_AU) + return __raw_readl(up->port.membase + 0x28); + else + return _serial_dl_read(up); +} + +static void serial_dl_write(struct uart_8250_port *up, int value) +{ + if (up->port.iotype == UPIO_AU) + __raw_writel(value, up->port.membase + 0x28); + else + _serial_dl_write(up, value); +} +#else +#define serial_dl_read(up) _serial_dl_read(up) +#define serial_dl_write(up, value) _serial_dl_write(up, value) +#endif /* * For the 16C950 @@ -494,7 +528,8 @@ static void disable_rsa(struct uart_8250_port *up) */ static int size_fifo(struct uart_8250_port *up) { - unsigned char old_fcr, old_mcr, old_dll, old_dlm, old_lcr; + unsigned char old_fcr, old_mcr, old_lcr; + unsigned short old_dl; int count; old_lcr = serial_inp(up, UART_LCR); @@ -505,10 +540,8 @@ static int size_fifo(struct uart_8250_port *up) UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(up, UART_MCR, UART_MCR_LOOP); serial_outp(up, UART_LCR, UART_LCR_DLAB); - old_dll = serial_inp(up, UART_DLL); - old_dlm = serial_inp(up, UART_DLM); - serial_outp(up, UART_DLL, 0x01); - serial_outp(up, UART_DLM, 0x00); + old_dl = serial_dl_read(up); + serial_dl_write(up, 0x0001); serial_outp(up, UART_LCR, 0x03); for (count = 0; count < 256; count++) serial_outp(up, UART_TX, count); @@ -519,8 +552,7 @@ static int size_fifo(struct uart_8250_port *up) serial_outp(up, UART_FCR, old_fcr); serial_outp(up, UART_MCR, old_mcr); serial_outp(up, UART_LCR, UART_LCR_DLAB); - serial_outp(up, UART_DLL, old_dll); - serial_outp(up, UART_DLM, old_dlm); + serial_dl_write(up, old_dl); serial_outp(up, UART_LCR, old_lcr); return count; @@ -750,8 +782,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) serial_outp(up, UART_LCR, 0xE0); - quot = serial_inp(up, UART_DLM) << 8; - quot += serial_inp(up, UART_DLL); + quot = serial_dl_read(up); quot <<= 3; status1 = serial_in(up, 0x04); /* EXCR1 */ @@ -759,8 +790,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) status1 |= 0x10; /* 1.625 divisor for baud_base --> 921600 */ serial_outp(up, 0x04, status1); - serial_outp(up, UART_DLL, quot & 0xff); - serial_outp(up, UART_DLM, quot >> 8); + serial_dl_write(up, quot); serial_outp(up, UART_LCR, 0); @@ -1862,8 +1892,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ } - serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */ - serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */ + serial_dl_write(up, quot); /* * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR @@ -1906,6 +1935,9 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) int ret = 0; switch (up->port.iotype) { + case UPIO_AU: + size = 0x100000; + /* fall thru */ case UPIO_MEM: if (!up->port.mapbase) break; @@ -1938,6 +1970,9 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) unsigned int size = 8 << up->port.regshift; switch (up->port.iotype) { + case UPIO_AU: + size = 0x100000; + /* fall thru */ case UPIO_MEM: if (!up->port.mapbase) break; @@ -2200,10 +2235,17 @@ static void serial8250_console_write(struct console *co, const char *s, unsigned int count) { struct uart_8250_port *up = &serial8250_ports[co->index]; + unsigned long flags; unsigned int ier; + int locked = 1; touch_nmi_watchdog(); + if (oops_in_progress) { + locked = spin_trylock_irqsave(&up->port.lock, flags); + } else + spin_lock_irqsave(&up->port.lock, flags); + /* * First save the IER then disable the interrupts */ @@ -2221,8 +2263,10 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) * and restore the IER */ wait_for_xmitr(up, BOTH_EMPTY); - up->ier |= UART_IER_THRI; - serial_out(up, UART_IER, ier | UART_IER_THRI); + serial_out(up, UART_IER, ier); + + if (locked) + spin_unlock_irqrestore(&up->port.lock, flags); } static int serial8250_console_setup(struct console *co, char *options) diff --git a/drivers/serial/8250_au1x00.c b/drivers/serial/8250_au1x00.c index 3d1bfd07208..58015fd14be 100644 --- a/drivers/serial/8250_au1x00.c +++ b/drivers/serial/8250_au1x00.c @@ -30,13 +30,12 @@ { \ .iobase = _base, \ .membase = (void __iomem *)_base,\ - .mapbase = _base, \ + .mapbase = CPHYSADDR(_base), \ .irq = _irq, \ .uartclk = 0, /* filled */ \ .regshift = 2, \ .iotype = UPIO_AU, \ - .flags = UPF_SKIP_TEST | \ - UPF_IOREMAP, \ + .flags = UPF_SKIP_TEST \ } static struct plat_serial8250_port au1x00_data[] = { diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h index 73c8a088c16..3b35cb77953 100644 --- a/drivers/serial/cpm_uart/cpm_uart.h +++ b/drivers/serial/cpm_uart/cpm_uart.h @@ -5,11 +5,20 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * + * 2006 (c) MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * */ #ifndef CPM_UART_H #define CPM_UART_H #include <linux/config.h> +#include <linux/platform_device.h> +#include <linux/fs_uart_pd.h> #if defined(CONFIG_CPM2) #include "cpm_uart_cpm2.h" @@ -26,14 +35,14 @@ #define FLAG_SMC 0x00000002 #define FLAG_CONSOLE 0x00000001 -#define UART_SMC1 0 -#define UART_SMC2 1 -#define UART_SCC1 2 -#define UART_SCC2 3 -#define UART_SCC3 4 -#define UART_SCC4 5 +#define UART_SMC1 fsid_smc1_uart +#define UART_SMC2 fsid_smc2_uart +#define UART_SCC1 fsid_scc1_uart +#define UART_SCC2 fsid_scc2_uart +#define UART_SCC3 fsid_scc3_uart +#define UART_SCC4 fsid_scc4_uart -#define UART_NR 6 +#define UART_NR fs_uart_nr #define RX_NUM_FIFO 4 #define RX_BUF_SIZE 32 @@ -64,6 +73,7 @@ struct uart_cpm_port { uint dp_addr; void *mem_addr; dma_addr_t dma_addr; + u32 mem_size; /* helpers */ int baud; int bits; @@ -90,4 +100,38 @@ void scc2_lineif(struct uart_cpm_port *pinfo); void scc3_lineif(struct uart_cpm_port *pinfo); void scc4_lineif(struct uart_cpm_port *pinfo); +/* + virtual to phys transtalion +*/ +static inline unsigned long cpu2cpm_addr(void* addr, struct uart_cpm_port *pinfo) +{ + int offset; + u32 val = (u32)addr; + /* sane check */ + if (likely((val >= (u32)pinfo->mem_addr)) && + (val<((u32)pinfo->mem_addr + pinfo->mem_size))) { + offset = val - (u32)pinfo->mem_addr; + return pinfo->dma_addr+offset; + } + /* something nasty happened */ + BUG(); + return 0; +} + +static inline void *cpm2cpu_addr(unsigned long addr, struct uart_cpm_port *pinfo) +{ + int offset; + u32 val = addr; + /* sane check */ + if (likely((val >= pinfo->dma_addr) && + (val<(pinfo->dma_addr + pinfo->mem_size)))) { + offset = val - (u32)pinfo->dma_addr; + return (void*)(pinfo->mem_addr+offset); + } + /* something nasty happened */ + BUG(); + return 0; +} + + #endif /* CPM_UART_H */ diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index b7bf4c698a4..969f9490043 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -12,7 +12,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. - * (C) 2005 MontaVista Software, Inc. by Vitaly Bordug <vbordug@ru.mvista.com> + * (C) 2005-2006 MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,6 +42,7 @@ #include <linux/device.h> #include <linux/bootmem.h> #include <linux/dma-mapping.h> +#include <linux/fs_uart_pd.h> #include <asm/io.h> #include <asm/irq.h> @@ -60,7 +62,7 @@ /* Track which ports are configured as uarts */ int cpm_uart_port_map[UART_NR]; /* How many ports did we config as uarts */ -int cpm_uart_nr; +int cpm_uart_nr = 0; /**************************************************************/ @@ -71,18 +73,51 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo); /**************************************************************/ -static inline unsigned long cpu2cpm_addr(void *addr) + +/* Place-holder for board-specific stuff */ +struct platform_device* __attribute__ ((weak)) __init +early_uart_get_pdev(int index) +{ + return NULL; +} + + +static void cpm_uart_count(void) { - if ((unsigned long)addr >= CPM_ADDR) - return (unsigned long)addr; - return virt_to_bus(addr); + cpm_uart_nr = 0; +#ifdef CONFIG_SERIAL_CPM_SMC1 + cpm_uart_port_map[cpm_uart_nr++] = UART_SMC1; +#endif +#ifdef CONFIG_SERIAL_CPM_SMC2 + cpm_uart_port_map[cpm_uart_nr++] = UART_SMC2; +#endif +#ifdef CONFIG_SERIAL_CPM_SCC1 + cpm_uart_port_map[cpm_uart_nr++] = UART_SCC1; +#endif +#ifdef CONFIG_SERIAL_CPM_SCC2 + cpm_uart_port_map[cpm_uart_nr++] = UART_SCC2; +#endif +#ifdef CONFIG_SERIAL_CPM_SCC3 + cpm_uart_port_map[cpm_uart_nr++] = UART_SCC3; +#endif +#ifdef CONFIG_SERIAL_CPM_SCC4 + cpm_uart_port_map[cpm_uart_nr++] = UART_SCC4; +#endif } -static inline void *cpm2cpu_addr(unsigned long addr) +/* Get UART number by its id */ +static int cpm_uart_id2nr(int id) { - if (addr >= CPM_ADDR) - return (void *)addr; - return bus_to_virt(addr); + int i; + if (id < UART_NR) { + for (i=0; i<UART_NR; i++) { + if (cpm_uart_port_map[i] == id) + return i; + } + } + + /* not found or invalid argument */ + return -1; } /* @@ -258,7 +293,7 @@ static void cpm_uart_int_rx(struct uart_port *port, struct pt_regs *regs) } /* get pointer */ - cp = cpm2cpu_addr(bdp->cbd_bufaddr); + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); /* loop through the buffer */ while (i-- > 0) { @@ -438,7 +473,11 @@ static void cpm_uart_shutdown(struct uart_port *port) } /* Shut them really down and reinit buffer descriptors */ - cpm_line_cr_cmd(line, CPM_CR_STOP_TX); + if (IS_SMC(pinfo)) + cpm_line_cr_cmd(line, CPM_CR_STOP_TX); + else + cpm_line_cr_cmd(line, CPM_CR_GRA_STOP_TX); + cpm_uart_initbd(pinfo); } } @@ -601,7 +640,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) /* Pick next descriptor and fill from buffer */ bdp = pinfo->tx_cur; - p = cpm2cpu_addr(bdp->cbd_bufaddr); + p = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); *p++ = port->x_char; bdp->cbd_datlen = 1; @@ -628,7 +667,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) while (!(bdp->cbd_sc & BD_SC_READY) && (xmit->tail != xmit->head)) { count = 0; - p = cpm2cpu_addr(bdp->cbd_bufaddr); + p = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); while (count < pinfo->tx_fifosize) { *p++ = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -677,12 +716,12 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo) mem_addr = pinfo->mem_addr; bdp = pinfo->rx_cur = pinfo->rx_bd_base; for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) { - bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr); + bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo); bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; mem_addr += pinfo->rx_fifosize; } - bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr); + bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo); bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; /* Set the physical address of the host memory @@ -692,12 +731,12 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo) mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize); bdp = pinfo->tx_cur = pinfo->tx_bd_base; for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) { - bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr); + bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo); bdp->cbd_sc = BD_SC_INTRPT; mem_addr += pinfo->tx_fifosize; } - bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr); + bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo); bdp->cbd_sc = BD_SC_WRAP | BD_SC_INTRPT; } @@ -829,14 +868,6 @@ static int cpm_uart_request_port(struct uart_port *port) if (pinfo->flags & FLAG_CONSOLE) return 0; - /* - * Setup any port IO, connect any baud rate generators, - * etc. This is expected to be handled by board - * dependant code - */ - if (pinfo->set_lineif) - pinfo->set_lineif(pinfo); - if (IS_SMC(pinfo)) { pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); @@ -988,6 +1019,58 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = { }, }; +int cpm_uart_drv_get_platform_data(struct platform_device *pdev, int is_con) +{ + struct resource *r; + struct fs_uart_platform_info *pdata = pdev->dev.platform_data; + int idx = pdata->fs_no; /* It is UART_SMCx or UART_SCCx index */ + struct uart_cpm_port *pinfo; + int line; + u32 mem, pram; + + line = cpm_uart_id2nr(idx); + if(line < 0) { + printk(KERN_ERR"%s(): port %d is not registered", __FUNCTION__, idx); + return -1; + } + + pinfo = (struct uart_cpm_port *) &cpm_uart_ports[idx]; + + pinfo->brg = pdata->brg; + + if (!is_con) { + pinfo->port.line = line; + pinfo->port.flags = UPF_BOOT_AUTOCONF; + } + + if (!(r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"))) + return -EINVAL; + mem = r->start; + + if (!(r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram"))) + return -EINVAL; + pram = r->start; + + if(idx > fsid_smc2_uart) { + pinfo->sccp = (scc_t *)mem; + pinfo->sccup = (scc_uart_t *)pram; + } else { + pinfo->smcp = (smc_t *)mem; + pinfo->smcup = (smc_uart_t *)pram; + } + pinfo->tx_nrfifos = pdata->tx_num_fifo; + pinfo->tx_fifosize = pdata->tx_buf_size; + + pinfo->rx_nrfifos = pdata->rx_num_fifo; + pinfo->rx_fifosize = pdata->rx_buf_size; + + pinfo->port.uartclk = pdata->uart_clk; + pinfo->port.mapbase = (unsigned long)mem; + pinfo->port.irq = platform_get_irq(pdev, 0); + + return 0; +} + #ifdef CONFIG_SERIAL_CPM_CONSOLE /* * Print a string to the serial port trying not to disturb @@ -1027,7 +1110,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, * If the buffer address is in the CPM DPRAM, don't * convert it. */ - cp = cpm2cpu_addr(bdp->cbd_bufaddr); + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); *cp = *s; @@ -1044,7 +1127,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, while ((bdp->cbd_sc & BD_SC_READY) != 0) ; - cp = cpm2cpu_addr(bdp->cbd_bufaddr); + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); *cp = 13; bdp->cbd_datlen = 1; @@ -1067,9 +1150,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, pinfo->tx_cur = (volatile cbd_t *) bdp; } -/* - * Setup console. Be careful is called early ! - */ + static int __init cpm_uart_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -1080,9 +1161,27 @@ static int __init cpm_uart_console_setup(struct console *co, char *options) int flow = 'n'; int ret; + struct fs_uart_platform_info *pdata; + struct platform_device* pdev = early_uart_get_pdev(co->index); + port = (struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]]; pinfo = (struct uart_cpm_port *)port; + if (!pdev) { + pr_info("cpm_uart: console: compat mode\n"); + /* compatibility - will be cleaned up */ + cpm_uart_init_portdesc(); + + if (pinfo->set_lineif) + pinfo->set_lineif(pinfo); + } else { + pdata = pdev->dev.platform_data; + if (pdata) + if (pdata->init_ioports) + pdata->init_ioports(); + + cpm_uart_drv_get_platform_data(pdev, 1); + } pinfo->flags |= FLAG_CONSOLE; @@ -1097,14 +1196,6 @@ static int __init cpm_uart_console_setup(struct console *co, char *options) baud = 9600; } - /* - * Setup any port IO, connect any baud rate generators, - * etc. This is expected to be handled by board - * dependant code - */ - if (pinfo->set_lineif) - pinfo->set_lineif(pinfo); - if (IS_SMC(pinfo)) { pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); @@ -1143,11 +1234,8 @@ static struct console cpm_scc_uart_console = { int __init cpm_uart_console_init(void) { - int ret = cpm_uart_init_portdesc(); - - if (!ret) - register_console(&cpm_scc_uart_console); - return ret; + register_console(&cpm_scc_uart_console); + return 0; } console_initcall(cpm_uart_console_init); @@ -1165,44 +1253,129 @@ static struct uart_driver cpm_reg = { .minor = SERIAL_CPM_MINOR, .cons = CPM_UART_CONSOLE, }; - -static int __init cpm_uart_init(void) +static int cpm_uart_drv_probe(struct device *dev) { - int ret, i; - - printk(KERN_INFO "Serial: CPM driver $Revision: 0.01 $\n"); + struct platform_device *pdev = to_platform_device(dev); + struct fs_uart_platform_info *pdata; + int ret = -ENODEV; -#ifndef CONFIG_SERIAL_CPM_CONSOLE - ret = cpm_uart_init_portdesc(); - if (ret) + if(!pdev) { + printk(KERN_ERR"CPM UART: platform data missing!\n"); return ret; -#endif + } - cpm_reg.nr = cpm_uart_nr; - ret = uart_register_driver(&cpm_reg); + pdata = pdev->dev.platform_data; + pr_debug("cpm_uart_drv_probe: Adding CPM UART %d\n", cpm_uart_id2nr(pdata->fs_no)); - if (ret) + if ((ret = cpm_uart_drv_get_platform_data(pdev, 0))) return ret; - for (i = 0; i < cpm_uart_nr; i++) { - int con = cpm_uart_port_map[i]; - cpm_uart_ports[con].port.line = i; - cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF; - uart_add_one_port(&cpm_reg, &cpm_uart_ports[con].port); - } + if (pdata->init_ioports) + pdata->init_ioports(); - return ret; + ret = uart_add_one_port(&cpm_reg, &cpm_uart_ports[pdata->fs_no].port); + + return ret; } -static void __exit cpm_uart_exit(void) +static int cpm_uart_drv_remove(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); + struct fs_uart_platform_info *pdata = pdev->dev.platform_data; + + pr_debug("cpm_uart_drv_remove: Removing CPM UART %d\n", + cpm_uart_id2nr(pdata->fs_no)); + + uart_remove_one_port(&cpm_reg, &cpm_uart_ports[pdata->fs_no].port); + return 0; +} + +static struct device_driver cpm_smc_uart_driver = { + .name = "fsl-cpm-smc:uart", + .bus = &platform_bus_type, + .probe = cpm_uart_drv_probe, + .remove = cpm_uart_drv_remove, +}; + +static struct device_driver cpm_scc_uart_driver = { + .name = "fsl-cpm-scc:uart", + .bus = &platform_bus_type, + .probe = cpm_uart_drv_probe, + .remove = cpm_uart_drv_remove, +}; + +/* + This is supposed to match uart devices on platform bus, + */ +static int match_is_uart (struct device* dev, void* data) +{ + struct platform_device* pdev = container_of(dev, struct platform_device, dev); + int ret = 0; + /* this was setfunc as uart */ + if(strstr(pdev->name,":uart")) { + ret = 1; + } + return ret; +} + + +static int cpm_uart_init(void) { + + int ret; int i; + struct device *dev; + printk(KERN_INFO "Serial: CPM driver $Revision: 0.02 $\n"); + + /* lookup the bus for uart devices */ + dev = bus_find_device(&platform_bus_type, NULL, 0, match_is_uart); + + /* There are devices on the bus - all should be OK */ + if (dev) { + cpm_uart_count(); + cpm_reg.nr = cpm_uart_nr; + + if (!(ret = uart_register_driver(&cpm_reg))) { + if ((ret = driver_register(&cpm_smc_uart_driver))) { + uart_unregister_driver(&cpm_reg); + return ret; + } + if ((ret = driver_register(&cpm_scc_uart_driver))) { + driver_unregister(&cpm_scc_uart_driver); + uart_unregister_driver(&cpm_reg); + } + } + } else { + /* No capable platform devices found - falling back to legacy mode */ + pr_info("cpm_uart: WARNING: no UART devices found on platform bus!\n"); + pr_info( + "cpm_uart: the driver will guess configuration, but this mode is no longer supported.\n"); +#ifndef CONFIG_SERIAL_CPM_CONSOLE + ret = cpm_uart_init_portdesc(); + if (ret) + return ret; +#endif + + cpm_reg.nr = cpm_uart_nr; + ret = uart_register_driver(&cpm_reg); + + if (ret) + return ret; + + for (i = 0; i < cpm_uart_nr; i++) { + int con = cpm_uart_port_map[i]; + cpm_uart_ports[con].port.line = i; + cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF; + uart_add_one_port(&cpm_reg, &cpm_uart_ports[con].port); + } - for (i = 0; i < cpm_uart_nr; i++) { - int con = cpm_uart_port_map[i]; - uart_remove_one_port(&cpm_reg, &cpm_uart_ports[con].port); } + return ret; +} +static void __exit cpm_uart_exit(void) +{ + driver_unregister(&cpm_scc_uart_driver); + driver_unregister(&cpm_smc_uart_driver); uart_unregister_driver(&cpm_reg); } diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c index d789ee55cbb..17406a05ce1 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -8,6 +8,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. + * (C) 2006 MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -81,58 +83,11 @@ void cpm_line_cr_cmd(int line, int cmd) void smc1_lineif(struct uart_cpm_port *pinfo) { - volatile cpm8xx_t *cp = cpmp; - - (void)cp; /* fix warning */ -#if defined (CONFIG_MPC885ADS) - /* Enable SMC1 transceivers */ - { - cp->cp_pepar |= 0x000000c0; - cp->cp_pedir &= ~0x000000c0; - cp->cp_peso &= ~0x00000040; - cp->cp_peso |= 0x00000080; - } -#elif defined (CONFIG_MPC86XADS) - unsigned int iobits = 0x000000c0; - - if (!pinfo->is_portb) { - cp->cp_pbpar |= iobits; - cp->cp_pbdir &= ~iobits; - cp->cp_pbodr &= ~iobits; - } else { - ((immap_t *)IMAP_ADDR)->im_ioport.iop_papar |= iobits; - ((immap_t *)IMAP_ADDR)->im_ioport.iop_padir &= ~iobits; - ((immap_t *)IMAP_ADDR)->im_ioport.iop_paodr &= ~iobits; - } -#endif pinfo->brg = 1; } void smc2_lineif(struct uart_cpm_port *pinfo) { - volatile cpm8xx_t *cp = cpmp; - - (void)cp; /* fix warning */ -#if defined (CONFIG_MPC885ADS) - cp->cp_pepar |= 0x00000c00; - cp->cp_pedir &= ~0x00000c00; - cp->cp_peso &= ~0x00000400; - cp->cp_peso |= 0x00000800; -#elif defined (CONFIG_MPC86XADS) - unsigned int iobits = 0x00000c00; - - if (!pinfo->is_portb) { - cp->cp_pbpar |= iobits; - cp->cp_pbdir &= ~iobits; - cp->cp_pbodr &= ~iobits; - } else { - ((immap_t *)IMAP_ADDR)->im_ioport.iop_papar |= iobits; - ((immap_t *)IMAP_ADDR)->im_ioport.iop_padir &= ~iobits; - ((immap_t *)IMAP_ADDR)->im_ioport.iop_paodr &= ~iobits; - } - -#endif - pinfo->brg = 2; } @@ -191,7 +146,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) /* was hostalloc but changed cause it blows away the */ /* large tlb mapping when pinning the kernel area */ mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8)); - dma_addr = 0; + dma_addr = (u32)mem_addr; } else mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr, GFP_KERNEL); @@ -204,8 +159,9 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) } pinfo->dp_addr = dp_offset; - pinfo->mem_addr = mem_addr; - pinfo->dma_addr = dma_addr; + pinfo->mem_addr = mem_addr; /* virtual address*/ + pinfo->dma_addr = dma_addr; /* physical address*/ + pinfo->mem_size = memsz; pinfo->rx_buf = mem_addr; pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index fd9e53ed3fe..4b2de08f46d 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -8,6 +8,8 @@ * * Copyright (C) 2004 Freescale Semiconductor, Inc. * (C) 2004 Intracom, S.A. + * (C) 2006 MontaVista Software, Inc. + * Vitaly Bordug <vbordug@ru.mvista.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -142,14 +144,6 @@ void scc2_lineif(struct uart_cpm_port *pinfo) * be supported in a sane fashion. */ #ifndef CONFIG_STX_GP3 -#ifdef CONFIG_MPC8560_ADS - volatile iop_cpm2_t *io = &cpm2_immr->im_ioport; - io->iop_ppard |= 0x00000018; - io->iop_psord &= ~0x00000008; /* Rx */ - io->iop_psord &= ~0x00000010; /* Tx */ - io->iop_pdird &= ~0x00000008; /* Rx */ - io->iop_pdird |= 0x00000010; /* Tx */ -#else volatile iop_cpm2_t *io = &cpm2_immr->im_ioport; io->iop_pparb |= 0x008b0000; io->iop_pdirb |= 0x00880000; @@ -157,7 +151,6 @@ void scc2_lineif(struct uart_cpm_port *pinfo) io->iop_pdirb &= ~0x00030000; io->iop_psorb &= ~0x00030000; #endif -#endif cpm2_immr->im_cpmux.cmx_scr &= 0xff00ffff; cpm2_immr->im_cpmux.cmx_scr |= 0x00090000; pinfo->brg = 2; @@ -218,8 +211,10 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); - if (is_con) + if (is_con) { mem_addr = alloc_bootmem(memsz); + dma_addr = mem_addr; + } else mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr, GFP_KERNEL); @@ -234,6 +229,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) pinfo->dp_addr = dp_offset; pinfo->mem_addr = mem_addr; pinfo->dma_addr = dma_addr; + pinfo->mem_size = memsz; pinfo->rx_buf = mem_addr; pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index c3b7a6673e9..d202eb4f384 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -45,6 +45,7 @@ #include <asm/io.h> #include <asm/irq.h> #include <asm/hardware.h> +#include <asm/arch/imx-uart.h> /* We've been assigned a range on the "Low-density serial ports" major */ #define SERIAL_IMX_MAJOR 204 @@ -73,7 +74,8 @@ struct imx_port { struct uart_port port; struct timer_list timer; unsigned int old_status; - int txirq,rxirq,rtsirq; + int txirq,rxirq,rtsirq; + int have_rtscts:1; }; /* @@ -491,8 +493,12 @@ imx_set_termios(struct uart_port *port, struct termios *termios, ucr2 = UCR2_SRST | UCR2_IRTS; if (termios->c_cflag & CRTSCTS) { - ucr2 &= ~UCR2_IRTS; - ucr2 |= UCR2_CTSC; + if( sport->have_rtscts ) { + ucr2 &= ~UCR2_IRTS; + ucr2 |= UCR2_CTSC; + } else { + termios->c_cflag &= ~CRTSCTS; + } } if (termios->c_cflag & CSTOPB) @@ -719,27 +725,6 @@ static void __init imx_init_ports(void) imx_ports[i].timer.function = imx_timeout; imx_ports[i].timer.data = (unsigned long)&imx_ports[i]; } - - imx_gpio_mode(PC9_PF_UART1_CTS); - imx_gpio_mode(PC10_PF_UART1_RTS); - imx_gpio_mode(PC11_PF_UART1_TXD); - imx_gpio_mode(PC12_PF_UART1_RXD); - imx_gpio_mode(PB28_PF_UART2_CTS); - imx_gpio_mode(PB29_PF_UART2_RTS); - - imx_gpio_mode(PB30_PF_UART2_TXD); - imx_gpio_mode(PB31_PF_UART2_RXD); - -#if 0 /* We don't need these, on the mx1 the _modem_ side of the uart - * is implemented. - */ - imx_gpio_mode(PD7_AF_UART2_DTR); - imx_gpio_mode(PD8_AF_UART2_DCD); - imx_gpio_mode(PD9_AF_UART2_RI); - imx_gpio_mode(PD10_AF_UART2_DSR); -#endif - - } #ifdef CONFIG_SERIAL_IMX_CONSOLE @@ -932,7 +917,14 @@ static int serial_imx_resume(struct platform_device *dev) static int serial_imx_probe(struct platform_device *dev) { + struct imxuart_platform_data *pdata; + imx_ports[dev->id].port.dev = &dev->dev; + + pdata = (struct imxuart_platform_data *)dev->dev.platform_data; + if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS)) + imx_ports[dev->id].have_rtscts = 1; + uart_add_one_port(&imx_reg, &imx_ports[dev->id].port); platform_set_drvdata(dev, &imx_ports[dev->id]); return 0; diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index fcd7744c425..aeb8153ccf2 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -1500,20 +1500,18 @@ uart_block_til_ready(struct file *filp, struct uart_state *state) static struct uart_state *uart_get(struct uart_driver *drv, int line) { struct uart_state *state; + int ret = 0; - mutex_lock(&port_mutex); state = drv->state + line; if (mutex_lock_interruptible(&state->mutex)) { - state = ERR_PTR(-ERESTARTSYS); - goto out; + ret = -ERESTARTSYS; + goto err; } state->count++; - if (!state->port) { - state->count--; - mutex_unlock(&state->mutex); - state = ERR_PTR(-ENXIO); - goto out; + if (!state->port || state->port->flags & UPF_DEAD) { + ret = -ENXIO; + goto err_unlock; } if (!state->info) { @@ -1531,15 +1529,17 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line) tasklet_init(&state->info->tlet, uart_tasklet_action, (unsigned long)state); } else { - state->count--; - mutex_unlock(&state->mutex); - state = ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err_unlock; } } - - out: - mutex_unlock(&port_mutex); return state; + + err_unlock: + state->count--; + mutex_unlock(&state->mutex); + err: + return ERR_PTR(ret); } /* @@ -2085,45 +2085,6 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, } } -/* - * This reverses the effects of uart_configure_port, hanging up the - * port before removal. - */ -static void -uart_unconfigure_port(struct uart_driver *drv, struct uart_state *state) -{ - struct uart_port *port = state->port; - struct uart_info *info = state->info; - - if (info && info->tty) - tty_vhangup(info->tty); - - mutex_lock(&state->mutex); - - state->info = NULL; - - /* - * Free the port IO and memory resources, if any. - */ - if (port->type != PORT_UNKNOWN) - port->ops->release_port(port); - - /* - * Indicate that there isn't a port here anymore. - */ - port->type = PORT_UNKNOWN; - - /* - * Kill the tasklet, and free resources. - */ - if (info) { - tasklet_kill(&info->tlet); - kfree(info); - } - - mutex_unlock(&state->mutex); -} - static struct tty_operations uart_ops = { .open = uart_open, .close = uart_close, @@ -2270,6 +2231,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) state = drv->state + port->line; mutex_lock(&port_mutex); + mutex_lock(&state->mutex); if (state->port) { ret = -EINVAL; goto out; @@ -2304,7 +2266,13 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) port->cons && !(port->cons->flags & CON_ENABLED)) register_console(port->cons); + /* + * Ensure UPF_DEAD is not set. + */ + port->flags &= ~UPF_DEAD; + out: + mutex_unlock(&state->mutex); mutex_unlock(&port_mutex); return ret; @@ -2322,6 +2290,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) { struct uart_state *state = drv->state + port->line; + struct uart_info *info; BUG_ON(in_interrupt()); @@ -2332,11 +2301,48 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) mutex_lock(&port_mutex); /* + * Mark the port "dead" - this prevents any opens from + * succeeding while we shut down the port. + */ + mutex_lock(&state->mutex); + port->flags |= UPF_DEAD; + mutex_unlock(&state->mutex); + + /* * Remove the devices from devfs */ tty_unregister_device(drv->tty_driver, port->line); - uart_unconfigure_port(drv, state); + info = state->info; + if (info && info->tty) + tty_vhangup(info->tty); + + /* + * All users of this port should now be disconnected from + * this driver, and the port shut down. We should be the + * only thread fiddling with this port from now on. + */ + state->info = NULL; + + /* + * Free the port IO and memory resources, if any. + */ + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + /* + * Indicate that there isn't a port here anymore. + */ + port->type = PORT_UNKNOWN; + + /* + * Kill the tasklet, and free resources. + */ + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } + state->port = NULL; mutex_unlock(&port_mutex); diff --git a/drivers/sn/ioc3.c b/drivers/sn/ioc3.c index 0b49ff78efc..501316b198e 100644 --- a/drivers/sn/ioc3.c +++ b/drivers/sn/ioc3.c @@ -678,7 +678,7 @@ static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) /* Track PCI-device specific data */ pci_set_drvdata(pdev, idd); down_write(&ioc3_devices_rwsem); - list_add(&idd->list, &ioc3_devices); + list_add_tail(&idd->list, &ioc3_devices); idd->id = ioc3_counter++; up_write(&ioc3_devices_rwsem); diff --git a/drivers/sn/ioc4.c b/drivers/sn/ioc4.c index 67140a5804f..cdeff909403 100644 --- a/drivers/sn/ioc4.c +++ b/drivers/sn/ioc4.c @@ -310,7 +310,7 @@ ioc4_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) pci_set_drvdata(idd->idd_pdev, idd); mutex_lock(&ioc4_mutex); - list_add(&idd->idd_list, &ioc4_devices); + list_add_tail(&idd->idd_list, &ioc4_devices); /* Add this IOC4 to all submodules */ list_for_each_entry(is, &ioc4_submodules, is_list) { diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 6a4b93ad108..0b929349395 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2166,7 +2166,7 @@ static void handle_ep_small (struct net2280_ep *ep) ep->stopped = 1; set_halt (ep); mode = 2; - } else if (!req && ep->stopped) + } else if (!req && !ep->stopped) write_fifo (ep, NULL); } } else { @@ -2280,9 +2280,7 @@ static void handle_ep_small (struct net2280_ep *ep) /* if we wrote it all, we're usually done */ if (req->req.actual == req->req.length) { if (ep->num == 0) { - /* wait for control status */ - if (mode != 2) - req = NULL; + /* send zlps until the status stage */ } else if (!req->req.zero || len != ep->ep.maxpacket) mode = 2; } @@ -2744,6 +2742,10 @@ static irqreturn_t net2280_irq (int irq, void *_dev, struct pt_regs * r) { struct net2280 *dev = _dev; + /* shared interrupt, not ours */ + if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED))) + return IRQ_NONE; + spin_lock (&dev->lock); /* handle disconnect, dma, and more */ @@ -2831,13 +2833,13 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) } /* alloc, and start init */ - dev = kmalloc (sizeof *dev, SLAB_KERNEL); + dev = kzalloc (sizeof *dev, SLAB_KERNEL); if (dev == NULL){ retval = -ENOMEM; goto done; } - memset (dev, 0, sizeof *dev); + pci_set_drvdata (pdev, dev); spin_lock_init (&dev->lock); dev->pdev = pdev; dev->gadget.ops = &net2280_ops; @@ -2950,7 +2952,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff; /* done */ - pci_set_drvdata (pdev, dev); INFO (dev, "%s\n", driver_desc); INFO (dev, "irq %s, pci mem %p, chip rev %04x\n", bufp, base, dev->chiprev); diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 1e03f1a5a5f..a1bd2bea6de 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -350,7 +350,7 @@ static const struct hc_driver ehci_pci_hc_driver = { /* PCI driver selection metadata; PCI hotplugging uses this */ static const struct pci_device_id pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ - PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0), + PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0), .driver_data = (unsigned long) &ehci_pci_hc_driver, }, { /* end: all zeroes */ } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 1bfe96f4d04..b268537e389 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -206,7 +206,7 @@ static const struct hc_driver ohci_pci_hc_driver = { static const struct pci_device_id pci_ids [] = { { /* handle any USB OHCI controller */ - PCI_DEVICE_CLASS((PCI_CLASS_SERIAL_USB << 8) | 0x10, ~0), + PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0), .driver_data = (unsigned long) &ohci_pci_hc_driver, }, { /* end: all zeroes */ } }; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c0c4db78b59..d225e11f405 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -858,7 +858,7 @@ static const struct hc_driver uhci_driver = { static const struct pci_device_id uhci_pci_ids[] = { { /* handle any USB UHCI controller */ - PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x00), ~0), + PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0), .driver_data = (unsigned long) &uhci_driver, }, { /* end: all zeroes */ } }; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f5851db67f5..82151207d81 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -308,6 +308,7 @@ static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = { static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, @@ -493,6 +494,8 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) }, { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) }, { USB_DEVICE(ICOM_ID1_VID, ICOM_ID1_PID) }, { USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) }, { }, /* Optional parameter entry */ diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 2155f0e4a37..2c55a5ea9c9 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -39,6 +39,9 @@ /* www.thoughttechnology.com/ TT-USB provide with procomp use ftdi_sio */ #define FTDI_TTUSB_PID 0xFF20 /* Product Id */ +/* iPlus device */ +#define FTDI_IPLUS_PID 0xD070 /* Product Id */ + /* www.crystalfontz.com devices - thanx for providing free devices for evaluation ! */ /* they use the ftdi chipset for the USB interface and the vendor id is the same */ #define FTDI_XF_632_PID 0xFC08 /* 632: 16x2 Character Display */ @@ -153,6 +156,11 @@ #define ICOM_ID1_PID 0x0004 /* + * ASK.fr devices + */ +#define FTDI_ASK_RDR400_PID 0xC991 /* ASK RDR 400 series card reader */ + +/* * DSS-20 Sync Station for Sony Ericsson P800 */ @@ -400,6 +408,11 @@ #define FTDI_WESTREX_MODEL_8900F_PID 0xDC01 /* Model 8900F */ /* + * RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com) + */ +#define FTDI_RRCIRKITS_LOCOBUFFER_PID 0xc7d0 /* LocoBuffer USB */ + +/* * Eclo (http://www.eclo.pt/) product IDs. * PID 0xEA90 submitted by Martin Grill. */ diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ccf746b27d4..c96714bb1cb 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -61,6 +61,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, + { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) }, { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 09f379b19e9..7f29e81d3e3 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -26,6 +26,7 @@ #define ITEGNO_VENDOR_ID 0x0eba #define ITEGNO_PRODUCT_ID 0x1080 +#define ITEGNO_PRODUCT_ID_2080 0x2080 #define MA620_VENDOR_ID 0x0df7 #define MA620_PRODUCT_ID 0x0620 diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 557411c6e7c..f806553cd9a 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -508,6 +508,7 @@ no_firmware: err("%s: Unable to retrieve firmware version, try replugging\n", serial->type->description); err("%s: If the firmware is not running (status led not blinking)\n", serial->type->description); err("%s: please contact support@connecttech.com\n", serial->type->description); + kfree(result); return -ENODEV; no_command_private: diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index c4a9dcff5f2..aec5ea8682d 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -411,7 +411,7 @@ UNUSUAL_DEV( 0x050d, 0x0115, 0x0133, 0x0133, UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100, "Iomega", "USB Clik! 40", - US_SC_8070, US_PR_BULK, NULL, + US_SC_8070, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), /* Yakumo Mega Image 37 @@ -773,6 +773,13 @@ UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +/* Reported by Olivier Blondeau <zeitoun@gmail.com> */ +UNUSUAL_DEV( 0x0727, 0x0306, 0x0100, 0x0100, + "ATMEL", + "SND1 Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), + /* Submitted by Roman Hodek <roman@hodek.net> */ UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200, "Sandisk", diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 9060e713744..4587087d777 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -400,6 +400,8 @@ config FB_ASILIANT select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + help + This is the frame buffer device driver for the Asiliant 69030 chipset config FB_IMSTT bool "IMS Twin Turbo display support" diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c index b367de30b98..600d3e0e08b 100644 --- a/drivers/video/au1200fb.c +++ b/drivers/video/au1200fb.c @@ -1920,1925 +1920,3 @@ module_exit(au1200fb_cleanup); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -/* - * BRIEF MODULE DESCRIPTION - * Au1200 LCD Driver. - * - * Copyright 2004-2005 AMD - * Author: AMD - * - * Based on: - * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device - * Created 28 Dec 1997 by Geert Uytterhoeven - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/fb.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/ctype.h> -#include <linux/dma-mapping.h> - -#include <asm/mach-au1x00/au1000.h> -#include "au1200fb.h" - -#ifdef CONFIG_PM -#include <asm/mach-au1x00/au1xxx_pm.h> -#endif - -#ifndef CONFIG_FB_AU1200_DEVS -#define CONFIG_FB_AU1200_DEVS 4 -#endif - -#define DRIVER_NAME "au1200fb" -#define DRIVER_DESC "LCD controller driver for AU1200 processors" - -#define DEBUG 1 - -#define print_err(f, arg...) printk(KERN_ERR DRIVER_NAME ": " f "\n", ## arg) -#define print_warn(f, arg...) printk(KERN_WARNING DRIVER_NAME ": " f "\n", ## arg) -#define print_info(f, arg...) printk(KERN_INFO DRIVER_NAME ": " f "\n", ## arg) - -#if DEBUG -#define print_dbg(f, arg...) printk(KERN_DEBUG __FILE__ ": " f "\n", ## arg) -#else -#define print_dbg(f, arg...) do {} while (0) -#endif - - -#define AU1200_LCD_FB_IOCTL 0x46FF - -#define AU1200_LCD_SET_SCREEN 1 -#define AU1200_LCD_GET_SCREEN 2 -#define AU1200_LCD_SET_WINDOW 3 -#define AU1200_LCD_GET_WINDOW 4 -#define AU1200_LCD_SET_PANEL 5 -#define AU1200_LCD_GET_PANEL 6 - -#define SCREEN_SIZE (1<< 1) -#define SCREEN_BACKCOLOR (1<< 2) -#define SCREEN_BRIGHTNESS (1<< 3) -#define SCREEN_COLORKEY (1<< 4) -#define SCREEN_MASK (1<< 5) - -struct au1200_lcd_global_regs_t { - unsigned int flags; - unsigned int xsize; - unsigned int ysize; - unsigned int backcolor; - unsigned int brightness; - unsigned int colorkey; - unsigned int mask; - unsigned int panel_choice; - char panel_desc[80]; - -}; - -#define WIN_POSITION (1<< 0) -#define WIN_ALPHA_COLOR (1<< 1) -#define WIN_ALPHA_MODE (1<< 2) -#define WIN_PRIORITY (1<< 3) -#define WIN_CHANNEL (1<< 4) -#define WIN_BUFFER_FORMAT (1<< 5) -#define WIN_COLOR_ORDER (1<< 6) -#define WIN_PIXEL_ORDER (1<< 7) -#define WIN_SIZE (1<< 8) -#define WIN_COLORKEY_MODE (1<< 9) -#define WIN_DOUBLE_BUFFER_MODE (1<< 10) -#define WIN_RAM_ARRAY_MODE (1<< 11) -#define WIN_BUFFER_SCALE (1<< 12) -#define WIN_ENABLE (1<< 13) - -struct au1200_lcd_window_regs_t { - unsigned int flags; - unsigned int xpos; - unsigned int ypos; - unsigned int alpha_color; - unsigned int alpha_mode; - unsigned int priority; - unsigned int channel; - unsigned int buffer_format; - unsigned int color_order; - unsigned int pixel_order; - unsigned int xsize; - unsigned int ysize; - unsigned int colorkey_mode; - unsigned int double_buffer_mode; - unsigned int ram_array_mode; - unsigned int xscale; - unsigned int yscale; - unsigned int enable; -}; - - -struct au1200_lcd_iodata_t { - unsigned int subcmd; - struct au1200_lcd_global_regs_t global; - struct au1200_lcd_window_regs_t window; -}; - -#if defined(__BIG_ENDIAN) -#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_11 -#else -#define LCD_CONTROL_DEFAULT_PO LCD_CONTROL_PO_00 -#endif -#define LCD_CONTROL_DEFAULT_SBPPF LCD_CONTROL_SBPPF_565 - -/* Private, per-framebuffer management information (independent of the panel itself) */ -struct au1200fb_device { - struct fb_info fb_info; /* FB driver info record */ - - int plane; - unsigned char* fb_mem; /* FrameBuffer memory map */ - unsigned int fb_len; - dma_addr_t fb_phys; -}; - -static struct au1200fb_device _au1200fb_devices[CONFIG_FB_AU1200_DEVS]; -/********************************************************************/ - -/* LCD controller restrictions */ -#define AU1200_LCD_MAX_XRES 1280 -#define AU1200_LCD_MAX_YRES 1024 -#define AU1200_LCD_MAX_BPP 32 -#define AU1200_LCD_MAX_CLK 96000000 /* fixme: this needs to go away ? */ -#define AU1200_LCD_NBR_PALETTE_ENTRIES 256 - -/* Default number of visible screen buffer to allocate */ -#define AU1200FB_NBR_VIDEO_BUFFERS 1 - -/********************************************************************/ - -static struct au1200_lcd *lcd = (struct au1200_lcd *) AU1200_LCD_ADDR; -static int window_index = 2; /* default is zero */ -static int panel_index = 2; /* default is zero */ -static struct window_settings *win; -static struct panel_settings *panel; -static int noblanking = 1; -static int nohwcursor = 0; - -struct window_settings { - unsigned char name[64]; - uint32 mode_backcolor; - uint32 mode_colorkey; - uint32 mode_colorkeymsk; - struct { - int xres; - int yres; - int xpos; - int ypos; - uint32 mode_winctrl1; /* winctrl1[FRM,CCO,PO,PIPE] */ - uint32 mode_winenable; - } w[4]; -}; - -#if defined(__BIG_ENDIAN) -#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_00 -#else -#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_01 -#endif - -extern int board_au1200fb_panel_init (void); -extern int board_au1200fb_panel_shutdown (void); - -#ifdef CONFIG_PM -int au1200fb_pm_callback(au1xxx_power_dev_t *dev, - au1xxx_request_t request, void *data); -au1xxx_power_dev_t *LCD_pm_dev; -#endif - -/* - * Default window configurations - */ -static struct window_settings windows[] = { - { /* Index 0 */ - "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", - /* mode_backcolor */ 0x006600ff, - /* mode_colorkey,msk*/ 0, 0, - { - { - /* xres, yres, xpos, ypos */ 0, 0, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP, - /* mode_winenable*/ LCD_WINENABLE_WEN0, - }, - { - /* xres, yres, xpos, ypos */ 100, 100, 100, 100, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP | - LCD_WINCTRL1_PIPE, - /* mode_winenable*/ LCD_WINENABLE_WEN1, - }, - { - /* xres, yres, xpos, ypos */ 0, 0, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP, - /* mode_winenable*/ 0, - }, - { - /* xres, yres, xpos, ypos */ 0, 0, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP | - LCD_WINCTRL1_PIPE, - /* mode_winenable*/ 0, - }, - }, - }, - - { /* Index 1 */ - "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", - /* mode_backcolor */ 0x006600ff, - /* mode_colorkey,msk*/ 0, 0, - { - { - /* xres, yres, xpos, ypos */ 320, 240, 5, 5, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_24BPP | - LCD_WINCTRL1_PO_00, - /* mode_winenable*/ LCD_WINENABLE_WEN0, - }, - { - /* xres, yres, xpos, ypos */ 0, 0, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 - | LCD_WINCTRL1_PO_16BPP, - /* mode_winenable*/ 0, - }, - { - /* xres, yres, xpos, ypos */ 100, 100, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP | - LCD_WINCTRL1_PIPE, - /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, - }, - { - /* xres, yres, xpos, ypos */ 200, 25, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP | - LCD_WINCTRL1_PIPE, - /* mode_winenable*/ 0, - }, - }, - }, - { /* Index 2 */ - "0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx", - /* mode_backcolor */ 0x006600ff, - /* mode_colorkey,msk*/ 0, 0, - { - { - /* xres, yres, xpos, ypos */ 0, 0, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP, - /* mode_winenable*/ LCD_WINENABLE_WEN0, - }, - { - /* xres, yres, xpos, ypos */ 0, 0, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP, - /* mode_winenable*/ 0, - }, - { - /* xres, yres, xpos, ypos */ 0, 0, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_32BPP | - LCD_WINCTRL1_PO_00|LCD_WINCTRL1_PIPE, - /* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/, - }, - { - /* xres, yres, xpos, ypos */ 0, 0, 0, 0, - /* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565 | - LCD_WINCTRL1_PO_16BPP | - LCD_WINCTRL1_PIPE, - /* mode_winenable*/ 0, - }, - }, - }, - /* Need VGA 640 @ 24bpp, @ 32bpp */ - /* Need VGA 800 @ 24bpp, @ 32bpp */ - /* Need VGA 1024 @ 24bpp, @ 32bpp */ -}; - -/* - * Controller configurations for various panels. - */ - -struct panel_settings -{ - const char name[25]; /* Full name <vendor>_<model> */ - - struct fb_monspecs monspecs; /* FB monitor specs */ - - /* panel timings */ - uint32 mode_screen; - uint32 mode_horztiming; - uint32 mode_verttiming; - uint32 mode_clkcontrol; - uint32 mode_pwmdiv; - uint32 mode_pwmhi; - uint32 mode_outmask; - uint32 mode_fifoctrl; - uint32 mode_toyclksrc; - uint32 mode_backlight; - uint32 mode_auxpll; - int (*device_init)(void); - int (*device_shutdown)(void); -#define Xres min_xres -#define Yres min_yres - u32 min_xres; /* Minimum horizontal resolution */ - u32 max_xres; /* Maximum horizontal resolution */ - u32 min_yres; /* Minimum vertical resolution */ - u32 max_yres; /* Maximum vertical resolution */ -}; - -/********************************************************************/ -/* fixme: Maybe a modedb for the CRT ? otherwise panels should be as-is */ - -/* List of panels known to work with the AU1200 LCD controller. - * To add a new panel, enter the same specifications as the - * Generic_TFT one, and MAKE SURE that it doesn't conflicts - * with the controller restrictions. Restrictions are: - * - * STN color panels: max_bpp <= 12 - * STN mono panels: max_bpp <= 4 - * TFT panels: max_bpp <= 16 - * max_xres <= 800 - * max_yres <= 600 - */ -static struct panel_settings known_lcd_panels[] = -{ - [0] = { /* QVGA 320x240 H:33.3kHz V:110Hz */ - .name = "QVGA_320x240", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 30000, - .hfmax = 70000, - .vfmin = 60, - .vfmax = 60, - .dclkmin = 6000000, - .dclkmax = 28000000, - .input = FB_DISP_RGB, - }, - .mode_screen = LCD_SCREEN_SX_N(320) | - LCD_SCREEN_SY_N(240), - .mode_horztiming = 0x00c4623b, - .mode_verttiming = 0x00502814, - .mode_clkcontrol = 0x00020002, /* /4=24Mhz */ - .mode_pwmdiv = 0x00000000, - .mode_pwmhi = 0x00000000, - .mode_outmask = 0x00FFFFFF, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 8, /* 96MHz AUXPLL */ - .device_init = NULL, - .device_shutdown = NULL, - 320, 320, - 240, 240, - }, - - [1] = { /* VGA 640x480 H:30.3kHz V:58Hz */ - .name = "VGA_640x480", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 30000, - .hfmax = 70000, - .vfmin = 60, - .vfmax = 60, - .dclkmin = 6000000, - .dclkmax = 28000000, - .input = FB_DISP_RGB, - }, - .mode_screen = 0x13f9df80, - .mode_horztiming = 0x003c5859, - .mode_verttiming = 0x00741201, - .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ - .mode_pwmdiv = 0x00000000, - .mode_pwmhi = 0x00000000, - .mode_outmask = 0x00FFFFFF, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 8, /* 96MHz AUXPLL */ - .device_init = NULL, - .device_shutdown = NULL, - 640, 480, - 640, 480, - }, - - [2] = { /* SVGA 800x600 H:46.1kHz V:69Hz */ - .name = "SVGA_800x600", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 30000, - .hfmax = 70000, - .vfmin = 60, - .vfmax = 60, - .dclkmin = 6000000, - .dclkmax = 28000000, - .input = FB_DISP_RGB, - }, - .mode_screen = 0x18fa5780, - .mode_horztiming = 0x00dc7e77, - .mode_verttiming = 0x00584805, - .mode_clkcontrol = 0x00020000, /* /2=48Mhz */ - .mode_pwmdiv = 0x00000000, - .mode_pwmhi = 0x00000000, - .mode_outmask = 0x00FFFFFF, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 8, /* 96MHz AUXPLL */ - .device_init = NULL, - .device_shutdown = NULL, - 800, 800, - 600, 600, - }, - - [3] = { /* XVGA 1024x768 H:56.2kHz V:70Hz */ - .name = "XVGA_1024x768", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 30000, - .hfmax = 70000, - .vfmin = 60, - .vfmax = 60, - .dclkmin = 6000000, - .dclkmax = 28000000, - .input = FB_DISP_RGB, - }, - .mode_screen = 0x1ffaff80, - .mode_horztiming = 0x007d0e57, - .mode_verttiming = 0x00740a01, - .mode_clkcontrol = 0x000A0000, /* /1 */ - .mode_pwmdiv = 0x00000000, - .mode_pwmhi = 0x00000000, - .mode_outmask = 0x00FFFFFF, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 6, /* 72MHz AUXPLL */ - .device_init = NULL, - .device_shutdown = NULL, - 1024, 1024, - 768, 768, - }, - - [4] = { /* XVGA XVGA 1280x1024 H:68.5kHz V:65Hz */ - .name = "XVGA_1280x1024", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 30000, - .hfmax = 70000, - .vfmin = 60, - .vfmax = 60, - .dclkmin = 6000000, - .dclkmax = 28000000, - .input = FB_DISP_RGB, - }, - .mode_screen = 0x27fbff80, - .mode_horztiming = 0x00cdb2c7, - .mode_verttiming = 0x00600002, - .mode_clkcontrol = 0x000A0000, /* /1 */ - .mode_pwmdiv = 0x00000000, - .mode_pwmhi = 0x00000000, - .mode_outmask = 0x00FFFFFF, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 10, /* 120MHz AUXPLL */ - .device_init = NULL, - .device_shutdown = NULL, - 1280, 1280, - 1024, 1024, - }, - - [5] = { /* Samsung 1024x768 TFT */ - .name = "Samsung_1024x768_TFT", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 30000, - .hfmax = 70000, - .vfmin = 60, - .vfmax = 60, - .dclkmin = 6000000, - .dclkmax = 28000000, - .input = FB_DISP_RGB, - }, - .mode_screen = 0x1ffaff80, - .mode_horztiming = 0x018cc677, - .mode_verttiming = 0x00241217, - .mode_clkcontrol = 0x00000000, /* SCB 0x1 /4=24Mhz */ - .mode_pwmdiv = 0x8000063f, /* SCB 0x0 */ - .mode_pwmhi = 0x03400000, /* SCB 0x0 */ - .mode_outmask = 0x00FFFFFF, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 8, /* 96MHz AUXPLL */ - .device_init = board_au1200fb_panel_init, - .device_shutdown = board_au1200fb_panel_shutdown, - 1024, 1024, - 768, 768, - }, - - [6] = { /* Toshiba 640x480 TFT */ - .name = "Toshiba_640x480_TFT", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 30000, - .hfmax = 70000, - .vfmin = 60, - .vfmax = 60, - .dclkmin = 6000000, - .dclkmax = 28000000, - .input = FB_DISP_RGB, - }, - .mode_screen = LCD_SCREEN_SX_N(640) | - LCD_SCREEN_SY_N(480), - .mode_horztiming = LCD_HORZTIMING_HPW_N(96) | - LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(51), - .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | - LCD_VERTTIMING_VND1_N(11) | LCD_VERTTIMING_VND2_N(32), - .mode_clkcontrol = 0x00000000, /* /4=24Mhz */ - .mode_pwmdiv = 0x8000063f, - .mode_pwmhi = 0x03400000, - .mode_outmask = 0x00fcfcfc, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 8, /* 96MHz AUXPLL */ - .device_init = board_au1200fb_panel_init, - .device_shutdown = board_au1200fb_panel_shutdown, - 640, 480, - 640, 480, - }, - - [7] = { /* Sharp 320x240 TFT */ - .name = "Sharp_320x240_TFT", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 12500, - .hfmax = 20000, - .vfmin = 38, - .vfmax = 81, - .dclkmin = 4500000, - .dclkmax = 6800000, - .input = FB_DISP_RGB, - }, - .mode_screen = LCD_SCREEN_SX_N(320) | - LCD_SCREEN_SY_N(240), - .mode_horztiming = LCD_HORZTIMING_HPW_N(60) | - LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(2), - .mode_verttiming = LCD_VERTTIMING_VPW_N(2) | - LCD_VERTTIMING_VND1_N(2) | LCD_VERTTIMING_VND2_N(5), - .mode_clkcontrol = LCD_CLKCONTROL_PCD_N(7), /*16=6Mhz*/ - .mode_pwmdiv = 0x8000063f, - .mode_pwmhi = 0x03400000, - .mode_outmask = 0x00fcfcfc, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 8, /* 96MHz AUXPLL */ - .device_init = board_au1200fb_panel_init, - .device_shutdown = board_au1200fb_panel_shutdown, - 320, 320, - 240, 240, - }, - - [8] = { /* Toppoly TD070WGCB2 7" 856x480 TFT */ - .name = "Toppoly_TD070WGCB2", - .monspecs = { - .modedb = NULL, - .modedb_len = 0, - .hfmin = 30000, - .hfmax = 70000, - .vfmin = 60, - .vfmax = 60, - .dclkmin = 6000000, - .dclkmax = 28000000, - .input = FB_DISP_RGB, - }, - .mode_screen = LCD_SCREEN_SX_N(856) | - LCD_SCREEN_SY_N(480), - .mode_horztiming = LCD_HORZTIMING_HND2_N(43) | - LCD_HORZTIMING_HND1_N(43) | LCD_HORZTIMING_HPW_N(114), - .mode_verttiming = LCD_VERTTIMING_VND2_N(20) | - LCD_VERTTIMING_VND1_N(21) | LCD_VERTTIMING_VPW_N(4), - .mode_clkcontrol = 0x00020001, /* /4=24Mhz */ - .mode_pwmdiv = 0x8000063f, - .mode_pwmhi = 0x03400000, - .mode_outmask = 0x00fcfcfc, - .mode_fifoctrl = 0x2f2f2f2f, - .mode_toyclksrc = 0x00000004, /* AUXPLL directly */ - .mode_backlight = 0x00000000, - .mode_auxpll = 8, /* 96MHz AUXPLL */ - .device_init = board_au1200fb_panel_init, - .device_shutdown = board_au1200fb_panel_shutdown, - 856, 856, - 480, 480, - }, -}; - -#define NUM_PANELS (ARRAY_SIZE(known_lcd_panels)) - -/********************************************************************/ - -#ifdef CONFIG_PM -static int set_brightness(unsigned int brightness) -{ - unsigned int hi1, divider; - - /* limit brightness pwm duty to >= 30/1600 */ - if (brightness < 30) { - brightness = 30; - } - divider = (lcd->pwmdiv & 0x3FFFF) + 1; - hi1 = (lcd->pwmhi >> 16) + 1; - hi1 = (((brightness & 0xFF) + 1) * divider >> 8); - lcd->pwmhi &= 0xFFFF; - lcd->pwmhi |= (hi1 << 16); - - return brightness; -} -#endif /* CONFIG_PM */ - -static int winbpp (unsigned int winctrl1) -{ - int bits = 0; - - /* how many bits are needed for each pixel format */ - switch (winctrl1 & LCD_WINCTRL1_FRM) { - case LCD_WINCTRL1_FRM_1BPP: - bits = 1; - break; - case LCD_WINCTRL1_FRM_2BPP: - bits = 2; - break; - case LCD_WINCTRL1_FRM_4BPP: - bits = 4; - break; - case LCD_WINCTRL1_FRM_8BPP: - bits = 8; - break; - case LCD_WINCTRL1_FRM_12BPP: - case LCD_WINCTRL1_FRM_16BPP655: - case LCD_WINCTRL1_FRM_16BPP565: - case LCD_WINCTRL1_FRM_16BPP556: - case LCD_WINCTRL1_FRM_16BPPI1555: - case LCD_WINCTRL1_FRM_16BPPI5551: - case LCD_WINCTRL1_FRM_16BPPA1555: - case LCD_WINCTRL1_FRM_16BPPA5551: - bits = 16; - break; - case LCD_WINCTRL1_FRM_24BPP: - case LCD_WINCTRL1_FRM_32BPP: - bits = 32; - break; - } - - return bits; -} - -static int fbinfo2index (struct fb_info *fb_info) -{ - int i; - - for (i = 0; i < CONFIG_FB_AU1200_DEVS; ++i) { - if (fb_info == (struct fb_info *)(&_au1200fb_devices[i].fb_info)) - return i; - } - printk("au1200fb: ERROR: fbinfo2index failed!\n"); - return -1; -} - -static int au1200_setlocation (struct au1200fb_device *fbdev, int plane, - int xpos, int ypos) -{ - uint32 winctrl0, winctrl1, winenable, fb_offset = 0; - int xsz, ysz; - - /* FIX!!! NOT CHECKING FOR COMPLETE OFFSCREEN YET */ - - winctrl0 = lcd->window[plane].winctrl0; - winctrl1 = lcd->window[plane].winctrl1; - winctrl0 &= (LCD_WINCTRL0_A | LCD_WINCTRL0_AEN); - winctrl1 &= ~(LCD_WINCTRL1_SZX | LCD_WINCTRL1_SZY); - - /* Check for off-screen adjustments */ - xsz = win->w[plane].xres; - ysz = win->w[plane].yres; - if ((xpos + win->w[plane].xres) > panel->Xres) { - /* Off-screen to the right */ - xsz = panel->Xres - xpos; /* off by 1 ??? */ - /*printk("off screen right\n");*/ - } - - if ((ypos + win->w[plane].yres) > panel->Yres) { - /* Off-screen to the bottom */ - ysz = panel->Yres - ypos; /* off by 1 ??? */ - /*printk("off screen bottom\n");*/ - } - - if (xpos < 0) { - /* Off-screen to the left */ - xsz = win->w[plane].xres + xpos; - fb_offset += (((0 - xpos) * winbpp(lcd->window[plane].winctrl1))/8); - xpos = 0; - /*printk("off screen left\n");*/ - } - - if (ypos < 0) { - /* Off-screen to the top */ - ysz = win->w[plane].yres + ypos; - /* fixme: fb_offset += ((0-ypos)*fb_pars[plane].line_length); */ - ypos = 0; - /*printk("off screen top\n");*/ - } - - /* record settings */ - win->w[plane].xpos = xpos; - win->w[plane].ypos = ypos; - - xsz -= 1; - ysz -= 1; - winctrl0 |= (xpos << 21); - winctrl0 |= (ypos << 10); - winctrl1 |= (xsz << 11); - winctrl1 |= (ysz << 0); - - /* Disable the window while making changes, then restore WINEN */ - winenable = lcd->winenable & (1 << plane); - au_sync(); - lcd->winenable &= ~(1 << plane); - lcd->window[plane].winctrl0 = winctrl0; - lcd->window[plane].winctrl1 = winctrl1; - lcd->window[plane].winbuf0 = - lcd->window[plane].winbuf1 = fbdev->fb_phys; - lcd->window[plane].winbufctrl = 0; /* select winbuf0 */ - lcd->winenable |= winenable; - au_sync(); - - return 0; -} - -static void au1200_setpanel (struct panel_settings *newpanel) -{ - /* - * Perform global setup/init of LCD controller - */ - uint32 winenable; - - /* Make sure all windows disabled */ - winenable = lcd->winenable; - lcd->winenable = 0; - au_sync(); - /* - * Ensure everything is disabled before reconfiguring - */ - if (lcd->screen & LCD_SCREEN_SEN) { - /* Wait for vertical sync period */ - lcd->intstatus = LCD_INT_SS; - while ((lcd->intstatus & LCD_INT_SS) == 0) { - au_sync(); - } - - lcd->screen &= ~LCD_SCREEN_SEN; /*disable the controller*/ - - do { - lcd->intstatus = lcd->intstatus; /*clear interrupts*/ - au_sync(); - /*wait for controller to shut down*/ - } while ((lcd->intstatus & LCD_INT_SD) == 0); - - /* Call shutdown of current panel (if up) */ - /* this must occur last, because if an external clock is driving - the controller, the clock cannot be turned off before first - shutting down the controller. - */ - if (panel->device_shutdown != NULL) - panel->device_shutdown(); - } - - /* Newpanel == NULL indicates a shutdown operation only */ - if (newpanel == NULL) - return; - - panel = newpanel; - - printk("Panel(%s), %dx%d\n", panel->name, panel->Xres, panel->Yres); - - /* - * Setup clocking if internal LCD clock source (assumes sys_auxpll valid) - */ - if (!(panel->mode_clkcontrol & LCD_CLKCONTROL_EXT)) - { - uint32 sys_clksrc; - au_writel(panel->mode_auxpll, SYS_AUXPLL); - sys_clksrc = au_readl(SYS_CLKSRC) & ~0x0000001f; - sys_clksrc |= panel->mode_toyclksrc; - au_writel(sys_clksrc, SYS_CLKSRC); - } - - /* - * Configure panel timings - */ - lcd->screen = panel->mode_screen; - lcd->horztiming = panel->mode_horztiming; - lcd->verttiming = panel->mode_verttiming; - lcd->clkcontrol = panel->mode_clkcontrol; - lcd->pwmdiv = panel->mode_pwmdiv; - lcd->pwmhi = panel->mode_pwmhi; - lcd->outmask = panel->mode_outmask; - lcd->fifoctrl = panel->mode_fifoctrl; - au_sync(); - - /* fixme: Check window settings to make sure still valid - * for new geometry */ -#if 0 - au1200_setlocation(fbdev, 0, win->w[0].xpos, win->w[0].ypos); - au1200_setlocation(fbdev, 1, win->w[1].xpos, win->w[1].ypos); - au1200_setlocation(fbdev, 2, win->w[2].xpos, win->w[2].ypos); - au1200_setlocation(fbdev, 3, win->w[3].xpos, win->w[3].ypos); -#endif - lcd->winenable = winenable; - - /* - * Re-enable screen now that it is configured - */ - lcd->screen |= LCD_SCREEN_SEN; - au_sync(); - - /* Call init of panel */ - if (panel->device_init != NULL) panel->device_init(); - - /* FIX!!!! not appropriate on panel change!!! Global setup/init */ - lcd->intenable = 0; - lcd->intstatus = ~0; - lcd->backcolor = win->mode_backcolor; - - /* Setup Color Key - FIX!!! */ - lcd->colorkey = win->mode_colorkey; - lcd->colorkeymsk = win->mode_colorkeymsk; - - /* Setup HWCursor - FIX!!! Need to support this eventually */ - lcd->hwc.cursorctrl = 0; - lcd->hwc.cursorpos = 0; - lcd->hwc.cursorcolor0 = 0; - lcd->hwc.cursorcolor1 = 0; - lcd->hwc.cursorcolor2 = 0; - lcd->hwc.cursorcolor3 = 0; - - -#if 0 -#define D(X) printk("%25s: %08X\n", #X, X) - D(lcd->screen); - D(lcd->horztiming); - D(lcd->verttiming); - D(lcd->clkcontrol); - D(lcd->pwmdiv); - D(lcd->pwmhi); - D(lcd->outmask); - D(lcd->fifoctrl); - D(lcd->window[0].winctrl0); - D(lcd->window[0].winctrl1); - D(lcd->window[0].winctrl2); - D(lcd->window[0].winbuf0); - D(lcd->window[0].winbuf1); - D(lcd->window[0].winbufctrl); - D(lcd->window[1].winctrl0); - D(lcd->window[1].winctrl1); - D(lcd->window[1].winctrl2); - D(lcd->window[1].winbuf0); - D(lcd->window[1].winbuf1); - D(lcd->window[1].winbufctrl); - D(lcd->window[2].winctrl0); - D(lcd->window[2].winctrl1); - D(lcd->window[2].winctrl2); - D(lcd->window[2].winbuf0); - D(lcd->window[2].winbuf1); - D(lcd->window[2].winbufctrl); - D(lcd->window[3].winctrl0); - D(lcd->window[3].winctrl1); - D(lcd->window[3].winctrl2); - D(lcd->window[3].winbuf0); - D(lcd->window[3].winbuf1); - D(lcd->window[3].winbufctrl); - D(lcd->winenable); - D(lcd->intenable); - D(lcd->intstatus); - D(lcd->backcolor); - D(lcd->winenable); - D(lcd->colorkey); - D(lcd->colorkeymsk); - D(lcd->hwc.cursorctrl); - D(lcd->hwc.cursorpos); - D(lcd->hwc.cursorcolor0); - D(lcd->hwc.cursorcolor1); - D(lcd->hwc.cursorcolor2); - D(lcd->hwc.cursorcolor3); -#endif -} - -static void au1200_setmode(struct au1200fb_device *fbdev) -{ - int plane = fbdev->plane; - /* Window/plane setup */ - lcd->window[plane].winctrl1 = ( 0 - | LCD_WINCTRL1_PRI_N(plane) - | win->w[plane].mode_winctrl1 /* FRM,CCO,PO,PIPE */ - ) ; - - au1200_setlocation(fbdev, plane, win->w[plane].xpos, win->w[plane].ypos); - - lcd->window[plane].winctrl2 = ( 0 - | LCD_WINCTRL2_CKMODE_00 - | LCD_WINCTRL2_DBM - | LCD_WINCTRL2_BX_N( fbdev->fb_info.fix.line_length) - | LCD_WINCTRL2_SCX_1 - | LCD_WINCTRL2_SCY_1 - ) ; - lcd->winenable |= win->w[plane].mode_winenable; - au_sync(); -} - - -/* Inline helpers */ - -/*#define panel_is_dual(panel) ((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ -/*#define panel_is_active(panel)((panel->mode_screen & LCD_SCREEN_PT) == LCD_SCREEN_PT_010)*/ - -#define panel_is_color(panel) ((panel->mode_screen & LCD_SCREEN_PT) <= LCD_SCREEN_PT_CDSTN) - -/* Bitfields format supported by the controller. */ -static struct fb_bitfield rgb_bitfields[][4] = { - /* Red, Green, Blue, Transp */ - [LCD_WINCTRL1_FRM_16BPP655 >> 25] = - { { 10, 6, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, - - [LCD_WINCTRL1_FRM_16BPP565 >> 25] = - { { 11, 5, 0 }, { 5, 6, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, - - [LCD_WINCTRL1_FRM_16BPP556 >> 25] = - { { 11, 5, 0 }, { 6, 5, 0 }, { 0, 6, 0 }, { 0, 0, 0 } }, - - [LCD_WINCTRL1_FRM_16BPPI1555 >> 25] = - { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 0, 0, 0 } }, - - [LCD_WINCTRL1_FRM_16BPPI5551 >> 25] = - { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 0, 0 } }, - - [LCD_WINCTRL1_FRM_16BPPA1555 >> 25] = - { { 10, 5, 0 }, { 5, 5, 0 }, { 0, 5, 0 }, { 15, 1, 0 } }, - - [LCD_WINCTRL1_FRM_16BPPA5551 >> 25] = - { { 11, 5, 0 }, { 6, 5, 0 }, { 1, 5, 0 }, { 0, 1, 0 } }, - - [LCD_WINCTRL1_FRM_24BPP >> 25] = - { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 0, 0, 0 } }, - - [LCD_WINCTRL1_FRM_32BPP >> 25] = - { { 16, 8, 0 }, { 8, 8, 0 }, { 0, 8, 0 }, { 24, 0, 0 } }, -}; - -/*-------------------------------------------------------------------------*/ - -/* Helpers */ - -static void au1200fb_update_fbinfo(struct fb_info *fbi) -{ - /* FIX!!!! This also needs to take the window pixel format into account!!! */ - - /* Update var-dependent FB info */ - if (panel_is_color(panel)) { - if (fbi->var.bits_per_pixel <= 8) { - /* palettized */ - fbi->fix.visual = FB_VISUAL_PSEUDOCOLOR; - fbi->fix.line_length = fbi->var.xres_virtual / - (8/fbi->var.bits_per_pixel); - } else { - /* non-palettized */ - fbi->fix.visual = FB_VISUAL_TRUECOLOR; - fbi->fix.line_length = fbi->var.xres_virtual * (fbi->var.bits_per_pixel / 8); - } - } else { - /* mono FIX!!! mono 8 and 4 bits */ - fbi->fix.visual = FB_VISUAL_MONO10; - fbi->fix.line_length = fbi->var.xres_virtual / 8; - } - - fbi->screen_size = fbi->fix.line_length * fbi->var.yres_virtual; - print_dbg("line length: %d\n", fbi->fix.line_length); - print_dbg("bits_per_pixel: %d\n", fbi->var.bits_per_pixel); -} - -/*-------------------------------------------------------------------------*/ - -/* AU1200 framebuffer driver */ - -/* fb_check_var - * Validate var settings with hardware restrictions and modify it if necessary - */ -static int au1200fb_fb_check_var(struct fb_var_screeninfo *var, - struct fb_info *fbi) -{ - struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; - u32 pixclock; - int screen_size, plane; - - plane = fbdev->plane; - - /* Make sure that the mode respect all LCD controller and - * panel restrictions. */ - var->xres = win->w[plane].xres; - var->yres = win->w[plane].yres; - - /* No need for virtual resolution support */ - var->xres_virtual = var->xres; - var->yres_virtual = var->yres; - - var->bits_per_pixel = winbpp(win->w[plane].mode_winctrl1); - - screen_size = var->xres_virtual * var->yres_virtual; - if (var->bits_per_pixel > 8) screen_size *= (var->bits_per_pixel / 8); - else screen_size /= (8/var->bits_per_pixel); - - if (fbdev->fb_len < screen_size) - return -EINVAL; /* Virtual screen is to big, abort */ - - /* FIX!!!! what are the implicaitons of ignoring this for windows ??? */ - /* The max LCD clock is fixed to 48MHz (value of AUX_CLK). The pixel - * clock can only be obtain by dividing this value by an even integer. - * Fallback to a slower pixel clock if necessary. */ - pixclock = max((u32)(PICOS2KHZ(var->pixclock) * 1000), fbi->monspecs.dclkmin); - pixclock = min(pixclock, min(fbi->monspecs.dclkmax, (u32)AU1200_LCD_MAX_CLK/2)); - - if (AU1200_LCD_MAX_CLK % pixclock) { - int diff = AU1200_LCD_MAX_CLK % pixclock; - pixclock -= diff; - } - - var->pixclock = KHZ2PICOS(pixclock/1000); -#if 0 - if (!panel_is_active(panel)) { - int pcd = AU1200_LCD_MAX_CLK / (pixclock * 2) - 1; - - if (!panel_is_color(panel) - && (panel->control_base & LCD_CONTROL_MPI) && (pcd < 3)) { - /* STN 8bit mono panel support is up to 6MHz pixclock */ - var->pixclock = KHZ2PICOS(6000); - } else if (!pcd) { - /* Other STN panel support is up to 12MHz */ - var->pixclock = KHZ2PICOS(12000); - } - } -#endif - /* Set bitfield accordingly */ - switch (var->bits_per_pixel) { - case 16: - { - /* 16bpp True color. - * These must be set to MATCH WINCTRL[FORM] */ - int idx; - idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; - var->red = rgb_bitfields[idx][0]; - var->green = rgb_bitfields[idx][1]; - var->blue = rgb_bitfields[idx][2]; - var->transp = rgb_bitfields[idx][3]; - break; - } - - case 32: - { - /* 32bpp True color. - * These must be set to MATCH WINCTRL[FORM] */ - int idx; - idx = (win->w[0].mode_winctrl1 & LCD_WINCTRL1_FRM) >> 25; - var->red = rgb_bitfields[idx][0]; - var->green = rgb_bitfields[idx][1]; - var->blue = rgb_bitfields[idx][2]; - var->transp = rgb_bitfields[idx][3]; - break; - } - default: - print_dbg("Unsupported depth %dbpp", var->bits_per_pixel); - return -EINVAL; - } - - return 0; -} - -/* fb_set_par - * Set hardware with var settings. This will enable the controller with a - * specific mode, normally validated with the fb_check_var method - */ -static int au1200fb_fb_set_par(struct fb_info *fbi) -{ - struct au1200fb_device *fbdev = (struct au1200fb_device *)fbi; - - au1200fb_update_fbinfo(fbi); - au1200_setmode(fbdev); - - return 0; -} - -/* fb_setcolreg - * Set color in LCD palette. - */ -static int au1200fb_fb_setcolreg(unsigned regno, unsigned red, unsigned green, - unsigned blue, unsigned transp, struct fb_info *fbi) -{ - volatile u32 *palette = lcd->palette; - u32 value; - - if (regno > (AU1200_LCD_NBR_PALETTE_ENTRIES - 1)) - return -EINVAL; - - if (fbi->var.grayscale) { - /* Convert color to grayscale */ - red = green = blue = - (19595 * red + 38470 * green + 7471 * blue) >> 16; - } - - if (fbi->fix.visual == FB_VISUAL_TRUECOLOR) { - /* Place color in the pseudopalette */ - if (regno > 16) - return -EINVAL; - - palette = (u32*) fbi->pseudo_palette; - - red >>= (16 - fbi->var.red.length); - green >>= (16 - fbi->var.green.length); - blue >>= (16 - fbi->var.blue.length); - - value = (red << fbi->var.red.offset) | - (green << fbi->var.green.offset)| - (blue << fbi->var.blue.offset); - value &= 0xFFFF; - - } else if (1 /*FIX!!! panel_is_active(fbdev->panel)*/) { - /* COLOR TFT PALLETTIZED (use RGB 565) */ - value = (red & 0xF800)|((green >> 5) & - 0x07E0)|((blue >> 11) & 0x001F); - value &= 0xFFFF; - - } else if (0 /*panel_is_color(fbdev->panel)*/) { - /* COLOR STN MODE */ - value = 0x1234; - value &= 0xFFF; - } else { - /* MONOCHROME MODE */ - value = (green >> 12) & 0x000F; - value &= 0xF; - } - - palette[regno] = value; - - return 0; -} - -/* fb_blank - * Blank the screen. Depending on the mode, the screen will be - * activated with the backlight color, or desactivated - */ -static int au1200fb_fb_blank(int blank_mode, struct fb_info *fbi) -{ - /* Short-circuit screen blanking */ - if (noblanking) - return 0; - - switch (blank_mode) { - - case FB_BLANK_UNBLANK: - case FB_BLANK_NORMAL: - /* printk("turn on panel\n"); */ - au1200_setpanel(panel); - break; - case FB_BLANK_VSYNC_SUSPEND: - case FB_BLANK_HSYNC_SUSPEND: - case FB_BLANK_POWERDOWN: - /* printk("turn off panel\n"); */ - au1200_setpanel(NULL); - break; - default: - break; - - } - - /* FB_BLANK_NORMAL is a soft blank */ - return (blank_mode == FB_BLANK_NORMAL) ? -EINVAL : 0; -} - -/* fb_mmap - * Map video memory in user space. We don't use the generic fb_mmap - * method mainly to allow the use of the TLB streaming flag (CCA=6) - */ -static int au1200fb_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) - -{ - unsigned int len; - unsigned long start=0, off; - struct au1200fb_device *fbdev = (struct au1200fb_device *) info; - -#ifdef CONFIG_PM - au1xxx_pm_access(LCD_pm_dev); -#endif - - if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { - return -EINVAL; - } - - start = fbdev->fb_phys & PAGE_MASK; - len = PAGE_ALIGN((start & ~PAGE_MASK) + fbdev->fb_len); - - off = vma->vm_pgoff << PAGE_SHIFT; - - if ((vma->vm_end - vma->vm_start + off) > len) { - return -EINVAL; - } - - off += start; - vma->vm_pgoff = off >> PAGE_SHIFT; - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - pgprot_val(vma->vm_page_prot) |= _CACHE_MASK; /* CCA=7 */ - - vma->vm_flags |= VM_IO; - - return io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot); - - return 0; -} - -static void set_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) -{ - - unsigned int hi1, divider; - - /* SCREEN_SIZE: user cannot reset size, must switch panel choice */ - - if (pdata->flags & SCREEN_BACKCOLOR) - lcd->backcolor = pdata->backcolor; - - if (pdata->flags & SCREEN_BRIGHTNESS) { - - // limit brightness pwm duty to >= 30/1600 - if (pdata->brightness < 30) { - pdata->brightness = 30; - } - divider = (lcd->pwmdiv & 0x3FFFF) + 1; - hi1 = (lcd->pwmhi >> 16) + 1; - hi1 = (((pdata->brightness & 0xFF)+1) * divider >> 8); - lcd->pwmhi &= 0xFFFF; - lcd->pwmhi |= (hi1 << 16); - } - - if (pdata->flags & SCREEN_COLORKEY) - lcd->colorkey = pdata->colorkey; - - if (pdata->flags & SCREEN_MASK) - lcd->colorkeymsk = pdata->mask; - au_sync(); -} - -static void get_global(u_int cmd, struct au1200_lcd_global_regs_t *pdata) -{ - unsigned int hi1, divider; - - pdata->xsize = ((lcd->screen & LCD_SCREEN_SX) >> 19) + 1; - pdata->ysize = ((lcd->screen & LCD_SCREEN_SY) >> 8) + 1; - - pdata->backcolor = lcd->backcolor; - pdata->colorkey = lcd->colorkey; - pdata->mask = lcd->colorkeymsk; - - // brightness - hi1 = (lcd->pwmhi >> 16) + 1; - divider = (lcd->pwmdiv & 0x3FFFF) + 1; - pdata->brightness = ((hi1 << 8) / divider) - 1; - au_sync(); -} - -static void set_window(unsigned int plane, - struct au1200_lcd_window_regs_t *pdata) -{ - unsigned int val, bpp; - - /* Window control register 0 */ - if (pdata->flags & WIN_POSITION) { - val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_OX | - LCD_WINCTRL0_OY); - val |= ((pdata->xpos << 21) & LCD_WINCTRL0_OX); - val |= ((pdata->ypos << 10) & LCD_WINCTRL0_OY); - lcd->window[plane].winctrl0 = val; - } - if (pdata->flags & WIN_ALPHA_COLOR) { - val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_A); - val |= ((pdata->alpha_color << 2) & LCD_WINCTRL0_A); - lcd->window[plane].winctrl0 = val; - } - if (pdata->flags & WIN_ALPHA_MODE) { - val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_AEN); - val |= ((pdata->alpha_mode << 1) & LCD_WINCTRL0_AEN); - lcd->window[plane].winctrl0 = val; - } - - /* Window control register 1 */ - if (pdata->flags & WIN_PRIORITY) { - val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PRI); - val |= ((pdata->priority << 30) & LCD_WINCTRL1_PRI); - lcd->window[plane].winctrl1 = val; - } - if (pdata->flags & WIN_CHANNEL) { - val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PIPE); - val |= ((pdata->channel << 29) & LCD_WINCTRL1_PIPE); - lcd->window[plane].winctrl1 = val; - } - if (pdata->flags & WIN_BUFFER_FORMAT) { - val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_FRM); - val |= ((pdata->buffer_format << 25) & LCD_WINCTRL1_FRM); - lcd->window[plane].winctrl1 = val; - } - if (pdata->flags & WIN_COLOR_ORDER) { - val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_CCO); - val |= ((pdata->color_order << 24) & LCD_WINCTRL1_CCO); - lcd->window[plane].winctrl1 = val; - } - if (pdata->flags & WIN_PIXEL_ORDER) { - val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PO); - val |= ((pdata->pixel_order << 22) & LCD_WINCTRL1_PO); - lcd->window[plane].winctrl1 = val; - } - if (pdata->flags & WIN_SIZE) { - val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_SZX | - LCD_WINCTRL1_SZY); - val |= (((pdata->xsize << 11) - 1) & LCD_WINCTRL1_SZX); - val |= (((pdata->ysize) - 1) & LCD_WINCTRL1_SZY); - lcd->window[plane].winctrl1 = val; - /* program buffer line width */ - bpp = winbpp(val) / 8; - val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_BX); - val |= (((pdata->xsize * bpp) << 8) & LCD_WINCTRL2_BX); - lcd->window[plane].winctrl2 = val; - } - - /* Window control register 2 */ - if (pdata->flags & WIN_COLORKEY_MODE) { - val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_CKMODE); - val |= ((pdata->colorkey_mode << 24) & LCD_WINCTRL2_CKMODE); - lcd->window[plane].winctrl2 = val; - } - if (pdata->flags & WIN_DOUBLE_BUFFER_MODE) { - val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_DBM); - val |= ((pdata->double_buffer_mode << 23) & LCD_WINCTRL2_DBM); - lcd->window[plane].winctrl2 = val; - } - if (pdata->flags & WIN_RAM_ARRAY_MODE) { - val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_RAM); - val |= ((pdata->ram_array_mode << 21) & LCD_WINCTRL2_RAM); - lcd->window[plane].winctrl2 = val; - } - - /* Buffer line width programmed with WIN_SIZE */ - - if (pdata->flags & WIN_BUFFER_SCALE) { - val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_SCX | - LCD_WINCTRL2_SCY); - val |= ((pdata->xsize << 11) & LCD_WINCTRL2_SCX); - val |= ((pdata->ysize) & LCD_WINCTRL2_SCY); - lcd->window[plane].winctrl2 = val; - } - - if (pdata->flags & WIN_ENABLE) { - val = lcd->winenable; - val &= ~(1<<plane); - val |= (pdata->enable & 1) << plane; - lcd->winenable = val; - } - au_sync(); -} - -static void get_window(unsigned int plane, - struct au1200_lcd_window_regs_t *pdata) -{ - /* Window control register 0 */ - pdata->xpos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OX) >> 21; - pdata->ypos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OY) >> 10; - pdata->alpha_color = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_A) >> 2; - pdata->alpha_mode = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_AEN) >> 1; - - /* Window control register 1 */ - pdata->priority = (lcd->window[plane].winctrl1& LCD_WINCTRL1_PRI) >> 30; - pdata->channel = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PIPE) >> 29; - pdata->buffer_format = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_FRM) >> 25; - pdata->color_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_CCO) >> 24; - pdata->pixel_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PO) >> 22; - pdata->xsize = ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZX) >> 11) + 1; - pdata->ysize = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZY) + 1; - - /* Window control register 2 */ - pdata->colorkey_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_CKMODE) >> 24; - pdata->double_buffer_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_DBM) >> 23; - pdata->ram_array_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_RAM) >> 21; - - pdata->enable = (lcd->winenable >> plane) & 1; - au_sync(); -} - -static int au1200fb_ioctl(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - int plane; - int val; - -#ifdef CONFIG_PM - au1xxx_pm_access(LCD_pm_dev); -#endif - - plane = fbinfo2index(info); - print_dbg("au1200fb: ioctl %d on plane %d\n", cmd, plane); - - if (cmd == AU1200_LCD_FB_IOCTL) { - struct au1200_lcd_iodata_t iodata; - - if (copy_from_user(&iodata, (void __user *) arg, sizeof(iodata))) - return -EFAULT; - - print_dbg("FB IOCTL called\n"); - - switch (iodata.subcmd) { - case AU1200_LCD_SET_SCREEN: - print_dbg("AU1200_LCD_SET_SCREEN\n"); - set_global(cmd, &iodata.global); - break; - - case AU1200_LCD_GET_SCREEN: - print_dbg("AU1200_LCD_GET_SCREEN\n"); - get_global(cmd, &iodata.global); - break; - - case AU1200_LCD_SET_WINDOW: - print_dbg("AU1200_LCD_SET_WINDOW\n"); - set_window(plane, &iodata.window); - break; - - case AU1200_LCD_GET_WINDOW: - print_dbg("AU1200_LCD_GET_WINDOW\n"); - get_window(plane, &iodata.window); - break; - - case AU1200_LCD_SET_PANEL: - print_dbg("AU1200_LCD_SET_PANEL\n"); - if ((iodata.global.panel_choice >= 0) && - (iodata.global.panel_choice < - NUM_PANELS)) - { - struct panel_settings *newpanel; - panel_index = iodata.global.panel_choice; - newpanel = &known_lcd_panels[panel_index]; - au1200_setpanel(newpanel); - } - break; - - case AU1200_LCD_GET_PANEL: - print_dbg("AU1200_LCD_GET_PANEL\n"); - iodata.global.panel_choice = panel_index; - break; - - default: - return -EINVAL; - } - - val = copy_to_user((void __user *) arg, &iodata, sizeof(iodata)); - if (val) { - print_dbg("error: could not copy %d bytes\n", val); - return -EFAULT; - } - } - - return 0; -} - - -static struct fb_ops au1200fb_fb_ops = { - .owner = THIS_MODULE, - .fb_check_var = au1200fb_fb_check_var, - .fb_set_par = au1200fb_fb_set_par, - .fb_setcolreg = au1200fb_fb_setcolreg, - .fb_blank = au1200fb_fb_blank, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, - .fb_imageblit = cfb_imageblit, - .fb_sync = NULL, - .fb_ioctl = au1200fb_ioctl, - .fb_mmap = au1200fb_fb_mmap, -}; - -/*-------------------------------------------------------------------------*/ - -static irqreturn_t au1200fb_handle_irq(int irq, void* dev_id, struct pt_regs *regs) -{ - /* Nothing to do for now, just clear any pending interrupt */ - lcd->intstatus = lcd->intstatus; - au_sync(); - - return IRQ_HANDLED; -} - -/*-------------------------------------------------------------------------*/ - -/* AU1200 LCD device probe helpers */ - -static int au1200fb_init_fbinfo(struct au1200fb_device *fbdev) -{ - struct fb_info *fbi = &fbdev->fb_info; - int bpp; - - memset(fbi, 0, sizeof(struct fb_info)); - fbi->fbops = &au1200fb_fb_ops; - - bpp = winbpp(win->w[fbdev->plane].mode_winctrl1); - - /* Copy monitor specs from panel data */ - /* fixme: we're setting up LCD controller windows, so these dont give a - damn as to what the monitor specs are (the panel itself does, but that - isnt done here...so maybe need a generic catchall monitor setting??? */ - memcpy(&fbi->monspecs, &panel->monspecs, sizeof(struct fb_monspecs)); - - /* We first try the user mode passed in argument. If that failed, - * or if no one has been specified, we default to the first mode of the - * panel list. Note that after this call, var data will be set */ - if (!fb_find_mode(&fbi->var, - fbi, - NULL, /* drv_info.opt_mode, */ - fbi->monspecs.modedb, - fbi->monspecs.modedb_len, - fbi->monspecs.modedb, - bpp)) { - - print_err("Cannot find valid mode for panel %s", panel->name); - return -EFAULT; - } - - fbi->pseudo_palette = kmalloc(sizeof(u32) * 16, GFP_KERNEL); - if (!fbi->pseudo_palette) { - return -ENOMEM; - } - memset(fbi->pseudo_palette, 0, sizeof(u32) * 16); - - if (fb_alloc_cmap(&fbi->cmap, AU1200_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { - print_err("Fail to allocate colormap (%d entries)", - AU1200_LCD_NBR_PALETTE_ENTRIES); - kfree(fbi->pseudo_palette); - return -EFAULT; - } - - strncpy(fbi->fix.id, "AU1200", sizeof(fbi->fix.id)); - fbi->fix.smem_start = fbdev->fb_phys; - fbi->fix.smem_len = fbdev->fb_len; - fbi->fix.type = FB_TYPE_PACKED_PIXELS; - fbi->fix.xpanstep = 0; - fbi->fix.ypanstep = 0; - fbi->fix.mmio_start = 0; - fbi->fix.mmio_len = 0; - fbi->fix.accel = FB_ACCEL_NONE; - - fbi->screen_base = (char __iomem *) fbdev->fb_mem; - - au1200fb_update_fbinfo(fbi); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -/* AU1200 LCD controller device driver */ - -static int au1200fb_drv_probe(struct device *dev) -{ - struct au1200fb_device *fbdev; - unsigned long page; - int bpp, plane, ret; - - if (!dev) - return -EINVAL; - - for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { - bpp = winbpp(win->w[plane].mode_winctrl1); - if (win->w[plane].xres == 0) - win->w[plane].xres = panel->Xres; - if (win->w[plane].yres == 0) - win->w[plane].yres = panel->Yres; - - fbdev = &_au1200fb_devices[plane]; - memset(fbdev, 0, sizeof(struct au1200fb_device)); - fbdev->plane = plane; - - /* Allocate the framebuffer to the maximum screen size */ - fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8; - - fbdev->fb_mem = dma_alloc_noncoherent(dev, - PAGE_ALIGN(fbdev->fb_len), - &fbdev->fb_phys, GFP_KERNEL); - if (!fbdev->fb_mem) { - print_err("fail to allocate frambuffer (size: %dK))", - fbdev->fb_len / 1024); - return -ENOMEM; - } - - /* - * Set page reserved so that mmap will work. This is necessary - * since we'll be remapping normal memory. - */ - for (page = (unsigned long)fbdev->fb_phys; - page < PAGE_ALIGN((unsigned long)fbdev->fb_phys + - fbdev->fb_len); - page += PAGE_SIZE) { - SetPageReserved(pfn_to_page(page >> PAGE_SHIFT)); /* LCD DMA is NOT coherent on Au1200 */ - } - print_dbg("Framebuffer memory map at %p", fbdev->fb_mem); - print_dbg("phys=0x%08x, size=%dK", fbdev->fb_phys, fbdev->fb_len / 1024); - - /* Init FB data */ - if ((ret = au1200fb_init_fbinfo(fbdev)) < 0) - goto failed; - - /* Register new framebuffer */ - if ((ret = register_framebuffer(&fbdev->fb_info)) < 0) { - print_err("cannot register new framebuffer"); - goto failed; - } - - au1200fb_fb_set_par(&fbdev->fb_info); - -#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO) - if (plane == 0) - if (fb_prepare_logo(&fbdev->fb_info, FB_ROTATE_UR)) { - /* Start display and show logo on boot */ - fb_set_cmap(&fbdev->fb_info.cmap, - &fbdev->fb_info); - - fb_show_logo(&fbdev->fb_info, FB_ROTATE_UR); - } -#endif - } - - /* Now hook interrupt too */ - if ((ret = request_irq(AU1200_LCD_INT, au1200fb_handle_irq, - SA_INTERRUPT | SA_SHIRQ, "lcd", (void *)dev)) < 0) { - print_err("fail to request interrupt line %d (err: %d)", - AU1200_LCD_INT, ret); - goto failed; - } - - return 0; - -failed: - /* NOTE: This only does the current plane/window that failed; others are still active */ - if (fbdev->fb_mem) - dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), - fbdev->fb_mem, fbdev->fb_phys); - if (fbdev->fb_info.cmap.len != 0) - fb_dealloc_cmap(&fbdev->fb_info.cmap); - if (fbdev->fb_info.pseudo_palette) - kfree(fbdev->fb_info.pseudo_palette); - if (plane == 0) - free_irq(AU1200_LCD_INT, (void*)dev); - return ret; -} - -static int au1200fb_drv_remove(struct device *dev) -{ - struct au1200fb_device *fbdev; - int plane; - - if (!dev) - return -ENODEV; - - /* Turn off the panel */ - au1200_setpanel(NULL); - - for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) - { - fbdev = &_au1200fb_devices[plane]; - - /* Clean up all probe data */ - unregister_framebuffer(&fbdev->fb_info); - if (fbdev->fb_mem) - dma_free_noncoherent(dev, PAGE_ALIGN(fbdev->fb_len), - fbdev->fb_mem, fbdev->fb_phys); - if (fbdev->fb_info.cmap.len != 0) - fb_dealloc_cmap(&fbdev->fb_info.cmap); - if (fbdev->fb_info.pseudo_palette) - kfree(fbdev->fb_info.pseudo_palette); - } - - free_irq(AU1200_LCD_INT, (void *)dev); - - return 0; -} - -#ifdef CONFIG_PM -static int au1200fb_drv_suspend(struct device *dev, u32 state, u32 level) -{ - /* TODO */ - return 0; -} - -static int au1200fb_drv_resume(struct device *dev, u32 level) -{ - /* TODO */ - return 0; -} -#endif /* CONFIG_PM */ - -static struct device_driver au1200fb_driver = { - .name = "au1200-lcd", - .bus = &platform_bus_type, - .probe = au1200fb_drv_probe, - .remove = au1200fb_drv_remove, -#ifdef CONFIG_PM - .suspend = au1200fb_drv_suspend, - .resume = au1200fb_drv_resume, -#endif -}; - -/*-------------------------------------------------------------------------*/ - -/* Kernel driver */ - -static void au1200fb_setup(void) -{ - char* options = NULL; - char* this_opt; - int num_panels = ARRAY_SIZE(known_lcd_panels); - int panel_idx = -1; - - fb_get_options(DRIVER_NAME, &options); - - if (options) { - while ((this_opt = strsep(&options,",")) != NULL) { - /* Panel option - can be panel name, - * "bs" for board-switch, or number/index */ - if (!strncmp(this_opt, "panel:", 6)) { - int i; - long int li; - char *endptr; - this_opt += 6; - /* First check for index, which allows - * to short circuit this mess */ - li = simple_strtol(this_opt, &endptr, 0); - if (*endptr == '\0') { - panel_idx = (int)li; - } - else if (strcmp(this_opt, "bs") == 0) { - extern int board_au1200fb_panel(void); - panel_idx = board_au1200fb_panel(); - } - - else - for (i = 0; i < num_panels; i++) { - if (!strcmp(this_opt, known_lcd_panels[i].name)) { - panel_idx = i; - break; - } - } - - if ((panel_idx < 0) || (panel_idx >= num_panels)) { - print_warn("Panel %s not supported!", this_opt); - } - else - panel_index = panel_idx; - } - - else if (strncmp(this_opt, "nohwcursor", 10) == 0) { - nohwcursor = 1; - } - - /* Unsupported option */ - else { - print_warn("Unsupported option \"%s\"", this_opt); - } - } - } -} - -#ifdef CONFIG_PM -static int au1200fb_pm_callback(au1xxx_power_dev_t *dev, - au1xxx_request_t request, void *data) { - int retval = -1; - unsigned int d = 0; - unsigned int brightness = 0; - - if (request == AU1XXX_PM_SLEEP) { - board_au1200fb_panel_shutdown(); - } - else if (request == AU1XXX_PM_WAKEUP) { - if(dev->prev_state == SLEEP_STATE) - { - int plane; - au1200_setpanel(panel); - for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane) { - struct au1200fb_device *fbdev; - fbdev = &_au1200fb_devices[plane]; - au1200fb_fb_set_par(&fbdev->fb_info); - } - } - - d = *((unsigned int*)data); - if(d <=10) brightness = 26; - else if(d<=20) brightness = 51; - else if(d<=30) brightness = 77; - else if(d<=40) brightness = 102; - else if(d<=50) brightness = 128; - else if(d<=60) brightness = 153; - else if(d<=70) brightness = 179; - else if(d<=80) brightness = 204; - else if(d<=90) brightness = 230; - else brightness = 255; - set_brightness(brightness); - } else if (request == AU1XXX_PM_GETSTATUS) { - return dev->cur_state; - } else if (request == AU1XXX_PM_ACCESS) { - if (dev->cur_state != SLEEP_STATE) - return retval; - else { - au1200_setpanel(panel); - } - } else if (request == AU1XXX_PM_IDLE) { - } else if (request == AU1XXX_PM_CLEANUP) { - } - - return retval; -} -#endif - -static int __init au1200fb_init(void) -{ - print_info("" DRIVER_DESC ""); - - /* Setup driver with options */ - au1200fb_setup(); - - /* Point to the panel selected */ - panel = &known_lcd_panels[panel_index]; - win = &windows[window_index]; - - printk(DRIVER_NAME ": Panel %d %s\n", panel_index, panel->name); - printk(DRIVER_NAME ": Win %d %s\n", window_index, win->name); - - /* Kickstart the panel, the framebuffers/windows come soon enough */ - au1200_setpanel(panel); - - #ifdef CONFIG_PM - LCD_pm_dev = new_au1xxx_power_device("LCD", &au1200fb_pm_callback, NULL); - if ( LCD_pm_dev == NULL) - printk(KERN_INFO "Unable to create a power management device entry for the au1200fb.\n"); - else - printk(KERN_INFO "Power management device entry for the au1200fb loaded.\n"); - #endif - - return driver_register(&au1200fb_driver); -} - -static void __exit au1200fb_cleanup(void) -{ - driver_unregister(&au1200fb_driver); -} - -module_init(au1200fb_init); -module_exit(au1200fb_cleanup); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index b72b05250a9..34e07399756 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -305,94 +305,6 @@ static ssize_t show_stride(struct class_device *class_device, char *buf) return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length); } -/* Format for cmap is "%02x%c%4x%4x%4x\n" */ -/* %02x entry %c transp %4x red %4x blue %4x green \n */ -/* 256 rows at 16 chars equals 4096, the normal page size */ -/* the code will automatically adjust for different page sizes */ -static ssize_t store_cmap(struct class_device *class_device, const char *buf, - size_t count) -{ - struct fb_info *fb_info = class_get_devdata(class_device); - int rc, i, start, length, transp = 0; - - if ((count > PAGE_SIZE) || ((count % 16) != 0)) - return -EINVAL; - - if (!fb_info->fbops->fb_setcolreg && !fb_info->fbops->fb_setcmap) - return -EINVAL; - - sscanf(buf, "%02x", &start); - length = count / 16; - - for (i = 0; i < length; i++) - if (buf[i * 16 + 2] != ' ') - transp = 1; - - /* If we can batch, do it */ - if (fb_info->fbops->fb_setcmap && length > 1) { - struct fb_cmap umap; - - memset(&umap, 0, sizeof(umap)); - if ((rc = fb_alloc_cmap(&umap, length, transp))) - return rc; - - umap.start = start; - for (i = 0; i < length; i++) { - sscanf(&buf[i * 16 + 3], "%4hx", &umap.red[i]); - sscanf(&buf[i * 16 + 7], "%4hx", &umap.blue[i]); - sscanf(&buf[i * 16 + 11], "%4hx", &umap.green[i]); - if (transp) - umap.transp[i] = (buf[i * 16 + 2] != ' '); - } - rc = fb_info->fbops->fb_setcmap(&umap, fb_info); - fb_copy_cmap(&umap, &fb_info->cmap); - fb_dealloc_cmap(&umap); - - return rc ?: count; - } - for (i = 0; i < length; i++) { - u16 red, blue, green, tsp; - - sscanf(&buf[i * 16 + 3], "%4hx", &red); - sscanf(&buf[i * 16 + 7], "%4hx", &blue); - sscanf(&buf[i * 16 + 11], "%4hx", &green); - tsp = (buf[i * 16 + 2] != ' '); - if ((rc = fb_info->fbops->fb_setcolreg(start++, - red, green, blue, tsp, fb_info))) - return rc; - - fb_info->cmap.red[i] = red; - fb_info->cmap.blue[i] = blue; - fb_info->cmap.green[i] = green; - if (transp) - fb_info->cmap.transp[i] = tsp; - } - return count; -} - -static ssize_t show_cmap(struct class_device *class_device, char *buf) -{ - struct fb_info *fb_info = class_get_devdata(class_device); - unsigned int i; - - if (!fb_info->cmap.red || !fb_info->cmap.blue || - !fb_info->cmap.green) - return -EINVAL; - - if (fb_info->cmap.len > PAGE_SIZE / 16) - return -EINVAL; - - /* don't mess with the format, the buffer is PAGE_SIZE */ - /* 256 entries at 16 chars per line equals 4096 = PAGE_SIZE */ - for (i = 0; i < fb_info->cmap.len; i++) { - snprintf(&buf[ i * 16], PAGE_SIZE - i * 16, "%02x%c%4x%4x%4x\n", i + fb_info->cmap.start, - ((fb_info->cmap.transp && fb_info->cmap.transp[i]) ? '*' : ' '), - fb_info->cmap.red[i], fb_info->cmap.blue[i], - fb_info->cmap.green[i]); - } - return 16 * fb_info->cmap.len; -} - static ssize_t store_blank(struct class_device *class_device, const char * buf, size_t count) { @@ -502,10 +414,12 @@ static ssize_t show_fbstate(struct class_device *class_device, char *buf) return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state); } +/* When cmap is added back in it should be a binary attribute + * not a text one. Consideration should also be given to converting + * fbdev to use configfs instead of sysfs */ static struct class_device_attribute class_device_attrs[] = { __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp), __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank), - __ATTR(color_map, S_IRUGO|S_IWUSR, show_cmap, store_cmap), __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console), __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor), __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode), diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile index 4ef5cd19609..b985dfad6c6 100644 --- a/drivers/video/logo/Makefile +++ b/drivers/video/logo/Makefile @@ -34,7 +34,7 @@ extra-y += $(call logo-cfiles,_clut224,ppm) extra-y += $(call logo-cfiles,_gray256,pgm) # Create commands like "pnmtologo -t mono -n logo_mac_mono -o ..." -quiet_cmd_logo = LOGO $@ +quiet_cmd_logo = LOGO $@ cmd_logo = scripts/pnmtologo \ -t $(patsubst $*_%,%,$(notdir $(basename $<))) \ -n $(notdir $(basename $<)) -o $@ $< |