From dd59b8537f6cb53ab863fafad86a5828f1e889a2 Mon Sep 17 00:00:00 2001 From: Yuri Tikhonov Date: Mon, 12 Jan 2009 15:17:20 -0700 Subject: dmaengine: fix dependency chaining In dmaengine we track the dependencies between the descriptors using the 'next' pointers of the structure. These pointers are set to NULL as soon as the corresponding descriptor has been submitted to the channel (in dma_run_dependencies()). But, the first 'next' in chain is still remaining set, regardless the fact, that tx->next has been already submitted. This may lead to multiple submissions of the same descriptor. This patch fixes this. Actually, some previous implementation of the xxx_run_dependencies() function already had this fix in place. The fdb..0eaf3 commit, beside the correct things, broke this. Cc: Signed-off-by: Yuri Tikhonov Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 403dbe78112..6df144a65fe 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -961,6 +961,8 @@ void dma_run_dependencies(struct dma_async_tx_descriptor *tx) if (!dep) return; + /* we'll submit tx->next now, so clear the link */ + tx->next = NULL; chan = dep->chan; /* keep submitting up until a channel switch is detected -- cgit v1.2.3 From 6527de6d6d25ebfae7c7572cb7a4ed768e2e20a5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 12 Jan 2009 15:18:34 -0700 Subject: fsldma: use a valid 'device' for dma_pool_create The dmaengine sysfs implementation was fixed to support proper lifetime rules which means that the current: new_fsl_chan->dev = &new_fsl_chan->common.dev->device; ...retrieves a NULL pointer because new_fsl_chan->common.dev has not been allocated at this point. So, set new_fsl_chan->dev to a valid device. Cc: Li Yang Cc: Zhang Wei Reported-by: Ira Snyder Tested-by: Ira Snyder Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index ca70a21afc6..748e140c5a1 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -822,7 +822,7 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev, */ WARN_ON(fdev->feature != new_fsl_chan->feature); - new_fsl_chan->dev = &new_fsl_chan->common.dev->device; + new_fsl_chan->dev = fdev->dev; new_fsl_chan->reg_base = ioremap(new_fsl_chan->reg.start, new_fsl_chan->reg.end - new_fsl_chan->reg.start + 1); -- cgit v1.2.3 From d86be86e9aab221089d72399072511f13fe2a771 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Tue, 13 Jan 2009 09:22:20 -0700 Subject: dmatest: Use custom map/unmap for destination buffer The dmatest driver should use DMA_BIDIRECTIONAL on the destination buffer to ensure that the poison values are written to RAM and not just written to cache and discarded. Acked-by: Haavard Skinnemoen Acked-by: Maciej Sosnowski Signed-off-by: Atsushi Nemoto Signed-off-by: Dan Williams --- drivers/dma/dmatest.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 3603f1ea5b2..732fa1ec36a 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -217,6 +217,10 @@ static int dmatest_func(void *data) chan = thread->chan; while (!kthread_should_stop()) { + struct dma_device *dev = chan->device; + struct dma_async_tx_descriptor *tx; + dma_addr_t dma_src, dma_dest; + total_tests++; len = dmatest_random() % test_buf_size + 1; @@ -226,10 +230,30 @@ static int dmatest_func(void *data) dmatest_init_srcbuf(thread->srcbuf, src_off, len); dmatest_init_dstbuf(thread->dstbuf, dst_off, len); - cookie = dma_async_memcpy_buf_to_buf(chan, - thread->dstbuf + dst_off, - thread->srcbuf + src_off, - len); + dma_src = dma_map_single(dev->dev, thread->srcbuf + src_off, + len, DMA_TO_DEVICE); + /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */ + dma_dest = dma_map_single(dev->dev, thread->dstbuf, + test_buf_size, DMA_BIDIRECTIONAL); + + tx = dev->device_prep_dma_memcpy(chan, dma_dest + dst_off, + dma_src, len, + DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP); + if (!tx) { + dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE); + dma_unmap_single(dev->dev, dma_dest, + test_buf_size, DMA_BIDIRECTIONAL); + pr_warning("%s: #%u: prep error with src_off=0x%x " + "dst_off=0x%x len=0x%x\n", + thread_name, total_tests - 1, + src_off, dst_off, len); + msleep(100); + failed_tests++; + continue; + } + tx->callback = NULL; + cookie = tx->tx_submit(tx); + if (dma_submit_error(cookie)) { pr_warning("%s: #%u: submit error %d with src_off=0x%x " "dst_off=0x%x len=0x%x\n", @@ -253,6 +277,9 @@ static int dmatest_func(void *data) failed_tests++; continue; } + /* Unmap by myself (see DMA_COMPL_SKIP_DEST_UNMAP above) */ + dma_unmap_single(dev->dev, dma_dest, + test_buf_size, DMA_BIDIRECTIONAL); error_count = 0; -- cgit v1.2.3 From 6782dfe44acedf1e583d84e9e0d4f966d8e9befa Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 14 Jan 2009 22:32:58 -0700 Subject: fsldma: check for NO_IRQ in fsl_dma_chan_remove() There's no per-channel IRQ on mpc83xx, so only call free_irq if we have one. Acked-by: Timur Tabi Acked-by: Li Yang Signed-off-by: Peter Korsgaard Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 748e140c5a1..b1b45eb42cb 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -890,7 +890,8 @@ err_no_reg: static void fsl_dma_chan_remove(struct fsl_dma_chan *fchan) { - free_irq(fchan->irq, fchan); + if (fchan->irq != NO_IRQ) + free_irq(fchan->irq, fchan); list_del(&fchan->common.device_node); iounmap(fchan->reg_base); kfree(fchan); -- cgit v1.2.3 From 73069e388d0f2509e45e1a58b0facca99ef2446e Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Thu, 15 Jan 2009 13:09:52 +0200 Subject: ARM: OMAP: Fix gpio by switching to generic gpio calls, v2 Fix compile by removing remaining omap specific gpio calls. Based on earlier patches by Jarkko Nikula. Also remove old GPIO key code, there is already a patch to do this with gpio_keys. Signed-off-by: Jarkko Nikula Signed-off-by: Tony Lindgren --- drivers/mtd/onenand/omap2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 96ecc1766fa..77a4f144615 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -629,7 +629,7 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) } if (c->gpio_irq) { - if ((r = omap_request_gpio(c->gpio_irq)) < 0) { + if ((r = gpio_request(c->gpio_irq, "OneNAND irq")) < 0) { dev_err(&pdev->dev, "Failed to request GPIO%d for " "OneNAND\n", c->gpio_irq); goto err_iounmap; @@ -726,7 +726,7 @@ err_release_dma: free_irq(gpio_to_irq(c->gpio_irq), c); err_release_gpio: if (c->gpio_irq) - omap_free_gpio(c->gpio_irq); + gpio_free(c->gpio_irq); err_iounmap: iounmap(c->onenand.base); err_release_mem_region: @@ -761,7 +761,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); if (c->gpio_irq) { free_irq(gpio_to_irq(c->gpio_irq), c); - omap_free_gpio(c->gpio_irq); + gpio_free(c->gpio_irq); } iounmap(c->onenand.base); release_mem_region(c->phys_base, ONENAND_IO_SIZE); -- cgit v1.2.3 From 169d5f663759ec494aa74a552ce99486235e6e50 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 14 Jan 2009 22:33:31 -0700 Subject: fsldma: print correct IRQ on mpc83xx The mpc83xx variant uses a shared IRQ for all channels, so the individual channel nodes don't have an interrupt property. Fix the code to print the controller IRQ instead if there isn't any for the channel. Acked-by: Timur Tabi Acked-by: Li Yang Signed-off-by: Peter Korsgaard Signed-off-by: Dan Williams --- drivers/dma/fsldma.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index b1b45eb42cb..70126a60623 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -875,7 +875,8 @@ static int __devinit fsl_dma_chan_probe(struct fsl_dma_device *fdev, } dev_info(fdev->dev, "#%d (%s), irq %d\n", new_fsl_chan->id, - compatible, new_fsl_chan->irq); + compatible, + new_fsl_chan->irq != NO_IRQ ? new_fsl_chan->irq : fdev->irq); return 0; -- cgit v1.2.3 From 0db29af1e767464d71b89410d61a1e5b668d0370 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Wed, 24 Dec 2008 17:27:04 +0900 Subject: PCI/MSI: bugfix/utilize for msi_capability_init() This patch fix a following bug and does a cleanup. bug: commit 5993760f7fc75b77e4701f1e56dc84c0d6cf18d5 had a wrong change (since is_64 is boolean[0|1]): - pci_write_config_dword(dev, - msi_mask_bits_reg(pos, is_64bit_address(control)), - maskbits); + pci_write_config_dword(dev, entry->msi_attrib.is_64, maskbits); utilize: Unify separated if (entry->msi_attrib.maskbit) statements. Signed-off-by: Hidetoshi Seto Acked-by: "Jike Song" Cc: stable@vger.kernel.org Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index b4a90badd0a..896a15d70f5 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -398,21 +398,19 @@ static int msi_capability_init(struct pci_dev *dev) entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.pos = pos; - if (entry->msi_attrib.maskbit) { - entry->mask_base = (void __iomem *)(long)msi_mask_bits_reg(pos, - entry->msi_attrib.is_64); - } entry->dev = dev; if (entry->msi_attrib.maskbit) { - unsigned int maskbits, temp; + unsigned int base, maskbits, temp; + + base = msi_mask_bits_reg(pos, entry->msi_attrib.is_64); + entry->mask_base = (void __iomem *)(long)base; + /* All MSIs are unmasked by default, Mask them all */ - pci_read_config_dword(dev, - msi_mask_bits_reg(pos, entry->msi_attrib.is_64), - &maskbits); + pci_read_config_dword(dev, base, &maskbits); temp = (1 << multi_msi_capable(control)); temp = ((temp - 1) & ~temp); maskbits |= temp; - pci_write_config_dword(dev, entry->msi_attrib.is_64, maskbits); + pci_write_config_dword(dev, base, maskbits); entry->msi_attrib.maskbits_mask = temp; } list_add_tail(&entry->list, &dev->msi_list); -- cgit v1.2.3 From aa8c6c93747f7b55fa11e1624fec8ca33763a805 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 16 Jan 2009 21:54:43 +0100 Subject: PCI PM: Restore standard config registers of all devices early There is a problem in our handling of suspend-resume of PCI devices that many of them have their standard config registers restored with interrupts enabled and they are put into the full power state with interrupts enabled as well. This may lead to the following scenario: * an interrupt vector is shared between two or more devices * one device is resumed earlier and generates an interrupt * the interrupt handler of another device tries to handle it and attempts to access the device the config space of which hasn't been restored yet and/or which still is in a low power state * the system crashes as a result To prevent this from happening we should restore the standard configuration registers of all devices with interrupts disabled and we should put them into the D0 power state right after that. Unfortunately, this cannot be done using the existing pci_set_power_state(), because it can sleep. Also, to do it we have to make sure that the config spaces of all devices were actually saved during suspend. Signed-off-by: Rafael J. Wysocki Acked-by: Linus Torvalds Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 91 ++++++++++++++---------------------------------- drivers/pci/pci.c | 63 +++++++++++++++++++++++++++++---- drivers/pci/pci.h | 6 ++++ 3 files changed, 90 insertions(+), 70 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c697f268085..9de07b75b99 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -355,17 +355,27 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) int i = 0; if (drv && drv->suspend) { + pci_dev->state_saved = false; + i = drv->suspend(pci_dev, state); suspend_report_result(drv->suspend, i); - } else { - pci_save_state(pci_dev); - /* - * This is for compatibility with existing code with legacy PM - * support. - */ - pci_pm_set_unknown_state(pci_dev); + if (i) + return i; + + if (pci_dev->state_saved) + goto Fixup; + + if (WARN_ON_ONCE(pci_dev->current_state != PCI_D0)) + goto Fixup; } + pci_save_state(pci_dev); + /* + * This is for compatibility with existing code with legacy PM support. + */ + pci_pm_set_unknown_state(pci_dev); + + Fixup: pci_fixup_device(pci_fixup_suspend, pci_dev); return i; @@ -386,81 +396,34 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) static int pci_legacy_resume_early(struct device *dev) { - int error = 0; struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_fixup_device(pci_fixup_resume_early, pci_dev); - - if (drv && drv->resume_early) - error = drv->resume_early(pci_dev); - return error; + return drv && drv->resume_early ? + drv->resume_early(pci_dev) : 0; } static int pci_legacy_resume(struct device *dev) { - int error; struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; pci_fixup_device(pci_fixup_resume, pci_dev); - if (drv && drv->resume) { - error = drv->resume(pci_dev); - } else { - /* restore the PCI config space */ - pci_restore_state(pci_dev); - error = pci_pm_reenable_device(pci_dev); - } - return error; + return drv && drv->resume ? + drv->resume(pci_dev) : pci_pm_reenable_device(pci_dev); } /* Auxiliary functions used by the new power management framework */ -static int pci_restore_standard_config(struct pci_dev *pci_dev) -{ - struct pci_dev *parent = pci_dev->bus->self; - int error = 0; - - /* Check if the device's bus is operational */ - if (!parent || parent->current_state == PCI_D0) { - pci_restore_state(pci_dev); - pci_update_current_state(pci_dev, PCI_D0); - } else { - dev_warn(&pci_dev->dev, "unable to restore config, " - "bridge %s in low power state D%d\n", pci_name(parent), - parent->current_state); - pci_dev->current_state = PCI_UNKNOWN; - error = -EAGAIN; - } - - return error; -} - -static bool pci_is_bridge(struct pci_dev *pci_dev) -{ - return !!(pci_dev->subordinate); -} - static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) { - if (pci_restore_standard_config(pci_dev)) - pci_fixup_device(pci_fixup_resume_early, pci_dev); + pci_restore_standard_config(pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); } static int pci_pm_default_resume(struct pci_dev *pci_dev) { - /* - * pci_restore_standard_config() should have been called once already, - * but it would have failed if the device's parent bridge had not been - * in power state D0 at that time. Check it and try again if necessary. - */ - if (pci_dev->current_state == PCI_UNKNOWN) { - int error = pci_restore_standard_config(pci_dev); - if (error) - return error; - } - pci_fixup_device(pci_fixup_resume, pci_dev); if (!pci_is_bridge(pci_dev)) @@ -575,11 +538,11 @@ static int pci_pm_resume_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + pci_pm_default_resume_noirq(pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - pci_pm_default_resume_noirq(pci_dev); - if (drv && drv->pm && drv->pm->resume_noirq) error = drv->pm->resume_noirq(dev); @@ -730,11 +693,11 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + pci_pm_default_resume_noirq(pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - pci_pm_default_resume_noirq(pci_dev); - if (drv && drv->pm && drv->pm->restore_noirq) error = drv->pm->restore_noirq(dev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e491fdedf70..17bd9325a24 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -22,7 +22,7 @@ #include /* isa_dma_bridge_buggy */ #include "pci.h" -unsigned int pci_pm_d3_delay = 10; +unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; #ifdef CONFIG_PCI_DOMAINS int pci_domains_supported = 1; @@ -426,6 +426,7 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) * given PCI device * @dev: PCI device to handle. * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. + * @wait: If 'true', wait for the device to change its power state * * RETURN VALUE: * -EINVAL if the requested state is invalid. @@ -435,7 +436,7 @@ static inline int platform_pci_sleep_wake(struct pci_dev *dev, bool enable) * 0 if device's power state has been successfully changed. */ static int -pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) +pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state, bool wait) { u16 pmcsr; bool need_restore = false; @@ -480,8 +481,10 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) break; case PCI_UNKNOWN: /* Boot-up */ if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot - && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) + && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) { need_restore = true; + wait = true; + } /* Fall-through: force to D0 */ default: pmcsr = 0; @@ -491,12 +494,15 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) /* enter specified state */ pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); + if (!wait) + return 0; + /* Mandatory power management transition delays */ /* see PCI PM 1.1 5.6.1 table 18 */ if (state == PCI_D3hot || dev->current_state == PCI_D3hot) msleep(pci_pm_d3_delay); else if (state == PCI_D2 || dev->current_state == PCI_D2) - udelay(200); + udelay(PCI_PM_D2_DELAY); dev->current_state = state; @@ -515,7 +521,7 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) if (need_restore) pci_restore_bars(dev); - if (dev->bus->self) + if (wait && dev->bus->self) pcie_aspm_pm_state_change(dev->bus->self); return 0; @@ -585,7 +591,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (state == PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3)) return 0; - error = pci_raw_set_power_state(dev, state); + error = pci_raw_set_power_state(dev, state, true); if (state > PCI_D0 && platform_pci_power_manageable(dev)) { /* Allow the platform to finalize the transition */ @@ -730,6 +736,7 @@ pci_save_state(struct pci_dev *dev) /* XXX: 100% dword access ok here? */ for (i = 0; i < 16; i++) pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]); + dev->state_saved = true; if ((i = pci_save_pcie_state(dev)) != 0) return i; if ((i = pci_save_pcix_state(dev)) != 0) @@ -1373,6 +1380,50 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) "unable to preallocate PCI-X save buffer\n"); } +/** + * pci_restore_standard_config - restore standard config registers of PCI device + * @dev: PCI device to handle + * + * This function assumes that the device's configuration space is accessible. + * If the device needs to be powered up, the function will wait for it to + * change the state. + */ +int pci_restore_standard_config(struct pci_dev *dev) +{ + pci_power_t prev_state; + int error; + + pci_restore_state(dev); + pci_update_current_state(dev, PCI_D0); + + prev_state = dev->current_state; + if (prev_state == PCI_D0) + return 0; + + error = pci_raw_set_power_state(dev, PCI_D0, false); + if (error) + return error; + + if (pci_is_bridge(dev)) { + if (prev_state > PCI_D1) + mdelay(PCI_PM_BUS_WAIT); + } else { + switch(prev_state) { + case PCI_D3cold: + case PCI_D3hot: + mdelay(pci_pm_d3_delay); + break; + case PCI_D2: + udelay(PCI_PM_D2_DELAY); + break; + } + } + + dev->current_state = PCI_D0; + + return 0; +} + /** * pci_enable_ari - enable ARI forwarding if hardware support it * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1351bb4addd..26ddf78ac30 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -49,6 +49,12 @@ extern void pci_disable_enabled_device(struct pci_dev *dev); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); +extern int pci_restore_standard_config(struct pci_dev *dev); + +static inline bool pci_is_bridge(struct pci_dev *pci_dev) +{ + return !!(pci_dev->subordinate); +} extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); -- cgit v1.2.3 From ef15aa490f2e447ce04fe643500b814ef40f6ea9 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 9 Jan 2009 21:06:30 +0100 Subject: p54: fix oops caused by bad eeproms This patch fixes a bug that could occur, if it the eeprom is incomplete or partly corrupted. BUG: unable to handle kernel NULL pointer dereference at 00000008 IP: p54_assign_address+0x108/0x15d [p54common] Oops: 0002 [#1] SMP Pid: 12988, comm: phy1 Tainted: P W 2.6.28-rc6-wl #3 RIP: 0010: p54_assign_address+0x108/0x15d [p54common] [...] Call Trace: p54_alloc_skb+0xa3/0xc0 [p54common] p54_scan+0x37/0x204 [p54common] [...] Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index c6a370fa9bc..3b44e8e7603 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -1610,7 +1610,7 @@ static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell) err: printk(KERN_ERR "%s: frequency change failed\n", wiphy_name(dev->wiphy)); - kfree_skb(skb); + p54_free_skb(dev, skb); return -EINVAL; } -- cgit v1.2.3 From d71038c05970ad0c9d7da6f797803f69e4f91837 Mon Sep 17 00:00:00 2001 From: Andrey Yurovsky Date: Mon, 12 Jan 2009 13:14:27 -0800 Subject: libertas: Fix alignment issues in libertas core Data structures that come over the wire from the WLAN firmware must be packed. This fixes alignment problems on the blackfin architecture and, reportedly, on the AVR32. This is a replacement for the previous version of this patch which had also explicitly used get_unaligned_ macros. As Johannes Berg pointed out, these macros were unnecessary. Signed-off-by: Andrey Yurovsky Signed-off-by: Colin McCabe Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/hostcmd.h | 91 ++++++++++++++++----------------- 1 file changed, 45 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h index e173b1b46c2..f6a79a653b7 100644 --- a/drivers/net/wireless/libertas/hostcmd.h +++ b/drivers/net/wireless/libertas/hostcmd.h @@ -32,7 +32,7 @@ struct txpd { u8 pktdelay_2ms; /* reserved */ u8 reserved1; -}; +} __attribute__ ((packed)); /* RxPD Descriptor */ struct rxpd { @@ -63,7 +63,7 @@ struct rxpd { /* Pkt Priority */ u8 priority; u8 reserved[3]; -}; +} __attribute__ ((packed)); struct cmd_header { __le16 command; @@ -97,7 +97,7 @@ struct enc_key { struct lbs_offset_value { u32 offset; u32 value; -}; +} __attribute__ ((packed)); /* Define general data structure */ /* cmd_DS_GEN */ @@ -107,7 +107,7 @@ struct cmd_ds_gen { __le16 seqnum; __le16 result; void *cmdresp[0]; -}; +} __attribute__ ((packed)); #define S_DS_GEN sizeof(struct cmd_ds_gen) @@ -163,7 +163,7 @@ struct cmd_ds_802_11_subscribe_event { * bump this up a bit. */ uint8_t tlv[128]; -}; +} __attribute__ ((packed)); /* * This scan handle Country Information IE(802.11d compliant) @@ -180,7 +180,7 @@ struct cmd_ds_802_11_scan { mrvlietypes_chanlistparamset_t ChanListParamSet; mrvlietypes_ratesparamset_t OpRateSet; #endif -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_scan_rsp { struct cmd_header hdr; @@ -188,7 +188,7 @@ struct cmd_ds_802_11_scan_rsp { __le16 bssdescriptsize; uint8_t nr_sets; uint8_t bssdesc_and_tlvbuffer[0]; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_get_log { struct cmd_header hdr; @@ -206,33 +206,33 @@ struct cmd_ds_802_11_get_log { __le32 fcserror; __le32 txframe; __le32 wepundecryptable; -}; +} __attribute__ ((packed)); struct cmd_ds_mac_control { struct cmd_header hdr; __le16 action; u16 reserved; -}; +} __attribute__ ((packed)); struct cmd_ds_mac_multicast_adr { struct cmd_header hdr; __le16 action; __le16 nr_of_adrs; u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_authenticate { u8 macaddr[ETH_ALEN]; u8 authtype; u8 reserved[10]; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_deauthenticate { struct cmd_header hdr; u8 macaddr[ETH_ALEN]; __le16 reasoncode; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_associate { u8 peerstaaddr[6]; @@ -251,7 +251,7 @@ struct cmd_ds_802_11_associate { struct cmd_ds_802_11_associate_rsp { struct ieeetypes_assocrsp assocRsp; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_set_wep { struct cmd_header hdr; @@ -265,7 +265,7 @@ struct cmd_ds_802_11_set_wep { /* 40, 128bit or TXWEP */ uint8_t keytype[4]; uint8_t keymaterial[4][16]; -}; +} __attribute__ ((packed)); struct cmd_ds_802_3_get_stat { __le32 xmitok; @@ -274,7 +274,7 @@ struct cmd_ds_802_3_get_stat { __le32 rcverror; __le32 rcvnobuffer; __le32 rcvcrcerror; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_get_stat { __le32 txfragmentcnt; @@ -294,7 +294,7 @@ struct cmd_ds_802_11_get_stat { __le32 txbeacon; __le32 rxbeacon; __le32 wepundecryptable; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_snmp_mib { struct cmd_header hdr; @@ -303,58 +303,58 @@ struct cmd_ds_802_11_snmp_mib { __le16 oid; __le16 bufsize; u8 value[128]; -}; +} __attribute__ ((packed)); struct cmd_ds_mac_reg_map { __le16 buffersize; u8 regmap[128]; __le16 reserved; -}; +} __attribute__ ((packed)); struct cmd_ds_bbp_reg_map { __le16 buffersize; u8 regmap[128]; __le16 reserved; -}; +} __attribute__ ((packed)); struct cmd_ds_rf_reg_map { __le16 buffersize; u8 regmap[64]; __le16 reserved; -}; +} __attribute__ ((packed)); struct cmd_ds_mac_reg_access { __le16 action; __le16 offset; __le32 value; -}; +} __attribute__ ((packed)); struct cmd_ds_bbp_reg_access { __le16 action; __le16 offset; u8 value; u8 reserved[3]; -}; +} __attribute__ ((packed)); struct cmd_ds_rf_reg_access { __le16 action; __le16 offset; u8 value; u8 reserved[3]; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_radio_control { struct cmd_header hdr; __le16 action; __le16 control; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_beacon_control { __le16 action; __le16 beacon_enable; __le16 beacon_period; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_sleep_params { struct cmd_header hdr; @@ -379,7 +379,7 @@ struct cmd_ds_802_11_sleep_params { /* reserved field, should be set to zero */ __le16 reserved; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_inactivity_timeout { struct cmd_header hdr; @@ -389,7 +389,7 @@ struct cmd_ds_802_11_inactivity_timeout { /* Inactivity timeout in msec */ __le16 timeout; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_rf_channel { struct cmd_header hdr; @@ -399,7 +399,7 @@ struct cmd_ds_802_11_rf_channel { __le16 rftype; /* unused */ __le16 reserved; /* unused */ u8 channellist[32]; /* unused */ -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_rssi { /* weighting factor */ @@ -408,21 +408,21 @@ struct cmd_ds_802_11_rssi { __le16 reserved_0; __le16 reserved_1; __le16 reserved_2; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_rssi_rsp { __le16 SNR; __le16 noisefloor; __le16 avgSNR; __le16 avgnoisefloor; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_mac_address { struct cmd_header hdr; __le16 action; u8 macadd[ETH_ALEN]; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_rf_tx_power { struct cmd_header hdr; @@ -431,7 +431,7 @@ struct cmd_ds_802_11_rf_tx_power { __le16 curlevel; s8 maxlevel; s8 minlevel; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_rf_antenna { __le16 action; @@ -439,33 +439,33 @@ struct cmd_ds_802_11_rf_antenna { /* Number of antennas or 0xffff(diversity) */ __le16 antennamode; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_monitor_mode { __le16 action; __le16 mode; -}; +} __attribute__ ((packed)); struct cmd_ds_set_boot2_ver { struct cmd_header hdr; __le16 action; __le16 version; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_fw_wake_method { struct cmd_header hdr; __le16 action; __le16 method; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_sleep_period { struct cmd_header hdr; __le16 action; __le16 period; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_ps_mode { __le16 action; @@ -473,7 +473,7 @@ struct cmd_ds_802_11_ps_mode { __le16 multipledtim; __le16 reserved; __le16 locallisteninterval; -}; +} __attribute__ ((packed)); struct cmd_confirm_sleep { struct cmd_header hdr; @@ -483,7 +483,7 @@ struct cmd_confirm_sleep { __le16 multipledtim; __le16 reserved; __le16 locallisteninterval; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_data_rate { struct cmd_header hdr; @@ -491,14 +491,14 @@ struct cmd_ds_802_11_data_rate { __le16 action; __le16 reserved; u8 rates[MAX_RATES]; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_rate_adapt_rateset { struct cmd_header hdr; __le16 action; __le16 enablehwauto; __le16 bitmap; -}; +} __attribute__ ((packed)); struct cmd_ds_802_11_ad_hoc_start { struct cmd_header hdr; @@ -520,7 +520,7 @@ struct cmd_ds_802_11_ad_hoc_result { u8 pad[3]; u8 bssid[ETH_ALEN]; -}; +} __attribute__ ((packed)); struct adhoc_bssdesc { u8 bssid[ETH_ALEN]; @@ -578,7 +578,7 @@ struct MrvlIEtype_keyParamSet { /* key material of size keylen */ u8 key[32]; -}; +} __attribute__ ((packed)); #define MAX_WOL_RULES 16 @@ -590,7 +590,7 @@ struct host_wol_rule { __le16 reserve; __be32 sig_mask; __be32 signature; -}; +} __attribute__ ((packed)); struct wol_config { uint8_t action; @@ -598,8 +598,7 @@ struct wol_config { uint8_t no_rules_in_cmd; uint8_t result; struct host_wol_rule rule[MAX_WOL_RULES]; -}; - +} __attribute__ ((packed)); struct cmd_ds_host_sleep { struct cmd_header hdr; -- cgit v1.2.3 From b657eade2f98b5c689e405bd6e4e445471066380 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 13 Jan 2009 14:33:49 +0200 Subject: ath9k: Fix an operator typo in phy rate validation This was not supposed to be a bitwise AND operation, but a check of two separate conditions. Anyway, the old code happened to result in the same behavior, so this is just changing the code to be easier to understand and also to keep sparse from warning about dubious operators. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/rc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c index 04ab457a8fa..1b71b934bb5 100644 --- a/drivers/net/wireless/ath9k/rc.c +++ b/drivers/net/wireless/ath9k/rc.c @@ -490,7 +490,7 @@ static inline int ath_rc_get_nextvalid_txrate(struct ath_rate_table *rate_table, static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw) { - if (WLAN_RC_PHY_HT(phy) & !(capflag & WLAN_RC_HT_FLAG)) + if (WLAN_RC_PHY_HT(phy) && !(capflag & WLAN_RC_HT_FLAG)) return 0; if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG)) return 0; -- cgit v1.2.3 From 9d97f2e55e3df44e3b6b4cc58b091501ba7ee0ac Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 13 Jan 2009 14:35:08 +0200 Subject: ath9k: Fix an operator typo in REG_DOMAIN_2GHZ_MASK Incorrect operator causes the REG_DOMAIN_2GHZ_MASK to be zero which surely was not the goal of this definition. Mask out the 11a flags correctly. Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/regd_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath9k/regd_common.h b/drivers/net/wireless/ath9k/regd_common.h index 9112c030b1e..6df1b3b77c2 100644 --- a/drivers/net/wireless/ath9k/regd_common.h +++ b/drivers/net/wireless/ath9k/regd_common.h @@ -228,7 +228,7 @@ enum { }; #define REG_DOMAIN_2GHZ_MASK (REQ_MASK & \ - (!(ADHOC_NO_11A | DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB))) + (~(ADHOC_NO_11A | DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB))) #define REG_DOMAIN_5GHZ_MASK REQ_MASK static struct reg_dmn_pair_mapping regDomainPairs[] = { -- cgit v1.2.3 From 73e1a65d3c4a013f6fa56e47133be95143a75fe3 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Thu, 8 Jan 2009 10:19:58 -0800 Subject: iwlwifi: remove CMD_WANT_SKB flag if send_cmd_sync failure In function iwl_send_cmd_sync(), if the flag CMD_WANT_SKB is set but we are not provided with a valid SKB (cmd->meta.u.skb == NULL), we need to remove the CMD_WANT_SKB flag from the TX cmd queue. Otherwise in case the cmd comes in later, it will possibly set an invalid address. Thus it causes an invalid memory access. This fixed the bug http://bugzilla.kernel.org/show_bug.cgi?id=11326. Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-hcmd.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 8c71ad4f88c..4b35b30e493 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -224,7 +224,7 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) IWL_ERROR("Error: Response NULL in '%s'\n", get_cmd_string(cmd->id)); ret = -EIO; - goto out; + goto cancel; } ret = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index d64580805d6..15f5655c636 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -745,7 +745,7 @@ static int iwl3945_send_cmd_sync(struct iwl3945_priv *priv, struct iwl3945_host_ IWL_ERROR("Error: Response NULL in '%s'\n", get_cmd_string(cmd->id)); ret = -EIO; - goto out; + goto cancel; } ret = 0; -- cgit v1.2.3 From e223b6dc051ad030a70d5c6ed6226b95bdfc3af7 Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Wed, 14 Jan 2009 00:00:13 +0200 Subject: rt2x00: fix a wrong parameter for __test_and_clear_bit() in rt2x00rfkill_free(). When running modprobe rt73usb, and then rmmod rt73usb, and then iwconfig, the wlan0 device does not disappear. When repeating this process again, we get a kernel Oops errors and "BUG: unable to handle kernel paging request..." message in the kernel log. The reason for this is that there is an error in rt2x00rfkill_free(), which is called in the process of removing the device (rt2x00lib_remove_dev() in rt2x00dev.c). rt2x00rfkill_free() clears the RFKILL_STATE_ALLOCATED bit , which is bit number 1 () in rt2x00dev->flags instead of in rt2x00dev->rfkill_state. As a result, when checking the DEVICE_STATE_REGISTERED_HW bit (bit number 1 in rt2x00dev->flags) in rt2x00lib_remove_hw() it is **unset**, and we wrongly **don't** call ieee80211_unregister_hw(). This patch corrects this: the parameter for __test_and_clear_bit() in rt2x00rfkill_free() should be &rt2x00dev->rfkill_state and not &rt2x00dev->flags. Signed-off-by: Rami Rosen Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00rfkill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2x00rfkill.c b/drivers/net/wireless/rt2x00/rt2x00rfkill.c index c3f53a92180..3298cae1e12 100644 --- a/drivers/net/wireless/rt2x00/rt2x00rfkill.c +++ b/drivers/net/wireless/rt2x00/rt2x00rfkill.c @@ -162,7 +162,7 @@ void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev) void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev) { - if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->flags)) + if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state)) return; cancel_delayed_work_sync(&rt2x00dev->rfkill_work); -- cgit v1.2.3 From 275719089bfe7dbf446b72c3e520966e7fa42b6a Mon Sep 17 00:00:00 2001 From: Artur Skawina Date: Thu, 15 Jan 2009 21:07:03 +0100 Subject: p54: set_tim must be atomic. Fix for: BUG: scheduling while atomic: named/2004/0x10000200 Pid: 2004, comm: named Not tainted 2.6.29-rc1-00271-ge9fa6b0 #45 Call Trace: [] schedule+0x2a7/0x320 [] __alloc_skb+0x34/0x110 [] __cond_resched+0x13/0x30 [] _cond_resched+0x2d/0x40 [] kmem_cache_alloc+0x95/0xc0 [] check_object+0xc4/0x230 [] __alloc_skb+0x34/0x110 [] p54_alloc_skb+0x71/0xf0 [] p54_set_tim+0x3f/0xa0 [] sta_info_set_tim_bit+0x64/0x80 [] invoke_tx_handlers+0xd57/0xd80 [] free_debug_processing+0x197/0x210 [] pskb_expand_head+0xf5/0x170 [] __ieee80211_tx_prepare+0x164/0x2f0 [] ieee80211_skb_resize+0x6d/0xe0 [] ieee80211_master_start_xmit+0x23f/0x550 [] __slab_alloc+0x2b8/0x4f0 [] getnstimeofday+0x51/0x120 [] dev_hard_start_xmit+0x1db/0x240 [] __qdisc_run+0x1ab/0x200 [] __run_hrtimer+0x31/0xf0 [] dev_queue_xmit+0x247/0x500 [] ieee80211_subif_start_xmit+0x356/0x7d0 [] packet_rcv_spkt+0x37/0x150 [] packet_rcv_spkt+0x37/0x150 [] dev_hard_start_xmit+0x1db/0x240 [] __qdisc_run+0x1ab/0x200 [] dev_queue_xmit+0x247/0x500 [] neigh_resolve_output+0xe2/0x200 [] ip_finish_output+0x0/0x290 [] ip_finish_output+0x1e7/0x290 [] ip_local_out+0x15/0x20 [] ip_push_pending_frames+0x272/0x380 [] udp_push_pending_frames+0x146/0x3a0 [] udp_sendmsg+0x2fa/0x6b0 [] inet_sendmsg+0x37/0x70 [] sock_sendmsg+0xbe/0x100 [] autoremove_wake_function+0x0/0x50 [] __wake_up_common+0x43/0x70 [] copy_from_user+0x32/0x130 [] copy_from_user+0x32/0x130 [] verify_iovec+0x2e/0xb0 [] sys_sendmsg+0x17f/0x290 [] pipe_write+0x29a/0x570 [] update_wall_time+0x492/0x8e0 [] getnstimeofday+0x51/0x120 [] sched_slice+0x3d/0x80 [] getnstimeofday+0x51/0x120 [] hrtimer_forward+0x147/0x1a0 [] lapic_next_event+0x10/0x20 [] clockevents_program_event+0xa3/0x170 [] sys_socketcall+0xa4/0x290 [] smp_apic_timer_interrupt+0x40/0x70 [] sysenter_do_call+0x12/0x25 Signed-off-by: Artur Skawina Acked-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 3b44e8e7603..ae31e3775e0 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -1147,7 +1147,7 @@ static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(struct p54_hdr) + sizeof(*tim), - P54_CONTROL_TYPE_TIM, GFP_KERNEL); + P54_CONTROL_TYPE_TIM, GFP_ATOMIC); if (!skb) return -ENOMEM; -- cgit v1.2.3 From 674743033c1ae9f7cc94e1e0037f6f719e6d1d67 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Fri, 16 Jan 2009 19:46:28 +0100 Subject: p54: fix p54_set_key's return code p54 doesn't support AES-128-CMAC offload. This patch will fix the noisy mac80211 warnings, when 802.11w is enabled: mac80211-phy189: failed to set key (4, ff:ff:ff:ff:ff:ff) to hardware (-22) mac80211-phy189: failed to set key (5, ff:ff:ff:ff:ff:ff) to hardware (-22) Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index ae31e3775e0..12d0717c399 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -2077,7 +2077,7 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, algo = P54_CRYPTO_AESCCMP; break; default: - return -EINVAL; + return -EOPNOTSUPP; } } -- cgit v1.2.3 From fdb6a8f4db813b4e50f4e975efe6be12ba5bf460 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Sat, 17 Jan 2009 17:13:27 +0100 Subject: oprofile: fix uninitialized use of struct op_entry Impact: fix crash In case of losing samples struct op_entry could have been used uninitialized causing e.g. a wrong preemption count or NULL pointer access. This patch fixes this. Signed-off-by: Robert Richter Signed-off-by: Ingo Molnar --- drivers/oprofile/cpu_buffer.c | 5 +++++ drivers/oprofile/cpu_buffer.h | 7 +++++++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 2e03b6d796d..e76d715e434 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -393,16 +393,21 @@ oprofile_write_reserve(struct op_entry *entry, struct pt_regs * const regs, return; fail: + entry->event = NULL; cpu_buf->sample_lost_overflow++; } int oprofile_add_data(struct op_entry *entry, unsigned long val) { + if (!entry->event) + return 0; return op_cpu_buffer_add_data(entry, val); } int oprofile_write_commit(struct op_entry *entry) { + if (!entry->event) + return -EINVAL; return op_cpu_buffer_write_commit(entry); } diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h index 63f81c44846..272995d2029 100644 --- a/drivers/oprofile/cpu_buffer.h +++ b/drivers/oprofile/cpu_buffer.h @@ -66,6 +66,13 @@ static inline void op_cpu_buffer_reset(int cpu) cpu_buf->last_task = NULL; } +/* + * op_cpu_buffer_add_data() and op_cpu_buffer_write_commit() may be + * called only if op_cpu_buffer_write_reserve() did not return NULL or + * entry->event != NULL, otherwise entry->size or entry->event will be + * used uninitialized. + */ + struct op_sample *op_cpu_buffer_write_reserve(struct op_entry *entry, unsigned long size); int op_cpu_buffer_write_commit(struct op_entry *entry); -- cgit v1.2.3 From 141e6ebd1b1759bd5cebf092b7216b6f1c7b4c4f Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 5 Jan 2009 14:44:11 +0100 Subject: UBI: add ioctl for map operation This patch adds ioctl for the LEB map operation (as a debugging option so far). [Re-named ioctl to make it look the same as the other one and made some minor stylistic changes. Artem Bityutskiy.] Signed-off-by: Corentin Chary Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 98cf31ed081..055e3f563c1 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -518,6 +518,20 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, err = ubi_wl_flush(ubi); break; } + + /* Logical eraseblock map command */ + case UBI_IOCEBMAP: + { + struct ubi_map_req req; + + err = copy_from_user(&req, argp, sizeof(struct ubi_map_req)); + if (err) { + err = -EFAULT; + break; + } + err = ubi_leb_map(desc, req.lnum, req.dtype); + break; + } #endif default: -- cgit v1.2.3 From c3da23be1673be4e738aea235604b4e6cb259655 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 5 Jan 2009 14:46:19 +0100 Subject: UBI: add ioctl for unmap operation This patch adds ioctl for the LEB unmap operation (as a debugging option so far). [Re-named ioctl to make it look the same as the other one and made some minor stylistic changes. Artem Bityutskiy.] Signed-off-by: Corentin Chary Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 055e3f563c1..fd7e0f923b3 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -532,13 +532,26 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, err = ubi_leb_map(desc, req.lnum, req.dtype); break; } + + /* Logical eraseblock un-map command */ + case UBI_IOCEBUNMAP: + { + int32_t lnum; + + err = get_user(lnum, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + err = ubi_leb_unmap(desc, lnum); + break; + } #endif default: err = -ENOTTY; break; } - return err; } -- cgit v1.2.3 From a27ce8f55dd5fddf0b8ea179cce8f399c13dc94f Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 5 Jan 2009 14:48:59 +0100 Subject: UBI: add ioctl for is_mapped operation This patch adds ioctl to check if an LEB is mapped or not (as a debugging option so far). [Re-named ioctl to make it look the same as the other one and made some minor stylistic changes. Artem Bityutskiy.] Signed-off-by: Corentin Chary Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index fd7e0f923b3..9ddbade7288 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -546,6 +546,20 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, err = ubi_leb_unmap(desc, lnum); break; } + + /* Check if logical eraseblock is mapped command */ + case UBI_IOCEBISMAP: + { + int32_t lnum; + + err = get_user(lnum, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + err = ubi_is_mapped(desc, lnum); + break; + } #endif default: -- cgit v1.2.3 From 573135b5dbc02be12940558db23158cc9ee89c66 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 16 Jan 2009 18:02:08 +0200 Subject: UBI: remove unnecessry header inclusion Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 9ddbade7288..98cf7a4ceea 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include "ubi.h" -- cgit v1.2.3 From ade44ce07c9316351ae321051221c9bad3af3a44 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 16 Jan 2009 18:03:22 +0200 Subject: UBI: allow all ioctls Some ioctl's in UBI are enabled only when debugging is switched on. There is not particular reason for this, just noone needed them. However, some people need the now for their user-space development. Thus, allow these ioctl's even if UBI debugging is disabled. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 98cf7a4ceea..c183be99c6c 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -486,7 +486,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } -#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO /* Logical eraseblock erasure command */ case UBI_IOCEBER: { @@ -559,7 +558,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, err = ubi_is_mapped(desc, lnum); break; } -#endif default: err = -ENOTTY; -- cgit v1.2.3 From 4d187a88d3ee3be6a1a0b6859eb00f70e1601b5e Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sun, 11 Jan 2009 23:55:39 +0100 Subject: UBI: constify file operations Signed-off-by: Jan Engelhardt Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 6 +++--- drivers/mtd/ubi/ubi.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index c183be99c6c..d99935c0f3c 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -1025,20 +1025,20 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, } /* UBI control character device operations */ -struct file_operations ubi_ctrl_cdev_operations = { +const struct file_operations ubi_ctrl_cdev_operations = { .ioctl = ctrl_cdev_ioctl, .owner = THIS_MODULE, }; /* UBI character device operations */ -struct file_operations ubi_cdev_operations = { +const struct file_operations ubi_cdev_operations = { .owner = THIS_MODULE, .ioctl = ubi_cdev_ioctl, .llseek = no_llseek, }; /* UBI volume character device operations */ -struct file_operations ubi_vol_cdev_operations = { +const struct file_operations ubi_vol_cdev_operations = { .owner = THIS_MODULE, .open = vol_cdev_open, .release = vol_cdev_release, diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 4a8ec485c91..381f0e1d0a7 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -449,9 +449,9 @@ struct ubi_device { }; extern struct kmem_cache *ubi_wl_entry_slab; -extern struct file_operations ubi_ctrl_cdev_operations; -extern struct file_operations ubi_cdev_operations; -extern struct file_operations ubi_vol_cdev_operations; +extern const struct file_operations ubi_ctrl_cdev_operations; +extern const struct file_operations ubi_cdev_operations; +extern const struct file_operations ubi_vol_cdev_operations; extern struct class *ubi_class; extern struct mutex ubi_devices_mutex; -- cgit v1.2.3 From f429b2ea8eadb5a576542a70f7fd6f5c2a7455e1 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 16 Jan 2009 18:06:55 +0200 Subject: UBI: add ioctl compatibility UBI ioctl's do not work when running 64-bit kernel and 32-bit user-land. Fix this by adding the compat_ioctl method. Also, UBI serializes all ioctls, so more than one ioctl at a time is not a problem. Amd UBI does not seem to depend on anything else, so use unlocked_ioctl instead of ioctl (no BKL needed). Reported-by: Geert Uytterhoeven Signed-off-by: Artem Bityutskiy Reviewed-by: Arnd Bergmann --- drivers/mtd/ubi/cdev.c | 80 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index d99935c0f3c..0a2d835fec8 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include "ubi.h" @@ -401,8 +402,8 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, return count; } -static int vol_cdev_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long vol_cdev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { int err = 0; struct ubi_volume_desc *desc = file->private_data; @@ -800,8 +801,8 @@ out_free: return err; } -static int ubi_cdev_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { int err = 0; struct ubi_device *ubi; @@ -811,7 +812,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, if (!capable(CAP_SYS_RESOURCE)) return -EPERM; - ubi = ubi_get_by_major(imajor(inode)); + ubi = ubi_get_by_major(imajor(file->f_mapping->host)); if (!ubi) return -ENODEV; @@ -947,8 +948,8 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, return err; } -static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long ctrl_cdev_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { int err = 0; void __user *argp = (void __user *)arg; @@ -1024,26 +1025,59 @@ static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, return err; } -/* UBI control character device operations */ -const struct file_operations ubi_ctrl_cdev_operations = { - .ioctl = ctrl_cdev_ioctl, - .owner = THIS_MODULE, +#ifdef CONFIG_COMPAT +static long vol_cdev_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned long translated_arg = (unsigned long)compat_ptr(arg); + + return vol_cdev_ioctl(file, cmd, translated_arg); +} + +static long ubi_cdev_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned long translated_arg = (unsigned long)compat_ptr(arg); + + return ubi_cdev_ioctl(file, cmd, translated_arg); +} + +static long ctrl_cdev_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + unsigned long translated_arg = (unsigned long)compat_ptr(arg); + + return ctrl_cdev_ioctl(file, cmd, translated_arg); +} +#else +#define vol_cdev_compat_ioctl NULL +#define ubi_cdev_compat_ioctl NULL +#define ctrl_cdev_compat_ioctl NULL +#endif + +/* UBI volume character device operations */ +const struct file_operations ubi_vol_cdev_operations = { + .owner = THIS_MODULE, + .open = vol_cdev_open, + .release = vol_cdev_release, + .llseek = vol_cdev_llseek, + .read = vol_cdev_read, + .write = vol_cdev_write, + .unlocked_ioctl = vol_cdev_ioctl, + .compat_ioctl = vol_cdev_compat_ioctl, }; /* UBI character device operations */ const struct file_operations ubi_cdev_operations = { - .owner = THIS_MODULE, - .ioctl = ubi_cdev_ioctl, - .llseek = no_llseek, + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = ubi_cdev_ioctl, + .compat_ioctl = ubi_cdev_compat_ioctl, }; -/* UBI volume character device operations */ -const struct file_operations ubi_vol_cdev_operations = { - .owner = THIS_MODULE, - .open = vol_cdev_open, - .release = vol_cdev_release, - .llseek = vol_cdev_llseek, - .read = vol_cdev_read, - .write = vol_cdev_write, - .ioctl = vol_cdev_ioctl, +/* UBI control character device operations */ +const struct file_operations ubi_ctrl_cdev_operations = { + .owner = THIS_MODULE, + .unlocked_ioctl = ctrl_cdev_ioctl, + .compat_ioctl = ctrl_cdev_compat_ioctl, }; -- cgit v1.2.3 From 3013ee31b6c5fd9a49a81816d6c13e1cdb7a1288 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 16 Jan 2009 19:08:43 +0200 Subject: UBI: use nicer 64-bit math Get rid of 'do_div()' and use more user-friendly primitives from 'linux/math64.h'. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 20 +++++--------------- drivers/mtd/ubi/gluebi.c | 11 +++-------- drivers/mtd/ubi/scan.c | 8 +++----- drivers/mtd/ubi/upd.c | 21 +++++++-------------- drivers/mtd/ubi/vmt.c | 17 +++++++---------- 5 files changed, 25 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 0a2d835fec8..f9631eb3fef 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -41,8 +41,8 @@ #include #include #include +#include #include -#include #include "ubi.h" /** @@ -195,7 +195,6 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, int err, lnum, off, len, tbuf_size; size_t count_save = count; void *tbuf; - uint64_t tmp; dbg_gen("read %zd bytes from offset %lld of volume %d", count, *offp, vol->vol_id); @@ -225,10 +224,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, return -ENOMEM; len = count > tbuf_size ? tbuf_size : count; - - tmp = *offp; - off = do_div(tmp, vol->usable_leb_size); - lnum = tmp; + lnum = div_u64_rem(*offp, vol->usable_leb_size, &off); do { cond_resched(); @@ -279,7 +275,6 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, int lnum, off, len, tbuf_size, err = 0; size_t count_save = count; char *tbuf; - uint64_t tmp; dbg_gen("requested: write %zd bytes to offset %lld of volume %u", count, *offp, vol->vol_id); @@ -287,10 +282,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, if (vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; - tmp = *offp; - off = do_div(tmp, vol->usable_leb_size); - lnum = tmp; - + lnum = div_u64_rem(*offp, vol->usable_leb_size, &off); if (off & (ubi->min_io_size - 1)) { dbg_err("unaligned position"); return -EINVAL; @@ -882,7 +874,6 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, case UBI_IOCRSVOL: { int pebs; - uint64_t tmp; struct ubi_rsvol_req req; dbg_gen("re-size volume"); @@ -902,9 +893,8 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, break; } - tmp = req.bytes; - pebs = !!do_div(tmp, desc->vol->usable_leb_size); - pebs += tmp; + pebs = div_u64(req.bytes + desc->vol->usable_leb_size - 1, + desc->vol->usable_leb_size); mutex_lock(&ubi->volumes_mutex); err = ubi_resize_volume(desc, pebs); diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 6dd4f5e77f8..49cd55ade9c 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -28,7 +28,7 @@ * eraseblock size is equivalent to the logical eraseblock size of the volume. */ -#include +#include #include "ubi.h" /** @@ -109,7 +109,6 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, int err = 0, lnum, offs, total_read; struct ubi_volume *vol; struct ubi_device *ubi; - uint64_t tmp = from; dbg_gen("read %zd bytes from offset %lld", len, from); @@ -119,9 +118,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, vol = container_of(mtd, struct ubi_volume, gluebi_mtd); ubi = vol->ubi; - offs = do_div(tmp, mtd->erasesize); - lnum = tmp; - + lnum = div_u64_rem(from, mtd->erasesize, &offs); total_read = len; while (total_read) { size_t to_read = mtd->erasesize - offs; @@ -160,7 +157,6 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, int err = 0, lnum, offs, total_written; struct ubi_volume *vol; struct ubi_device *ubi; - uint64_t tmp = to; dbg_gen("write %zd bytes to offset %lld", len, to); @@ -173,8 +169,7 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, if (ubi->ro_mode) return -EROFS; - offs = do_div(tmp, mtd->erasesize); - lnum = tmp; + lnum = div_u64_rem(to, mtd->erasesize, &offs); if (len % mtd->writesize || offs % mtd->writesize) return -EINVAL; diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index ecde202a5a1..c3d653ba5ca 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -42,7 +42,7 @@ #include #include -#include +#include #include "ubi.h" #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID @@ -904,10 +904,8 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) dbg_msg("scanning is finished"); /* Calculate mean erase counter */ - if (si->ec_count) { - do_div(si->ec_sum, si->ec_count); - si->mean_ec = si->ec_sum; - } + if (si->ec_count) + si->mean_ec = div_u64(si->ec_sum, si->ec_count); if (si->is_empty) ubi_msg("empty MTD device detected"); diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 8b89cc18ff0..6b4d1ae891a 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -40,7 +40,7 @@ #include #include -#include +#include #include "ubi.h" /** @@ -89,7 +89,6 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, long long bytes) { int err; - uint64_t tmp; struct ubi_vtbl_record vtbl_rec; dbg_gen("clear update marker for volume %d", vol->vol_id); @@ -101,9 +100,9 @@ static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, if (vol->vol_type == UBI_STATIC_VOLUME) { vol->corrupted = 0; - vol->used_bytes = tmp = bytes; - vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size); - vol->used_ebs = tmp; + vol->used_bytes = bytes; + vol->used_ebs = div_u64_rem(bytes, vol->usable_leb_size, + &vol->last_eb_bytes); if (vol->last_eb_bytes) vol->used_ebs += 1; else @@ -131,7 +130,6 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, long long bytes) { int i, err; - uint64_t tmp; dbg_gen("start update of volume %d, %llu bytes", vol->vol_id, bytes); ubi_assert(!vol->updating && !vol->changing_leb); @@ -161,9 +159,8 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, if (!vol->upd_buf) return -ENOMEM; - tmp = bytes; - vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size); - vol->upd_ebs += tmp; + vol->upd_ebs = div_u64(bytes + vol->usable_leb_size - 1, + vol->usable_leb_size); vol->upd_bytes = bytes; vol->upd_received = 0; return 0; @@ -282,7 +279,6 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count) { - uint64_t tmp; int lnum, offs, err = 0, len, to_write = count; dbg_gen("write %d of %lld bytes, %lld already passed", @@ -291,10 +287,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, if (ubi->ro_mode) return -EROFS; - tmp = vol->upd_received; - offs = do_div(tmp, vol->usable_leb_size); - lnum = tmp; - + lnum = div_u64_rem(vol->upd_received, vol->usable_leb_size, &offs); if (vol->upd_received + count > vol->upd_bytes) to_write = count = vol->upd_bytes - vol->upd_received; diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 22e1d7398fc..df5483562b7 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -24,7 +24,7 @@ */ #include -#include +#include #include "ubi.h" #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID @@ -205,7 +205,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) int i, err, vol_id = req->vol_id, do_free = 1; struct ubi_volume *vol; struct ubi_vtbl_record vtbl_rec; - uint64_t bytes; dev_t dev; if (ubi->ro_mode) @@ -255,10 +254,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) /* Calculate how many eraseblocks are requested */ vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment; - bytes = req->bytes; - if (do_div(bytes, vol->usable_leb_size)) - vol->reserved_pebs = 1; - vol->reserved_pebs += bytes; + vol->reserved_pebs += div_u64(req->bytes + vol->usable_leb_size - 1, + vol->usable_leb_size); /* Reserve physical eraseblocks */ if (vol->reserved_pebs > ubi->avail_pebs) { @@ -301,10 +298,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->used_bytes = (long long)vol->used_ebs * vol->usable_leb_size; } else { - bytes = vol->used_bytes; - vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size); - vol->used_ebs = bytes; - if (vol->last_eb_bytes) + vol->used_ebs = div_u64_rem(vol->used_bytes, + vol->usable_leb_size, + &vol->last_eb_bytes); + if (vol->last_eb_bytes != 0) vol->used_ebs += 1; else vol->last_eb_bytes = vol->usable_leb_size; -- cgit v1.2.3 From c1ff85d97708550e634fb6fa099c463db90fc40d Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 19 Jan 2009 17:17:58 +1000 Subject: drm: fix leak of device mappings since multi-master changes. Device maps now contain a link to the master that created them, so when cleaning up the master, remove any maps that are connected to it. Also delete any remaining maps at driver unload time. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_drv.c | 4 ++++ drivers/gpu/drm/drm_stub.c | 8 ++++++++ 2 files changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 5ff88d95222..14c7a23dc15 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -294,6 +294,7 @@ EXPORT_SYMBOL(drm_init); */ static void drm_cleanup(struct drm_device * dev) { + struct drm_map_list *r_list, *list_temp; DRM_DEBUG("\n"); if (!dev) { @@ -325,6 +326,9 @@ static void drm_cleanup(struct drm_device * dev) drm_ht_remove(&dev->map_hash); drm_ctxbitmap_cleanup(dev); + list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) + drm_rmmap(dev, r_list->map); + if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_put_minor(&dev->control); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 5ca132afa4f..46bb923b097 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -118,12 +118,20 @@ static void drm_master_destroy(struct kref *kref) struct drm_master *master = container_of(kref, struct drm_master, refcount); struct drm_magic_entry *pt, *next; struct drm_device *dev = master->minor->dev; + struct drm_map_list *r_list, *list_temp; list_del(&master->head); if (dev->driver->master_destroy) dev->driver->master_destroy(dev, master); + list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) { + if (r_list->master == master) { + drm_rmmap_locked(dev, r_list->map); + r_list = NULL; + } + } + if (master->unique) { drm_free(master->unique, master->unique_size, DRM_MEM_DRIVER); master->unique = NULL; -- cgit v1.2.3 From bb54affa6fbdd6fe80f193ec1b6977a93078785d Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Mon, 19 Jan 2009 13:46:56 +0100 Subject: ide: fix IDE PMAC breakage Bartlomiej Zolnierkiewicz writes: > Signed-off-by: Bartlomiej Zolnierkiewicz > --- > drivers/ide/ide-probe.c | 9 ++------- > 1 file changed, 2 insertions(+), 7 deletions(-) > > Index: b/drivers/ide/ide-probe.c > =================================================================== > --- a/drivers/ide/ide-probe.c > +++ b/drivers/ide/ide-probe.c > @@ -640,14 +640,9 @@ static int ide_register_port(ide_hwif_t > /* register with global device tree */ > dev_set_name(&hwif->gendev, hwif->name); > hwif->gendev.driver_data = hwif; > - if (hwif->gendev.parent == NULL) { > - if (hwif->dev) > - hwif->gendev.parent = hwif->dev; > - else > - /* Would like to do = &device_legacy */ > - hwif->gendev.parent = NULL; > - } > + hwif->gendev.parent = hwif->dev; This [bart: commit 96d40941236722777c259775640b8880b7dc6f33 ("ide: small ide_register_port() cleanup")] breaks ide-pmac. It overwrites the parent that pmac_ide_macio_attach has set. Signed-off-by: Andreas Schwab Cc: Benjamin Herrenschmidt Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/ide-probe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 312127ea443..0db1ed9f5fc 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -649,7 +649,8 @@ static int ide_register_port(ide_hwif_t *hwif) /* register with global device tree */ dev_set_name(&hwif->gendev, hwif->name); hwif->gendev.driver_data = hwif; - hwif->gendev.parent = hwif->dev; + if (hwif->gendev.parent == NULL) + hwif->gendev.parent = hwif->dev; hwif->gendev.release = hwif_release_dev; ret = device_register(&hwif->gendev); -- cgit v1.2.3 From abb8817967cc080ee024f7d1a3d5a9830074ca1c Mon Sep 17 00:00:00 2001 From: Michael Schmitz Date: Mon, 19 Jan 2009 13:46:56 +0100 Subject: ide: fix Falcon IDE breakage [m68k] Falcon IDE: always serialize, in order to force execution of ide_get_lock() and friends. Signed-off-By: Michael Schmitz Cc: Geert Uytterhoeven [bart: set flag in falconide_port_info instead of falconide_init()] Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/falconide.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ide/falconide.c b/drivers/ide/falconide.c index a5ba820d69b..a638e952d67 100644 --- a/drivers/ide/falconide.c +++ b/drivers/ide/falconide.c @@ -82,7 +82,7 @@ static const struct ide_tp_ops falconide_tp_ops = { static const struct ide_port_info falconide_port_info = { .tp_ops = &falconide_tp_ops, - .host_flags = IDE_HFLAG_NO_DMA, + .host_flags = IDE_HFLAG_NO_DMA | IDE_HFLAG_SERIALIZE, }; static void __init falconide_setup_ports(hw_regs_t *hw) -- cgit v1.2.3 From ef183f6b5982aa10499432a0cb243c92ce623512 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 19 Jan 2009 13:46:57 +0100 Subject: drivers/ide/palm_bk3710.c buildfix CC drivers/ide/palm_bk3710.o drivers/ide/palm_bk3710.c: In function 'palm_bk3710_probe': drivers/ide/palm_bk3710.c:382: warning: assignment makes integer from pointer without a cast Someone should fix hw_regs_t to neither be a typedef, nor use "unsigned long" where it should use "void __iomem *". Signed-off-by: David Brownell Cc: Kevin Hilman Cc: Sergei Shtylyov Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/ide/palm_bk3710.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/ide/palm_bk3710.c b/drivers/ide/palm_bk3710.c index a7ac490c9ae..f38aac78044 100644 --- a/drivers/ide/palm_bk3710.c +++ b/drivers/ide/palm_bk3710.c @@ -346,7 +346,8 @@ static int __init palm_bk3710_probe(struct platform_device *pdev) { struct clk *clk; struct resource *mem, *irq; - unsigned long base, rate; + void __iomem *base; + unsigned long rate; int i, rc; hw_regs_t hw, *hws[] = { &hw, NULL, NULL, NULL }; @@ -382,11 +383,13 @@ static int __init palm_bk3710_probe(struct platform_device *pdev) base = IO_ADDRESS(mem->start); /* Configure the Palm Chip controller */ - palm_bk3710_chipinit((void __iomem *)base); + palm_bk3710_chipinit(base); for (i = 0; i < IDE_NR_PORTS - 2; i++) - hw.io_ports_array[i] = base + IDE_PALM_ATA_PRI_REG_OFFSET + i; - hw.io_ports.ctl_addr = base + IDE_PALM_ATA_PRI_CTL_OFFSET; + hw.io_ports_array[i] = (unsigned long) + (base + IDE_PALM_ATA_PRI_REG_OFFSET + i); + hw.io_ports.ctl_addr = (unsigned long) + (base + IDE_PALM_ATA_PRI_CTL_OFFSET); hw.irq = irq->start; hw.dev = &pdev->dev; hw.chipset = ide_palm3710; -- cgit v1.2.3 From c2fdd36b550659f5ac2240d1f5a83ffa1a092289 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 17 Jan 2009 16:23:55 +0100 Subject: PCI hotplug: fix lock imbalance in pciehp set_lock_status omits mutex_unlock in fail path. Add the omitted unlock. As a result a lockup caused by this can be triggered from userspace by writing 1 to /sys/bus/pci/slots/.../lock often enough. Signed-off-by: Jiri Slaby Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 5482d4ed825..c2485542f54 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -126,8 +126,10 @@ static int set_lock_status(struct hotplug_slot *hotplug_slot, u8 status) mutex_lock(&slot->ctrl->crit_sect); /* has it been >1 sec since our last toggle? */ - if ((get_seconds() - slot->last_emi_toggle) < 1) + if ((get_seconds() - slot->last_emi_toggle) < 1) { + mutex_unlock(&slot->ctrl->crit_sect); return -EINVAL; + } /* see what our current state is */ retval = get_lock_status(hotplug_slot, &value); -- cgit v1.2.3 From 83436a0560e9ef8af2f0796264dde4bed1415359 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 19 Jan 2009 14:39:10 -0700 Subject: dmaengine: kill some dubious WARN_ONCEs dma_find_channel and dma_issue_pending_all are good places to warn about improper api usage. However, warning correctly means synchronizing with dma_list_mutex, i.e. too much overhead for these fast-path calls. Reported-by: Ingo Molnar Signed-off-by: Dan Williams --- drivers/dma/dmaengine.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 6df144a65fe..a58993011ed 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -329,9 +329,6 @@ struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type) struct dma_chan *chan; int cpu; - WARN_ONCE(dmaengine_ref_count == 0, - "client called %s without a reference", __func__); - cpu = get_cpu(); chan = per_cpu_ptr(channel_table[tx_type], cpu)->chan; put_cpu(); @@ -348,9 +345,6 @@ void dma_issue_pending_all(void) struct dma_device *device; struct dma_chan *chan; - WARN_ONCE(dmaengine_ref_count == 0, - "client called %s without a reference", __func__); - rcu_read_lock(); list_for_each_entry_rcu(device, &dma_device_list, global_node) { if (dma_has_cap(DMA_PRIVATE, device->cap_mask)) -- cgit v1.2.3 From 5296b56d1b2000b60fb966be161c1f8fb629786b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 19 Jan 2009 15:36:21 -0700 Subject: i.MX31: Image Processing Unit DMA and IRQ drivers i.MX3x SoCs contain an Image Processing Unit, consisting of a Control Module (CM), Display Interface (DI), Synchronous Display Controller (SDC), Asynchronous Display Controller (ADC), Image Converter (IC), Post-Filter (PF), Camera Sensor Interface (CSI), and an Image DMA Controller (IDMAC). CM contains, among other blocks, an Interrupt Generator (IG) and a Clock and Reset Control Unit (CRCU). This driver serves IDMAC and IG. They are supported over dmaengine and irq-chip APIs respectively. IDMAC is a specialised DMA controller, its DMA channels cannot be used for general-purpose operations, even though it might be possible to configure a memory-to-memory channel for memcpy operation. This driver will not work with generic dmaengine clients, clients, wishing to use it must use respective wrapper structures, they also must specify which channels they require, as channels are hard-wired to specific IPU functions. Acked-by: Sascha Hauer Signed-off-by: Guennadi Liakhovetski Signed-off-by: Dan Williams --- drivers/dma/Kconfig | 19 + drivers/dma/Makefile | 1 + drivers/dma/ipu/Makefile | 1 + drivers/dma/ipu/ipu_idmac.c | 1740 ++++++++++++++++++++++++++++++++++++++++++ drivers/dma/ipu/ipu_intern.h | 176 +++++ drivers/dma/ipu/ipu_irq.c | 413 ++++++++++ 6 files changed, 2350 insertions(+) create mode 100644 drivers/dma/ipu/Makefile create mode 100644 drivers/dma/ipu/ipu_idmac.c create mode 100644 drivers/dma/ipu/ipu_intern.h create mode 100644 drivers/dma/ipu/ipu_irq.c (limited to 'drivers') diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index e34b0642081..48ea59e7967 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -62,6 +62,25 @@ config MV_XOR ---help--- Enable support for the Marvell XOR engine. +config MX3_IPU + bool "MX3x Image Processing Unit support" + depends on ARCH_MX3 + select DMA_ENGINE + default y + help + If you plan to use the Image Processing unit in the i.MX3x, say + Y here. If unsure, select Y. + +config MX3_IPU_IRQS + int "Number of dynamically mapped interrupts for IPU" + depends on MX3_IPU + range 2 137 + default 4 + help + Out of 137 interrupt sources on i.MX31 IPU only very few are used. + To avoid bloating the irq_desc[] array we allocate a sufficient + number of IRQ slots and map them dynamically to specific sources. + config DMA_ENGINE bool diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 14f59527d4f..2e5dc96700d 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_INTEL_IOP_ADMA) += iop-adma.o obj-$(CONFIG_FSL_DMA) += fsldma.o obj-$(CONFIG_MV_XOR) += mv_xor.o obj-$(CONFIG_DW_DMAC) += dw_dmac.o +obj-$(CONFIG_MX3_IPU) += ipu/ diff --git a/drivers/dma/ipu/Makefile b/drivers/dma/ipu/Makefile new file mode 100644 index 00000000000..6704cf48326 --- /dev/null +++ b/drivers/dma/ipu/Makefile @@ -0,0 +1 @@ +obj-y += ipu_irq.o ipu_idmac.o diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c new file mode 100644 index 00000000000..1f154d08e98 --- /dev/null +++ b/drivers/dma/ipu/ipu_idmac.c @@ -0,0 +1,1740 @@ +/* + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, + * + * Copyright (C) 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ipu_intern.h" + +#define FS_VF_IN_VALID 0x00000002 +#define FS_ENC_IN_VALID 0x00000001 + +/* + * There can be only one, we could allocate it dynamically, but then we'd have + * to add an extra parameter to some functions, and use something as ugly as + * struct ipu *ipu = to_ipu(to_idmac(ichan->dma_chan.device)); + * in the ISR + */ +static struct ipu ipu_data; + +#define to_ipu(id) container_of(id, struct ipu, idmac) + +static u32 __idmac_read_icreg(struct ipu *ipu, unsigned long reg) +{ + return __raw_readl(ipu->reg_ic + reg); +} + +#define idmac_read_icreg(ipu, reg) __idmac_read_icreg(ipu, reg - IC_CONF) + +static void __idmac_write_icreg(struct ipu *ipu, u32 value, unsigned long reg) +{ + __raw_writel(value, ipu->reg_ic + reg); +} + +#define idmac_write_icreg(ipu, v, reg) __idmac_write_icreg(ipu, v, reg - IC_CONF) + +static u32 idmac_read_ipureg(struct ipu *ipu, unsigned long reg) +{ + return __raw_readl(ipu->reg_ipu + reg); +} + +static void idmac_write_ipureg(struct ipu *ipu, u32 value, unsigned long reg) +{ + __raw_writel(value, ipu->reg_ipu + reg); +} + +/***************************************************************************** + * IPU / IC common functions + */ +static void dump_idmac_reg(struct ipu *ipu) +{ + dev_dbg(ipu->dev, "IDMAC_CONF 0x%x, IC_CONF 0x%x, IDMAC_CHA_EN 0x%x, " + "IDMAC_CHA_PRI 0x%x, IDMAC_CHA_BUSY 0x%x\n", + idmac_read_icreg(ipu, IDMAC_CONF), + idmac_read_icreg(ipu, IC_CONF), + idmac_read_icreg(ipu, IDMAC_CHA_EN), + idmac_read_icreg(ipu, IDMAC_CHA_PRI), + idmac_read_icreg(ipu, IDMAC_CHA_BUSY)); + dev_dbg(ipu->dev, "BUF0_RDY 0x%x, BUF1_RDY 0x%x, CUR_BUF 0x%x, " + "DB_MODE 0x%x, TASKS_STAT 0x%x\n", + idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY), + idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY), + idmac_read_ipureg(ipu, IPU_CHA_CUR_BUF), + idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL), + idmac_read_ipureg(ipu, IPU_TASKS_STAT)); +} + +static uint32_t bytes_per_pixel(enum pixel_fmt fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_GENERIC: /* generic data */ + case IPU_PIX_FMT_RGB332: + case IPU_PIX_FMT_YUV420P: + case IPU_PIX_FMT_YUV422P: + default: + return 1; + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_UYVY: + return 2; + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + return 3; + case IPU_PIX_FMT_GENERIC_32: /* generic data */ + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_ABGR32: + return 4; + } +} + +/* Enable / disable direct write to memory by the Camera Sensor Interface */ +static void ipu_ic_enable_task(struct ipu *ipu, enum ipu_channel channel) +{ + uint32_t ic_conf, mask; + + switch (channel) { + case IDMAC_IC_0: + mask = IC_CONF_PRPENC_EN; + break; + case IDMAC_IC_7: + mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN; + break; + default: + return; + } + ic_conf = idmac_read_icreg(ipu, IC_CONF) | mask; + idmac_write_icreg(ipu, ic_conf, IC_CONF); +} + +static void ipu_ic_disable_task(struct ipu *ipu, enum ipu_channel channel) +{ + uint32_t ic_conf, mask; + + switch (channel) { + case IDMAC_IC_0: + mask = IC_CONF_PRPENC_EN; + break; + case IDMAC_IC_7: + mask = IC_CONF_RWS_EN | IC_CONF_PRPENC_EN; + break; + default: + return; + } + ic_conf = idmac_read_icreg(ipu, IC_CONF) & ~mask; + idmac_write_icreg(ipu, ic_conf, IC_CONF); +} + +static uint32_t ipu_channel_status(struct ipu *ipu, enum ipu_channel channel) +{ + uint32_t stat = TASK_STAT_IDLE; + uint32_t task_stat_reg = idmac_read_ipureg(ipu, IPU_TASKS_STAT); + + switch (channel) { + case IDMAC_IC_7: + stat = (task_stat_reg & TSTAT_CSI2MEM_MASK) >> + TSTAT_CSI2MEM_OFFSET; + break; + case IDMAC_IC_0: + case IDMAC_SDC_0: + case IDMAC_SDC_1: + default: + break; + } + return stat; +} + +struct chan_param_mem_planar { + /* Word 0 */ + u32 xv:10; + u32 yv:10; + u32 xb:12; + + u32 yb:12; + u32 res1:2; + u32 nsb:1; + u32 lnpb:6; + u32 ubo_l:11; + + u32 ubo_h:15; + u32 vbo_l:17; + + u32 vbo_h:9; + u32 res2:3; + u32 fw:12; + u32 fh_l:8; + + u32 fh_h:4; + u32 res3:28; + + /* Word 1 */ + u32 eba0; + + u32 eba1; + + u32 bpp:3; + u32 sl:14; + u32 pfs:3; + u32 bam:3; + u32 res4:2; + u32 npb:6; + u32 res5:1; + + u32 sat:2; + u32 res6:30; +} __attribute__ ((packed)); + +struct chan_param_mem_interleaved { + /* Word 0 */ + u32 xv:10; + u32 yv:10; + u32 xb:12; + + u32 yb:12; + u32 sce:1; + u32 res1:1; + u32 nsb:1; + u32 lnpb:6; + u32 sx:10; + u32 sy_l:1; + + u32 sy_h:9; + u32 ns:10; + u32 sm:10; + u32 sdx_l:3; + + u32 sdx_h:2; + u32 sdy:5; + u32 sdrx:1; + u32 sdry:1; + u32 sdr1:1; + u32 res2:2; + u32 fw:12; + u32 fh_l:8; + + u32 fh_h:4; + u32 res3:28; + + /* Word 1 */ + u32 eba0; + + u32 eba1; + + u32 bpp:3; + u32 sl:14; + u32 pfs:3; + u32 bam:3; + u32 res4:2; + u32 npb:6; + u32 res5:1; + + u32 sat:2; + u32 scc:1; + u32 ofs0:5; + u32 ofs1:5; + u32 ofs2:5; + u32 ofs3:5; + u32 wid0:3; + u32 wid1:3; + u32 wid2:3; + + u32 wid3:3; + u32 dec_sel:1; + u32 res6:28; +} __attribute__ ((packed)); + +union chan_param_mem { + struct chan_param_mem_planar pp; + struct chan_param_mem_interleaved ip; +}; + +static void ipu_ch_param_set_plane_offset(union chan_param_mem *params, + u32 u_offset, u32 v_offset) +{ + params->pp.ubo_l = u_offset & 0x7ff; + params->pp.ubo_h = u_offset >> 11; + params->pp.vbo_l = v_offset & 0x1ffff; + params->pp.vbo_h = v_offset >> 17; +} + +static void ipu_ch_param_set_size(union chan_param_mem *params, + uint32_t pixel_fmt, uint16_t width, + uint16_t height, uint16_t stride) +{ + u32 u_offset; + u32 v_offset; + + params->pp.fw = width - 1; + params->pp.fh_l = height - 1; + params->pp.fh_h = (height - 1) >> 8; + params->pp.sl = stride - 1; + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + /*Represents 8-bit Generic data */ + params->pp.bpp = 3; + params->pp.pfs = 7; + params->pp.npb = 31; + params->pp.sat = 2; /* SAT = use 32-bit access */ + break; + case IPU_PIX_FMT_GENERIC_32: + /*Represents 32-bit Generic data */ + params->pp.bpp = 0; + params->pp.pfs = 7; + params->pp.npb = 7; + params->pp.sat = 2; /* SAT = use 32-bit access */ + break; + case IPU_PIX_FMT_RGB565: + params->ip.bpp = 2; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 0; /* Red bit offset */ + params->ip.ofs1 = 5; /* Green bit offset */ + params->ip.ofs2 = 11; /* Blue bit offset */ + params->ip.ofs3 = 16; /* Alpha bit offset */ + params->ip.wid0 = 4; /* Red bit width - 1 */ + params->ip.wid1 = 5; /* Green bit width - 1 */ + params->ip.wid2 = 4; /* Blue bit width - 1 */ + break; + case IPU_PIX_FMT_BGR24: + params->ip.bpp = 1; /* 24 BPP & RGB PFS */ + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 0; /* Red bit offset */ + params->ip.ofs1 = 8; /* Green bit offset */ + params->ip.ofs2 = 16; /* Blue bit offset */ + params->ip.ofs3 = 24; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + break; + case IPU_PIX_FMT_RGB24: + params->ip.bpp = 1; /* 24 BPP & RGB PFS */ + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 16; /* Red bit offset */ + params->ip.ofs1 = 8; /* Green bit offset */ + params->ip.ofs2 = 0; /* Blue bit offset */ + params->ip.ofs3 = 24; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + break; + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + params->ip.bpp = 0; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 8; /* Red bit offset */ + params->ip.ofs1 = 16; /* Green bit offset */ + params->ip.ofs2 = 24; /* Blue bit offset */ + params->ip.ofs3 = 0; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + params->ip.wid3 = 7; /* Alpha bit width - 1 */ + break; + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + params->ip.bpp = 0; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 24; /* Red bit offset */ + params->ip.ofs1 = 16; /* Green bit offset */ + params->ip.ofs2 = 8; /* Blue bit offset */ + params->ip.ofs3 = 0; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + params->ip.wid3 = 7; /* Alpha bit width - 1 */ + break; + case IPU_PIX_FMT_ABGR32: + params->ip.bpp = 0; + params->ip.pfs = 4; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + params->ip.ofs0 = 8; /* Red bit offset */ + params->ip.ofs1 = 16; /* Green bit offset */ + params->ip.ofs2 = 24; /* Blue bit offset */ + params->ip.ofs3 = 0; /* Alpha bit offset */ + params->ip.wid0 = 7; /* Red bit width - 1 */ + params->ip.wid1 = 7; /* Green bit width - 1 */ + params->ip.wid2 = 7; /* Blue bit width - 1 */ + params->ip.wid3 = 7; /* Alpha bit width - 1 */ + break; + case IPU_PIX_FMT_UYVY: + params->ip.bpp = 2; + params->ip.pfs = 6; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + break; + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + params->ip.bpp = 3; + params->ip.pfs = 3; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + u_offset = stride * height; + v_offset = u_offset + u_offset / 4; + ipu_ch_param_set_plane_offset(params, u_offset, v_offset); + break; + case IPU_PIX_FMT_YVU422P: + params->ip.bpp = 3; + params->ip.pfs = 2; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + v_offset = stride * height; + u_offset = v_offset + v_offset / 2; + ipu_ch_param_set_plane_offset(params, u_offset, v_offset); + break; + case IPU_PIX_FMT_YUV422P: + params->ip.bpp = 3; + params->ip.pfs = 2; + params->ip.npb = 7; + params->ip.sat = 2; /* SAT = 32-bit access */ + u_offset = stride * height; + v_offset = u_offset + u_offset / 2; + ipu_ch_param_set_plane_offset(params, u_offset, v_offset); + break; + default: + dev_err(ipu_data.dev, + "mxc ipu: unimplemented pixel format %d\n", pixel_fmt); + break; + } + + params->pp.nsb = 1; +} + +static void ipu_ch_param_set_burst_size(union chan_param_mem *params, + uint16_t burst_pixels) +{ + params->pp.npb = burst_pixels - 1; +}; + +static void ipu_ch_param_set_buffer(union chan_param_mem *params, + dma_addr_t buf0, dma_addr_t buf1) +{ + params->pp.eba0 = buf0; + params->pp.eba1 = buf1; +}; + +static void ipu_ch_param_set_rotation(union chan_param_mem *params, + enum ipu_rotate_mode rotate) +{ + params->pp.bam = rotate; +}; + +static void ipu_write_param_mem(uint32_t addr, uint32_t *data, + uint32_t num_words) +{ + for (; num_words > 0; num_words--) { + dev_dbg(ipu_data.dev, + "write param mem - addr = 0x%08X, data = 0x%08X\n", + addr, *data); + idmac_write_ipureg(&ipu_data, addr, IPU_IMA_ADDR); + idmac_write_ipureg(&ipu_data, *data++, IPU_IMA_DATA); + addr++; + if ((addr & 0x7) == 5) { + addr &= ~0x7; /* set to word 0 */ + addr += 8; /* increment to next row */ + } + } +} + +static int calc_resize_coeffs(uint32_t in_size, uint32_t out_size, + uint32_t *resize_coeff, + uint32_t *downsize_coeff) +{ + uint32_t temp_size; + uint32_t temp_downsize; + + *resize_coeff = 1 << 13; + *downsize_coeff = 1 << 13; + + /* Cannot downsize more than 8:1 */ + if (out_size << 3 < in_size) + return -EINVAL; + + /* compute downsizing coefficient */ + temp_downsize = 0; + temp_size = in_size; + while (temp_size >= out_size * 2 && temp_downsize < 2) { + temp_size >>= 1; + temp_downsize++; + } + *downsize_coeff = temp_downsize; + + /* + * compute resizing coefficient using the following formula: + * resize_coeff = M*(SI -1)/(SO - 1) + * where M = 2^13, SI - input size, SO - output size + */ + *resize_coeff = (8192L * (temp_size - 1)) / (out_size - 1); + if (*resize_coeff >= 16384L) { + dev_err(ipu_data.dev, "Warning! Overflow on resize coeff.\n"); + *resize_coeff = 0x3FFF; + } + + dev_dbg(ipu_data.dev, "resizing from %u -> %u pixels, " + "downsize=%u, resize=%u.%lu (reg=%u)\n", in_size, out_size, + *downsize_coeff, *resize_coeff >= 8192L ? 1 : 0, + ((*resize_coeff & 0x1FFF) * 10000L) / 8192L, *resize_coeff); + + return 0; +} + +static enum ipu_color_space format_to_colorspace(enum pixel_fmt fmt) +{ + switch (fmt) { + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_RGB32: + return IPU_COLORSPACE_RGB; + default: + return IPU_COLORSPACE_YCBCR; + } +} + +static int ipu_ic_init_prpenc(struct ipu *ipu, + union ipu_channel_param *params, bool src_is_csi) +{ + uint32_t reg, ic_conf; + uint32_t downsize_coeff, resize_coeff; + enum ipu_color_space in_fmt, out_fmt; + + /* Setup vertical resizing */ + calc_resize_coeffs(params->video.in_height, + params->video.out_height, + &resize_coeff, &downsize_coeff); + reg = (downsize_coeff << 30) | (resize_coeff << 16); + + /* Setup horizontal resizing */ + calc_resize_coeffs(params->video.in_width, + params->video.out_width, + &resize_coeff, &downsize_coeff); + reg |= (downsize_coeff << 14) | resize_coeff; + + /* Setup color space conversion */ + in_fmt = format_to_colorspace(params->video.in_pixel_fmt); + out_fmt = format_to_colorspace(params->video.out_pixel_fmt); + + /* + * Colourspace conversion unsupported yet - see _init_csc() in + * Freescale sources + */ + if (in_fmt != out_fmt) { + dev_err(ipu->dev, "Colourspace conversion unsupported!\n"); + return -EOPNOTSUPP; + } + + idmac_write_icreg(ipu, reg, IC_PRP_ENC_RSC); + + ic_conf = idmac_read_icreg(ipu, IC_CONF); + + if (src_is_csi) + ic_conf &= ~IC_CONF_RWS_EN; + else + ic_conf |= IC_CONF_RWS_EN; + + idmac_write_icreg(ipu, ic_conf, IC_CONF); + + return 0; +} + +static uint32_t dma_param_addr(uint32_t dma_ch) +{ + /* Channel Parameter Memory */ + return 0x10000 | (dma_ch << 4); +}; + +static void ipu_channel_set_priority(struct ipu *ipu, enum ipu_channel channel, + bool prio) +{ + u32 reg = idmac_read_icreg(ipu, IDMAC_CHA_PRI); + + if (prio) + reg |= 1UL << channel; + else + reg &= ~(1UL << channel); + + idmac_write_icreg(ipu, reg, IDMAC_CHA_PRI); + + dump_idmac_reg(ipu); +} + +static uint32_t ipu_channel_conf_mask(enum ipu_channel channel) +{ + uint32_t mask; + + switch (channel) { + case IDMAC_IC_0: + case IDMAC_IC_7: + mask = IPU_CONF_CSI_EN | IPU_CONF_IC_EN; + break; + case IDMAC_SDC_0: + case IDMAC_SDC_1: + mask = IPU_CONF_SDC_EN | IPU_CONF_DI_EN; + break; + default: + mask = 0; + break; + } + + return mask; +} + +/** + * ipu_enable_channel() - enable an IPU channel. + * @channel: channel ID. + * @return: 0 on success or negative error code on failure. + */ +static int ipu_enable_channel(struct idmac *idmac, struct idmac_channel *ichan) +{ + struct ipu *ipu = to_ipu(idmac); + enum ipu_channel channel = ichan->dma_chan.chan_id; + uint32_t reg; + unsigned long flags; + + spin_lock_irqsave(&ipu->lock, flags); + + /* Reset to buffer 0 */ + idmac_write_ipureg(ipu, 1UL << channel, IPU_CHA_CUR_BUF); + ichan->active_buffer = 0; + ichan->status = IPU_CHANNEL_ENABLED; + + switch (channel) { + case IDMAC_SDC_0: + case IDMAC_SDC_1: + case IDMAC_IC_7: + ipu_channel_set_priority(ipu, channel, true); + default: + break; + } + + reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); + + idmac_write_icreg(ipu, reg | (1UL << channel), IDMAC_CHA_EN); + + ipu_ic_enable_task(ipu, channel); + + spin_unlock_irqrestore(&ipu->lock, flags); + return 0; +} + +/** + * ipu_init_channel_buffer() - initialize a buffer for logical IPU channel. + * @channel: channel ID. + * @pixel_fmt: pixel format of buffer. Pixel format is a FOURCC ASCII code. + * @width: width of buffer in pixels. + * @height: height of buffer in pixels. + * @stride: stride length of buffer in pixels. + * @rot_mode: rotation mode of buffer. A rotation setting other than + * IPU_ROTATE_VERT_FLIP should only be used for input buffers of + * rotation channels. + * @phyaddr_0: buffer 0 physical address. + * @phyaddr_1: buffer 1 physical address. Setting this to a value other than + * NULL enables double buffering mode. + * @return: 0 on success or negative error code on failure. + */ +static int ipu_init_channel_buffer(struct idmac_channel *ichan, + enum pixel_fmt pixel_fmt, + uint16_t width, uint16_t height, + uint32_t stride, + enum ipu_rotate_mode rot_mode, + dma_addr_t phyaddr_0, dma_addr_t phyaddr_1) +{ + enum ipu_channel channel = ichan->dma_chan.chan_id; + struct idmac *idmac = to_idmac(ichan->dma_chan.device); + struct ipu *ipu = to_ipu(idmac); + union chan_param_mem params = {}; + unsigned long flags; + uint32_t reg; + uint32_t stride_bytes; + + stride_bytes = stride * bytes_per_pixel(pixel_fmt); + + if (stride_bytes % 4) { + dev_err(ipu->dev, + "Stride length must be 32-bit aligned, stride = %d, bytes = %d\n", + stride, stride_bytes); + return -EINVAL; + } + + /* IC channel's stride must be a multiple of 8 pixels */ + if ((channel <= 13) && (stride % 8)) { + dev_err(ipu->dev, "Stride must be 8 pixel multiple\n"); + return -EINVAL; + } + + /* Build parameter memory data for DMA channel */ + ipu_ch_param_set_size(¶ms, pixel_fmt, width, height, stride_bytes); + ipu_ch_param_set_buffer(¶ms, phyaddr_0, phyaddr_1); + ipu_ch_param_set_rotation(¶ms, rot_mode); + /* Some channels (rotation) have restriction on burst length */ + switch (channel) { + case IDMAC_IC_7: /* Hangs with burst 8, 16, other values + invalid - Table 44-30 */ +/* + ipu_ch_param_set_burst_size(¶ms, 8); + */ + break; + case IDMAC_SDC_0: + case IDMAC_SDC_1: + /* In original code only IPU_PIX_FMT_RGB565 was setting burst */ + ipu_ch_param_set_burst_size(¶ms, 16); + break; + case IDMAC_IC_0: + default: + break; + } + + spin_lock_irqsave(&ipu->lock, flags); + + ipu_write_param_mem(dma_param_addr(channel), (uint32_t *)¶ms, 10); + + reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL); + + if (phyaddr_1) + reg |= 1UL << channel; + else + reg &= ~(1UL << channel); + + idmac_write_ipureg(ipu, reg, IPU_CHA_DB_MODE_SEL); + + ichan->status = IPU_CHANNEL_READY; + + spin_unlock_irqrestore(ipu->lock, flags); + + return 0; +} + +/** + * ipu_select_buffer() - mark a channel's buffer as ready. + * @channel: channel ID. + * @buffer_n: buffer number to mark ready. + */ +static void ipu_select_buffer(enum ipu_channel channel, int buffer_n) +{ + /* No locking - this is a write-one-to-set register, cleared by IPU */ + if (buffer_n == 0) + /* Mark buffer 0 as ready. */ + idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF0_RDY); + else + /* Mark buffer 1 as ready. */ + idmac_write_ipureg(&ipu_data, 1UL << channel, IPU_CHA_BUF1_RDY); +} + +/** + * ipu_update_channel_buffer() - update physical address of a channel buffer. + * @channel: channel ID. + * @buffer_n: buffer number to update. + * 0 or 1 are the only valid values. + * @phyaddr: buffer physical address. + * @return: Returns 0 on success or negative error code on failure. This + * function will fail if the buffer is set to ready. + */ +/* Called under spin_lock(_irqsave)(&ichan->lock) */ +static int ipu_update_channel_buffer(enum ipu_channel channel, + int buffer_n, dma_addr_t phyaddr) +{ + uint32_t reg; + unsigned long flags; + + spin_lock_irqsave(&ipu_data.lock, flags); + + if (buffer_n == 0) { + reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY); + if (reg & (1UL << channel)) { + spin_unlock_irqrestore(&ipu_data.lock, flags); + return -EACCES; + } + + /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */ + idmac_write_ipureg(&ipu_data, dma_param_addr(channel) + + 0x0008UL, IPU_IMA_ADDR); + idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA); + } else { + reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY); + if (reg & (1UL << channel)) { + spin_unlock_irqrestore(&ipu_data.lock, flags); + return -EACCES; + } + + /* Check if double-buffering is already enabled */ + reg = idmac_read_ipureg(&ipu_data, IPU_CHA_DB_MODE_SEL); + + if (!(reg & (1UL << channel))) + idmac_write_ipureg(&ipu_data, reg | (1UL << channel), + IPU_CHA_DB_MODE_SEL); + + /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 1) */ + idmac_write_ipureg(&ipu_data, dma_param_addr(channel) + + 0x0009UL, IPU_IMA_ADDR); + idmac_write_ipureg(&ipu_data, phyaddr, IPU_IMA_DATA); + } + + spin_unlock_irqrestore(&ipu_data.lock, flags); + + return 0; +} + +/* Called under spin_lock_irqsave(&ichan->lock) */ +static int ipu_submit_channel_buffers(struct idmac_channel *ichan, + struct idmac_tx_desc *desc) +{ + struct scatterlist *sg; + int i, ret = 0; + + for (i = 0, sg = desc->sg; i < 2 && sg; i++) { + if (!ichan->sg[i]) { + ichan->sg[i] = sg; + + /* + * On first invocation this shouldn't be necessary, the + * call to ipu_init_channel_buffer() above will set + * addresses for us, so we could make it conditional + * on status >= IPU_CHANNEL_ENABLED, but doing it again + * shouldn't hurt either. + */ + ret = ipu_update_channel_buffer(ichan->dma_chan.chan_id, i, + sg_dma_address(sg)); + if (ret < 0) + return ret; + + ipu_select_buffer(ichan->dma_chan.chan_id, i); + + sg = sg_next(sg); + } + } + + return ret; +} + +static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct idmac_tx_desc *desc = to_tx_desc(tx); + struct idmac_channel *ichan = to_idmac_chan(tx->chan); + struct idmac *idmac = to_idmac(tx->chan->device); + struct ipu *ipu = to_ipu(idmac); + dma_cookie_t cookie; + unsigned long flags; + + /* Sanity check */ + if (!list_empty(&desc->list)) { + /* The descriptor doesn't belong to client */ + dev_err(&ichan->dma_chan.dev->device, + "Descriptor %p not prepared!\n", tx); + return -EBUSY; + } + + mutex_lock(&ichan->chan_mutex); + + if (ichan->status < IPU_CHANNEL_READY) { + struct idmac_video_param *video = &ichan->params.video; + /* + * Initial buffer assignment - the first two sg-entries from + * the descriptor will end up in the IDMAC buffers + */ + dma_addr_t dma_1 = sg_is_last(desc->sg) ? 0 : + sg_dma_address(&desc->sg[1]); + + WARN_ON(ichan->sg[0] || ichan->sg[1]); + + cookie = ipu_init_channel_buffer(ichan, + video->out_pixel_fmt, + video->out_width, + video->out_height, + video->out_stride, + IPU_ROTATE_NONE, + sg_dma_address(&desc->sg[0]), + dma_1); + if (cookie < 0) + goto out; + } + + /* ipu->lock can be taken under ichan->lock, but not v.v. */ + spin_lock_irqsave(&ichan->lock, flags); + + /* submit_buffers() atomically verifies and fills empty sg slots */ + cookie = ipu_submit_channel_buffers(ichan, desc); + + spin_unlock_irqrestore(&ichan->lock, flags); + + if (cookie < 0) + goto out; + + cookie = ichan->dma_chan.cookie; + + if (++cookie < 0) + cookie = 1; + + /* from dmaengine.h: "last cookie value returned to client" */ + ichan->dma_chan.cookie = cookie; + tx->cookie = cookie; + spin_lock_irqsave(&ichan->lock, flags); + list_add_tail(&desc->list, &ichan->queue); + spin_unlock_irqrestore(&ichan->lock, flags); + + if (ichan->status < IPU_CHANNEL_ENABLED) { + int ret = ipu_enable_channel(idmac, ichan); + if (ret < 0) { + cookie = ret; + spin_lock_irqsave(&ichan->lock, flags); + list_del_init(&desc->list); + spin_unlock_irqrestore(&ichan->lock, flags); + tx->cookie = cookie; + ichan->dma_chan.cookie = cookie; + } + } + + dump_idmac_reg(ipu); + +out: + mutex_unlock(&ichan->chan_mutex); + + return cookie; +} + +/* Called with ichan->chan_mutex held */ +static int idmac_desc_alloc(struct idmac_channel *ichan, int n) +{ + struct idmac_tx_desc *desc = vmalloc(n * sizeof(struct idmac_tx_desc)); + struct idmac *idmac = to_idmac(ichan->dma_chan.device); + + if (!desc) + return -ENOMEM; + + /* No interrupts, just disable the tasklet for a moment */ + tasklet_disable(&to_ipu(idmac)->tasklet); + + ichan->n_tx_desc = n; + ichan->desc = desc; + INIT_LIST_HEAD(&ichan->queue); + INIT_LIST_HEAD(&ichan->free_list); + + while (n--) { + struct dma_async_tx_descriptor *txd = &desc->txd; + + memset(txd, 0, sizeof(*txd)); + dma_async_tx_descriptor_init(txd, &ichan->dma_chan); + txd->tx_submit = idmac_tx_submit; + txd->chan = &ichan->dma_chan; + INIT_LIST_HEAD(&txd->tx_list); + + list_add(&desc->list, &ichan->free_list); + + desc++; + } + + tasklet_enable(&to_ipu(idmac)->tasklet); + + return 0; +} + +/** + * ipu_init_channel() - initialize an IPU channel. + * @idmac: IPU DMAC context. + * @ichan: pointer to the channel object. + * @return 0 on success or negative error code on failure. + */ +static int ipu_init_channel(struct idmac *idmac, struct idmac_channel *ichan) +{ + union ipu_channel_param *params = &ichan->params; + uint32_t ipu_conf; + enum ipu_channel channel = ichan->dma_chan.chan_id; + unsigned long flags; + uint32_t reg; + struct ipu *ipu = to_ipu(idmac); + int ret = 0, n_desc = 0; + + dev_dbg(ipu->dev, "init channel = %d\n", channel); + + if (channel != IDMAC_SDC_0 && channel != IDMAC_SDC_1 && + channel != IDMAC_IC_7) + return -EINVAL; + + spin_lock_irqsave(&ipu->lock, flags); + + switch (channel) { + case IDMAC_IC_7: + n_desc = 16; + reg = idmac_read_icreg(ipu, IC_CONF); + idmac_write_icreg(ipu, reg & ~IC_CONF_CSI_MEM_WR_EN, IC_CONF); + break; + case IDMAC_IC_0: + n_desc = 16; + reg = idmac_read_ipureg(ipu, IPU_FS_PROC_FLOW); + idmac_write_ipureg(ipu, reg & ~FS_ENC_IN_VALID, IPU_FS_PROC_FLOW); + ret = ipu_ic_init_prpenc(ipu, params, true); + break; + case IDMAC_SDC_0: + case IDMAC_SDC_1: + n_desc = 4; + default: + break; + } + + ipu->channel_init_mask |= 1L << channel; + + /* Enable IPU sub module */ + ipu_conf = idmac_read_ipureg(ipu, IPU_CONF) | + ipu_channel_conf_mask(channel); + idmac_write_ipureg(ipu, ipu_conf, IPU_CONF); + + spin_unlock_irqrestore(&ipu->lock, flags); + + if (n_desc && !ichan->desc) + ret = idmac_desc_alloc(ichan, n_desc); + + dump_idmac_reg(ipu); + + return ret; +} + +/** + * ipu_uninit_channel() - uninitialize an IPU channel. + * @idmac: IPU DMAC context. + * @ichan: pointer to the channel object. + */ +static void ipu_uninit_channel(struct idmac *idmac, struct idmac_channel *ichan) +{ + enum ipu_channel channel = ichan->dma_chan.chan_id; + unsigned long flags; + uint32_t reg; + unsigned long chan_mask = 1UL << channel; + uint32_t ipu_conf; + struct ipu *ipu = to_ipu(idmac); + + spin_lock_irqsave(&ipu->lock, flags); + + if (!(ipu->channel_init_mask & chan_mask)) { + dev_err(ipu->dev, "Channel already uninitialized %d\n", + channel); + spin_unlock_irqrestore(&ipu->lock, flags); + return; + } + + /* Reset the double buffer */ + reg = idmac_read_ipureg(ipu, IPU_CHA_DB_MODE_SEL); + idmac_write_ipureg(ipu, reg & ~chan_mask, IPU_CHA_DB_MODE_SEL); + + ichan->sec_chan_en = false; + + switch (channel) { + case IDMAC_IC_7: + reg = idmac_read_icreg(ipu, IC_CONF); + idmac_write_icreg(ipu, reg & ~(IC_CONF_RWS_EN | IC_CONF_PRPENC_EN), + IC_CONF); + break; + case IDMAC_IC_0: + reg = idmac_read_icreg(ipu, IC_CONF); + idmac_write_icreg(ipu, reg & ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1), + IC_CONF); + break; + case IDMAC_SDC_0: + case IDMAC_SDC_1: + default: + break; + } + + ipu->channel_init_mask &= ~(1L << channel); + + ipu_conf = idmac_read_ipureg(ipu, IPU_CONF) & + ~ipu_channel_conf_mask(channel); + idmac_write_ipureg(ipu, ipu_conf, IPU_CONF); + + spin_unlock_irqrestore(&ipu->lock, flags); + + ichan->n_tx_desc = 0; + vfree(ichan->desc); + ichan->desc = NULL; +} + +/** + * ipu_disable_channel() - disable an IPU channel. + * @idmac: IPU DMAC context. + * @ichan: channel object pointer. + * @wait_for_stop: flag to set whether to wait for channel end of frame or + * return immediately. + * @return: 0 on success or negative error code on failure. + */ +static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, + bool wait_for_stop) +{ + enum ipu_channel channel = ichan->dma_chan.chan_id; + struct ipu *ipu = to_ipu(idmac); + uint32_t reg; + unsigned long flags; + unsigned long chan_mask = 1UL << channel; + unsigned int timeout; + + if (wait_for_stop && channel != IDMAC_SDC_1 && channel != IDMAC_SDC_0) { + timeout = 40; + /* This waiting always fails. Related to spurious irq problem */ + while ((idmac_read_icreg(ipu, IDMAC_CHA_BUSY) & chan_mask) || + (ipu_channel_status(ipu, channel) == TASK_STAT_ACTIVE)) { + timeout--; + msleep(10); + + if (!timeout) { + dev_dbg(ipu->dev, + "Warning: timeout waiting for channel %u to " + "stop: buf0_rdy = 0x%08X, buf1_rdy = 0x%08X, " + "busy = 0x%08X, tstat = 0x%08X\n", channel, + idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY), + idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY), + idmac_read_icreg(ipu, IDMAC_CHA_BUSY), + idmac_read_ipureg(ipu, IPU_TASKS_STAT)); + break; + } + } + dev_dbg(ipu->dev, "timeout = %d * 10ms\n", 40 - timeout); + } + /* SDC BG and FG must be disabled before DMA is disabled */ + if (wait_for_stop && (channel == IDMAC_SDC_0 || + channel == IDMAC_SDC_1)) { + for (timeout = 5; + timeout && !ipu_irq_status(ichan->eof_irq); timeout--) + msleep(5); + } + + spin_lock_irqsave(&ipu->lock, flags); + + /* Disable IC task */ + ipu_ic_disable_task(ipu, channel); + + /* Disable DMA channel(s) */ + reg = idmac_read_icreg(ipu, IDMAC_CHA_EN); + idmac_write_icreg(ipu, reg & ~chan_mask, IDMAC_CHA_EN); + + /* + * Problem (observed with channel DMAIC_7): after enabling the channel + * and initialising buffers, there comes an interrupt with current still + * pointing at buffer 0, whereas it should use buffer 0 first and only + * generate an interrupt when it is done, then current should already + * point to buffer 1. This spurious interrupt also comes on channel + * DMASDC_0. With DMAIC_7 normally, is we just leave the ISR after the + * first interrupt, there comes the second with current correctly + * pointing to buffer 1 this time. But sometimes this second interrupt + * doesn't come and the channel hangs. Clearing BUFx_RDY when disabling + * the channel seems to prevent the channel from hanging, but it doesn't + * prevent the spurious interrupt. This might also be unsafe. Think + * about the IDMAC controller trying to switch to a buffer, when we + * clear the ready bit, and re-enable it a moment later. + */ + reg = idmac_read_ipureg(ipu, IPU_CHA_BUF0_RDY); + idmac_write_ipureg(ipu, 0, IPU_CHA_BUF0_RDY); + idmac_write_ipureg(ipu, reg & ~(1UL << channel), IPU_CHA_BUF0_RDY); + + reg = idmac_read_ipureg(ipu, IPU_CHA_BUF1_RDY); + idmac_write_ipureg(ipu, 0, IPU_CHA_BUF1_RDY); + idmac_write_ipureg(ipu, reg & ~(1UL << channel), IPU_CHA_BUF1_RDY); + + spin_unlock_irqrestore(&ipu->lock, flags); + + return 0; +} + +/* + * We have several possibilities here: + * current BUF next BUF + * + * not last sg next not last sg + * not last sg next last sg + * last sg first sg from next descriptor + * last sg NULL + * + * Besides, the descriptor queue might be empty or not. We process all these + * cases carefully. + */ +static irqreturn_t idmac_interrupt(int irq, void *dev_id) +{ + struct idmac_channel *ichan = dev_id; + unsigned int chan_id = ichan->dma_chan.chan_id; + struct scatterlist **sg, *sgnext, *sgnew = NULL; + /* Next transfer descriptor */ + struct idmac_tx_desc *desc = NULL, *descnew; + dma_async_tx_callback callback; + void *callback_param; + bool done = false; + u32 ready0 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY), + ready1 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY), + curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF); + + /* IDMAC has cleared the respective BUFx_RDY bit, we manage the buffer */ + + pr_debug("IDMAC irq %d\n", irq); + /* Other interrupts do not interfere with this channel */ + spin_lock(&ichan->lock); + + if (unlikely(chan_id != IDMAC_SDC_0 && chan_id != IDMAC_SDC_1 && + ((curbuf >> chan_id) & 1) == ichan->active_buffer)) { + int i = 100; + + /* This doesn't help. See comment in ipu_disable_channel() */ + while (--i) { + curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF); + if (((curbuf >> chan_id) & 1) != ichan->active_buffer) + break; + cpu_relax(); + } + + if (!i) { + spin_unlock(&ichan->lock); + dev_dbg(ichan->dma_chan.device->dev, + "IRQ on active buffer on channel %x, active " + "%d, ready %x, %x, current %x!\n", chan_id, + ichan->active_buffer, ready0, ready1, curbuf); + return IRQ_NONE; + } + } + + if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) || + (!ichan->active_buffer && (ready0 >> chan_id) & 1) + )) { + spin_unlock(&ichan->lock); + dev_dbg(ichan->dma_chan.device->dev, + "IRQ with active buffer still ready on channel %x, " + "active %d, ready %x, %x!\n", chan_id, + ichan->active_buffer, ready0, ready1); + return IRQ_NONE; + } + + if (unlikely(list_empty(&ichan->queue))) { + spin_unlock(&ichan->lock); + dev_err(ichan->dma_chan.device->dev, + "IRQ without queued buffers on channel %x, active %d, " + "ready %x, %x!\n", chan_id, + ichan->active_buffer, ready0, ready1); + return IRQ_NONE; + } + + /* + * active_buffer is a software flag, it shows which buffer we are + * currently expecting back from the hardware, IDMAC should be + * processing the other buffer already + */ + sg = &ichan->sg[ichan->active_buffer]; + sgnext = ichan->sg[!ichan->active_buffer]; + + /* + * if sgnext == NULL sg must be the last element in a scatterlist and + * queue must be empty + */ + if (unlikely(!sgnext)) { + if (unlikely(sg_next(*sg))) { + dev_err(ichan->dma_chan.device->dev, + "Broken buffer-update locking on channel %x!\n", + chan_id); + /* We'll let the user catch up */ + } else { + /* Underrun */ + ipu_ic_disable_task(&ipu_data, chan_id); + dev_dbg(ichan->dma_chan.device->dev, + "Underrun on channel %x\n", chan_id); + ichan->status = IPU_CHANNEL_READY; + /* Continue to check for complete descriptor */ + } + } + + desc = list_entry(ichan->queue.next, struct idmac_tx_desc, list); + + /* First calculate and submit the next sg element */ + if (likely(sgnext)) + sgnew = sg_next(sgnext); + + if (unlikely(!sgnew)) { + /* Start a new scatterlist, if any queued */ + if (likely(desc->list.next != &ichan->queue)) { + descnew = list_entry(desc->list.next, + struct idmac_tx_desc, list); + sgnew = &descnew->sg[0]; + } + } + + if (unlikely(!sg_next(*sg)) || !sgnext) { + /* + * Last element in scatterlist done, remove from the queue, + * _init for debugging + */ + list_del_init(&desc->list); + done = true; + } + + *sg = sgnew; + + if (likely(sgnew)) { + int ret; + + ret = ipu_update_channel_buffer(chan_id, ichan->active_buffer, + sg_dma_address(*sg)); + if (ret < 0) + dev_err(ichan->dma_chan.device->dev, + "Failed to update buffer on channel %x buffer %d!\n", + chan_id, ichan->active_buffer); + else + ipu_select_buffer(chan_id, ichan->active_buffer); + } + + /* Flip the active buffer - even if update above failed */ + ichan->active_buffer = !ichan->active_buffer; + if (done) + ichan->completed = desc->txd.cookie; + + callback = desc->txd.callback; + callback_param = desc->txd.callback_param; + + spin_unlock(&ichan->lock); + + if (done && (desc->txd.flags & DMA_PREP_INTERRUPT) && callback) + callback(callback_param); + + return IRQ_HANDLED; +} + +static void ipu_gc_tasklet(unsigned long arg) +{ + struct ipu *ipu = (struct ipu *)arg; + int i; + + for (i = 0; i < IPU_CHANNELS_NUM; i++) { + struct idmac_channel *ichan = ipu->channel + i; + struct idmac_tx_desc *desc; + unsigned long flags; + int j; + + for (j = 0; j < ichan->n_tx_desc; j++) { + desc = ichan->desc + j; + spin_lock_irqsave(&ichan->lock, flags); + if (async_tx_test_ack(&desc->txd)) { + list_move(&desc->list, &ichan->free_list); + async_tx_clear_ack(&desc->txd); + } + spin_unlock_irqrestore(&ichan->lock, flags); + } + } +} + +/* + * At the time .device_alloc_chan_resources() method is called, we cannot know, + * whether the client will accept the channel. Thus we must only check, if we + * can satisfy client's request but the only real criterion to verify, whether + * the client has accepted our offer is the client_count. That's why we have to + * perform the rest of our allocation tasks on the first call to this function. + */ +static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan, + struct scatterlist *sgl, unsigned int sg_len, + enum dma_data_direction direction, unsigned long tx_flags) +{ + struct idmac_channel *ichan = to_idmac_chan(chan); + struct idmac_tx_desc *desc = NULL; + struct dma_async_tx_descriptor *txd = NULL; + unsigned long flags; + + /* We only can handle these three channels so far */ + if (ichan->dma_chan.chan_id != IDMAC_SDC_0 && ichan->dma_chan.chan_id != IDMAC_SDC_1 && + ichan->dma_chan.chan_id != IDMAC_IC_7) + return NULL; + + if (direction != DMA_FROM_DEVICE && direction != DMA_TO_DEVICE) { + dev_err(chan->device->dev, "Invalid DMA direction %d!\n", direction); + return NULL; + } + + mutex_lock(&ichan->chan_mutex); + + spin_lock_irqsave(&ichan->lock, flags); + if (!list_empty(&ichan->free_list)) { + desc = list_entry(ichan->free_list.next, + struct idmac_tx_desc, list); + + list_del_init(&desc->list); + + desc->sg_len = sg_len; + desc->sg = sgl; + txd = &desc->txd; + txd->flags = tx_flags; + } + spin_unlock_irqrestore(&ichan->lock, flags); + + mutex_unlock(&ichan->chan_mutex); + + tasklet_schedule(&to_ipu(to_idmac(chan->device))->tasklet); + + return txd; +} + +/* Re-select the current buffer and re-activate the channel */ +static void idmac_issue_pending(struct dma_chan *chan) +{ + struct idmac_channel *ichan = to_idmac_chan(chan); + struct idmac *idmac = to_idmac(chan->device); + struct ipu *ipu = to_ipu(idmac); + unsigned long flags; + + /* This is not always needed, but doesn't hurt either */ + spin_lock_irqsave(&ipu->lock, flags); + ipu_select_buffer(ichan->dma_chan.chan_id, ichan->active_buffer); + spin_unlock_irqrestore(&ipu->lock, flags); + + /* + * Might need to perform some parts of initialisation from + * ipu_enable_channel(), but not all, we do not want to reset to buffer + * 0, don't need to set priority again either, but re-enabling the task + * and the channel might be a good idea. + */ +} + +static void __idmac_terminate_all(struct dma_chan *chan) +{ + struct idmac_channel *ichan = to_idmac_chan(chan); + struct idmac *idmac = to_idmac(chan->device); + unsigned long flags; + int i; + + ipu_disable_channel(idmac, ichan, + ichan->status >= IPU_CHANNEL_ENABLED); + + tasklet_disable(&to_ipu(idmac)->tasklet); + + /* ichan->queue is modified in ISR, have to spinlock */ + spin_lock_irqsave(&ichan->lock, flags); + list_splice_init(&ichan->queue, &ichan->free_list); + + if (ichan->desc) + for (i = 0; i < ichan->n_tx_desc; i++) { + struct idmac_tx_desc *desc = ichan->desc + i; + if (list_empty(&desc->list)) + /* Descriptor was prepared, but not submitted */ + list_add(&desc->list, + &ichan->free_list); + + async_tx_clear_ack(&desc->txd); + } + + ichan->sg[0] = NULL; + ichan->sg[1] = NULL; + spin_unlock_irqrestore(&ichan->lock, flags); + + tasklet_enable(&to_ipu(idmac)->tasklet); + + ichan->status = IPU_CHANNEL_INITIALIZED; +} + +static void idmac_terminate_all(struct dma_chan *chan) +{ + struct idmac_channel *ichan = to_idmac_chan(chan); + + mutex_lock(&ichan->chan_mutex); + + __idmac_terminate_all(chan); + + mutex_unlock(&ichan->chan_mutex); +} + +static int idmac_alloc_chan_resources(struct dma_chan *chan) +{ + struct idmac_channel *ichan = to_idmac_chan(chan); + struct idmac *idmac = to_idmac(chan->device); + int ret; + + /* dmaengine.c now guarantees to only offer free channels */ + BUG_ON(chan->client_count > 1); + WARN_ON(ichan->status != IPU_CHANNEL_FREE); + + chan->cookie = 1; + ichan->completed = -ENXIO; + + ret = ipu_irq_map(ichan->dma_chan.chan_id); + if (ret < 0) + goto eimap; + + ichan->eof_irq = ret; + ret = request_irq(ichan->eof_irq, idmac_interrupt, 0, + ichan->eof_name, ichan); + if (ret < 0) + goto erirq; + + ret = ipu_init_channel(idmac, ichan); + if (ret < 0) + goto eichan; + + ichan->status = IPU_CHANNEL_INITIALIZED; + + dev_dbg(&ichan->dma_chan.dev->device, "Found channel 0x%x, irq %d\n", + ichan->dma_chan.chan_id, ichan->eof_irq); + + return ret; + +eichan: + free_irq(ichan->eof_irq, ichan); +erirq: + ipu_irq_unmap(ichan->dma_chan.chan_id); +eimap: + return ret; +} + +static void idmac_free_chan_resources(struct dma_chan *chan) +{ + struct idmac_channel *ichan = to_idmac_chan(chan); + struct idmac *idmac = to_idmac(chan->device); + + mutex_lock(&ichan->chan_mutex); + + __idmac_terminate_all(chan); + + if (ichan->status > IPU_CHANNEL_FREE) { + free_irq(ichan->eof_irq, ichan); + ipu_irq_unmap(ichan->dma_chan.chan_id); + } + + ichan->status = IPU_CHANNEL_FREE; + + ipu_uninit_channel(idmac, ichan); + + mutex_unlock(&ichan->chan_mutex); + + tasklet_schedule(&to_ipu(idmac)->tasklet); +} + +static enum dma_status idmac_is_tx_complete(struct dma_chan *chan, + dma_cookie_t cookie, dma_cookie_t *done, dma_cookie_t *used) +{ + struct idmac_channel *ichan = to_idmac_chan(chan); + + if (done) + *done = ichan->completed; + if (used) + *used = chan->cookie; + if (cookie != chan->cookie) + return DMA_ERROR; + return DMA_SUCCESS; +} + +static int __init ipu_idmac_init(struct ipu *ipu) +{ + struct idmac *idmac = &ipu->idmac; + struct dma_device *dma = &idmac->dma; + int i; + + dma_cap_set(DMA_SLAVE, dma->cap_mask); + dma_cap_set(DMA_PRIVATE, dma->cap_mask); + + /* Compulsory common fields */ + dma->dev = ipu->dev; + dma->device_alloc_chan_resources = idmac_alloc_chan_resources; + dma->device_free_chan_resources = idmac_free_chan_resources; + dma->device_is_tx_complete = idmac_is_tx_complete; + dma->device_issue_pending = idmac_issue_pending; + + /* Compulsory for DMA_SLAVE fields */ + dma->device_prep_slave_sg = idmac_prep_slave_sg; + dma->device_terminate_all = idmac_terminate_all; + + INIT_LIST_HEAD(&dma->channels); + for (i = 0; i < IPU_CHANNELS_NUM; i++) { + struct idmac_channel *ichan = ipu->channel + i; + struct dma_chan *dma_chan = &ichan->dma_chan; + + spin_lock_init(&ichan->lock); + mutex_init(&ichan->chan_mutex); + + ichan->status = IPU_CHANNEL_FREE; + ichan->sec_chan_en = false; + ichan->completed = -ENXIO; + snprintf(ichan->eof_name, sizeof(ichan->eof_name), "IDMAC EOF %d", i); + + dma_chan->device = &idmac->dma; + dma_chan->cookie = 1; + dma_chan->chan_id = i; + list_add_tail(&ichan->dma_chan.device_node, &dma->channels); + } + + idmac_write_icreg(ipu, 0x00000070, IDMAC_CONF); + + return dma_async_device_register(&idmac->dma); +} + +static void ipu_idmac_exit(struct ipu *ipu) +{ + int i; + struct idmac *idmac = &ipu->idmac; + + for (i = 0; i < IPU_CHANNELS_NUM; i++) { + struct idmac_channel *ichan = ipu->channel + i; + + idmac_terminate_all(&ichan->dma_chan); + idmac_prep_slave_sg(&ichan->dma_chan, NULL, 0, DMA_NONE, 0); + } + + dma_async_device_unregister(&idmac->dma); +} + +/***************************************************************************** + * IPU common probe / remove + */ + +static int ipu_probe(struct platform_device *pdev) +{ + struct ipu_platform_data *pdata = pdev->dev.platform_data; + struct resource *mem_ipu, *mem_ic; + int ret; + + spin_lock_init(&ipu_data.lock); + + mem_ipu = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mem_ic = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!pdata || !mem_ipu || !mem_ic) + return -EINVAL; + + ipu_data.dev = &pdev->dev; + + platform_set_drvdata(pdev, &ipu_data); + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + goto err_noirq; + + ipu_data.irq_fn = ret; + ret = platform_get_irq(pdev, 1); + if (ret < 0) + goto err_noirq; + + ipu_data.irq_err = ret; + ipu_data.irq_base = pdata->irq_base; + + dev_dbg(&pdev->dev, "fn irq %u, err irq %u, irq-base %u\n", + ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base); + + /* Remap IPU common registers */ + ipu_data.reg_ipu = ioremap(mem_ipu->start, + mem_ipu->end - mem_ipu->start + 1); + if (!ipu_data.reg_ipu) { + ret = -ENOMEM; + goto err_ioremap_ipu; + } + + /* Remap Image Converter and Image DMA Controller registers */ + ipu_data.reg_ic = ioremap(mem_ic->start, + mem_ic->end - mem_ic->start + 1); + if (!ipu_data.reg_ic) { + ret = -ENOMEM; + goto err_ioremap_ic; + } + + /* Get IPU clock */ + ipu_data.ipu_clk = clk_get(&pdev->dev, "ipu_clk"); + if (IS_ERR(ipu_data.ipu_clk)) { + ret = PTR_ERR(ipu_data.ipu_clk); + goto err_clk_get; + } + + /* Make sure IPU HSP clock is running */ + clk_enable(ipu_data.ipu_clk); + + /* Disable all interrupts */ + idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_1); + idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_2); + idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_3); + idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_4); + idmac_write_ipureg(&ipu_data, 0, IPU_INT_CTRL_5); + + dev_dbg(&pdev->dev, "%s @ 0x%08lx, fn irq %u, err irq %u\n", pdev->name, + (unsigned long)mem_ipu->start, ipu_data.irq_fn, ipu_data.irq_err); + + ret = ipu_irq_attach_irq(&ipu_data, pdev); + if (ret < 0) + goto err_attach_irq; + + /* Initialize DMA engine */ + ret = ipu_idmac_init(&ipu_data); + if (ret < 0) + goto err_idmac_init; + + tasklet_init(&ipu_data.tasklet, ipu_gc_tasklet, (unsigned long)&ipu_data); + + ipu_data.dev = &pdev->dev; + + dev_dbg(ipu_data.dev, "IPU initialized\n"); + + return 0; + +err_idmac_init: +err_attach_irq: + ipu_irq_detach_irq(&ipu_data, pdev); + clk_disable(ipu_data.ipu_clk); + clk_put(ipu_data.ipu_clk); +err_clk_get: + iounmap(ipu_data.reg_ic); +err_ioremap_ic: + iounmap(ipu_data.reg_ipu); +err_ioremap_ipu: +err_noirq: + dev_err(&pdev->dev, "Failed to probe IPU: %d\n", ret); + return ret; +} + +static int ipu_remove(struct platform_device *pdev) +{ + struct ipu *ipu = platform_get_drvdata(pdev); + + ipu_idmac_exit(ipu); + ipu_irq_detach_irq(ipu, pdev); + clk_disable(ipu->ipu_clk); + clk_put(ipu->ipu_clk); + iounmap(ipu->reg_ic); + iounmap(ipu->reg_ipu); + tasklet_kill(&ipu->tasklet); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +/* + * We need two MEM resources - with IPU-common and Image Converter registers, + * including PF_CONF and IDMAC_* registers, and two IRQs - function and error + */ +static struct platform_driver ipu_platform_driver = { + .driver = { + .name = "ipu-core", + .owner = THIS_MODULE, + }, + .remove = ipu_remove, +}; + +static int __init ipu_init(void) +{ + return platform_driver_probe(&ipu_platform_driver, ipu_probe); +} +subsys_initcall(ipu_init); + +MODULE_DESCRIPTION("IPU core driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_ALIAS("platform:ipu-core"); diff --git a/drivers/dma/ipu/ipu_intern.h b/drivers/dma/ipu/ipu_intern.h new file mode 100644 index 00000000000..545cf11a94a --- /dev/null +++ b/drivers/dma/ipu/ipu_intern.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, + * + * Copyright (C) 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _IPU_INTERN_H_ +#define _IPU_INTERN_H_ + +#include +#include +#include + +/* IPU Common registers */ +#define IPU_CONF 0x00 +#define IPU_CHA_BUF0_RDY 0x04 +#define IPU_CHA_BUF1_RDY 0x08 +#define IPU_CHA_DB_MODE_SEL 0x0C +#define IPU_CHA_CUR_BUF 0x10 +#define IPU_FS_PROC_FLOW 0x14 +#define IPU_FS_DISP_FLOW 0x18 +#define IPU_TASKS_STAT 0x1C +#define IPU_IMA_ADDR 0x20 +#define IPU_IMA_DATA 0x24 +#define IPU_INT_CTRL_1 0x28 +#define IPU_INT_CTRL_2 0x2C +#define IPU_INT_CTRL_3 0x30 +#define IPU_INT_CTRL_4 0x34 +#define IPU_INT_CTRL_5 0x38 +#define IPU_INT_STAT_1 0x3C +#define IPU_INT_STAT_2 0x40 +#define IPU_INT_STAT_3 0x44 +#define IPU_INT_STAT_4 0x48 +#define IPU_INT_STAT_5 0x4C +#define IPU_BRK_CTRL_1 0x50 +#define IPU_BRK_CTRL_2 0x54 +#define IPU_BRK_STAT 0x58 +#define IPU_DIAGB_CTRL 0x5C + +/* IPU_CONF Register bits */ +#define IPU_CONF_CSI_EN 0x00000001 +#define IPU_CONF_IC_EN 0x00000002 +#define IPU_CONF_ROT_EN 0x00000004 +#define IPU_CONF_PF_EN 0x00000008 +#define IPU_CONF_SDC_EN 0x00000010 +#define IPU_CONF_ADC_EN 0x00000020 +#define IPU_CONF_DI_EN 0x00000040 +#define IPU_CONF_DU_EN 0x00000080 +#define IPU_CONF_PXL_ENDIAN 0x00000100 + +/* Image Converter Registers */ +#define IC_CONF 0x88 +#define IC_PRP_ENC_RSC 0x8C +#define IC_PRP_VF_RSC 0x90 +#define IC_PP_RSC 0x94 +#define IC_CMBP_1 0x98 +#define IC_CMBP_2 0x9C +#define PF_CONF 0xA0 +#define IDMAC_CONF 0xA4 +#define IDMAC_CHA_EN 0xA8 +#define IDMAC_CHA_PRI 0xAC +#define IDMAC_CHA_BUSY 0xB0 + +/* Image Converter Register bits */ +#define IC_CONF_PRPENC_EN 0x00000001 +#define IC_CONF_PRPENC_CSC1 0x00000002 +#define IC_CONF_PRPENC_ROT_EN 0x00000004 +#define IC_CONF_PRPVF_EN 0x00000100 +#define IC_CONF_PRPVF_CSC1 0x00000200 +#define IC_CONF_PRPVF_CSC2 0x00000400 +#define IC_CONF_PRPVF_CMB 0x00000800 +#define IC_CONF_PRPVF_ROT_EN 0x00001000 +#define IC_CONF_PP_EN 0x00010000 +#define IC_CONF_PP_CSC1 0x00020000 +#define IC_CONF_PP_CSC2 0x00040000 +#define IC_CONF_PP_CMB 0x00080000 +#define IC_CONF_PP_ROT_EN 0x00100000 +#define IC_CONF_IC_GLB_LOC_A 0x10000000 +#define IC_CONF_KEY_COLOR_EN 0x20000000 +#define IC_CONF_RWS_EN 0x40000000 +#define IC_CONF_CSI_MEM_WR_EN 0x80000000 + +#define IDMA_CHAN_INVALID 0x000000FF +#define IDMA_IC_0 0x00000001 +#define IDMA_IC_1 0x00000002 +#define IDMA_IC_2 0x00000004 +#define IDMA_IC_3 0x00000008 +#define IDMA_IC_4 0x00000010 +#define IDMA_IC_5 0x00000020 +#define IDMA_IC_6 0x00000040 +#define IDMA_IC_7 0x00000080 +#define IDMA_IC_8 0x00000100 +#define IDMA_IC_9 0x00000200 +#define IDMA_IC_10 0x00000400 +#define IDMA_IC_11 0x00000800 +#define IDMA_IC_12 0x00001000 +#define IDMA_IC_13 0x00002000 +#define IDMA_SDC_BG 0x00004000 +#define IDMA_SDC_FG 0x00008000 +#define IDMA_SDC_MASK 0x00010000 +#define IDMA_SDC_PARTIAL 0x00020000 +#define IDMA_ADC_SYS1_WR 0x00040000 +#define IDMA_ADC_SYS2_WR 0x00080000 +#define IDMA_ADC_SYS1_CMD 0x00100000 +#define IDMA_ADC_SYS2_CMD 0x00200000 +#define IDMA_ADC_SYS1_RD 0x00400000 +#define IDMA_ADC_SYS2_RD 0x00800000 +#define IDMA_PF_QP 0x01000000 +#define IDMA_PF_BSP 0x02000000 +#define IDMA_PF_Y_IN 0x04000000 +#define IDMA_PF_U_IN 0x08000000 +#define IDMA_PF_V_IN 0x10000000 +#define IDMA_PF_Y_OUT 0x20000000 +#define IDMA_PF_U_OUT 0x40000000 +#define IDMA_PF_V_OUT 0x80000000 + +#define TSTAT_PF_H264_PAUSE 0x00000001 +#define TSTAT_CSI2MEM_MASK 0x0000000C +#define TSTAT_CSI2MEM_OFFSET 2 +#define TSTAT_VF_MASK 0x00000600 +#define TSTAT_VF_OFFSET 9 +#define TSTAT_VF_ROT_MASK 0x000C0000 +#define TSTAT_VF_ROT_OFFSET 18 +#define TSTAT_ENC_MASK 0x00000180 +#define TSTAT_ENC_OFFSET 7 +#define TSTAT_ENC_ROT_MASK 0x00030000 +#define TSTAT_ENC_ROT_OFFSET 16 +#define TSTAT_PP_MASK 0x00001800 +#define TSTAT_PP_OFFSET 11 +#define TSTAT_PP_ROT_MASK 0x00300000 +#define TSTAT_PP_ROT_OFFSET 20 +#define TSTAT_PF_MASK 0x00C00000 +#define TSTAT_PF_OFFSET 22 +#define TSTAT_ADCSYS1_MASK 0x03000000 +#define TSTAT_ADCSYS1_OFFSET 24 +#define TSTAT_ADCSYS2_MASK 0x0C000000 +#define TSTAT_ADCSYS2_OFFSET 26 + +#define TASK_STAT_IDLE 0 +#define TASK_STAT_ACTIVE 1 +#define TASK_STAT_WAIT4READY 2 + +struct idmac { + struct dma_device dma; +}; + +struct ipu { + void __iomem *reg_ipu; + void __iomem *reg_ic; + unsigned int irq_fn; /* IPU Function IRQ to the CPU */ + unsigned int irq_err; /* IPU Error IRQ to the CPU */ + unsigned int irq_base; /* Beginning of the IPU IRQ range */ + unsigned long channel_init_mask; + spinlock_t lock; + struct clk *ipu_clk; + struct device *dev; + struct idmac idmac; + struct idmac_channel channel[IPU_CHANNELS_NUM]; + struct tasklet_struct tasklet; +}; + +#define to_idmac(d) container_of(d, struct idmac, dma) + +extern int ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev); +extern void ipu_irq_detach_irq(struct ipu *ipu, struct platform_device *dev); + +extern bool ipu_irq_status(uint32_t irq); +extern int ipu_irq_map(unsigned int source); +extern int ipu_irq_unmap(unsigned int source); + +#endif diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c new file mode 100644 index 00000000000..83f532cc767 --- /dev/null +++ b/drivers/dma/ipu/ipu_irq.c @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ipu_intern.h" + +/* + * Register read / write - shall be inlined by the compiler + */ +static u32 ipu_read_reg(struct ipu *ipu, unsigned long reg) +{ + return __raw_readl(ipu->reg_ipu + reg); +} + +static void ipu_write_reg(struct ipu *ipu, u32 value, unsigned long reg) +{ + __raw_writel(value, ipu->reg_ipu + reg); +} + + +/* + * IPU IRQ chip driver + */ + +#define IPU_IRQ_NR_FN_BANKS 3 +#define IPU_IRQ_NR_ERR_BANKS 2 +#define IPU_IRQ_NR_BANKS (IPU_IRQ_NR_FN_BANKS + IPU_IRQ_NR_ERR_BANKS) + +struct ipu_irq_bank { + unsigned int control; + unsigned int status; + spinlock_t lock; + struct ipu *ipu; +}; + +static struct ipu_irq_bank irq_bank[IPU_IRQ_NR_BANKS] = { + /* 3 groups of functional interrupts */ + { + .control = IPU_INT_CTRL_1, + .status = IPU_INT_STAT_1, + }, { + .control = IPU_INT_CTRL_2, + .status = IPU_INT_STAT_2, + }, { + .control = IPU_INT_CTRL_3, + .status = IPU_INT_STAT_3, + }, + /* 2 groups of error interrupts */ + { + .control = IPU_INT_CTRL_4, + .status = IPU_INT_STAT_4, + }, { + .control = IPU_INT_CTRL_5, + .status = IPU_INT_STAT_5, + }, +}; + +struct ipu_irq_map { + unsigned int irq; + int source; + struct ipu_irq_bank *bank; + struct ipu *ipu; +}; + +static struct ipu_irq_map irq_map[CONFIG_MX3_IPU_IRQS]; +/* Protects allocations from the above array of maps */ +static DEFINE_MUTEX(map_lock); +/* Protects register accesses and individual mappings */ +static DEFINE_SPINLOCK(bank_lock); + +static struct ipu_irq_map *src2map(unsigned int src) +{ + int i; + + for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) + if (irq_map[i].source == src) + return irq_map + i; + + return NULL; +} + +static void ipu_irq_unmask(unsigned int irq) +{ + struct ipu_irq_map *map = get_irq_chip_data(irq); + struct ipu_irq_bank *bank; + uint32_t reg; + unsigned long lock_flags; + + spin_lock_irqsave(&bank_lock, lock_flags); + + bank = map->bank; + if (!bank) { + spin_unlock_irqrestore(&bank_lock, lock_flags); + pr_err("IPU: %s(%u) - unmapped!\n", __func__, irq); + return; + } + + reg = ipu_read_reg(bank->ipu, bank->control); + reg |= (1UL << (map->source & 31)); + ipu_write_reg(bank->ipu, reg, bank->control); + + spin_unlock_irqrestore(&bank_lock, lock_flags); +} + +static void ipu_irq_mask(unsigned int irq) +{ + struct ipu_irq_map *map = get_irq_chip_data(irq); + struct ipu_irq_bank *bank; + uint32_t reg; + unsigned long lock_flags; + + spin_lock_irqsave(&bank_lock, lock_flags); + + bank = map->bank; + if (!bank) { + spin_unlock_irqrestore(&bank_lock, lock_flags); + pr_err("IPU: %s(%u) - unmapped!\n", __func__, irq); + return; + } + + reg = ipu_read_reg(bank->ipu, bank->control); + reg &= ~(1UL << (map->source & 31)); + ipu_write_reg(bank->ipu, reg, bank->control); + + spin_unlock_irqrestore(&bank_lock, lock_flags); +} + +static void ipu_irq_ack(unsigned int irq) +{ + struct ipu_irq_map *map = get_irq_chip_data(irq); + struct ipu_irq_bank *bank; + unsigned long lock_flags; + + spin_lock_irqsave(&bank_lock, lock_flags); + + bank = map->bank; + if (!bank) { + spin_unlock_irqrestore(&bank_lock, lock_flags); + pr_err("IPU: %s(%u) - unmapped!\n", __func__, irq); + return; + } + + ipu_write_reg(bank->ipu, 1UL << (map->source & 31), bank->status); + spin_unlock_irqrestore(&bank_lock, lock_flags); +} + +/** + * ipu_irq_status() - returns the current interrupt status of the specified IRQ. + * @irq: interrupt line to get status for. + * @return: true if the interrupt is pending/asserted or false if the + * interrupt is not pending. + */ +bool ipu_irq_status(unsigned int irq) +{ + struct ipu_irq_map *map = get_irq_chip_data(irq); + struct ipu_irq_bank *bank; + unsigned long lock_flags; + bool ret; + + spin_lock_irqsave(&bank_lock, lock_flags); + bank = map->bank; + ret = bank && ipu_read_reg(bank->ipu, bank->status) & + (1UL << (map->source & 31)); + spin_unlock_irqrestore(&bank_lock, lock_flags); + + return ret; +} + +/** + * ipu_irq_map() - map an IPU interrupt source to an IRQ number + * @source: interrupt source bit position (see below) + * @return: mapped IRQ number or negative error code + * + * The source parameter has to be explained further. On i.MX31 IPU has 137 IRQ + * sources, they are broken down in 5 32-bit registers, like 32, 32, 24, 32, 17. + * However, the source argument of this function is not the sequence number of + * the possible IRQ, but rather its bit position. So, first interrupt in fourth + * register has source number 96, and not 88. This makes calculations easier, + * and also provides forward compatibility with any future IPU implementations + * with any interrupt bit assignments. + */ +int ipu_irq_map(unsigned int source) +{ + int i, ret = -ENOMEM; + struct ipu_irq_map *map; + + might_sleep(); + + mutex_lock(&map_lock); + map = src2map(source); + if (map) { + pr_err("IPU: Source %u already mapped to IRQ %u\n", source, map->irq); + ret = -EBUSY; + goto out; + } + + for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) { + if (irq_map[i].source < 0) { + unsigned long lock_flags; + + spin_lock_irqsave(&bank_lock, lock_flags); + irq_map[i].source = source; + irq_map[i].bank = irq_bank + source / 32; + spin_unlock_irqrestore(&bank_lock, lock_flags); + + ret = irq_map[i].irq; + pr_debug("IPU: mapped source %u to IRQ %u\n", + source, ret); + break; + } + } +out: + mutex_unlock(&map_lock); + + if (ret < 0) + pr_err("IPU: couldn't map source %u: %d\n", source, ret); + + return ret; +} + +/** + * ipu_irq_map() - map an IPU interrupt source to an IRQ number + * @source: interrupt source bit position (see ipu_irq_map()) + * @return: 0 or negative error code + */ +int ipu_irq_unmap(unsigned int source) +{ + int i, ret = -EINVAL; + + might_sleep(); + + mutex_lock(&map_lock); + for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) { + if (irq_map[i].source == source) { + unsigned long lock_flags; + + pr_debug("IPU: unmapped source %u from IRQ %u\n", + source, irq_map[i].irq); + + spin_lock_irqsave(&bank_lock, lock_flags); + irq_map[i].source = -EINVAL; + irq_map[i].bank = NULL; + spin_unlock_irqrestore(&bank_lock, lock_flags); + + ret = 0; + break; + } + } + mutex_unlock(&map_lock); + + return ret; +} + +/* Chained IRQ handler for IPU error interrupt */ +static void ipu_irq_err(unsigned int irq, struct irq_desc *desc) +{ + struct ipu *ipu = get_irq_data(irq); + u32 status; + int i, line; + + for (i = IPU_IRQ_NR_FN_BANKS; i < IPU_IRQ_NR_BANKS; i++) { + struct ipu_irq_bank *bank = irq_bank + i; + + spin_lock(&bank_lock); + status = ipu_read_reg(ipu, bank->status); + /* + * Don't think we have to clear all interrupts here, they will + * be acked by ->handle_irq() (handle_level_irq). However, we + * might want to clear unhandled interrupts after the loop... + */ + status &= ipu_read_reg(ipu, bank->control); + spin_unlock(&bank_lock); + while ((line = ffs(status))) { + struct ipu_irq_map *map; + + line--; + status &= ~(1UL << line); + + spin_lock(&bank_lock); + map = src2map(32 * i + line); + if (map) + irq = map->irq; + spin_unlock(&bank_lock); + + if (!map) { + pr_err("IPU: Interrupt on unmapped source %u bank %d\n", + line, i); + continue; + } + generic_handle_irq(irq); + } + } +} + +/* Chained IRQ handler for IPU function interrupt */ +static void ipu_irq_fn(unsigned int irq, struct irq_desc *desc) +{ + struct ipu *ipu = get_irq_data(irq); + u32 status; + int i, line; + + for (i = 0; i < IPU_IRQ_NR_FN_BANKS; i++) { + struct ipu_irq_bank *bank = irq_bank + i; + + spin_lock(&bank_lock); + status = ipu_read_reg(ipu, bank->status); + /* Not clearing all interrupts, see above */ + status &= ipu_read_reg(ipu, bank->control); + spin_unlock(&bank_lock); + while ((line = ffs(status))) { + struct ipu_irq_map *map; + + line--; + status &= ~(1UL << line); + + spin_lock(&bank_lock); + map = src2map(32 * i + line); + if (map) + irq = map->irq; + spin_unlock(&bank_lock); + + if (!map) { + pr_err("IPU: Interrupt on unmapped source %u bank %d\n", + line, i); + continue; + } + generic_handle_irq(irq); + } + } +} + +static struct irq_chip ipu_irq_chip = { + .name = "ipu_irq", + .ack = ipu_irq_ack, + .mask = ipu_irq_mask, + .unmask = ipu_irq_unmask, +}; + +/* Install the IRQ handler */ +int ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev) +{ + struct ipu_platform_data *pdata = dev->dev.platform_data; + unsigned int irq, irq_base, i; + + irq_base = pdata->irq_base; + + for (i = 0; i < IPU_IRQ_NR_BANKS; i++) + irq_bank[i].ipu = ipu; + + for (i = 0; i < CONFIG_MX3_IPU_IRQS; i++) { + int ret; + + irq = irq_base + i; + ret = set_irq_chip(irq, &ipu_irq_chip); + if (ret < 0) + return ret; + ret = set_irq_chip_data(irq, irq_map + i); + if (ret < 0) + return ret; + irq_map[i].ipu = ipu; + irq_map[i].irq = irq; + irq_map[i].source = -EINVAL; + set_irq_handler(irq, handle_level_irq); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); +#endif + } + + set_irq_data(ipu->irq_fn, ipu); + set_irq_chained_handler(ipu->irq_fn, ipu_irq_fn); + + set_irq_data(ipu->irq_err, ipu); + set_irq_chained_handler(ipu->irq_err, ipu_irq_err); + + return 0; +} + +void ipu_irq_detach_irq(struct ipu *ipu, struct platform_device *dev) +{ + struct ipu_platform_data *pdata = dev->dev.platform_data; + unsigned int irq, irq_base; + + irq_base = pdata->irq_base; + + set_irq_chained_handler(ipu->irq_fn, NULL); + set_irq_data(ipu->irq_fn, NULL); + + set_irq_chained_handler(ipu->irq_err, NULL); + set_irq_data(ipu->irq_err, NULL); + + for (irq = irq_base; irq < irq_base + CONFIG_MX3_IPU_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip(irq, NULL); + set_irq_chip_data(irq, NULL); + } +} -- cgit v1.2.3 From 70b9986ca4baaf6deb6f0e01d50f72457579adea Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:43:48 +0000 Subject: bnx2x: Free IRQ Error check could result with not freeing the IRQ Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 7c533797c06..ce98ecf8cb1 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -6684,6 +6684,9 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) (DRV_PULSE_ALWAYS_ALIVE | bp->fw_drv_pulse_wr_seq)); bnx2x_stats_handle(bp, STATS_EVENT_STOP); + /* Release IRQs */ + bnx2x_free_irq(bp); + /* Wait until tx fast path tasks complete */ for_each_queue(bp, i) { struct bnx2x_fastpath *fp = &bp->fp[i]; @@ -6711,9 +6714,6 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) /* Give HW time to discard old tx messages */ msleep(1); - /* Release IRQs */ - bnx2x_free_irq(bp); - if (CHIP_IS_E1(bp)) { struct mac_configuration_cmd *config = bnx2x_sp(bp, mcast_config); -- cgit v1.2.3 From 693fc0d14334859430733ab902adac182fdd8153 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:43:52 +0000 Subject: bnx2x: Handling probe failures Failures in the probe not handled correctly - separate the flow to handle different failures Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index ce98ecf8cb1..911067586a4 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -10269,17 +10269,15 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, return rc; } - rc = register_netdev(dev); - if (rc) { - dev_err(&pdev->dev, "Cannot register net device\n"); - goto init_one_exit; - } - pci_set_drvdata(pdev, dev); rc = bnx2x_init_bp(bp); + if (rc) + goto init_one_exit; + + rc = register_netdev(dev); if (rc) { - unregister_netdev(dev); + dev_err(&pdev->dev, "Cannot register net device\n"); goto init_one_exit; } -- cgit v1.2.3 From b4661739c67acd15a02f8e112f8cc52d24b609ed Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:43:56 +0000 Subject: bnx2x: Potential race after iSCSI boot The lock was release too soon. Make sure the HW is marked as locked until the boot driver was unloaded from FW perspective Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 911067586a4..70fc61033dd 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -6874,10 +6874,6 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp) */ bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_UNDI); val = REG_RD(bp, DORQ_REG_NORM_CID_OFST); - if (val == 0x7) - REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0); - bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_UNDI); - if (val == 0x7) { u32 reset_code = DRV_MSG_CODE_UNLOAD_REQ_WOL_DIS; /* save our func */ @@ -6885,6 +6881,9 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp) u32 swap_en; u32 swap_val; + /* clear the UNDI indication */ + REG_WR(bp, DORQ_REG_NORM_CID_OFST, 0); + BNX2X_DEV_INFO("UNDI is active! reset device\n"); /* try unload UNDI on port 0 */ @@ -6910,6 +6909,9 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp) bnx2x_fw_command(bp, reset_code); } + /* now it's safe to release the lock */ + bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_UNDI); + REG_WR(bp, (BP_PORT(bp) ? HC_REG_CONFIG_1 : HC_REG_CONFIG_0), 0x1000); @@ -6954,7 +6956,9 @@ static void __devinit bnx2x_undi_unload(struct bnx2x *bp) bp->fw_seq = (SHMEM_RD(bp, func_mb[bp->func].drv_mb_header) & DRV_MSG_SEQ_NUMBER_MASK); - } + + } else + bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_UNDI); } } -- cgit v1.2.3 From af2464011f0954785687071b298f066f6cbb1c84 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:43:59 +0000 Subject: bnx2x: Wrong HDR offset in CAM Has a negative side effect when sending MAC update with no content (as done in the self-test) Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 70fc61033dd..8f8271d76db 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -6144,7 +6144,7 @@ static void bnx2x_set_mac_addr_e1(struct bnx2x *bp, int set) * multicast 64-127:port0 128-191:port1 */ config->hdr.length_6b = 2; - config->hdr.offset = port ? 31 : 0; + config->hdr.offset = port ? 32 : 0; config->hdr.client_id = BP_CL_ID(bp); config->hdr.reserved1 = 0; @@ -8910,7 +8910,10 @@ static int bnx2x_test_intr(struct bnx2x *bp) return -ENODEV; config->hdr.length_6b = 0; - config->hdr.offset = 0; + if (CHIP_IS_E1(bp)) + config->hdr.offset = (BP_PORT(bp) ? 32 : 0); + else + config->hdr.offset = BP_FUNC(bp); config->hdr.client_id = BP_CL_ID(bp); config->hdr.reserved1 = 0; @@ -9863,7 +9866,7 @@ static void bnx2x_set_rx_mode(struct net_device *dev) for (; i < old; i++) { if (CAM_IS_INVALID(config-> config_table[i])) { - i--; /* already invalidated */ + /* already invalidated */ break; } /* invalidate */ -- cgit v1.2.3 From 5a40e08e666e8caa1227333de41fd1e2cd84d4f5 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:44:04 +0000 Subject: bnx2x: Read chip ID Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 8f8271d76db..7ee6a211f9b 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -6975,7 +6975,7 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp) id |= ((val & 0xf) << 12); val = REG_RD(bp, MISC_REG_CHIP_METAL); id |= ((val & 0xff) << 4); - REG_RD(bp, MISC_REG_BOND_ID); + val = REG_RD(bp, MISC_REG_BOND_ID); id |= (val & 0xf); bp->common.chip_id = id; bp->link_params.chip_id = bp->common.chip_id; -- cgit v1.2.3 From 2add3acb11a26cc14b54669433ae6ace6406cbf2 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:44:07 +0000 Subject: bnx2x: Block nvram access when the device is inactive Don't dump eeprom when bnx2x adapter is down. Running ethtool -e causes an eeh without it when the device is down Signed-off-by: Paul Larson Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 7ee6a211f9b..ca5090c3341 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -8107,6 +8107,9 @@ static int bnx2x_get_eeprom(struct net_device *dev, struct bnx2x *bp = netdev_priv(dev); int rc; + if (!netif_running(dev)) + return -EAGAIN; + DP(BNX2X_MSG_NVM, "ethtool_eeprom: cmd %d\n" DP_LEVEL " magic 0x%x offset 0x%x (%d) len 0x%x (%d)\n", eeprom->cmd, eeprom->magic, eeprom->offset, eeprom->offset, -- cgit v1.2.3 From 632da4d66324b5baf947a048dd1f1e9093b6dd90 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:44:10 +0000 Subject: bnx2x: Overstepping array bounds If the page size is > 8KB this violation happens Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index ca5090c3341..2e00da6ab17 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -9427,6 +9427,7 @@ static inline u32 bnx2x_xmit_type(struct bnx2x *bp, struct sk_buff *skb) return rc; } +#if (MAX_SKB_FRAGS >= MAX_FETCH_BD - 3) /* check if packet requires linearization (packet is too fragmented) */ static int bnx2x_pkt_req_lin(struct bnx2x *bp, struct sk_buff *skb, u32 xmit_type) @@ -9504,6 +9505,7 @@ exit_lbl: return to_copy; } +#endif /* called with netif_tx_lock * bnx2x_tx_int() runs without netif_tx_lock unless it needs to call @@ -9544,6 +9546,7 @@ static int bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) skb->ip_summed, skb->protocol, ipv6_hdr(skb)->nexthdr, ip_hdr(skb)->protocol, skb_shinfo(skb)->gso_type, xmit_type); +#if (MAX_SKB_FRAGS >= MAX_FETCH_BD - 3) /* First, check if we need to linearize the skb (due to FW restrictions) */ if (bnx2x_pkt_req_lin(bp, skb, xmit_type)) { @@ -9556,6 +9559,7 @@ static int bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } } +#endif /* Please read carefully. First we use one BD which we mark as start, -- cgit v1.2.3 From 6c55c3cdc86881383075a933594748b30dd0054b Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:44:13 +0000 Subject: bnx2x: 1G-10G toggling race The HW should be configured so fast toggling between 1G and 10G will not be missed. Make sure that the HW is re-configured in full Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_link.c | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index fefa6ab1306..d9e1cfcd06d 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -317,6 +317,9 @@ static u8 bnx2x_emac_enable(struct link_params *params, val &= ~0x810; EMAC_WR(bp, EMAC_REG_EMAC_MODE, val); + /* enable emac */ + REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 1); + /* enable emac for jumbo packets */ EMAC_WR(bp, EMAC_REG_EMAC_RX_MTU_SIZE, (EMAC_RX_MTU_SIZE_JUMBO_ENA | @@ -1609,7 +1612,7 @@ static u8 bnx2x_link_settings_status(struct link_params *params, u32 gp_status) { struct bnx2x *bp = params->bp; - + u16 new_line_speed; u8 rc = 0; vars->link_status = 0; @@ -1629,7 +1632,7 @@ static u8 bnx2x_link_settings_status(struct link_params *params, switch (gp_status & GP_STATUS_SPEED_MASK) { case GP_STATUS_10M: - vars->line_speed = SPEED_10; + new_line_speed = SPEED_10; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_10TFD; else @@ -1637,7 +1640,7 @@ static u8 bnx2x_link_settings_status(struct link_params *params, break; case GP_STATUS_100M: - vars->line_speed = SPEED_100; + new_line_speed = SPEED_100; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_100TXFD; else @@ -1646,7 +1649,7 @@ static u8 bnx2x_link_settings_status(struct link_params *params, case GP_STATUS_1G: case GP_STATUS_1G_KX: - vars->line_speed = SPEED_1000; + new_line_speed = SPEED_1000; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_1000TFD; else @@ -1654,7 +1657,7 @@ static u8 bnx2x_link_settings_status(struct link_params *params, break; case GP_STATUS_2_5G: - vars->line_speed = SPEED_2500; + new_line_speed = SPEED_2500; if (vars->duplex == DUPLEX_FULL) vars->link_status |= LINK_2500TFD; else @@ -1671,32 +1674,32 @@ static u8 bnx2x_link_settings_status(struct link_params *params, case GP_STATUS_10G_KX4: case GP_STATUS_10G_HIG: case GP_STATUS_10G_CX4: - vars->line_speed = SPEED_10000; + new_line_speed = SPEED_10000; vars->link_status |= LINK_10GTFD; break; case GP_STATUS_12G_HIG: - vars->line_speed = SPEED_12000; + new_line_speed = SPEED_12000; vars->link_status |= LINK_12GTFD; break; case GP_STATUS_12_5G: - vars->line_speed = SPEED_12500; + new_line_speed = SPEED_12500; vars->link_status |= LINK_12_5GTFD; break; case GP_STATUS_13G: - vars->line_speed = SPEED_13000; + new_line_speed = SPEED_13000; vars->link_status |= LINK_13GTFD; break; case GP_STATUS_15G: - vars->line_speed = SPEED_15000; + new_line_speed = SPEED_15000; vars->link_status |= LINK_15GTFD; break; case GP_STATUS_16G: - vars->line_speed = SPEED_16000; + new_line_speed = SPEED_16000; vars->link_status |= LINK_16GTFD; break; @@ -1708,6 +1711,15 @@ static u8 bnx2x_link_settings_status(struct link_params *params, break; } + /* Upon link speed change set the NIG into drain mode. + Comes to deals with possible FIFO glitch due to clk change + when speed is decreased without link down indicator */ + if (new_line_speed != vars->line_speed) { + REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + + params->port*4, 0); + msleep(1); + } + vars->line_speed = new_line_speed; vars->link_status |= LINK_STATUS_SERDES_LINK; if ((params->req_line_speed == SPEED_AUTO_NEG) && @@ -4194,6 +4206,11 @@ static u8 bnx2x_update_link_down(struct link_params *params, /* activate nig drain */ REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + port*4, 1); + /* disable emac */ + REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); + + msleep(10); + /* reset BigMac */ bnx2x_bmac_rx_disable(bp, params->port); REG_WR(bp, GRCBASE_MISC + @@ -4238,6 +4255,7 @@ static u8 bnx2x_update_link_up(struct link_params *params, /* update shared memory */ bnx2x_update_mng(params, vars->link_status); + msleep(20); return rc; } /* This function should called upon link interrupt */ @@ -4276,6 +4294,9 @@ u8 bnx2x_link_update(struct link_params *params, struct link_vars *vars) REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK10G + port*0x68), REG_RD(bp, NIG_REG_XGXS0_STATUS_LINK_STATUS + port*0x68)); + /* disable emac */ + REG_WR(bp, NIG_REG_NIG_EMAC0_EN + port*4, 0); + ext_phy_type = XGXS_EXT_PHY_TYPE(params->ext_phy_config); /* Check external link change only for non-direct */ -- cgit v1.2.3 From 3858276b7198074bf3570470463808627f0c9e31 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:44:16 +0000 Subject: bnx2x: Prevent self test loopback failures Setting loopback requires time to take effect Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_link.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index d9e1cfcd06d..4ce107c125d 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -3583,7 +3583,7 @@ static void bnx2x_set_xgxs_loopback(struct link_params *params, (MDIO_REG_BANK_CL73_IEEEB0 + (MDIO_CL73_IEEEB0_CL73_AN_CONTROL & 0xf)), 0x6041); - + msleep(200); /* set aer mmd back */ bnx2x_set_aer_mmd(params, vars); -- cgit v1.2.3 From 44722d1d216c9dd4536de5f88fe8320b07e68a96 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:44:21 +0000 Subject: bnx2x: Legacy speeds autoneg failures 10M/100M autoneg was not establishing link. Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_link.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index 4ce107c125d..2fd6be0cca1 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -3882,9 +3882,15 @@ static u8 bnx2x_link_initialize(struct link_params *params, } if (vars->phy_flags & PHY_XGXS_FLAG) { - if (params->req_line_speed && + if ((params->req_line_speed && ((params->req_line_speed == SPEED_100) || - (params->req_line_speed == SPEED_10))) { + (params->req_line_speed == SPEED_10))) || + (!params->req_line_speed && + (params->speed_cap_mask >= + PORT_HW_CFG_SPEED_CAPABILITY_D0_10M_FULL) && + (params->speed_cap_mask < + PORT_HW_CFG_SPEED_CAPABILITY_D0_1G) + )) { vars->phy_flags |= PHY_SGMII_FLAG; } else { vars->phy_flags &= ~PHY_SGMII_FLAG; -- cgit v1.2.3 From 16b311cc29806bb968746c1a752a087b32841af9 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:44:24 +0000 Subject: bnx2x: Handling PHY FW load failure If the default PHY version (0x4321) is read - the PHY FW load failed Signed-off-by: Yaniv Rosner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_link.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index 2fd6be0cca1..b4527a4a60e 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -4404,10 +4404,11 @@ static u8 bnx2x_8073_common_init_phy(struct bnx2x *bp, u32 shmem_base) ext_phy_addr[port], MDIO_PMA_DEVAD, MDIO_PMA_REG_ROM_VER1, &fw_ver1); - if (fw_ver1 == 0) { + if (fw_ver1 == 0 || fw_ver1 == 0x4321) { DP(NETIF_MSG_LINK, - "bnx2x_8073_common_init_phy port %x " - "fw Download failed\n", port); + "bnx2x_8073_common_init_phy port %x:" + "Download failed. fw version = 0x%x\n", + port, fw_ver1); return -EINVAL; } -- cgit v1.2.3 From e47d7e6eb841c1850f0e69b95ae6cf3c86881f53 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:44:28 +0000 Subject: bnx2x: Driver description update The Driver supports the 57711 and 57711E as well but the description was out of date Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 2e00da6ab17..dc05e37c581 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -69,7 +69,7 @@ static char version[] __devinitdata = DRV_MODULE_NAME " " DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; MODULE_AUTHOR("Eliezer Tamir"); -MODULE_DESCRIPTION("Broadcom NetXtreme II BCM57710 Driver"); +MODULE_DESCRIPTION("Broadcom NetXtreme II BCM57710/57711/57711E Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_MODULE_VERSION); -- cgit v1.2.3 From 237907c1ded8a1a447cea7c4f97ab853e8b46052 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Wed, 14 Jan 2009 06:42:44 +0000 Subject: bnx2x: Barriers for the compiler To make sure no swapping are made by the compiler, changed HAS_WORK to inline functions and added all the necessary barriers Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x.h | 9 +-------- drivers/net/bnx2x_main.c | 37 ++++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x.h b/drivers/net/bnx2x.h index 6fcccef4cf3..18f60aa3efd 100644 --- a/drivers/net/bnx2x.h +++ b/drivers/net/bnx2x.h @@ -271,14 +271,7 @@ struct bnx2x_fastpath { #define bnx2x_fp(bp, nr, var) (bp->fp[nr].var) -#define BNX2X_HAS_TX_WORK(fp) \ - ((fp->tx_pkt_prod != le16_to_cpu(*fp->tx_cons_sb)) || \ - (fp->tx_pkt_prod != fp->tx_pkt_cons)) - -#define BNX2X_HAS_RX_WORK(fp) \ - (fp->rx_comp_cons != rx_cons_sb) - -#define BNX2X_HAS_WORK(fp) (BNX2X_HAS_RX_WORK(fp) || BNX2X_HAS_TX_WORK(fp)) +#define BNX2X_HAS_WORK(fp) (bnx2x_has_rx_work(fp) || bnx2x_has_tx_work(fp)) /* MC hsi */ diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index dc05e37c581..3236527477a 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -733,6 +733,17 @@ static u16 bnx2x_ack_int(struct bnx2x *bp) * fast path service functions */ +static inline int bnx2x_has_tx_work(struct bnx2x_fastpath *fp) +{ + u16 tx_cons_sb; + + /* Tell compiler that status block fields can change */ + barrier(); + tx_cons_sb = le16_to_cpu(*fp->tx_cons_sb); + return ((fp->tx_pkt_prod != tx_cons_sb) || + (fp->tx_pkt_prod != fp->tx_pkt_cons)); +} + /* free skb in the packet ring at pos idx * return idx of last bd freed */ @@ -6693,7 +6704,7 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) cnt = 1000; smp_rmb(); - while (BNX2X_HAS_TX_WORK(fp)) { + while (bnx2x_has_tx_work(fp)) { bnx2x_tx_int(fp, 1000); if (!cnt) { @@ -9281,6 +9292,18 @@ static int bnx2x_set_power_state(struct bnx2x *bp, pci_power_t state) return 0; } +static inline int bnx2x_has_rx_work(struct bnx2x_fastpath *fp) +{ + u16 rx_cons_sb; + + /* Tell compiler that status block fields can change */ + barrier(); + rx_cons_sb = le16_to_cpu(*fp->rx_cons_sb); + if ((rx_cons_sb & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) + rx_cons_sb++; + return (fp->rx_comp_cons != rx_cons_sb); +} + /* * net_device service functions */ @@ -9291,7 +9314,6 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) napi); struct bnx2x *bp = fp->bp; int work_done = 0; - u16 rx_cons_sb; #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) @@ -9304,19 +9326,12 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) bnx2x_update_fpsb_idx(fp); - if (BNX2X_HAS_TX_WORK(fp)) + if (bnx2x_has_tx_work(fp)) bnx2x_tx_int(fp, budget); - rx_cons_sb = le16_to_cpu(*fp->rx_cons_sb); - if ((rx_cons_sb & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) - rx_cons_sb++; - if (BNX2X_HAS_RX_WORK(fp)) + if (bnx2x_has_rx_work(fp)) work_done = bnx2x_rx_int(fp, budget); - rmb(); /* BNX2X_HAS_WORK() reads the status block */ - rx_cons_sb = le16_to_cpu(*fp->rx_cons_sb); - if ((rx_cons_sb & MAX_RCQ_DESC_CNT) == MAX_RCQ_DESC_CNT) - rx_cons_sb++; /* must not complete if we consumed full budget */ if ((work_done < budget) && !BNX2X_HAS_WORK(fp)) { -- cgit v1.2.3 From d05c26ce690e867aabfc7d708d481e0f86f23496 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Sat, 17 Jan 2009 23:26:13 -0800 Subject: bnx2x: Version update Updating the version and the year of updated files Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x.h | 2 +- drivers/net/bnx2x_link.c | 2 +- drivers/net/bnx2x_main.c | 6 +++--- drivers/net/bnx2x_reg.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x.h b/drivers/net/bnx2x.h index 18f60aa3efd..15a5cf0f676 100644 --- a/drivers/net/bnx2x.h +++ b/drivers/net/bnx2x.h @@ -1,6 +1,6 @@ /* bnx2x.h: Broadcom Everest network driver. * - * Copyright (c) 2007-2008 Broadcom Corporation + * Copyright (c) 2007-2009 Broadcom Corporation * * 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 diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c index b4527a4a60e..aea26b4dc45 100644 --- a/drivers/net/bnx2x_link.c +++ b/drivers/net/bnx2x_link.c @@ -1,4 +1,4 @@ -/* Copyright 2008 Broadcom Corporation +/* Copyright 2008-2009 Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 3236527477a..074374ff93f 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -1,6 +1,6 @@ /* bnx2x_main.c: Broadcom Everest network driver. * - * Copyright (c) 2007-2008 Broadcom Corporation + * Copyright (c) 2007-2009 Broadcom Corporation * * 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 @@ -57,8 +57,8 @@ #include "bnx2x.h" #include "bnx2x_init.h" -#define DRV_MODULE_VERSION "1.45.23" -#define DRV_MODULE_RELDATE "2008/11/03" +#define DRV_MODULE_VERSION "1.45.24" +#define DRV_MODULE_RELDATE "2009/01/14" #define BNX2X_BC_VER 0x040200 /* Time in jiffies before concluding the transmitter is hung */ diff --git a/drivers/net/bnx2x_reg.h b/drivers/net/bnx2x_reg.h index a67b0c358ae..d084e5fc4b5 100644 --- a/drivers/net/bnx2x_reg.h +++ b/drivers/net/bnx2x_reg.h @@ -1,6 +1,6 @@ /* bnx2x_reg.h: Broadcom Everest network driver. * - * Copyright (c) 2007-2008 Broadcom Corporation + * Copyright (c) 2007-2009 Broadcom Corporation * * 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 -- cgit v1.2.3 From 39eddb4c3970e9aadbc87b8a7cab7b4fefff077f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20R=C3=B6jfors?= Date: Sun, 18 Jan 2009 21:57:35 -0800 Subject: macb: avoid lockup when TGO during underrun MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In rare cases when an underrun occur, all macb buffers where consumed and the netif_queue was stopped infinitely. This happens then the TGO (transfer ongoing) bit in the TSR is set (and UND). It seems like clening up after the underrun makes the driver and the macb hardware end up in an inconsistent state. The result of this is that in the following calls to macb_tx no TX buffers are released -> the netif_queue was stopped, and never woken up again. The solution is to disable the transmitter, if TGO is set, before clening up after the underrun, and re-enable the transmitter when the cleaning up is done. Signed-off-by: Richard Röjfors Signed-off-by: David S. Miller --- drivers/net/macb.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/macb.c b/drivers/net/macb.c index a04da4ecaa8..f6c4936e2fa 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -321,6 +321,10 @@ static void macb_tx(struct macb *bp) printk(KERN_ERR "%s: TX underrun, resetting buffers\n", bp->dev->name); + /* Transfer ongoing, disable transmitter, to avoid confusion */ + if (status & MACB_BIT(TGO)) + macb_writel(bp, NCR, macb_readl(bp, NCR) & ~MACB_BIT(TE)); + head = bp->tx_head; /*Mark all the buffer as used to avoid sending a lost buffer*/ @@ -343,6 +347,10 @@ static void macb_tx(struct macb *bp) } bp->tx_head = bp->tx_tail = 0; + + /* Enable the transmitter again */ + if (status & MACB_BIT(TGO)) + macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TE)); } if (!(status & MACB_BIT(COMP))) -- cgit v1.2.3 From eed087e367591fc08490d7c6c2779b4b72c8f20c Mon Sep 17 00:00:00 2001 From: Divy Le Ray Date: Sun, 18 Jan 2009 22:01:32 -0800 Subject: cxgb3: Fix LRO misalignment The lro manager's frag_align_pad setting was missing, leading to misaligned access to the skb passed up to the stack. Tested-by: Rick Jones Signed-off-by: Divy Le Ray Signed-off-by: David S. Miller --- drivers/net/cxgb3/sge.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 14f9fb3e879..379a1324db4 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -2104,6 +2104,7 @@ static void init_lro_mgr(struct sge_qset *qs, struct net_lro_mgr *lro_mgr) { lro_mgr->dev = qs->netdev; lro_mgr->features = LRO_F_NAPI; + lro_mgr->frag_align_pad = NET_IP_ALIGN; lro_mgr->ip_summed = CHECKSUM_UNNECESSARY; lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY; lro_mgr->max_desc = T3_MAX_LRO_SES; -- cgit v1.2.3 From 6a2fe9834e578590f4a2fbe18a574465ab0e127c Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 15 Jan 2009 12:29:55 +0000 Subject: korina: fix loop back of receive descriptors After the last loop iteration, i has the value RC32434_NUM_RDS and therefore leads to an index overflow when used afterwards to address the last element. This is yet another another bug introduced when rewriting parts of the driver for upstream preparation, as the original driver used 'RC32434_NUM_RDS - 1' instead. Signed-off-by: Phil Sutter Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/korina.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/korina.c b/drivers/net/korina.c index 1d6e48e1336..67fbdf40ace 100644 --- a/drivers/net/korina.c +++ b/drivers/net/korina.c @@ -769,11 +769,12 @@ static void korina_alloc_ring(struct net_device *dev) lp->rd_ring[i].link = CPHYSADDR(&lp->rd_ring[i+1]); } - /* loop back */ - lp->rd_ring[i].link = CPHYSADDR(&lp->rd_ring[0]); - lp->rx_next_done = 0; + /* loop back receive descriptors, so the last + * descriptor points to the first one */ + lp->rd_ring[i - 1].link = CPHYSADDR(&lp->rd_ring[0]); + lp->rd_ring[i - 1].control |= DMA_DESC_COD; - lp->rd_ring[i].control |= DMA_DESC_COD; + lp->rx_next_done = 0; lp->rx_chain_head = 0; lp->rx_chain_tail = 0; lp->rx_chain_status = desc_empty; -- cgit v1.2.3 From 63a66c6c0debcae70183849121734fd4809e1dde Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 15 Jan 2009 12:29:56 +0000 Subject: korina: adjust headroom for new skb's also This is copy and paste from the original driver. As skb_reserve() is also called within korina_alloc_ring() when initially allocating the receive descriptors, the same should be done when allocating new space after passing an skb to upper layers. Signed-off-by: Phil Sutter Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/korina.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/net/korina.c b/drivers/net/korina.c index 67fbdf40ace..60ae7bfb1d9 100644 --- a/drivers/net/korina.c +++ b/drivers/net/korina.c @@ -416,6 +416,9 @@ static int korina_rx(struct net_device *dev, int limit) if (devcs & ETH_RX_MP) dev->stats.multicast++; + /* 16 bit align */ + skb_reserve(skb_new, 2); + lp->rx_skb[lp->rx_next_done] = skb_new; } -- cgit v1.2.3 From e85bf47e6ded66ea138f692fe149c00a4998afe8 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 15 Jan 2009 12:29:57 +0000 Subject: korina: drop leftover assignment As the assigned value is being overwritten shortly after, it can be dropped and so the whole variable definition moved to the start of the function. Signed-off-by: Phil Sutter Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/korina.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/korina.c b/drivers/net/korina.c index 60ae7bfb1d9..75010cac76a 100644 --- a/drivers/net/korina.c +++ b/drivers/net/korina.c @@ -743,6 +743,7 @@ static struct ethtool_ops netdev_ethtool_ops = { static void korina_alloc_ring(struct net_device *dev) { struct korina_private *lp = netdev_priv(dev); + struct sk_buff *skb; int i; /* Initialize the transmit descriptors */ @@ -758,8 +759,6 @@ static void korina_alloc_ring(struct net_device *dev) /* Initialize the receive descriptors */ for (i = 0; i < KORINA_NUM_RDS; i++) { - struct sk_buff *skb = lp->rx_skb[i]; - skb = dev_alloc_skb(KORINA_RBSIZE + 2); if (!skb) break; -- cgit v1.2.3 From 15005a320473b8d3676b878deb29bbe738ef9027 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Mon, 19 Jan 2009 16:54:13 -0800 Subject: ixgbe: fix dca issue with relaxed ordering turned on The is an issue where setting Relaxed Ordering (RO) bit (in a PCI-E write transaction) on 82598 causing the chipset to drop DCA hints. This patch forces RO not to be set for descriptors as well as payload. This will only be in effect while DCA is enabled and no performance difference was noticed in testing. Signed-off-by: Don Skidmore Signed-off-by: Peter P Waskiewicz Jr Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ixgbe/ixgbe_main.c | 3 +++ drivers/net/ixgbe/ixgbe_type.h | 3 +++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index acef3c65cd2..92d9b17a081 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -318,6 +318,9 @@ static void ixgbe_update_rx_dca(struct ixgbe_adapter *adapter, rxctrl |= dca3_get_tag(&adapter->pdev->dev, cpu); rxctrl |= IXGBE_DCA_RXCTRL_DESC_DCA_EN; rxctrl |= IXGBE_DCA_RXCTRL_HEAD_DCA_EN; + rxctrl &= ~(IXGBE_DCA_RXCTRL_DESC_RRO_EN); + rxctrl &= ~(IXGBE_DCA_RXCTRL_DESC_WRO_EN | + IXGBE_DCA_RXCTRL_DESC_HSRO_EN); IXGBE_WRITE_REG(&adapter->hw, IXGBE_DCA_RXCTRL(q), rxctrl); rx_ring->cpu = cpu; } diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h index 83a11ff9ffd..f011c57c920 100644 --- a/drivers/net/ixgbe/ixgbe_type.h +++ b/drivers/net/ixgbe/ixgbe_type.h @@ -404,6 +404,9 @@ #define IXGBE_DCA_RXCTRL_DESC_DCA_EN (1 << 5) /* DCA Rx Desc enable */ #define IXGBE_DCA_RXCTRL_HEAD_DCA_EN (1 << 6) /* DCA Rx Desc header enable */ #define IXGBE_DCA_RXCTRL_DATA_DCA_EN (1 << 7) /* DCA Rx Desc payload enable */ +#define IXGBE_DCA_RXCTRL_DESC_RRO_EN (1 << 9) /* DCA Rx rd Desc Relax Order */ +#define IXGBE_DCA_RXCTRL_DESC_WRO_EN (1 << 13) /* DCA Rx wr Desc Relax Order */ +#define IXGBE_DCA_RXCTRL_DESC_HSRO_EN (1 << 15) /* DCA Rx Split Header RO */ #define IXGBE_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */ #define IXGBE_DCA_TXCTRL_DESC_DCA_EN (1 << 5) /* DCA Tx Desc enable */ -- cgit v1.2.3 From 068c89b014ebd27b1c09c3c772e9d982988e7786 Mon Sep 17 00:00:00 2001 From: Don Skidmore Date: Mon, 19 Jan 2009 16:54:36 -0800 Subject: ixgbe: fix tag stripping for VLAN ID 0 Register VLAN ID 0 so that frames with VLAN ID 0 are received and get their tag stripped when ixgbe is not in DCB mode. VLAN ID 0 means that the frame is 'priority tagged' only - it is not a VLAN, but the priority value is the tag in valid. The functions ixgbe_vlan_rx_register() and ixgbe_vlan_rx_kill_vid() were moved up a couple functions to correct compiling issues with this change. Signed-off-by: Don Skidmore Signed-off-by: Peter P Waskiewicz Jr Signed-off-by: Eric W Multanen Signed-off-by: David S. Miller --- drivers/net/ixgbe/ixgbe_main.c | 53 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 92d9b17a081..bf36737e2ec 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -1744,6 +1744,32 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum); } +static void ixgbe_vlan_rx_add_vid(struct net_device *netdev, u16 vid) +{ + struct ixgbe_adapter *adapter = netdev_priv(netdev); + struct ixgbe_hw *hw = &adapter->hw; + + /* add VID to filter table */ + hw->mac.ops.set_vfta(&adapter->hw, vid, 0, true); +} + +static void ixgbe_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) +{ + struct ixgbe_adapter *adapter = netdev_priv(netdev); + struct ixgbe_hw *hw = &adapter->hw; + + if (!test_bit(__IXGBE_DOWN, &adapter->state)) + ixgbe_irq_disable(adapter); + + vlan_group_set_device(adapter->vlgrp, vid, NULL); + + if (!test_bit(__IXGBE_DOWN, &adapter->state)) + ixgbe_irq_enable(adapter); + + /* remove VID from filter table */ + hw->mac.ops.set_vfta(&adapter->hw, vid, 0, false); +} + static void ixgbe_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) { @@ -1763,6 +1789,7 @@ static void ixgbe_vlan_rx_register(struct net_device *netdev, ctrl |= IXGBE_VLNCTRL_VME; ctrl &= ~IXGBE_VLNCTRL_CFIEN; IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl); + ixgbe_vlan_rx_add_vid(netdev, 0); if (grp) { /* enable VLAN tag insert/strip */ @@ -1776,32 +1803,6 @@ static void ixgbe_vlan_rx_register(struct net_device *netdev, ixgbe_irq_enable(adapter); } -static void ixgbe_vlan_rx_add_vid(struct net_device *netdev, u16 vid) -{ - struct ixgbe_adapter *adapter = netdev_priv(netdev); - struct ixgbe_hw *hw = &adapter->hw; - - /* add VID to filter table */ - hw->mac.ops.set_vfta(&adapter->hw, vid, 0, true); -} - -static void ixgbe_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) -{ - struct ixgbe_adapter *adapter = netdev_priv(netdev); - struct ixgbe_hw *hw = &adapter->hw; - - if (!test_bit(__IXGBE_DOWN, &adapter->state)) - ixgbe_irq_disable(adapter); - - vlan_group_set_device(adapter->vlgrp, vid, NULL); - - if (!test_bit(__IXGBE_DOWN, &adapter->state)) - ixgbe_irq_enable(adapter); - - /* remove VID from filter table */ - hw->mac.ops.set_vfta(&adapter->hw, vid, 0, false); -} - static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter) { ixgbe_vlan_rx_register(adapter->netdev, adapter->vlgrp); -- cgit v1.2.3 From 1da100bb47ef32cb43bb6a365f64183898f830b5 Mon Sep 17 00:00:00 2001 From: Peter P Waskiewicz Jr Date: Mon, 19 Jan 2009 16:55:03 -0800 Subject: ixgbe: Fix usage of netif_*_all_queues() with netif_carrier_{off|on}() netif_carrier_off() is sufficient to stop Tx into the driver. Stopping the Tx queues is redundant and unnecessary. By the same token, netif_carrier_on() will be sufficient to re-enable Tx, so waking the queues is unnecessary. Signed-off-by: Peter P Waskiewicz Jr Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/ixgbe/ixgbe_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index bf36737e2ec..d2f4d5f508b 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -2078,6 +2078,9 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) ixgbe_irq_enable(adapter); + /* enable transmits */ + netif_tx_start_all_queues(netdev); + /* bring the link up in the watchdog, this could race with our first * link up interrupt but shouldn't be a problem */ adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; @@ -3479,7 +3482,6 @@ static void ixgbe_watchdog_task(struct work_struct *work) (FLOW_TX ? "TX" : "None")))); netif_carrier_on(netdev); - netif_tx_wake_all_queues(netdev); } else { /* Force detection of hung controller */ adapter->detect_tx_hung = true; @@ -3491,7 +3493,6 @@ static void ixgbe_watchdog_task(struct work_struct *work) printk(KERN_INFO "ixgbe: %s NIC Link is Down\n", netdev->name); netif_carrier_off(netdev); - netif_tx_stop_all_queues(netdev); } } @@ -4222,7 +4223,6 @@ static int __devinit ixgbe_probe(struct pci_dev *pdev, } netif_carrier_off(netdev); - netif_tx_stop_all_queues(netdev); strcpy(netdev->name, "eth%d"); err = register_netdev(netdev); -- cgit v1.2.3 From 9e9fd12dc0679643c191fc9795a3021807e77de4 Mon Sep 17 00:00:00 2001 From: Matt Carlson Date: Mon, 19 Jan 2009 16:57:45 -0800 Subject: tg3: Fix firmware loading This patch modifies how the tg3 driver handles device firmware. The patch starts by consolidating David Woodhouse's earlier patch under the same name. Specifically, the patch moves the request_firmware call into a separate tg3_request_firmware() function and calls that function from tg3_open() rather than tg3_init_one(). The patch then goes on to limit the number of devices that will make request_firmware calls. The original firmware patch unnecessarily requested TSO firmware for devices that did not need it. This patch reduces the set of devices making TSO firmware patches to approximately the following device set : 5703, 5704, and 5705. Finally, the patch reduces the effects of a request_firmware() failure. For those devices that are requesting TSO firmware, the driver will turn off the TSO capability. If TSO firmware becomes available at a later time, the device can be closed and then opened again to reacquire the TSO capability. Signed-off-by: Matt Carlson Signed-off-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/tg3.c | 81 ++++++++++++++++++++++++++++++++++--------------------- drivers/net/tg3.h | 1 + 2 files changed, 51 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 5e2dbaee125..8b3f8468538 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -7535,11 +7535,58 @@ static int tg3_test_msi(struct tg3 *tp) return err; } +static int tg3_request_firmware(struct tg3 *tp) +{ + const __be32 *fw_data; + + if (request_firmware(&tp->fw, tp->fw_needed, &tp->pdev->dev)) { + printk(KERN_ERR "%s: Failed to load firmware \"%s\"\n", + tp->dev->name, tp->fw_needed); + return -ENOENT; + } + + fw_data = (void *)tp->fw->data; + + /* Firmware blob starts with version numbers, followed by + * start address and _full_ length including BSS sections + * (which must be longer than the actual data, of course + */ + + tp->fw_len = be32_to_cpu(fw_data[2]); /* includes bss */ + if (tp->fw_len < (tp->fw->size - 12)) { + printk(KERN_ERR "%s: bogus length %d in \"%s\"\n", + tp->dev->name, tp->fw_len, tp->fw_needed); + release_firmware(tp->fw); + tp->fw = NULL; + return -EINVAL; + } + + /* We no longer need firmware; we have it. */ + tp->fw_needed = NULL; + return 0; +} + static int tg3_open(struct net_device *dev) { struct tg3 *tp = netdev_priv(dev); int err; + if (tp->fw_needed) { + err = tg3_request_firmware(tp); + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) { + if (err) + return err; + } else if (err) { + printk(KERN_WARNING "%s: TSO capability disabled.\n", + tp->dev->name); + tp->tg3_flags2 &= ~TG3_FLG2_TSO_CAPABLE; + } else if (!(tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE)) { + printk(KERN_NOTICE "%s: TSO capability restored.\n", + tp->dev->name); + tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE; + } + } + netif_carrier_off(tp->dev); err = tg3_set_power_state(tp, PCI_D0); @@ -12934,7 +12981,6 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, struct net_device *dev; struct tg3 *tp; int err, pm_cap; - const char *fw_name = NULL; char str[40]; u64 dma_mask, persist_dma_mask; @@ -13091,7 +13137,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, tg3_init_bufmgr_config(tp); if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0) - fw_name = FIRMWARE_TG3; + tp->fw_needed = FIRMWARE_TG3; if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE; @@ -13104,37 +13150,10 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, tp->tg3_flags2 &= ~TG3_FLG2_TSO_CAPABLE; } else { tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE | TG3_FLG2_TSO_BUG; - } - if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) - fw_name = FIRMWARE_TG3TSO5; + tp->fw_needed = FIRMWARE_TG3TSO5; else - fw_name = FIRMWARE_TG3TSO; - } - - if (fw_name) { - const __be32 *fw_data; - - err = request_firmware(&tp->fw, fw_name, &tp->pdev->dev); - if (err) { - printk(KERN_ERR "tg3: Failed to load firmware \"%s\"\n", - fw_name); - goto err_out_iounmap; - } - - fw_data = (void *)tp->fw->data; - - /* Firmware blob starts with version numbers, followed by - start address and _full_ length including BSS sections - (which must be longer than the actual data, of course */ - - tp->fw_len = be32_to_cpu(fw_data[2]); /* includes bss */ - if (tp->fw_len < (tp->fw->size - 12)) { - printk(KERN_ERR "tg3: bogus length %d in \"%s\"\n", - tp->fw_len, fw_name); - err = -EINVAL; - goto err_out_fw; - } + tp->fw_needed = FIRMWARE_TG3TSO; } /* TSO is on by default on chips that support hardware TSO. diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index ae5da603c6a..508def3e077 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2764,6 +2764,7 @@ struct tg3 { struct ethtool_coalesce coal; /* firmware info */ + const char *fw_needed; const struct firmware *fw; u32 fw_len; /* includes BSS */ }; -- cgit v1.2.3 From e0c6ef9388b58f297937fc9651331941d1579b25 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 19 Jan 2009 17:16:00 -0800 Subject: Revert "mv643xx_eth: use longer DMA bursts". This reverts commit cd4ccf76bfd2c36d351e68be7e6a597268f98a1a. On the Pegasos board, we can't do DMA burst that are longer than one cache line. For now, go back to using 32 byte DMA bursts for all mv643xx_eth platforms -- we can switch the ARM-based platforms back to doing long 128 byte bursts in the next development cycle. Signed-off-by: Lennert Buytenhek Reported-by: Alan Curry Reported-by: Gabriel Paubert Signed-off-by: David S. Miller --- drivers/net/mv643xx_eth.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 7253a499d9c..e2aa468d40f 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -136,21 +136,23 @@ static char mv643xx_eth_driver_version[] = "1.4"; /* * SDMA configuration register. */ +#define RX_BURST_SIZE_4_64BIT (2 << 1) #define RX_BURST_SIZE_16_64BIT (4 << 1) #define BLM_RX_NO_SWAP (1 << 4) #define BLM_TX_NO_SWAP (1 << 5) +#define TX_BURST_SIZE_4_64BIT (2 << 22) #define TX_BURST_SIZE_16_64BIT (4 << 22) #if defined(__BIG_ENDIAN) #define PORT_SDMA_CONFIG_DEFAULT_VALUE \ - (RX_BURST_SIZE_16_64BIT | \ - TX_BURST_SIZE_16_64BIT) + (RX_BURST_SIZE_4_64BIT | \ + TX_BURST_SIZE_4_64BIT) #elif defined(__LITTLE_ENDIAN) #define PORT_SDMA_CONFIG_DEFAULT_VALUE \ - (RX_BURST_SIZE_16_64BIT | \ - BLM_RX_NO_SWAP | \ - BLM_TX_NO_SWAP | \ - TX_BURST_SIZE_16_64BIT) + (RX_BURST_SIZE_4_64BIT | \ + BLM_RX_NO_SWAP | \ + BLM_TX_NO_SWAP | \ + TX_BURST_SIZE_4_64BIT) #else #error One of __BIG_ENDIAN or __LITTLE_ENDIAN must be defined #endif -- cgit v1.2.3 From 2b448334a255d34401562229f467ffd95d8ed6ef Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 19 Jan 2009 17:17:18 -0800 Subject: mv643xx_eth: fix multicast filter programming Commit 66e63ffbc04706568d8789cbb00eaa8ddbcae648 ("mv643xx_eth: implement ->set_rx_mode()") cleaned up mv643xx_eth's multicast filter programming, but broke it as well. The non-special multicast filter table (for multicast addresses that are not of the form 01:00:5e:00:00:xx) consists of 256 hash table buckets organised as 64 32-bit words, where the 'accept' bits are in the LSB of each byte, so in bits 24 16 8 0 of each 32-bit word. The old code got this right, but the referenced commit broke this by using bits 3 2 1 0 instead. This commit fixes this up. Signed-off-by: Lennert Buytenhek Signed-off-by: David S. Miller --- drivers/net/mv643xx_eth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index e2aa468d40f..8c6979a26d6 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1596,7 +1596,7 @@ oom: entry = addr_crc(a); } - table[entry >> 2] |= 1 << (entry & 3); + table[entry >> 2] |= 1 << (8 * (entry & 3)); } for (i = 0; i < 0x100; i += 4) { -- cgit v1.2.3 From fe65e704534de5d0661ebc3466a2b9018945f694 Mon Sep 17 00:00:00 2001 From: Gabriel Paubert Date: Mon, 19 Jan 2009 17:18:09 -0800 Subject: mv643xx_eth: prevent interrupt storm on ifconfig down Contrary to what the docs say, the 'extended interrupt cause' bit in the interrupt cause register (bit 1) appears to not be maskable on at least some of the mv643xx_eth platforms, making writing zeroes to the interrupt mask register but not the extended interrupt mask register insufficient to stop interrupts from occuring. Therefore, also write zeroes to the extended interrupt mask register when shutting down the port. This fixes the interrupt storm seen on the Pegasos board when shutting down the interface. Signed-off-by: Lennert Buytenhek Signed-off-by: David S. Miller --- drivers/net/mv643xx_eth.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 8c6979a26d6..5f31bbb614a 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -2212,6 +2212,7 @@ static int mv643xx_eth_stop(struct net_device *dev) struct mv643xx_eth_private *mp = netdev_priv(dev); int i; + wrlp(mp, INT_MASK_EXT, 0x00000000); wrlp(mp, INT_MASK, 0x00000000); rdlp(mp, INT_MASK); -- cgit v1.2.3 From f4895b8bc83a22a36446c4aee277e1750fcc6a18 Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Mon, 19 Jan 2009 13:19:30 +0000 Subject: wimax/i2400m: error paths that need to free an skb should use kfree_skb() Roel Kluin reported a bug in two error paths where skbs were wrongly being freed using kfree(). He provided a fix where it was replaced to kfree_skb(), as it should be. However, in i2400mu_rx(), the error path was missing returning an indication of the failure. Changed to reset rx_skb to NULL and return it to the caller, i2400mu_rxd(). It will be treated as a transient error and just ignore the packet. Depending on the buffering conditions inside the device, the data packet might be dropped or the device will signal the host again for data-ready-to-read and the host will retry. Signed-off-by: Inaky Perez-Gonzalez Signed-off-by: David S. Miller --- drivers/net/wimax/i2400m/control.c | 2 +- drivers/net/wimax/i2400m/usb-rx.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c index d3d37fed689..15d9f51b292 100644 --- a/drivers/net/wimax/i2400m/control.c +++ b/drivers/net/wimax/i2400m/control.c @@ -609,7 +609,7 @@ void i2400m_msg_to_dev_cancel_wait(struct i2400m *i2400m, int code) spin_lock_irqsave(&i2400m->rx_lock, flags); ack_skb = i2400m->ack_skb; if (ack_skb && !IS_ERR(ack_skb)) - kfree(ack_skb); + kfree_skb(ack_skb); i2400m->ack_skb = ERR_PTR(code); spin_unlock_irqrestore(&i2400m->rx_lock, flags); } diff --git a/drivers/net/wimax/i2400m/usb-rx.c b/drivers/net/wimax/i2400m/usb-rx.c index 074cc1f8985..a314799967c 100644 --- a/drivers/net/wimax/i2400m/usb-rx.c +++ b/drivers/net/wimax/i2400m/usb-rx.c @@ -184,6 +184,8 @@ void i2400mu_rx_size_maybe_shrink(struct i2400mu *i2400mu) * NOTE: this function might realloc the skb (if it is too small), * so always update with the one returned. * ERR_PTR() is < 0 on error. + * Will return NULL if it cannot reallocate -- this can be + * considered a transient retryable error. */ static struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb) @@ -243,8 +245,8 @@ retry: if (printk_ratelimit()) dev_err(dev, "RX: Can't reallocate skb to %d; " "RX dropped\n", rx_size); - kfree(rx_skb); - result = 0; + kfree_skb(rx_skb); + rx_skb = NULL; goto out; /* drop it...*/ } kfree_skb(rx_skb); @@ -344,7 +346,8 @@ int i2400mu_rxd(void *_i2400mu) if (IS_ERR(rx_skb)) goto out; atomic_dec(&i2400mu->rx_pending_count); - if (rx_skb->len == 0) { /* some ignorable condition */ + if (rx_skb == NULL || rx_skb->len == 0) { + /* some "ignorable" condition */ kfree_skb(rx_skb); continue; } -- cgit v1.2.3 From 8c4c19f1367435afdc16ac122a2a95a4d6cff9f0 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 20 Jan 2009 17:48:02 +0200 Subject: UBI: remove unused variable Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 9082768cc6c..09a326ecd05 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -380,7 +380,7 @@ static void free_user_volumes(struct ubi_device *ubi) */ static int uif_init(struct ubi_device *ubi) { - int i, err, do_free = 0; + int i, err; dev_t dev; sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); @@ -427,13 +427,10 @@ static int uif_init(struct ubi_device *ubi) out_volumes: kill_volumes(ubi); - do_free = 0; out_sysfs: ubi_sysfs_close(ubi); cdev_del(&ubi->cdev); out_unreg: - if (do_free) - free_user_volumes(ubi); unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err); return err; -- cgit v1.2.3 From 36b477d005fbda29e7581c3cef7ee31a59d8970b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 20 Jan 2009 18:04:09 +0200 Subject: UBI: fix resource de-allocation GregKH asked to fix UBI which has fake device release method. Indeed, we have to free UBI device description object from the release method, because otherwise we'll oops is someone opens a UBI device sysfs file, then the device is removed, and he reads the file. With this fix, he will get -ENODEV instead of an oops. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 09a326ecd05..4048db83aef 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -263,8 +263,12 @@ static ssize_t dev_attribute_show(struct device *dev, return ret; } -/* Fake "release" method for UBI devices */ -static void dev_release(struct device *dev) { } +static void dev_release(struct device *dev) +{ + struct ubi_device *ubi = container_of(dev, struct ubi_device, dev); + + kfree(ubi); +} /** * ubi_sysfs_init - initialize sysfs for an UBI device. @@ -944,6 +948,12 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) if (ubi->bgt_thread) kthread_stop(ubi->bgt_thread); + /* + * Get a reference to the device in order to prevent 'dev_release()' + * from freeing @ubi object. + */ + get_device(&ubi->dev); + uif_close(ubi); ubi_wl_close(ubi); free_internal_volumes(ubi); @@ -955,7 +965,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) vfree(ubi->dbg_peb_buf); #endif ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); - kfree(ubi); + put_device(&ubi->dev); return 0; } -- cgit v1.2.3 From a5c7f4710fba334bf613d705f97b4471b36446f8 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 19 Mar 2008 22:02:40 +0100 Subject: firewire: insist on successive self ID complete events The whole topology code only works if the old and new topologies which are compared come from immediately successive self ID complete events. If there happened bus resets without self ID complete events in the meantime, or self ID complete events with invalid selfIDs, the topology comparison could identify nodes wrongly, or more likely just corrupt kernel memory or panic right away. We now discard all nodes of the old topology and treat all current nodes as new ones if the current self ID generation is not the previous one plus 1. Signed-off-by: Stefan Richter Signed-off-by: Jarod Wilson --- drivers/firewire/fw-topology.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/firewire/fw-topology.c b/drivers/firewire/fw-topology.c index c9be6e6948c..e7520e4bd6b 100644 --- a/drivers/firewire/fw-topology.c +++ b/drivers/firewire/fw-topology.c @@ -518,6 +518,18 @@ fw_core_handle_bus_reset(struct fw_card *card, struct fw_node *local_node; unsigned long flags; + /* + * If the selfID buffer is not the immediate successor of the + * previously processed one, we cannot reliably compare the + * old and new topologies. + */ + if ((generation & 0xff) != ((card->generation + 1) & 0xff) && + card->local_node != NULL) { + fw_notify("skipped bus generations, destroying all nodes\n"); + fw_destroy_nodes(card); + card->bm_retries = 0; + } + spin_lock_irqsave(&card->lock, flags); card->node_id = node_id; -- cgit v1.2.3 From 8cd0bbbdff7471163cc6a058be8b8610ddd01d6b Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 24 Mar 2008 20:56:40 +0100 Subject: firewire: unnecessary BM delay after generation rollover Noticed by Jarod Wilson: The bus manager work was unnecessarily delayed each time the bus generation counter rolled over. Signed-off-by: Stefan Richter Signed-off-by: Jarod Wilson --- drivers/firewire/fw-card.c | 2 +- drivers/firewire/fw-topology.c | 2 +- drivers/firewire/fw-transaction.h | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index 6bd91a15d5e..17a80cecdc1 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c @@ -232,7 +232,7 @@ fw_card_bm_work(struct work_struct *work) root_id = root_node->node_id; grace = time_after(jiffies, card->reset_jiffies + DIV_ROUND_UP(HZ, 10)); - if (card->bm_generation + 1 == generation || + if (is_next_generation(generation, card->bm_generation) || (card->bm_generation != generation && grace)) { /* * This first step is to figure out who is IRM and diff --git a/drivers/firewire/fw-topology.c b/drivers/firewire/fw-topology.c index e7520e4bd6b..8dd6703b55c 100644 --- a/drivers/firewire/fw-topology.c +++ b/drivers/firewire/fw-topology.c @@ -523,7 +523,7 @@ fw_core_handle_bus_reset(struct fw_card *card, * previously processed one, we cannot reliably compare the * old and new topologies. */ - if ((generation & 0xff) != ((card->generation + 1) & 0xff) && + if (!is_next_generation(generation, card->generation) && card->local_node != NULL) { fw_notify("skipped bus generations, destroying all nodes\n"); fw_destroy_nodes(card); diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index c9ab12a15f6..1d78e9cc594 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h @@ -275,6 +275,15 @@ static inline void fw_card_put(struct fw_card *card) extern void fw_schedule_bm_work(struct fw_card *card, unsigned long delay); +/* + * Check whether new_generation is the immediate successor of old_generation. + * Take counter roll-over at 255 (as per to OHCI) into account. + */ +static inline bool is_next_generation(int new_generation, int old_generation) +{ + return (new_generation & 0xff) == ((old_generation + 1) & 0xff); +} + /* * The iso packet format allows for an immediate header/payload part * stored in 'header' immediately after the packet info plus an -- cgit v1.2.3 From 3d36a0df3b473fb53531484df227f2da8bc7494b Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 17 Jan 2009 22:45:54 +0100 Subject: firewire: keep highlevel drivers attached during brief connection loss There are situations when nodes vanish from the bus and come back quickly thereafter: - When certain bus-powered hubs are plugged in, - when certain devices are plugged into 6-port hubs, - when certain disk enclosures are switched from self-power to bus power or vice versa and break the daisy chain during the transition, - when the user plugs a cable out and quickly plugs it back in, e.g. to reorder a daisy chain (works on Mac OS X if done quickly enough), - when certain hubs temporarily malfunction during high bus traffic. Until now, firewire-core reported affected nodes as lost to the highlevel drivers (firewire-sbp2 and userspace drivers). We now delay the destruction of device representations until after at least two seconds after the last bus reset. If a "new" device is detected in this period whose bus information block and root directory header match that of a device which is pending for deletion, we resurrect that device and send update calls to highlevel drivers. Signed-off-by: Stefan Richter --- drivers/firewire/fw-device.c | 121 +++++++++++++++++++++++++++++++++++-------- drivers/firewire/fw-device.h | 1 + 2 files changed, 100 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 2af5a8d1e01..0925d91b261 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -634,12 +635,38 @@ struct fw_device *fw_device_get_by_devt(dev_t devt) return device; } +/* + * These defines control the retry behavior for reading the config + * rom. It shouldn't be necessary to tweak these; if the device + * doesn't respond to a config rom read within 10 seconds, it's not + * going to respond at all. As for the initial delay, a lot of + * devices will be able to respond within half a second after bus + * reset. On the other hand, it's not really worth being more + * aggressive than that, since it scales pretty well; if 10 devices + * are plugged in, they're all getting read within one second. + */ + +#define MAX_RETRIES 10 +#define RETRY_DELAY (3 * HZ) +#define INITIAL_DELAY (HZ / 2) +#define SHUTDOWN_DELAY (2 * HZ) + static void fw_device_shutdown(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); int minor = MINOR(device->device.devt); + if (time_is_after_jiffies(device->card->reset_jiffies + SHUTDOWN_DELAY)) { + schedule_delayed_work(&device->work, SHUTDOWN_DELAY); + return; + } + + if (atomic_cmpxchg(&device->state, + FW_DEVICE_GONE, + FW_DEVICE_SHUTDOWN) != FW_DEVICE_GONE) + return; + fw_device_cdev_remove(device); device_for_each_child(&device->device, NULL, shutdown_unit); device_unregister(&device->device); @@ -647,6 +674,7 @@ static void fw_device_shutdown(struct work_struct *work) down_write(&fw_device_rwsem); idr_remove(&fw_device_idr, minor); up_write(&fw_device_rwsem); + fw_device_put(device); } @@ -654,25 +682,63 @@ static struct device_type fw_device_type = { .release = fw_device_release, }; +static void fw_device_update(struct work_struct *work); + /* - * These defines control the retry behavior for reading the config - * rom. It shouldn't be necessary to tweak these; if the device - * doesn't respond to a config rom read within 10 seconds, it's not - * going to respond at all. As for the initial delay, a lot of - * devices will be able to respond within half a second after bus - * reset. On the other hand, it's not really worth being more - * aggressive than that, since it scales pretty well; if 10 devices - * are plugged in, they're all getting read within one second. + * If a device was pending for deletion because its node went away but its + * bus info block and root directory header matches that of a newly discovered + * device, revive the existing fw_device. + * The newly allocated fw_device becomes obsolete instead. */ +static int lookup_existing_device(struct device *dev, void *data) +{ + struct fw_device *old = fw_device(dev); + struct fw_device *new = data; + struct fw_card *card = new->card; + int match = 0; + + down_read(&fw_device_rwsem); /* serialize config_rom access */ + spin_lock_irq(&card->lock); /* serialize node access */ + + if (memcmp(old->config_rom, new->config_rom, 6 * 4) == 0 && + atomic_cmpxchg(&old->state, + FW_DEVICE_GONE, + FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { + struct fw_node *current_node = new->node; + struct fw_node *obsolete_node = old->node; + + new->node = obsolete_node; + new->node->data = new; + old->node = current_node; + old->node->data = old; + + old->max_speed = new->max_speed; + old->node_id = current_node->node_id; + smp_wmb(); /* update node_id before generation */ + old->generation = card->generation; + old->config_rom_retries = 0; + fw_notify("rediscovered device %s\n", dev_name(dev)); -#define MAX_RETRIES 10 -#define RETRY_DELAY (3 * HZ) -#define INITIAL_DELAY (HZ / 2) + PREPARE_DELAYED_WORK(&old->work, fw_device_update); + schedule_delayed_work(&old->work, 0); + + if (current_node == card->root_node) + fw_schedule_bm_work(card, 0); + + match = 1; + } + + spin_unlock_irq(&card->lock); + up_read(&fw_device_rwsem); + + return match; +} static void fw_device_init(struct work_struct *work) { struct fw_device *device = container_of(work, struct fw_device, work.work); + struct device *revived_dev; int minor, err; /* @@ -696,6 +762,15 @@ static void fw_device_init(struct work_struct *work) return; } + revived_dev = device_find_child(device->card->device, + device, lookup_existing_device); + if (revived_dev) { + put_device(revived_dev); + fw_device_release(&device->device); + + return; + } + device_initialize(&device->device); fw_device_get(device); @@ -734,9 +809,10 @@ static void fw_device_init(struct work_struct *work) * fw_node_event(). */ if (atomic_cmpxchg(&device->state, - FW_DEVICE_INITIALIZING, - FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) { - fw_device_shutdown(work); + FW_DEVICE_INITIALIZING, + FW_DEVICE_RUNNING) == FW_DEVICE_GONE) { + PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); + schedule_delayed_work(&device->work, SHUTDOWN_DELAY); } else { if (device->config_rom_retries) fw_notify("created device %s: GUID %08x%08x, S%d00, " @@ -847,8 +923,8 @@ static void fw_device_refresh(struct work_struct *work) case REREAD_BIB_UNCHANGED: if (atomic_cmpxchg(&device->state, - FW_DEVICE_INITIALIZING, - FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) + FW_DEVICE_INITIALIZING, + FW_DEVICE_RUNNING) == FW_DEVICE_GONE) goto gone; fw_device_update(work); @@ -879,8 +955,8 @@ static void fw_device_refresh(struct work_struct *work) create_units(device); if (atomic_cmpxchg(&device->state, - FW_DEVICE_INITIALIZING, - FW_DEVICE_RUNNING) == FW_DEVICE_SHUTDOWN) + FW_DEVICE_INITIALIZING, + FW_DEVICE_RUNNING) == FW_DEVICE_GONE) goto gone; fw_notify("refreshed device %s\n", dev_name(&device->device)); @@ -890,8 +966,9 @@ static void fw_device_refresh(struct work_struct *work) give_up: fw_notify("giving up on refresh of device %s\n", dev_name(&device->device)); gone: - atomic_set(&device->state, FW_DEVICE_SHUTDOWN); - fw_device_shutdown(work); + atomic_set(&device->state, FW_DEVICE_GONE); + PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); + schedule_delayed_work(&device->work, SHUTDOWN_DELAY); out: if (node_id == card->root_node->node_id) fw_schedule_bm_work(card, 0); @@ -995,9 +1072,9 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) */ device = node->data; if (atomic_xchg(&device->state, - FW_DEVICE_SHUTDOWN) == FW_DEVICE_RUNNING) { + FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); - schedule_delayed_work(&device->work, 0); + schedule_delayed_work(&device->work, SHUTDOWN_DELAY); } break; } diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h index df51732608d..8ef6ec2ca21 100644 --- a/drivers/firewire/fw-device.h +++ b/drivers/firewire/fw-device.h @@ -28,6 +28,7 @@ enum fw_device_state { FW_DEVICE_INITIALIZING, FW_DEVICE_RUNNING, + FW_DEVICE_GONE, FW_DEVICE_SHUTDOWN, }; -- cgit v1.2.3 From e3fd553468738e0342cbd82a63ede00c983a0eb4 Mon Sep 17 00:00:00 2001 From: Brice Goglin Date: Sat, 17 Jan 2009 08:27:19 +0000 Subject: myri10ge: don't forget pci_disable_device() Don't forget to call pci_disable_device() in myri10ge_remove() and when myri10ge_probe() fails. By the way, update the copyright years. Signed-off-by: Brice Goglin Signed-off-by: David S. Miller --- drivers/net/myri10ge/myri10ge.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 6bb71b687f7..e9c1296b267 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -1,7 +1,7 @@ /************************************************************************* * myri10ge.c: Myricom Myri-10G Ethernet driver. * - * Copyright (C) 2005 - 2007 Myricom, Inc. + * Copyright (C) 2005 - 2009 Myricom, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -75,7 +75,7 @@ #include "myri10ge_mcp.h" #include "myri10ge_mcp_gen_header.h" -#define MYRI10GE_VERSION_STR "1.4.4-1.398" +#define MYRI10GE_VERSION_STR "1.4.4-1.401" MODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); MODULE_AUTHOR("Maintainer: help@myri.com"); @@ -3786,7 +3786,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (status != 0) { dev_err(&pdev->dev, "Error %d writing PCI_EXP_DEVCTL\n", status); - goto abort_with_netdev; + goto abort_with_enabled; } pci_set_master(pdev); @@ -3801,13 +3801,13 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } if (status != 0) { dev_err(&pdev->dev, "Error %d setting DMA mask\n", status); - goto abort_with_netdev; + goto abort_with_enabled; } (void)pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); mgp->cmd = dma_alloc_coherent(&pdev->dev, sizeof(*mgp->cmd), &mgp->cmd_bus, GFP_KERNEL); if (mgp->cmd == NULL) - goto abort_with_netdev; + goto abort_with_enabled; mgp->board_span = pci_resource_len(pdev, 0); mgp->iomem_base = pci_resource_start(pdev, 0); @@ -3943,8 +3943,10 @@ abort_with_mtrr: dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus); -abort_with_netdev: +abort_with_enabled: + pci_disable_device(pdev); +abort_with_netdev: free_netdev(netdev); return status; } @@ -3990,6 +3992,7 @@ static void myri10ge_remove(struct pci_dev *pdev) mgp->cmd, mgp->cmd_bus); free_netdev(netdev); + pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } -- cgit v1.2.3 From 0d1cfd20cc5f785d5345d249d4b6a6f84b29e6a6 Mon Sep 17 00:00:00 2001 From: roel kluin Date: Sat, 17 Jan 2009 11:14:31 +0000 Subject: via-velocity: fix hot spin while(--j >= 0) keeps spinning when j is unsigned: Signed-off-by: Roel Kluin Signed-off-by: David S. Miller --- drivers/net/via-velocity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index a75f91dc315..c5691fdb707 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -1302,7 +1302,7 @@ static void velocity_free_rd_ring(struct velocity_info *vptr) static int velocity_init_td_ring(struct velocity_info *vptr) { dma_addr_t curr; - unsigned int j; + int j; /* Init the TD ring entries */ for (j = 0; j < vptr->tx.numq; j++) { -- cgit v1.2.3 From 7143f7a1a3603002e4ef3719fa92e8dd6e607099 Mon Sep 17 00:00:00 2001 From: Roland Dreier Date: Fri, 9 Jan 2009 22:27:42 -0800 Subject: driver core: Convert '/' to '!' in dev_set_name() Commit 3ada8b7e ("block: struct device - replace bus_id with dev_name(), dev_set_name()") deleted the code in register_disk() that changed a '/' to a '!' in the device name when registering a disk, but dev_set_name() does not perform this conversion. This leads to amusing problems with disks that have '/' in their names: for example a failure to boot with the root partition on a cciss device, even though the kernel says it knows about the root device: VFS: Cannot open root device "cciss/c0d0p6" or unknown-block(0,0) Please append a correct "root=" boot option; here are the available partitions: 6800 71652960 cciss/c0d0 driver: cciss 6802 1 cciss/c0d0p2 6805 2931831 cciss/c0d0p5 6806 34354908 cciss/c0d0p6 6810 71652960 cciss/c0d1 driver: cciss Fix this by adding code to change '/' to '!' in dev_set_name() to handle this until dev_set_name() is converted to use kobject_set_name(). Signed-off-by: Roland Dreier Acked-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 8079afca497..55e530942ab 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -777,10 +777,16 @@ static void device_remove_class_symlinks(struct device *dev) int dev_set_name(struct device *dev, const char *fmt, ...) { va_list vargs; + char *s; va_start(vargs, fmt); vsnprintf(dev->bus_id, sizeof(dev->bus_id), fmt, vargs); va_end(vargs); + + /* ewww... some of these buggers have / in the name... */ + while ((s = strchr(dev->bus_id, '/'))) + *s = '!'; + return 0; } EXPORT_SYMBOL_GPL(dev_set_name); -- cgit v1.2.3 From fd88cac90587a456eb944bf794634229553c11b9 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 9 Jan 2009 16:32:08 +0900 Subject: serial: sh-sci: Fix up SH7720/SH7721 SCI build. Missing definitions for PORT_xxx defs. Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 38c600c0dbb..c0f4823ab78 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -32,7 +32,9 @@ #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ defined(CONFIG_CPU_SUBTYPE_SH7721) # define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */ -#define SCIF_ORER 0x0200 /* overrun error bit */ +# define PORT_PTCR 0xA405011EUL +# define PORT_PVCR 0xA4050122UL +# define SCIF_ORER 0x0200 /* overrun error bit */ #elif defined(CONFIG_SH_RTS7751R2D) # define SCSPTR1 0xFFE0001C /* 8 bit SCIF */ # define SCSPTR2 0xFFE80020 /* 16 bit SCIF */ -- cgit v1.2.3 From f686359e0da5ae71459ee045646a5f508f9ff6d8 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 20 Jan 2009 12:18:22 +0900 Subject: sh: fix sh-sci / early printk build on sh7723 This patch adds the SCSPTR register to the sh-sci driver in the case of sh7723 to make sure early printk builds properly. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/serial/sh-sci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index c0f4823ab78..3599828b976 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -395,6 +395,7 @@ SCIx_FNS(SCSCR, 0x08, 16, 0x08, 16) SCIx_FNS(SCxTDR, 0x20, 8, 0x0c, 8) SCIx_FNS(SCxSR, 0x14, 16, 0x10, 16) SCIx_FNS(SCxRDR, 0x24, 8, 0x14, 8) +SCIx_FNS(SCSPTR, 0, 0, 0, 0) SCIF_FNS(SCTDSR, 0x0c, 8) SCIF_FNS(SCFER, 0x10, 16) SCIF_FNS(SCFCR, 0x18, 16) -- cgit v1.2.3 From 86528da229a448577a8401a17c295883640d336c Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 21 Jan 2009 10:32:34 -0700 Subject: i.MX31: framebuffer driver This is a framebuffer driver for i.MX31 SoCs. It only supports synchronous displays, vertical panning supported, no overlay support. Acked-by: Sascha Hauer Signed-off-by: Guennadi Liakhovetski Signed-off-by: Dan Williams --- drivers/video/Kconfig | 12 + drivers/video/Makefile | 1 + drivers/video/mx3fb.c | 1555 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1568 insertions(+) create mode 100644 drivers/video/mx3fb.c (limited to 'drivers') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6372f8b17b4..c94f71980c1 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -2123,6 +2123,18 @@ config FB_PRE_INIT_FB Select this option if display contents should be inherited as set by the bootloader. +config FB_MX3 + tristate "MX3 Framebuffer support" + depends on FB && MX3_IPU + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + default y + help + This is a framebuffer device for the i.MX31 LCD Controller. So + far only synchronous displays are supported. If you plan to use + an LCD display with your i.MX31 system, say Y here. + source "drivers/video/omap/Kconfig" source "drivers/video/backlight/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index e39e33e797d..a9c641c956c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -132,6 +132,7 @@ obj-$(CONFIG_FB_VGA16) += vga16fb.o obj-$(CONFIG_FB_OF) += offb.o obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o +obj-$(CONFIG_FB_MX3) += mx3fb.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c new file mode 100644 index 00000000000..8a75d05f433 --- /dev/null +++ b/drivers/video/mx3fb.c @@ -0,0 +1,1555 @@ +/* + * Copyright (C) 2008 + * Guennadi Liakhovetski, DENX Software Engineering, + * + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define MX3FB_NAME "mx3_sdc_fb" + +#define MX3FB_REG_OFFSET 0xB4 + +/* SDC Registers */ +#define SDC_COM_CONF (0xB4 - MX3FB_REG_OFFSET) +#define SDC_GW_CTRL (0xB8 - MX3FB_REG_OFFSET) +#define SDC_FG_POS (0xBC - MX3FB_REG_OFFSET) +#define SDC_BG_POS (0xC0 - MX3FB_REG_OFFSET) +#define SDC_CUR_POS (0xC4 - MX3FB_REG_OFFSET) +#define SDC_PWM_CTRL (0xC8 - MX3FB_REG_OFFSET) +#define SDC_CUR_MAP (0xCC - MX3FB_REG_OFFSET) +#define SDC_HOR_CONF (0xD0 - MX3FB_REG_OFFSET) +#define SDC_VER_CONF (0xD4 - MX3FB_REG_OFFSET) +#define SDC_SHARP_CONF_1 (0xD8 - MX3FB_REG_OFFSET) +#define SDC_SHARP_CONF_2 (0xDC - MX3FB_REG_OFFSET) + +/* Register bits */ +#define SDC_COM_TFT_COLOR 0x00000001UL +#define SDC_COM_FG_EN 0x00000010UL +#define SDC_COM_GWSEL 0x00000020UL +#define SDC_COM_GLB_A 0x00000040UL +#define SDC_COM_KEY_COLOR_G 0x00000080UL +#define SDC_COM_BG_EN 0x00000200UL +#define SDC_COM_SHARP 0x00001000UL + +#define SDC_V_SYNC_WIDTH_L 0x00000001UL + +/* Display Interface registers */ +#define DI_DISP_IF_CONF (0x0124 - MX3FB_REG_OFFSET) +#define DI_DISP_SIG_POL (0x0128 - MX3FB_REG_OFFSET) +#define DI_SER_DISP1_CONF (0x012C - MX3FB_REG_OFFSET) +#define DI_SER_DISP2_CONF (0x0130 - MX3FB_REG_OFFSET) +#define DI_HSP_CLK_PER (0x0134 - MX3FB_REG_OFFSET) +#define DI_DISP0_TIME_CONF_1 (0x0138 - MX3FB_REG_OFFSET) +#define DI_DISP0_TIME_CONF_2 (0x013C - MX3FB_REG_OFFSET) +#define DI_DISP0_TIME_CONF_3 (0x0140 - MX3FB_REG_OFFSET) +#define DI_DISP1_TIME_CONF_1 (0x0144 - MX3FB_REG_OFFSET) +#define DI_DISP1_TIME_CONF_2 (0x0148 - MX3FB_REG_OFFSET) +#define DI_DISP1_TIME_CONF_3 (0x014C - MX3FB_REG_OFFSET) +#define DI_DISP2_TIME_CONF_1 (0x0150 - MX3FB_REG_OFFSET) +#define DI_DISP2_TIME_CONF_2 (0x0154 - MX3FB_REG_OFFSET) +#define DI_DISP2_TIME_CONF_3 (0x0158 - MX3FB_REG_OFFSET) +#define DI_DISP3_TIME_CONF (0x015C - MX3FB_REG_OFFSET) +#define DI_DISP0_DB0_MAP (0x0160 - MX3FB_REG_OFFSET) +#define DI_DISP0_DB1_MAP (0x0164 - MX3FB_REG_OFFSET) +#define DI_DISP0_DB2_MAP (0x0168 - MX3FB_REG_OFFSET) +#define DI_DISP0_CB0_MAP (0x016C - MX3FB_REG_OFFSET) +#define DI_DISP0_CB1_MAP (0x0170 - MX3FB_REG_OFFSET) +#define DI_DISP0_CB2_MAP (0x0174 - MX3FB_REG_OFFSET) +#define DI_DISP1_DB0_MAP (0x0178 - MX3FB_REG_OFFSET) +#define DI_DISP1_DB1_MAP (0x017C - MX3FB_REG_OFFSET) +#define DI_DISP1_DB2_MAP (0x0180 - MX3FB_REG_OFFSET) +#define DI_DISP1_CB0_MAP (0x0184 - MX3FB_REG_OFFSET) +#define DI_DISP1_CB1_MAP (0x0188 - MX3FB_REG_OFFSET) +#define DI_DISP1_CB2_MAP (0x018C - MX3FB_REG_OFFSET) +#define DI_DISP2_DB0_MAP (0x0190 - MX3FB_REG_OFFSET) +#define DI_DISP2_DB1_MAP (0x0194 - MX3FB_REG_OFFSET) +#define DI_DISP2_DB2_MAP (0x0198 - MX3FB_REG_OFFSET) +#define DI_DISP2_CB0_MAP (0x019C - MX3FB_REG_OFFSET) +#define DI_DISP2_CB1_MAP (0x01A0 - MX3FB_REG_OFFSET) +#define DI_DISP2_CB2_MAP (0x01A4 - MX3FB_REG_OFFSET) +#define DI_DISP3_B0_MAP (0x01A8 - MX3FB_REG_OFFSET) +#define DI_DISP3_B1_MAP (0x01AC - MX3FB_REG_OFFSET) +#define DI_DISP3_B2_MAP (0x01B0 - MX3FB_REG_OFFSET) +#define DI_DISP_ACC_CC (0x01B4 - MX3FB_REG_OFFSET) +#define DI_DISP_LLA_CONF (0x01B8 - MX3FB_REG_OFFSET) +#define DI_DISP_LLA_DATA (0x01BC - MX3FB_REG_OFFSET) + +/* DI_DISP_SIG_POL bits */ +#define DI_D3_VSYNC_POL_SHIFT 28 +#define DI_D3_HSYNC_POL_SHIFT 27 +#define DI_D3_DRDY_SHARP_POL_SHIFT 26 +#define DI_D3_CLK_POL_SHIFT 25 +#define DI_D3_DATA_POL_SHIFT 24 + +/* DI_DISP_IF_CONF bits */ +#define DI_D3_CLK_IDLE_SHIFT 26 +#define DI_D3_CLK_SEL_SHIFT 25 +#define DI_D3_DATAMSK_SHIFT 24 + +enum ipu_panel { + IPU_PANEL_SHARP_TFT, + IPU_PANEL_TFT, +}; + +struct ipu_di_signal_cfg { + unsigned datamask_en:1; + unsigned clksel_en:1; + unsigned clkidle_en:1; + unsigned data_pol:1; /* true = inverted */ + unsigned clk_pol:1; /* true = rising edge */ + unsigned enable_pol:1; + unsigned Hsync_pol:1; /* true = active high */ + unsigned Vsync_pol:1; +}; + +static const struct fb_videomode mx3fb_modedb[] = { + { + /* 240x320 @ 60 Hz */ + .name = "Sharp-QVGA", + .refresh = 60, + .xres = 240, + .yres = 320, + .pixclock = 185925, + .left_margin = 9, + .right_margin = 16, + .upper_margin = 7, + .lower_margin = 9, + .hsync_len = 1, + .vsync_len = 1, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | + FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT | + FB_SYNC_CLK_IDLE_EN, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, { + /* 240x33 @ 60 Hz */ + .name = "Sharp-CLI", + .refresh = 60, + .xres = 240, + .yres = 33, + .pixclock = 185925, + .left_margin = 9, + .right_margin = 16, + .upper_margin = 7, + .lower_margin = 9 + 287, + .hsync_len = 1, + .vsync_len = 1, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE | + FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT | + FB_SYNC_CLK_IDLE_EN, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, { + /* 640x480 @ 60 Hz */ + .name = "NEC-VGA", + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 38255, + .left_margin = 144, + .right_margin = 0, + .upper_margin = 34, + .lower_margin = 40, + .hsync_len = 1, + .vsync_len = 1, + .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, { + /* NTSC TV output */ + .name = "TV-NTSC", + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 37538, + .left_margin = 38, + .right_margin = 858 - 640 - 38 - 3, + .upper_margin = 36, + .lower_margin = 518 - 480 - 36 - 1, + .hsync_len = 3, + .vsync_len = 1, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, { + /* PAL TV output */ + .name = "TV-PAL", + .refresh = 50, + .xres = 640, + .yres = 480, + .pixclock = 37538, + .left_margin = 38, + .right_margin = 960 - 640 - 38 - 32, + .upper_margin = 32, + .lower_margin = 555 - 480 - 32 - 3, + .hsync_len = 32, + .vsync_len = 3, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, { + /* TV output VGA mode, 640x480 @ 65 Hz */ + .name = "TV-VGA", + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 40574, + .left_margin = 35, + .right_margin = 45, + .upper_margin = 9, + .lower_margin = 1, + .hsync_len = 46, + .vsync_len = 5, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + .flag = 0, + }, +}; + +struct mx3fb_data { + struct fb_info *fbi; + int backlight_level; + void __iomem *reg_base; + spinlock_t lock; + struct device *dev; + + uint32_t h_start_width; + uint32_t v_start_width; +}; + +struct dma_chan_request { + struct mx3fb_data *mx3fb; + enum ipu_channel id; +}; + +/* MX3 specific framebuffer information. */ +struct mx3fb_info { + int blank; + enum ipu_channel ipu_ch; + uint32_t cur_ipu_buf; + + u32 pseudo_palette[16]; + + struct completion flip_cmpl; + struct mutex mutex; /* Protects fb-ops */ + struct mx3fb_data *mx3fb; + struct idmac_channel *idmac_channel; + struct dma_async_tx_descriptor *txd; + dma_cookie_t cookie; + struct scatterlist sg[2]; + + u32 sync; /* preserve var->sync flags */ +}; + +static void mx3fb_dma_done(void *); + +/* Used fb-mode and bpp. Can be set on kernel command line, therefore file-static. */ +static const char *fb_mode; +static unsigned long default_bpp = 16; + +static u32 mx3fb_read_reg(struct mx3fb_data *mx3fb, unsigned long reg) +{ + return __raw_readl(mx3fb->reg_base + reg); +} + +static void mx3fb_write_reg(struct mx3fb_data *mx3fb, u32 value, unsigned long reg) +{ + __raw_writel(value, mx3fb->reg_base + reg); +} + +static const uint32_t di_mappings[] = { + 0x1600AAAA, 0x00E05555, 0x00070000, 3, /* RGB888 */ + 0x0005000F, 0x000B000F, 0x0011000F, 1, /* RGB666 */ + 0x0011000F, 0x000B000F, 0x0005000F, 1, /* BGR666 */ + 0x0004003F, 0x000A000F, 0x000F003F, 1 /* RGB565 */ +}; + +static void sdc_fb_init(struct mx3fb_info *fbi) +{ + struct mx3fb_data *mx3fb = fbi->mx3fb; + uint32_t reg; + + reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); + + mx3fb_write_reg(mx3fb, reg | SDC_COM_BG_EN, SDC_COM_CONF); +} + +/* Returns enabled flag before uninit */ +static uint32_t sdc_fb_uninit(struct mx3fb_info *fbi) +{ + struct mx3fb_data *mx3fb = fbi->mx3fb; + uint32_t reg; + + reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); + + mx3fb_write_reg(mx3fb, reg & ~SDC_COM_BG_EN, SDC_COM_CONF); + + return reg & SDC_COM_BG_EN; +} + +static void sdc_enable_channel(struct mx3fb_info *mx3_fbi) +{ + struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; + struct idmac_channel *ichan = mx3_fbi->idmac_channel; + struct dma_chan *dma_chan = &ichan->dma_chan; + unsigned long flags; + dma_cookie_t cookie; + + dev_dbg(mx3fb->dev, "mx3fbi %p, desc %p, sg %p\n", mx3_fbi, + to_tx_desc(mx3_fbi->txd), to_tx_desc(mx3_fbi->txd)->sg); + + /* This enables the channel */ + if (mx3_fbi->cookie < 0) { + mx3_fbi->txd = dma_chan->device->device_prep_slave_sg(dma_chan, + &mx3_fbi->sg[0], 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT); + if (!mx3_fbi->txd) { + dev_err(mx3fb->dev, "Cannot allocate descriptor on %d\n", + dma_chan->chan_id); + return; + } + + mx3_fbi->txd->callback_param = mx3_fbi->txd; + mx3_fbi->txd->callback = mx3fb_dma_done; + + cookie = mx3_fbi->txd->tx_submit(mx3_fbi->txd); + dev_dbg(mx3fb->dev, "%d: Submit %p #%d [%c]\n", __LINE__, + mx3_fbi->txd, cookie, list_empty(&ichan->queue) ? '-' : '+'); + } else { + if (!mx3_fbi->txd || !mx3_fbi->txd->tx_submit) { + dev_err(mx3fb->dev, "Cannot enable channel %d\n", + dma_chan->chan_id); + return; + } + + /* Just re-activate the same buffer */ + dma_async_issue_pending(dma_chan); + cookie = mx3_fbi->cookie; + dev_dbg(mx3fb->dev, "%d: Re-submit %p #%d [%c]\n", __LINE__, + mx3_fbi->txd, cookie, list_empty(&ichan->queue) ? '-' : '+'); + } + + if (cookie >= 0) { + spin_lock_irqsave(&mx3fb->lock, flags); + sdc_fb_init(mx3_fbi); + mx3_fbi->cookie = cookie; + spin_unlock_irqrestore(&mx3fb->lock, flags); + } + + /* + * Attention! Without this msleep the channel keeps generating + * interrupts. Next sdc_set_brightness() is going to be called + * from mx3fb_blank(). + */ + msleep(2); +} + +static void sdc_disable_channel(struct mx3fb_info *mx3_fbi) +{ + struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; + uint32_t enabled; + unsigned long flags; + + spin_lock_irqsave(&mx3fb->lock, flags); + + enabled = sdc_fb_uninit(mx3_fbi); + + spin_unlock_irqrestore(&mx3fb->lock, flags); + + mx3_fbi->txd->chan->device->device_terminate_all(mx3_fbi->txd->chan); + mx3_fbi->txd = NULL; + mx3_fbi->cookie = -EINVAL; +} + +/** + * sdc_set_window_pos() - set window position of the respective plane. + * @mx3fb: mx3fb context. + * @channel: IPU DMAC channel ID. + * @x_pos: X coordinate relative to the top left corner to place window at. + * @y_pos: Y coordinate relative to the top left corner to place window at. + * @return: 0 on success or negative error code on failure. + */ +static int sdc_set_window_pos(struct mx3fb_data *mx3fb, enum ipu_channel channel, + int16_t x_pos, int16_t y_pos) +{ + x_pos += mx3fb->h_start_width; + y_pos += mx3fb->v_start_width; + + if (channel != IDMAC_SDC_0) + return -EINVAL; + + mx3fb_write_reg(mx3fb, (x_pos << 16) | y_pos, SDC_BG_POS); + return 0; +} + +/** + * sdc_init_panel() - initialize a synchronous LCD panel. + * @mx3fb: mx3fb context. + * @panel: panel type. + * @pixel_clk: desired pixel clock frequency in Hz. + * @width: width of panel in pixels. + * @height: height of panel in pixels. + * @pixel_fmt: pixel format of buffer as FOURCC ASCII code. + * @h_start_width: number of pixel clocks between the HSYNC signal pulse + * and the start of valid data. + * @h_sync_width: width of the HSYNC signal in units of pixel clocks. + * @h_end_width: number of pixel clocks between the end of valid data + * and the HSYNC signal for next line. + * @v_start_width: number of lines between the VSYNC signal pulse and the + * start of valid data. + * @v_sync_width: width of the VSYNC signal in units of lines + * @v_end_width: number of lines between the end of valid data and the + * VSYNC signal for next frame. + * @sig: bitfield of signal polarities for LCD interface. + * @return: 0 on success or negative error code on failure. + */ +static int sdc_init_panel(struct mx3fb_data *mx3fb, enum ipu_panel panel, + uint32_t pixel_clk, + uint16_t width, uint16_t height, + enum pixel_fmt pixel_fmt, + uint16_t h_start_width, uint16_t h_sync_width, + uint16_t h_end_width, uint16_t v_start_width, + uint16_t v_sync_width, uint16_t v_end_width, + struct ipu_di_signal_cfg sig) +{ + unsigned long lock_flags; + uint32_t reg; + uint32_t old_conf; + uint32_t div; + struct clk *ipu_clk; + + dev_dbg(mx3fb->dev, "panel size = %d x %d", width, height); + + if (v_sync_width == 0 || h_sync_width == 0) + return -EINVAL; + + /* Init panel size and blanking periods */ + reg = ((uint32_t) (h_sync_width - 1) << 26) | + ((uint32_t) (width + h_start_width + h_end_width - 1) << 16); + mx3fb_write_reg(mx3fb, reg, SDC_HOR_CONF); + +#ifdef DEBUG + printk(KERN_CONT " hor_conf %x,", reg); +#endif + + reg = ((uint32_t) (v_sync_width - 1) << 26) | SDC_V_SYNC_WIDTH_L | + ((uint32_t) (height + v_start_width + v_end_width - 1) << 16); + mx3fb_write_reg(mx3fb, reg, SDC_VER_CONF); + +#ifdef DEBUG + printk(KERN_CONT " ver_conf %x\n", reg); +#endif + + mx3fb->h_start_width = h_start_width; + mx3fb->v_start_width = v_start_width; + + switch (panel) { + case IPU_PANEL_SHARP_TFT: + mx3fb_write_reg(mx3fb, 0x00FD0102L, SDC_SHARP_CONF_1); + mx3fb_write_reg(mx3fb, 0x00F500F4L, SDC_SHARP_CONF_2); + mx3fb_write_reg(mx3fb, SDC_COM_SHARP | SDC_COM_TFT_COLOR, SDC_COM_CONF); + break; + case IPU_PANEL_TFT: + mx3fb_write_reg(mx3fb, SDC_COM_TFT_COLOR, SDC_COM_CONF); + break; + default: + return -EINVAL; + } + + /* Init clocking */ + + /* + * Calculate divider: fractional part is 4 bits so simply multiple by + * 24 to get fractional part, as long as we stay under ~250MHz and on + * i.MX31 it (HSP_CLK) is <= 178MHz. Currently 128.267MHz + */ + dev_dbg(mx3fb->dev, "pixel clk = %d\n", pixel_clk); + + ipu_clk = clk_get(mx3fb->dev, "ipu_clk"); + div = clk_get_rate(ipu_clk) * 16 / pixel_clk; + clk_put(ipu_clk); + + if (div < 0x40) { /* Divider less than 4 */ + dev_dbg(mx3fb->dev, + "InitPanel() - Pixel clock divider less than 4\n"); + div = 0x40; + } + + spin_lock_irqsave(&mx3fb->lock, lock_flags); + + /* + * DISP3_IF_CLK_DOWN_WR is half the divider value and 2 fraction bits + * fewer. Subtract 1 extra from DISP3_IF_CLK_DOWN_WR based on timing + * debug. DISP3_IF_CLK_UP_WR is 0 + */ + mx3fb_write_reg(mx3fb, (((div / 8) - 1) << 22) | div, DI_DISP3_TIME_CONF); + + /* DI settings */ + old_conf = mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF) & 0x78FFFFFF; + old_conf |= sig.datamask_en << DI_D3_DATAMSK_SHIFT | + sig.clksel_en << DI_D3_CLK_SEL_SHIFT | + sig.clkidle_en << DI_D3_CLK_IDLE_SHIFT; + mx3fb_write_reg(mx3fb, old_conf, DI_DISP_IF_CONF); + + old_conf = mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL) & 0xE0FFFFFF; + old_conf |= sig.data_pol << DI_D3_DATA_POL_SHIFT | + sig.clk_pol << DI_D3_CLK_POL_SHIFT | + sig.enable_pol << DI_D3_DRDY_SHARP_POL_SHIFT | + sig.Hsync_pol << DI_D3_HSYNC_POL_SHIFT | + sig.Vsync_pol << DI_D3_VSYNC_POL_SHIFT; + mx3fb_write_reg(mx3fb, old_conf, DI_DISP_SIG_POL); + + switch (pixel_fmt) { + case IPU_PIX_FMT_RGB24: + mx3fb_write_reg(mx3fb, di_mappings[0], DI_DISP3_B0_MAP); + mx3fb_write_reg(mx3fb, di_mappings[1], DI_DISP3_B1_MAP); + mx3fb_write_reg(mx3fb, di_mappings[2], DI_DISP3_B2_MAP); + mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | + ((di_mappings[3] - 1) << 12), DI_DISP_ACC_CC); + break; + case IPU_PIX_FMT_RGB666: + mx3fb_write_reg(mx3fb, di_mappings[4], DI_DISP3_B0_MAP); + mx3fb_write_reg(mx3fb, di_mappings[5], DI_DISP3_B1_MAP); + mx3fb_write_reg(mx3fb, di_mappings[6], DI_DISP3_B2_MAP); + mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | + ((di_mappings[7] - 1) << 12), DI_DISP_ACC_CC); + break; + case IPU_PIX_FMT_BGR666: + mx3fb_write_reg(mx3fb, di_mappings[8], DI_DISP3_B0_MAP); + mx3fb_write_reg(mx3fb, di_mappings[9], DI_DISP3_B1_MAP); + mx3fb_write_reg(mx3fb, di_mappings[10], DI_DISP3_B2_MAP); + mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | + ((di_mappings[11] - 1) << 12), DI_DISP_ACC_CC); + break; + default: + mx3fb_write_reg(mx3fb, di_mappings[12], DI_DISP3_B0_MAP); + mx3fb_write_reg(mx3fb, di_mappings[13], DI_DISP3_B1_MAP); + mx3fb_write_reg(mx3fb, di_mappings[14], DI_DISP3_B2_MAP); + mx3fb_write_reg(mx3fb, mx3fb_read_reg(mx3fb, DI_DISP_ACC_CC) | + ((di_mappings[15] - 1) << 12), DI_DISP_ACC_CC); + break; + } + + spin_unlock_irqrestore(&mx3fb->lock, lock_flags); + + dev_dbg(mx3fb->dev, "DI_DISP_IF_CONF = 0x%08X\n", + mx3fb_read_reg(mx3fb, DI_DISP_IF_CONF)); + dev_dbg(mx3fb->dev, "DI_DISP_SIG_POL = 0x%08X\n", + mx3fb_read_reg(mx3fb, DI_DISP_SIG_POL)); + dev_dbg(mx3fb->dev, "DI_DISP3_TIME_CONF = 0x%08X\n", + mx3fb_read_reg(mx3fb, DI_DISP3_TIME_CONF)); + + return 0; +} + +/** + * sdc_set_color_key() - set the transparent color key for SDC graphic plane. + * @mx3fb: mx3fb context. + * @channel: IPU DMAC channel ID. + * @enable: boolean to enable or disable color keyl. + * @color_key: 24-bit RGB color to use as transparent color key. + * @return: 0 on success or negative error code on failure. + */ +static int sdc_set_color_key(struct mx3fb_data *mx3fb, enum ipu_channel channel, + bool enable, uint32_t color_key) +{ + uint32_t reg, sdc_conf; + unsigned long lock_flags; + + spin_lock_irqsave(&mx3fb->lock, lock_flags); + + sdc_conf = mx3fb_read_reg(mx3fb, SDC_COM_CONF); + if (channel == IDMAC_SDC_0) + sdc_conf &= ~SDC_COM_GWSEL; + else + sdc_conf |= SDC_COM_GWSEL; + + if (enable) { + reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0xFF000000L; + mx3fb_write_reg(mx3fb, reg | (color_key & 0x00FFFFFFL), + SDC_GW_CTRL); + + sdc_conf |= SDC_COM_KEY_COLOR_G; + } else { + sdc_conf &= ~SDC_COM_KEY_COLOR_G; + } + mx3fb_write_reg(mx3fb, sdc_conf, SDC_COM_CONF); + + spin_unlock_irqrestore(&mx3fb->lock, lock_flags); + + return 0; +} + +/** + * sdc_set_global_alpha() - set global alpha blending modes. + * @mx3fb: mx3fb context. + * @enable: boolean to enable or disable global alpha blending. If disabled, + * per pixel blending is used. + * @alpha: global alpha value. + * @return: 0 on success or negative error code on failure. + */ +static int sdc_set_global_alpha(struct mx3fb_data *mx3fb, bool enable, uint8_t alpha) +{ + uint32_t reg; + unsigned long lock_flags; + + spin_lock_irqsave(&mx3fb->lock, lock_flags); + + if (enable) { + reg = mx3fb_read_reg(mx3fb, SDC_GW_CTRL) & 0x00FFFFFFL; + mx3fb_write_reg(mx3fb, reg | ((uint32_t) alpha << 24), SDC_GW_CTRL); + + reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); + mx3fb_write_reg(mx3fb, reg | SDC_COM_GLB_A, SDC_COM_CONF); + } else { + reg = mx3fb_read_reg(mx3fb, SDC_COM_CONF); + mx3fb_write_reg(mx3fb, reg & ~SDC_COM_GLB_A, SDC_COM_CONF); + } + + spin_unlock_irqrestore(&mx3fb->lock, lock_flags); + + return 0; +} + +static void sdc_set_brightness(struct mx3fb_data *mx3fb, uint8_t value) +{ + /* This might be board-specific */ + mx3fb_write_reg(mx3fb, 0x03000000UL | value << 16, SDC_PWM_CTRL); + return; +} + +static uint32_t bpp_to_pixfmt(int bpp) +{ + uint32_t pixfmt = 0; + switch (bpp) { + case 24: + pixfmt = IPU_PIX_FMT_BGR24; + break; + case 32: + pixfmt = IPU_PIX_FMT_BGR32; + break; + case 16: + pixfmt = IPU_PIX_FMT_RGB565; + break; + } + return pixfmt; +} + +static int mx3fb_blank(int blank, struct fb_info *fbi); +static int mx3fb_map_video_memory(struct fb_info *fbi); +static int mx3fb_unmap_video_memory(struct fb_info *fbi); + +/** + * mx3fb_set_fix() - set fixed framebuffer parameters from variable settings. + * @info: framebuffer information pointer + * @return: 0 on success or negative error code on failure. + */ +static int mx3fb_set_fix(struct fb_info *fbi) +{ + struct fb_fix_screeninfo *fix = &fbi->fix; + struct fb_var_screeninfo *var = &fbi->var; + + strncpy(fix->id, "DISP3 BG", 8); + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + + fix->type = FB_TYPE_PACKED_PIXELS; + fix->accel = FB_ACCEL_NONE; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 1; + fix->ypanstep = 1; + + return 0; +} + +static void mx3fb_dma_done(void *arg) +{ + struct idmac_tx_desc *tx_desc = to_tx_desc(arg); + struct dma_chan *chan = tx_desc->txd.chan; + struct idmac_channel *ichannel = to_idmac_chan(chan); + struct mx3fb_data *mx3fb = ichannel->client; + struct mx3fb_info *mx3_fbi = mx3fb->fbi->par; + + dev_dbg(mx3fb->dev, "irq %d callback\n", ichannel->eof_irq); + + /* We only need one interrupt, it will be re-enabled as needed */ + disable_irq(ichannel->eof_irq); + + complete(&mx3_fbi->flip_cmpl); +} + +/** + * mx3fb_set_par() - set framebuffer parameters and change the operating mode. + * @fbi: framebuffer information pointer. + * @return: 0 on success or negative error code on failure. + */ +static int mx3fb_set_par(struct fb_info *fbi) +{ + u32 mem_len; + struct ipu_di_signal_cfg sig_cfg; + enum ipu_panel mode = IPU_PANEL_TFT; + struct mx3fb_info *mx3_fbi = fbi->par; + struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; + struct idmac_channel *ichan = mx3_fbi->idmac_channel; + struct idmac_video_param *video = &ichan->params.video; + struct scatterlist *sg = mx3_fbi->sg; + size_t screen_size; + + dev_dbg(mx3fb->dev, "%s [%c]\n", __func__, list_empty(&ichan->queue) ? '-' : '+'); + + mutex_lock(&mx3_fbi->mutex); + + /* Total cleanup */ + if (mx3_fbi->txd) + sdc_disable_channel(mx3_fbi); + + mx3fb_set_fix(fbi); + + mem_len = fbi->var.yres_virtual * fbi->fix.line_length; + if (mem_len > fbi->fix.smem_len) { + if (fbi->fix.smem_start) + mx3fb_unmap_video_memory(fbi); + + fbi->fix.smem_len = mem_len; + if (mx3fb_map_video_memory(fbi) < 0) { + mutex_unlock(&mx3_fbi->mutex); + return -ENOMEM; + } + } + + screen_size = fbi->fix.line_length * fbi->var.yres; + + sg_init_table(&sg[0], 1); + sg_init_table(&sg[1], 1); + + sg_dma_address(&sg[0]) = fbi->fix.smem_start; + sg_set_page(&sg[0], virt_to_page(fbi->screen_base), + fbi->fix.smem_len, + offset_in_page(fbi->screen_base)); + + if (mx3_fbi->ipu_ch == IDMAC_SDC_0) { + memset(&sig_cfg, 0, sizeof(sig_cfg)); + if (fbi->var.sync & FB_SYNC_HOR_HIGH_ACT) + sig_cfg.Hsync_pol = true; + if (fbi->var.sync & FB_SYNC_VERT_HIGH_ACT) + sig_cfg.Vsync_pol = true; + if (fbi->var.sync & FB_SYNC_CLK_INVERT) + sig_cfg.clk_pol = true; + if (fbi->var.sync & FB_SYNC_DATA_INVERT) + sig_cfg.data_pol = true; + if (fbi->var.sync & FB_SYNC_OE_ACT_HIGH) + sig_cfg.enable_pol = true; + if (fbi->var.sync & FB_SYNC_CLK_IDLE_EN) + sig_cfg.clkidle_en = true; + if (fbi->var.sync & FB_SYNC_CLK_SEL_EN) + sig_cfg.clksel_en = true; + if (fbi->var.sync & FB_SYNC_SHARP_MODE) + mode = IPU_PANEL_SHARP_TFT; + + dev_dbg(fbi->device, "pixclock = %ul Hz\n", + (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL)); + + if (sdc_init_panel(mx3fb, mode, + (PICOS2KHZ(fbi->var.pixclock)) * 1000UL, + fbi->var.xres, fbi->var.yres, + (fbi->var.sync & FB_SYNC_SWAP_RGB) ? + IPU_PIX_FMT_BGR666 : IPU_PIX_FMT_RGB666, + fbi->var.left_margin, + fbi->var.hsync_len, + fbi->var.right_margin + + fbi->var.hsync_len, + fbi->var.upper_margin, + fbi->var.vsync_len, + fbi->var.lower_margin + + fbi->var.vsync_len, sig_cfg) != 0) { + mutex_unlock(&mx3_fbi->mutex); + dev_err(fbi->device, + "mx3fb: Error initializing panel.\n"); + return -EINVAL; + } + } + + sdc_set_window_pos(mx3fb, mx3_fbi->ipu_ch, 0, 0); + + mx3_fbi->cur_ipu_buf = 0; + + video->out_pixel_fmt = bpp_to_pixfmt(fbi->var.bits_per_pixel); + video->out_width = fbi->var.xres; + video->out_height = fbi->var.yres; + video->out_stride = fbi->var.xres_virtual; + + if (mx3_fbi->blank == FB_BLANK_UNBLANK) + sdc_enable_channel(mx3_fbi); + + mutex_unlock(&mx3_fbi->mutex); + + return 0; +} + +/** + * mx3fb_check_var() - check and adjust framebuffer variable parameters. + * @var: framebuffer variable parameters + * @fbi: framebuffer information pointer + */ +static int mx3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) +{ + struct mx3fb_info *mx3_fbi = fbi->par; + u32 vtotal; + u32 htotal; + + dev_dbg(fbi->device, "%s\n", __func__); + + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + + if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && + (var->bits_per_pixel != 16)) + var->bits_per_pixel = default_bpp; + + switch (var->bits_per_pixel) { + case 16: + var->red.length = 5; + var->red.offset = 11; + var->red.msb_right = 0; + + var->green.length = 6; + var->green.offset = 5; + var->green.msb_right = 0; + + var->blue.length = 5; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 24: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 0; + var->transp.offset = 0; + var->transp.msb_right = 0; + break; + case 32: + var->red.length = 8; + var->red.offset = 16; + var->red.msb_right = 0; + + var->green.length = 8; + var->green.offset = 8; + var->green.msb_right = 0; + + var->blue.length = 8; + var->blue.offset = 0; + var->blue.msb_right = 0; + + var->transp.length = 8; + var->transp.offset = 24; + var->transp.msb_right = 0; + break; + } + + if (var->pixclock < 1000) { + htotal = var->xres + var->right_margin + var->hsync_len + + var->left_margin; + vtotal = var->yres + var->lower_margin + var->vsync_len + + var->upper_margin; + var->pixclock = (vtotal * htotal * 6UL) / 100UL; + var->pixclock = KHZ2PICOS(var->pixclock); + dev_dbg(fbi->device, "pixclock set for 60Hz refresh = %u ps\n", + var->pixclock); + } + + var->height = -1; + var->width = -1; + var->grayscale = 0; + + /* Preserve sync flags */ + var->sync |= mx3_fbi->sync; + mx3_fbi->sync |= var->sync; + + return 0; +} + +static u32 chan_to_field(unsigned int chan, struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +static int mx3fb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int trans, struct fb_info *fbi) +{ + struct mx3fb_info *mx3_fbi = fbi->par; + u32 val; + int ret = 1; + + dev_dbg(fbi->device, "%s\n", __func__); + + mutex_lock(&mx3_fbi->mutex); + /* + * If greyscale is true, then we convert the RGB value + * to greyscale no matter what visual we are using. + */ + if (fbi->var.grayscale) + red = green = blue = (19595 * red + 38470 * green + + 7471 * blue) >> 16; + switch (fbi->fix.visual) { + case FB_VISUAL_TRUECOLOR: + /* + * 16-bit True Colour. We encode the RGB value + * according to the RGB bitfield information. + */ + if (regno < 16) { + u32 *pal = fbi->pseudo_palette; + + val = chan_to_field(red, &fbi->var.red); + val |= chan_to_field(green, &fbi->var.green); + val |= chan_to_field(blue, &fbi->var.blue); + + pal[regno] = val; + + ret = 0; + } + break; + + case FB_VISUAL_STATIC_PSEUDOCOLOR: + case FB_VISUAL_PSEUDOCOLOR: + break; + } + mutex_unlock(&mx3_fbi->mutex); + + return ret; +} + +/** + * mx3fb_blank() - blank the display. + */ +static int mx3fb_blank(int blank, struct fb_info *fbi) +{ + struct mx3fb_info *mx3_fbi = fbi->par; + struct mx3fb_data *mx3fb = mx3_fbi->mx3fb; + + dev_dbg(fbi->device, "%s\n", __func__); + + dev_dbg(fbi->device, "blank = %d\n", blank); + + if (mx3_fbi->blank == blank) + return 0; + + mutex_lock(&mx3_fbi->mutex); + mx3_fbi->blank = blank; + + switch (blank) { + case FB_BLANK_POWERDOWN: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + sdc_disable_channel(mx3_fbi); + sdc_set_brightness(mx3fb, 0); + break; + case FB_BLANK_UNBLANK: + sdc_enable_channel(mx3_fbi); + sdc_set_brightness(mx3fb, mx3fb->backlight_level); + break; + } + mutex_unlock(&mx3_fbi->mutex); + + return 0; +} + +/** + * mx3fb_pan_display() - pan or wrap the display + * @var: variable screen buffer information. + * @info: framebuffer information pointer. + * + * We look only at xoffset, yoffset and the FB_VMODE_YWRAP flag + */ +static int mx3fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct mx3fb_info *mx3_fbi = fbi->par; + u32 y_bottom; + unsigned long base; + off_t offset; + dma_cookie_t cookie; + struct scatterlist *sg = mx3_fbi->sg; + struct dma_chan *dma_chan = &mx3_fbi->idmac_channel->dma_chan; + struct dma_async_tx_descriptor *txd; + int ret; + + dev_dbg(fbi->device, "%s [%c]\n", __func__, + list_empty(&mx3_fbi->idmac_channel->queue) ? '-' : '+'); + + if (var->xoffset > 0) { + dev_dbg(fbi->device, "x panning not supported\n"); + return -EINVAL; + } + + if (fbi->var.xoffset == var->xoffset && + fbi->var.yoffset == var->yoffset) + return 0; /* No change, do nothing */ + + y_bottom = var->yoffset; + + if (!(var->vmode & FB_VMODE_YWRAP)) + y_bottom += var->yres; + + if (y_bottom > fbi->var.yres_virtual) + return -EINVAL; + + mutex_lock(&mx3_fbi->mutex); + + offset = (var->yoffset * var->xres_virtual + var->xoffset) * + (var->bits_per_pixel / 8); + base = fbi->fix.smem_start + offset; + + dev_dbg(fbi->device, "Updating SDC BG buf %d address=0x%08lX\n", + mx3_fbi->cur_ipu_buf, base); + + /* + * We enable the End of Frame interrupt, which will free a tx-descriptor, + * which we will need for the next device_prep_slave_sg(). The + * IRQ-handler will disable the IRQ again. + */ + init_completion(&mx3_fbi->flip_cmpl); + enable_irq(mx3_fbi->idmac_channel->eof_irq); + + ret = wait_for_completion_timeout(&mx3_fbi->flip_cmpl, HZ / 10); + if (ret <= 0) { + mutex_unlock(&mx3_fbi->mutex); + dev_info(fbi->device, "Panning failed due to %s\n", ret < 0 ? + "user interrupt" : "timeout"); + return ret ? : -ETIMEDOUT; + } + + mx3_fbi->cur_ipu_buf = !mx3_fbi->cur_ipu_buf; + + sg_dma_address(&sg[mx3_fbi->cur_ipu_buf]) = base; + sg_set_page(&sg[mx3_fbi->cur_ipu_buf], + virt_to_page(fbi->screen_base + offset), fbi->fix.smem_len, + offset_in_page(fbi->screen_base + offset)); + + txd = dma_chan->device->device_prep_slave_sg(dma_chan, sg + + mx3_fbi->cur_ipu_buf, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT); + if (!txd) { + dev_err(fbi->device, + "Error preparing a DMA transaction descriptor.\n"); + mutex_unlock(&mx3_fbi->mutex); + return -EIO; + } + + txd->callback_param = txd; + txd->callback = mx3fb_dma_done; + + /* + * Emulate original mx3fb behaviour: each new call to idmac_tx_submit() + * should switch to another buffer + */ + cookie = txd->tx_submit(txd); + dev_dbg(fbi->device, "%d: Submit %p #%d\n", __LINE__, txd, cookie); + if (cookie < 0) { + dev_err(fbi->device, + "Error updating SDC buf %d to address=0x%08lX\n", + mx3_fbi->cur_ipu_buf, base); + mutex_unlock(&mx3_fbi->mutex); + return -EIO; + } + + if (mx3_fbi->txd) + async_tx_ack(mx3_fbi->txd); + mx3_fbi->txd = txd; + + fbi->var.xoffset = var->xoffset; + fbi->var.yoffset = var->yoffset; + + if (var->vmode & FB_VMODE_YWRAP) + fbi->var.vmode |= FB_VMODE_YWRAP; + else + fbi->var.vmode &= ~FB_VMODE_YWRAP; + + mutex_unlock(&mx3_fbi->mutex); + + dev_dbg(fbi->device, "Update complete\n"); + + return 0; +} + +/* + * This structure contains the pointers to the control functions that are + * invoked by the core framebuffer driver to perform operations like + * blitting, rectangle filling, copy regions and cursor definition. + */ +static struct fb_ops mx3fb_ops = { + .owner = THIS_MODULE, + .fb_set_par = mx3fb_set_par, + .fb_check_var = mx3fb_check_var, + .fb_setcolreg = mx3fb_setcolreg, + .fb_pan_display = mx3fb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = mx3fb_blank, +}; + +#ifdef CONFIG_PM +/* + * Power management hooks. Note that we won't be called from IRQ context, + * unlike the blank functions above, so we may sleep. + */ + +/* + * Suspends the framebuffer and blanks the screen. Power management support + */ +static int mx3fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mx3fb_data *drv_data = platform_get_drvdata(pdev); + struct mx3fb_info *mx3_fbi = drv_data->fbi->par; + + acquire_console_sem(); + fb_set_suspend(drv_data->fbi, 1); + release_console_sem(); + + if (mx3_fbi->blank == FB_BLANK_UNBLANK) { + sdc_disable_channel(mx3_fbi); + sdc_set_brightness(mx3fb, 0); + + } + return 0; +} + +/* + * Resumes the framebuffer and unblanks the screen. Power management support + */ +static int mx3fb_resume(struct platform_device *pdev) +{ + struct mx3fb_data *drv_data = platform_get_drvdata(pdev); + struct mx3fb_info *mx3_fbi = drv_data->fbi->par; + + if (mx3_fbi->blank == FB_BLANK_UNBLANK) { + sdc_enable_channel(mx3_fbi); + sdc_set_brightness(mx3fb, drv_data->backlight_level); + } + + acquire_console_sem(); + fb_set_suspend(drv_data->fbi, 0); + release_console_sem(); + + return 0; +} +#else +#define mx3fb_suspend NULL +#define mx3fb_resume NULL +#endif + +/* + * Main framebuffer functions + */ + +/** + * mx3fb_map_video_memory() - allocates the DRAM memory for the frame buffer. + * @fbi: framebuffer information pointer + * @return: Error code indicating success or failure + * + * This buffer is remapped into a non-cached, non-buffered, memory region to + * allow palette and pixel writes to occur without flushing the cache. Once this + * area is remapped, all virtual memory access to the video memory should occur + * at the new region. + */ +static int mx3fb_map_video_memory(struct fb_info *fbi) +{ + int retval = 0; + dma_addr_t addr; + + fbi->screen_base = dma_alloc_writecombine(fbi->device, + fbi->fix.smem_len, + &addr, GFP_DMA); + + if (!fbi->screen_base) { + dev_err(fbi->device, "Cannot allocate %u bytes framebuffer memory\n", + fbi->fix.smem_len); + retval = -EBUSY; + goto err0; + } + + fbi->fix.smem_start = addr; + + dev_dbg(fbi->device, "allocated fb @ p=0x%08x, v=0x%p, size=%d.\n", + (uint32_t) fbi->fix.smem_start, fbi->screen_base, fbi->fix.smem_len); + + fbi->screen_size = fbi->fix.smem_len; + + /* Clear the screen */ + memset((char *)fbi->screen_base, 0, fbi->fix.smem_len); + + return 0; + +err0: + fbi->fix.smem_len = 0; + fbi->fix.smem_start = 0; + fbi->screen_base = NULL; + return retval; +} + +/** + * mx3fb_unmap_video_memory() - de-allocate frame buffer memory. + * @fbi: framebuffer information pointer + * @return: error code indicating success or failure + */ +static int mx3fb_unmap_video_memory(struct fb_info *fbi) +{ + dma_free_writecombine(fbi->device, fbi->fix.smem_len, + fbi->screen_base, fbi->fix.smem_start); + + fbi->screen_base = 0; + fbi->fix.smem_start = 0; + fbi->fix.smem_len = 0; + return 0; +} + +/** + * mx3fb_init_fbinfo() - initialize framebuffer information object. + * @return: initialized framebuffer structure. + */ +static struct fb_info *mx3fb_init_fbinfo(struct device *dev, struct fb_ops *ops) +{ + struct fb_info *fbi; + struct mx3fb_info *mx3fbi; + int ret; + + /* Allocate sufficient memory for the fb structure */ + fbi = framebuffer_alloc(sizeof(struct mx3fb_info), dev); + if (!fbi) + return NULL; + + mx3fbi = fbi->par; + mx3fbi->cookie = -EINVAL; + mx3fbi->cur_ipu_buf = 0; + + fbi->var.activate = FB_ACTIVATE_NOW; + + fbi->fbops = ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = mx3fbi->pseudo_palette; + + mutex_init(&mx3fbi->mutex); + + /* Allocate colormap */ + ret = fb_alloc_cmap(&fbi->cmap, 16, 0); + if (ret < 0) { + framebuffer_release(fbi); + return NULL; + } + + return fbi; +} + +static int init_fb_chan(struct mx3fb_data *mx3fb, struct idmac_channel *ichan) +{ + struct device *dev = mx3fb->dev; + struct mx3fb_platform_data *mx3fb_pdata = dev->platform_data; + const char *name = mx3fb_pdata->name; + unsigned int irq; + struct fb_info *fbi; + struct mx3fb_info *mx3fbi; + const struct fb_videomode *mode; + int ret, num_modes; + + ichan->client = mx3fb; + irq = ichan->eof_irq; + + if (ichan->dma_chan.chan_id != IDMAC_SDC_0) + return -EINVAL; + + fbi = mx3fb_init_fbinfo(dev, &mx3fb_ops); + if (!fbi) + return -ENOMEM; + + if (!fb_mode) + fb_mode = name; + + if (!fb_mode) { + ret = -EINVAL; + goto emode; + } + + if (mx3fb_pdata->mode && mx3fb_pdata->num_modes) { + mode = mx3fb_pdata->mode; + num_modes = mx3fb_pdata->num_modes; + } else { + mode = mx3fb_modedb; + num_modes = ARRAY_SIZE(mx3fb_modedb); + } + + if (!fb_find_mode(&fbi->var, fbi, fb_mode, mode, + num_modes, NULL, default_bpp)) { + ret = -EBUSY; + goto emode; + } + + fb_videomode_to_modelist(mode, num_modes, &fbi->modelist); + + /* Default Y virtual size is 2x panel size */ + fbi->var.yres_virtual = fbi->var.yres * 2; + + mx3fb->fbi = fbi; + + /* set Display Interface clock period */ + mx3fb_write_reg(mx3fb, 0x00100010L, DI_HSP_CLK_PER); + /* Might need to trigger HSP clock change - see 44.3.3.8.5 */ + + sdc_set_brightness(mx3fb, 255); + sdc_set_global_alpha(mx3fb, true, 0xFF); + sdc_set_color_key(mx3fb, IDMAC_SDC_0, false, 0); + + mx3fbi = fbi->par; + mx3fbi->idmac_channel = ichan; + mx3fbi->ipu_ch = ichan->dma_chan.chan_id; + mx3fbi->mx3fb = mx3fb; + mx3fbi->blank = FB_BLANK_NORMAL; + + init_completion(&mx3fbi->flip_cmpl); + disable_irq(ichan->eof_irq); + dev_dbg(mx3fb->dev, "disabling irq %d\n", ichan->eof_irq); + ret = mx3fb_set_par(fbi); + if (ret < 0) + goto esetpar; + + mx3fb_blank(FB_BLANK_UNBLANK, fbi); + + dev_info(dev, "mx3fb: fb registered, using mode %s\n", fb_mode); + + ret = register_framebuffer(fbi); + if (ret < 0) + goto erfb; + + return 0; + +erfb: +esetpar: +emode: + fb_dealloc_cmap(&fbi->cmap); + framebuffer_release(fbi); + + return ret; +} + +static bool chan_filter(struct dma_chan *chan, void *arg) +{ + struct dma_chan_request *rq = arg; + struct device *dev; + struct mx3fb_platform_data *mx3fb_pdata; + + if (!rq) + return false; + + dev = rq->mx3fb->dev; + mx3fb_pdata = dev->platform_data; + + return rq->id == chan->chan_id && + mx3fb_pdata->dma_dev == chan->device->dev; +} + +static void release_fbi(struct fb_info *fbi) +{ + mx3fb_unmap_video_memory(fbi); + + fb_dealloc_cmap(&fbi->cmap); + + unregister_framebuffer(fbi); + framebuffer_release(fbi); +} + +static int mx3fb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + struct resource *sdc_reg; + struct mx3fb_data *mx3fb; + dma_cap_mask_t mask; + struct dma_chan *chan; + struct dma_chan_request rq; + + /* + * Display Interface (DI) and Synchronous Display Controller (SDC) + * registers + */ + sdc_reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!sdc_reg) + return -EINVAL; + + mx3fb = kzalloc(sizeof(*mx3fb), GFP_KERNEL); + if (!mx3fb) + return -ENOMEM; + + spin_lock_init(&mx3fb->lock); + + mx3fb->reg_base = ioremap(sdc_reg->start, resource_size(sdc_reg)); + if (!mx3fb->reg_base) { + ret = -ENOMEM; + goto eremap; + } + + pr_debug("Remapped %x to %x at %p\n", sdc_reg->start, sdc_reg->end, + mx3fb->reg_base); + + /* IDMAC interface */ + dmaengine_get(); + + mx3fb->dev = dev; + platform_set_drvdata(pdev, mx3fb); + + rq.mx3fb = mx3fb; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + rq.id = IDMAC_SDC_0; + chan = dma_request_channel(mask, chan_filter, &rq); + if (!chan) { + ret = -EBUSY; + goto ersdc0; + } + + ret = init_fb_chan(mx3fb, to_idmac_chan(chan)); + if (ret < 0) + goto eisdc0; + + mx3fb->backlight_level = 255; + + return 0; + +eisdc0: + dma_release_channel(chan); +ersdc0: + dmaengine_put(); + iounmap(mx3fb->reg_base); +eremap: + kfree(mx3fb); + dev_err(dev, "mx3fb: failed to register fb\n"); + return ret; +} + +static int mx3fb_remove(struct platform_device *dev) +{ + struct mx3fb_data *mx3fb = platform_get_drvdata(dev); + struct fb_info *fbi = mx3fb->fbi; + struct mx3fb_info *mx3_fbi = fbi->par; + struct dma_chan *chan; + + chan = &mx3_fbi->idmac_channel->dma_chan; + release_fbi(fbi); + + dma_release_channel(chan); + dmaengine_put(); + + iounmap(mx3fb->reg_base); + kfree(mx3fb); + return 0; +} + +static struct platform_driver mx3fb_driver = { + .driver = { + .name = MX3FB_NAME, + }, + .probe = mx3fb_probe, + .remove = mx3fb_remove, + .suspend = mx3fb_suspend, + .resume = mx3fb_resume, +}; + +/* + * Parse user specified options (`video=mx3fb:') + * example: + * video=mx3fb:bpp=16 + */ +static int mx3fb_setup(void) +{ +#ifndef MODULE + char *opt, *options = NULL; + + if (fb_get_options("mx3fb", &options)) + return -ENODEV; + + if (!options || !*options) + return 0; + + while ((opt = strsep(&options, ",")) != NULL) { + if (!*opt) + continue; + if (!strncmp(opt, "bpp=", 4)) + default_bpp = simple_strtoul(opt + 4, NULL, 0); + else + fb_mode = opt; + } +#endif + + return 0; +} + +static int __init mx3fb_init(void) +{ + int ret = mx3fb_setup(); + + if (ret < 0) + return ret; + + ret = platform_driver_register(&mx3fb_driver); + return ret; +} + +static void __exit mx3fb_exit(void) +{ + platform_driver_unregister(&mx3fb_driver); +} + +module_init(mx3fb_init); +module_exit(mx3fb_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MX3 framebuffer driver"); +MODULE_ALIAS("platform:" MX3FB_NAME); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 0b491eee46012772cbf029450d123e933c2e7940 Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Wed, 21 Jan 2009 12:35:43 -0800 Subject: usbnet: allow type check of devdbg arguments in non-debug build Improve usbnet's devdbg to always type-check diagnostic arguments, like dev_dbg (device.h). This makes no change to the resulting size of usbnet modules. This patch also removes an #ifdef DEBUG directive from rndis_wlan so it's devdbg statements are always type-checked at compile time. Signed-off-by: Steve Glendinning Signed-off-by: David Brownell Signed-off-by: David S. Miller --- drivers/net/wireless/rndis_wlan.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 607ce9f61b5..ed93ac41297 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -1649,9 +1649,7 @@ static char *rndis_translate_scan(struct net_device *dev, char *end_buf, struct ndis_80211_bssid_ex *bssid) { -#ifdef DEBUG struct usbnet *usbdev = netdev_priv(dev); -#endif u8 *ie; char *current_val; int bssid_len, ie_len, i; -- cgit v1.2.3 From 6d317482944250228255bcbe97a95b7e7ad9a538 Mon Sep 17 00:00:00 2001 From: Christian Eggers Date: Wed, 21 Jan 2009 12:56:24 -0800 Subject: usb/mcs7830: Don't use buffers from stack for USB transfers mcs7830_set_reg() and mcs7830_get_reg() are called with buffers from stack which must not be used directly for USB transfers. This causes corruption of the stack particulary on non x86 architectures because DMA may be used for these transfers. Signed-off-by: Christian Eggers Acked-by: Arnd Bergmann Signed-off-by: David S. Miller --- drivers/net/usb/mcs7830.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 5385d66b306..ced8f36ebd0 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -94,10 +94,18 @@ static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data) { struct usb_device *xdev = dev->udev; int ret; + void *buffer; + + buffer = kmalloc(size, GFP_NOIO); + if (buffer == NULL) + return -ENOMEM; ret = usb_control_msg(xdev, usb_rcvctrlpipe(xdev, 0), MCS7830_RD_BREQ, - MCS7830_RD_BMREQ, 0x0000, index, data, + MCS7830_RD_BMREQ, 0x0000, index, buffer, size, MCS7830_CTRL_TIMEOUT); + memcpy(data, buffer, size); + kfree(buffer); + return ret; } @@ -105,10 +113,18 @@ static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, void *data) { struct usb_device *xdev = dev->udev; int ret; + void *buffer; + + buffer = kmalloc(size, GFP_NOIO); + if (buffer == NULL) + return -ENOMEM; + + memcpy(buffer, data, size); ret = usb_control_msg(xdev, usb_sndctrlpipe(xdev, 0), MCS7830_WR_BREQ, - MCS7830_WR_BMREQ, 0x0000, index, data, + MCS7830_WR_BMREQ, 0x0000, index, buffer, size, MCS7830_CTRL_TIMEOUT); + kfree(buffer); return ret; } -- cgit v1.2.3 From ad2563c2e42fc67b0976aeb70e9f3faf1c1196e8 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 19 Jan 2009 17:21:45 +1000 Subject: drm: create mode_config idr lock Create a separate mode_config IDR lock for simplicity. The core DRM config structures (connector, mode, etc. lists) are still protected by the mode_config mutex, but the CRTC IDR (used for the various identifier IDs) is now protected by the mode_config idr_mutex. Simplifies the locking a bit and removes a warning. All objects are protected by the config mutex, we may in the future, split the object further to have reference counts. Signed-off-by: Jesse Barnes Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 5b2cbb77816..bfce0992fef 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -194,7 +194,6 @@ char *drm_get_connector_status_name(enum drm_connector_status status) * @type: object type * * LOCKING: - * Caller must hold DRM mode_config lock. * * Create a unique identifier based on @ptr in @dev's identifier space. Used * for tracking modes, CRTCs and connectors. @@ -209,15 +208,15 @@ static int drm_mode_object_get(struct drm_device *dev, int new_id = 0; int ret; - WARN(!mutex_is_locked(&dev->mode_config.mutex), - "%s called w/o mode_config lock\n", __func__); again: if (idr_pre_get(&dev->mode_config.crtc_idr, GFP_KERNEL) == 0) { DRM_ERROR("Ran out memory getting a mode number\n"); return -EINVAL; } + mutex_lock(&dev->mode_config.idr_mutex); ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); + mutex_unlock(&dev->mode_config.idr_mutex); if (ret == -EAGAIN) goto again; @@ -239,16 +238,20 @@ again: static void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *object) { + mutex_lock(&dev->mode_config.idr_mutex); idr_remove(&dev->mode_config.crtc_idr, object->id); + mutex_unlock(&dev->mode_config.idr_mutex); } void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) { - struct drm_mode_object *obj; + struct drm_mode_object *obj = NULL; + mutex_lock(&dev->mode_config.idr_mutex); obj = idr_find(&dev->mode_config.crtc_idr, id); if (!obj || (obj->type != type) || (obj->id != id)) - return NULL; + obj = NULL; + mutex_unlock(&dev->mode_config.idr_mutex); return obj; } @@ -786,6 +789,7 @@ EXPORT_SYMBOL(drm_mode_create_dithering_property); void drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); + mutex_init(&dev->mode_config.idr_mutex); INIT_LIST_HEAD(&dev->mode_config.fb_list); INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list); INIT_LIST_HEAD(&dev->mode_config.crtc_list); -- cgit v1.2.3 From 260883c85611d3a7e27130af9aef15252856e14f Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 22 Jan 2009 17:58:49 +1000 Subject: i915: fix freeing path for gem phys objects. This off-by-one was pointed out by Jesse Barnes. Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 96316fd4723..b15ee1f7c01 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3364,7 +3364,7 @@ void i915_gem_free_all_phys_object(struct drm_device *dev) { int i; - for (i = 0; i < I915_MAX_PHYS_OBJECT; i++) + for (i = I915_GEM_PHYS_CURSOR_0; i <= I915_MAX_PHYS_OBJECT; i++) i915_gem_free_phys_object(dev, i); } -- cgit v1.2.3 From ed2dd4b0cc1494c27478f4ea8452f68d2037a60c Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 22 Jan 2009 22:21:16 +1000 Subject: drm/i915: remove unnecessary debug output in KMS init We don't really need to print out the FB BAR... Signed-off-by: Jesse Barnes Signed-off-by: Dave Airlie --- drivers/gpu/drm/i915/i915_dma.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index bbadf1c0414..57e8eb636a7 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -944,8 +944,6 @@ static int i915_load_modeset_init(struct drm_device *dev) dev->mode_config.fb_base = drm_get_resource_start(dev, fb_bar) & 0xff000000; - DRM_DEBUG("*** fb base 0x%08lx\n", dev->mode_config.fb_base); - if (IS_MOBILE(dev) || (IS_I9XX(dev) && !IS_I965G(dev) && !IS_G33(dev))) dev_priv->cursor_needs_physical = true; else -- cgit v1.2.3 From 335041ed31d774391d9add49824d05e7d19d93e9 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 22 Jan 2009 22:22:06 +1000 Subject: drm/i915: hook up LVDS DPMS property The LVDS output supports DPMS calls, but we never hooked up the property code, so set property calls didn't actually do anything. Implement a set_property callback for the LVDS output so that the right thing happens. Signed-off-by: Jesse Barnes --- drivers/gpu/drm/i915/intel_lvds.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 2fafdcc108f..6b1148fc2cb 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -340,6 +340,18 @@ static void intel_lvds_destroy(struct drm_connector *connector) kfree(connector); } +static int intel_lvds_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_device *dev = connector->dev; + + if (property == dev->mode_config.dpms_property && connector->encoder) + intel_lvds_dpms(connector->encoder, (uint32_t)(value & 0xf)); + + return 0; +} + static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { .dpms = intel_lvds_dpms, .mode_fixup = intel_lvds_mode_fixup, @@ -359,6 +371,7 @@ static const struct drm_connector_funcs intel_lvds_connector_funcs = { .restore = intel_lvds_restore, .detect = intel_lvds_detect, .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = intel_lvds_set_property, .destroy = intel_lvds_destroy, }; -- cgit v1.2.3 From 4942f8b23b56a3f9a713d4436338710579329ffc Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 22 Jan 2009 22:23:53 +1000 Subject: drm: don't whine about not reading EDID data Make this message a little quieter, since it's common and not necessarily indicative of a problem. Signed-off-by: Jesse Barnes Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_edid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 0fbb0da342c..5a4d3244758 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -660,7 +660,7 @@ struct edid *drm_get_edid(struct drm_connector *connector, edid = (struct edid *)drm_ddc_read(adapter); if (!edid) { - dev_warn(&connector->dev->pdev->dev, "%s: no EDID data\n", + dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n", drm_get_connector_name(connector)); return NULL; } -- cgit v1.2.3 From 1bb88edb7a3769992026f34fd648bb459b0469aa Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 15 Jan 2009 01:16:25 -0800 Subject: drm: stash AGP include under the do-we-have-AGP ifdef This fixes the MIPS with DRM build. Signed-off-by: Eric Anholt Tested-by: Martin Michlmayr Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_agpsupport.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_agpsupport.c b/drivers/gpu/drm/drm_agpsupport.c index 3d33b8252b5..14796594e5d 100644 --- a/drivers/gpu/drm/drm_agpsupport.c +++ b/drivers/gpu/drm/drm_agpsupport.c @@ -33,10 +33,11 @@ #include "drmP.h" #include -#include #if __OS_HAS_AGP +#include + /** * Get AGP information. * -- cgit v1.2.3 From 2906f0258770d3a9c4e65364df8acc904e148bbe Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 20 Jan 2009 19:10:54 -0800 Subject: drm/i915: Fix cursor physical address choice to match the 2D driver. Signed-off-by: Jesse Barnes Signed-off-by: Eric Anholt --- drivers/gpu/drm/i915/i915_dma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 57e8eb636a7..ee64b7301f6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -944,11 +944,14 @@ static int i915_load_modeset_init(struct drm_device *dev) dev->mode_config.fb_base = drm_get_resource_start(dev, fb_bar) & 0xff000000; - if (IS_MOBILE(dev) || (IS_I9XX(dev) && !IS_I965G(dev) && !IS_G33(dev))) + if (IS_MOBILE(dev) || IS_I9XX(dev)) dev_priv->cursor_needs_physical = true; else dev_priv->cursor_needs_physical = false; + if (IS_I965G(dev) || IS_G33(dev)) + dev_priv->cursor_needs_physical = false; + ret = i915_probe_agp(dev, &agp_size, &prealloc_size); if (ret) goto kfree_devname; -- cgit v1.2.3 From 7fe99c4e28ab54eada8aa456b417114e6ef21587 Mon Sep 17 00:00:00 2001 From: Andrey Borzenkov Date: Tue, 20 Jan 2009 20:26:46 +0300 Subject: orinoco: move kmalloc(..., GFP_KERNEL) outside spinlock in orinoco_ioctl_set_genie [ 56.923623] BUG: sleeping function called from invalid context at /home/bor/src/linux-git/mm/slub.c:1599 [ 56.923644] in_atomic(): 0, irqs_disabled(): 1, pid: 3031, name: wpa_supplicant [ 56.923656] 2 locks held by wpa_supplicant/3031: [ 56.923662] #0: (rtnl_mutex){--..}, at: [] rtnl_lock+0xf/0x20 [ 56.923703] #1: (&priv->lock){++..}, at: [] orinoco_ioctl_set_genie+0x52/0x130 [orinoco] [ 56.923782] irq event stamp: 910 [ 56.923788] hardirqs last enabled at (909): [] __kmalloc+0x7b/0x140 [ 56.923820] hardirqs last disabled at (910): [] _spin_lock_irqsave+0x19/0x80 [ 56.923847] softirqs last enabled at (880): [] __do_softirq+0xc4/0x110 [ 56.923865] softirqs last disabled at (871): [] do_softirq+0x8e/0xe0 [ 56.923895] Pid: 3031, comm: wpa_supplicant Not tainted 2.6.29-rc2-1avb #1 [ 56.923905] Call Trace: [ 56.923919] [] ? do_softirq+0x8e/0xe0 [ 56.923941] [] __might_sleep+0xd2/0x100 [ 56.923952] [] __kmalloc+0xd7/0x140 [ 56.923963] [] ? _spin_lock_irqsave+0x6a/0x80 [ 56.923981] [] ? orinoco_ioctl_set_genie+0x79/0x130 [orinoco] [ 56.923999] [] ? orinoco_ioctl_set_genie+0x52/0x130 [orinoco] [ 56.924017] [] orinoco_ioctl_set_genie+0x79/0x130 [orinoco] [ 56.924036] [] ? copy_from_user+0x35/0x130 [ 56.924061] [] ioctl_standard_call+0x196/0x380 [ 56.924085] [] ? __dev_get_by_name+0x85/0xb0 [ 56.924096] [] wext_handle_ioctl+0x14f/0x230 [ 56.924113] [] ? orinoco_ioctl_set_genie+0x0/0x130 [orinoco] [ 56.924132] [] dev_ioctl+0x495/0x570 [ 56.924155] [] ? sys_sendto+0xa5/0xd0 [ 56.924171] [] ? mark_held_locks+0x48/0x90 [ 56.924183] [] ? sock_ioctl+0x0/0x280 [ 56.924193] [] sock_ioctl+0xfd/0x280 [ 56.924203] [] ? sock_ioctl+0x0/0x280 [ 56.924235] [] vfs_ioctl+0x20/0x80 [ 56.924246] [] do_vfs_ioctl+0x72/0x570 [ 56.924257] [] ? sys_send+0x32/0x40 [ 56.924268] [] ? sys_socketcall+0x1d0/0x2a0 [ 56.924280] [] ? sysenter_exit+0xf/0x16 [ 56.924292] [] sys_ioctl+0x39/0x70 [ 56.924302] [] sysenter_do_call+0x12/0x31 Signed-off-by: Andrey Borzenkov Cc: stable@kernel.org Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/orinoco.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco/orinoco.c b/drivers/net/wireless/orinoco/orinoco.c index c3bb85e0251..39eab39b065 100644 --- a/drivers/net/wireless/orinoco/orinoco.c +++ b/drivers/net/wireless/orinoco/orinoco.c @@ -5068,33 +5068,30 @@ static int orinoco_ioctl_set_genie(struct net_device *dev, struct orinoco_private *priv = netdev_priv(dev); u8 *buf; unsigned long flags; - int err = 0; /* cut off at IEEE80211_MAX_DATA_LEN */ if ((wrqu->data.length > IEEE80211_MAX_DATA_LEN) || (wrqu->data.length && (extra == NULL))) return -EINVAL; - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - if (wrqu->data.length) { buf = kmalloc(wrqu->data.length, GFP_KERNEL); - if (buf == NULL) { - err = -ENOMEM; - goto out; - } + if (buf == NULL) + return -ENOMEM; memcpy(buf, extra, wrqu->data.length); - kfree(priv->wpa_ie); - priv->wpa_ie = buf; - priv->wpa_ie_len = wrqu->data.length; - } else { - kfree(priv->wpa_ie); - priv->wpa_ie = NULL; - priv->wpa_ie_len = 0; + } else + buf = NULL; + + if (orinoco_lock(priv, &flags) != 0) { + kfree(buf); + return -EBUSY; } + kfree(priv->wpa_ie); + priv->wpa_ie = buf; + priv->wpa_ie_len = wrqu->data.length; + if (priv->wpa_ie) { /* Looks like wl_lkm wants to check the auth alg, and * somehow pass it to the firmware. @@ -5103,9 +5100,8 @@ static int orinoco_ioctl_set_genie(struct net_device *dev, */ } -out: orinoco_unlock(priv, &flags); - return err; + return 0; } static int orinoco_ioctl_get_genie(struct net_device *dev, -- cgit v1.2.3 From 7490889c105764d80af58dee5983d91a84e4aec8 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sun, 18 Jan 2009 20:15:24 +0100 Subject: rt2x00: Fix TX rate short preamble detection Mac80211 provides 2 structures to handle bitrates, namely ieee80211_rate and ieee80211_tx_rate. To determine the short preamble mode for an outgoing frame, the flag IEEE80211_TX_RC_USE_SHORT_PREAMBLE must be checked on ieee80211_tx_rate and not ieee80211_rate (which rt2x00 did). This fixes a regression which was triggered in 2.6.29-rcX as reported by Chris Clayton. Signed-off-by: Ivo van Doorn Tested-By: Chris Clayton Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 746a8f36b93..0709decec9c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -154,6 +154,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data; + struct ieee80211_tx_rate *txrate = &tx_info->control.rates[0]; struct ieee80211_rate *rate = ieee80211_get_tx_rate(rt2x00dev->hw, tx_info); const struct rt2x00_rate *hwrate; @@ -313,7 +314,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, * When preamble is enabled we should set the * preamble bit for the signal. */ - if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) txdesc->signal |= 0x08; } } -- cgit v1.2.3 From 11eaea416716deebcb18383b201ba8033cbf33dc Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Sun, 18 Jan 2009 23:20:58 -0500 Subject: orinoco: use KERN_DEBUG for link status messages KERN_INFO is too "loud" for messages that are generated by the ordinary events, such as accociation. Use of KERN_DEBUG is consistent with mac80211. Suggested by Michael Gilbert Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco/orinoco.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/orinoco/orinoco.c b/drivers/net/wireless/orinoco/orinoco.c index 39eab39b065..45a04faa781 100644 --- a/drivers/net/wireless/orinoco/orinoco.c +++ b/drivers/net/wireless/orinoco/orinoco.c @@ -1673,7 +1673,7 @@ static void print_linkstatus(struct net_device *dev, u16 status) s = "UNKNOWN"; } - printk(KERN_INFO "%s: New link status: %s (%04x)\n", + printk(KERN_DEBUG "%s: New link status: %s (%04x)\n", dev->name, s, status); } -- cgit v1.2.3 From 40ab73cc6c38ce93253fe8c2d7e502c948adfd13 Mon Sep 17 00:00:00 2001 From: Chr Date: Mon, 19 Jan 2009 14:30:26 +0100 Subject: p54: add missing break in eeprom parser This patch fixes a obvious memory leak in the eeprom parser. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 12d0717c399..61b01930d9a 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -451,8 +451,8 @@ static int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) } if (err) goto err; - - } + } + break; case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: priv->iq_autocal = kmalloc(data_len, GFP_KERNEL); if (!priv->iq_autocal) { -- cgit v1.2.3 From 12da401e0d616f738c8b8a368d1f63f365cc78e4 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Mon, 19 Jan 2009 16:08:48 +0100 Subject: p54: more cryptographic accelerator fixes If we let the firmware do the data encryption, we have to remove the ICV and (M)MIC at the end of the frame before we can give it back to mac80211. Or, these data frames have a few trailing bytes on cooked monitor interfaces. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54common.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 61b01930d9a..34561e6e816 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -745,7 +745,7 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(entry); struct p54_hdr *entry_hdr; struct p54_tx_data *entry_data; - int pad = 0; + unsigned int pad = 0, frame_len; range = (void *)info->rate_driver_data; if (range->start_addr != addr) { @@ -768,6 +768,7 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) __skb_unlink(entry, &priv->tx_queue); spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + frame_len = entry->len; entry_hdr = (struct p54_hdr *) entry->data; entry_data = (struct p54_tx_data *) entry_hdr->data; priv->tx_stats[entry_data->hw_queue].len--; @@ -814,15 +815,28 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) info->status.ack_signal = p54_rssi_to_dbm(dev, (int)payload->ack_rssi); - if (entry_data->key_type == P54_CRYPTO_TKIPMICHAEL) { + /* Undo all changes to the frame. */ + switch (entry_data->key_type) { + case P54_CRYPTO_TKIPMICHAEL: { u8 *iv = (u8 *)(entry_data->align + pad + - entry_data->crypt_offset); + entry_data->crypt_offset); /* Restore the original TKIP IV. */ iv[2] = iv[0]; iv[0] = iv[1]; iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */ + + frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */ + break; + } + case P54_CRYPTO_AESCCMP: + frame_len -= 8; /* remove CCMP_MIC */ + break; + case P54_CRYPTO_WEP: + frame_len -= 4; /* remove WEP_ICV */ + break; } + skb_trim(entry, frame_len); skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data)); ieee80211_tx_status_irqsafe(dev, entry); goto out; -- cgit v1.2.3 From e2fe154e918276e900067a9d1d3a6a963faee041 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 20 Jan 2009 00:27:57 +0100 Subject: p54usb: fix nasty use after free In theory, the firmware acks the received a data frame, before signaling the driver to free it again. However Artur Skawina has shown that it can happen in reverse order as well. This is very bad and could lead to memory corruptions, oopses and panics. Thanks to Artur Skawina for reporting and debugging this issue. Signed-off-by: Christian Lamparter Tested-by: Artur Skawina Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54usb.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 6a6a72f6f82..4487cc5c928 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -144,11 +144,8 @@ static void p54u_tx_cb(struct urb *urb) struct sk_buff *skb = urb->context; struct ieee80211_hw *dev = (struct ieee80211_hw *) usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); - struct p54u_priv *priv = dev->priv; - skb_pull(skb, priv->common.tx_hdr_len); - if (FREE_AFTER_TX(skb)) - p54_free_skb(dev, skb); + p54_free_skb(dev, skb); } static void p54u_tx_dummy_cb(struct urb *urb) { } @@ -230,7 +227,8 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb) p54u_tx_dummy_cb, dev); usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), - skb->data, skb->len, p54u_tx_cb, skb); + skb->data, skb->len, FREE_AFTER_TX(skb) ? + p54u_tx_cb : p54u_tx_dummy_cb, skb); usb_anchor_urb(addr_urb, &priv->submitted); err = usb_submit_urb(addr_urb, GFP_ATOMIC); @@ -269,28 +267,24 @@ static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54u_priv *priv = dev->priv; struct urb *data_urb; - struct lm87_tx_hdr *hdr; - __le32 checksum; - __le32 addr = ((struct p54_hdr *)skb->data)->req_id; + struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); data_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!data_urb) return; - checksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); - hdr = (struct lm87_tx_hdr *)skb_push(skb, sizeof(*hdr)); - hdr->chksum = checksum; - hdr->device_addr = addr; + hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); + hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), - skb->data, skb->len, p54u_tx_cb, skb); + hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? + p54u_tx_cb : p54u_tx_dummy_cb, skb); data_urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(data_urb, &priv->submitted); if (usb_submit_urb(data_urb, GFP_ATOMIC)) { usb_unanchor_urb(data_urb); - skb_pull(skb, sizeof(*hdr)); p54_free_skb(dev, skb); } usb_free_urb(data_urb); @@ -300,11 +294,9 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54u_priv *priv = dev->priv; struct urb *int_urb, *data_urb; - struct net2280_tx_hdr *hdr; + struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); struct net2280_reg_write *reg; int err = 0; - __le32 addr = ((struct p54_hdr *) skb->data)->req_id; - __le16 len = cpu_to_le16(skb->len); reg = kmalloc(sizeof(*reg), GFP_ATOMIC); if (!reg) @@ -327,10 +319,9 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) reg->addr = cpu_to_le32(P54U_DEV_BASE); reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); - hdr = (void *)skb_push(skb, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr)); - hdr->len = len; - hdr->device_addr = addr; + hdr->len = cpu_to_le16(skb->len); + hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id; usb_fill_bulk_urb(int_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), @@ -345,7 +336,8 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), - skb->data, skb->len, p54u_tx_cb, skb); + hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? + p54u_tx_cb : p54u_tx_dummy_cb, skb); usb_anchor_urb(int_urb, &priv->submitted); err = usb_submit_urb(int_urb, GFP_ATOMIC); -- cgit v1.2.3 From de2624966f9bc6ffafc4fd6555336fabc2854420 Mon Sep 17 00:00:00 2001 From: Hin-Tak Leung Date: Mon, 19 Jan 2009 23:39:09 +0000 Subject: zd1211rw: adding Sitecom WL-603 (0df6:0036) to the USB id list Giuseppe Cala (The second "a" in "Cala" should be a grave, U+00E0) reported success on zd1211-devs@lists.sourceforge.net. The chip info is: zd1211b chip 0df6:0036 v4810 high 00-0c-f6 AL2230_RF pa0 g--N- The Sitecom WL-603 is detected as a zd1211b with a AL2230 RF transceiver chip. Signed-off-by: Giuseppe Cala Signed-off-by: Hin-Tak Leung Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_usb.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index b5db57d2fcf..17527f765b3 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -84,6 +84,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0df6, 0x0036), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, -- cgit v1.2.3 From 637f883739b32746889a191f282c9ea2590ecf4f Mon Sep 17 00:00:00 2001 From: Reinette Chatre Date: Mon, 19 Jan 2009 15:30:32 -0800 Subject: iwlwifi: return NETDEV_TX_OK from _tx ops be consistent with mac80211 drivers and return correct return code. NETDEV_TX_OK is 0, but we need to be consistent wrt formatting amongst implementations re: http://marc.info/?l=linux-wireless&m=123119327419865&w=2 Signed-off-by: Reinette Chatre Reviewed-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 5da6b35cd26..0dc8eed1640 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2482,7 +2482,7 @@ static int iwl_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb_any(skb); IWL_DEBUG_MACDUMP("leave\n"); - return 0; + return NETDEV_TX_OK; } static int iwl_mac_add_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 15f5655c636..95d01984c80 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -6538,7 +6538,7 @@ static int iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb_any(skb); IWL_DEBUG_MAC80211("leave\n"); - return 0; + return NETDEV_TX_OK; } static int iwl3945_mac_add_interface(struct ieee80211_hw *hw, -- cgit v1.2.3 From 81f75bbf67c7a26ea1266ac93b9319bb985d588d Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Thu, 22 Jan 2009 03:37:31 +0000 Subject: bnx2x: Reset HW before use To avoid complications, make sure that the HW is in reset (as it should be) before trying to take it out of reset. In normal flows, the HW is indeed in rest so this should have no effect Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 074374ff93f..ed8b3466593 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -5148,12 +5148,21 @@ static void enable_blocks_attention(struct bnx2x *bp) } +static void bnx2x_reset_common(struct bnx2x *bp) +{ + /* reset_common */ + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, + 0xd3ffff7f); + REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x1403); +} + static int bnx2x_init_common(struct bnx2x *bp) { u32 val, i; DP(BNX2X_MSG_MCP, "starting common init func %d\n", BP_FUNC(bp)); + bnx2x_reset_common(bp); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_SET, 0xffffffff); REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_SET, 0xfffc); @@ -6640,14 +6649,6 @@ static void bnx2x_reset_port(struct bnx2x *bp) /* TODO: Close Doorbell port? */ } -static void bnx2x_reset_common(struct bnx2x *bp) -{ - /* reset_common */ - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_1_CLEAR, - 0xd3ffff7f); - REG_WR(bp, GRCBASE_MISC + MISC_REGISTERS_RESET_REG_2_CLEAR, 0x1403); -} - static void bnx2x_reset_chip(struct bnx2x *bp, u32 reset_code) { DP(BNX2X_MSG_MCP, "function %d reset_code %x\n", -- cgit v1.2.3 From e94d8af3da79f4bfbd22819d28ecf0602456f06f Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Thu, 22 Jan 2009 03:37:36 +0000 Subject: bnx2x: Disable napi Calling napi disabled unconditionally at netif stop Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index ed8b3466593..860b9f8ddd3 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -6143,8 +6143,8 @@ static void bnx2x_netif_start(struct bnx2x *bp) static void bnx2x_netif_stop(struct bnx2x *bp, int disable_hw) { bnx2x_int_disable_sync(bp, disable_hw); + bnx2x_napi_disable(bp); if (netif_running(bp->dev)) { - bnx2x_napi_disable(bp); netif_tx_disable(bp->dev); bp->dev->trans_start = jiffies; /* prevent tx timeout */ } @@ -6689,8 +6689,7 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) bnx2x_set_storm_rx_mode(bp); bnx2x_netif_stop(bp, 1); - if (!netif_running(bp->dev)) - bnx2x_napi_disable(bp); + del_timer_sync(&bp->timer); SHMEM_WR(bp, func_mb[BP_FUNC(bp)].drv_pulse_mb, (DRV_PULSE_ALWAYS_ALIVE | bp->fw_drv_pulse_wr_seq)); -- cgit v1.2.3 From 2dfe0e1fecb582b4aae7f2490904864c05472f3a Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Thu, 22 Jan 2009 03:37:44 +0000 Subject: bnx2x: Handling load failures Failures on load were not handled correctly - separate the flow to handle different failures Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 154 +++++++++++++++++++++++++++-------------------- 1 file changed, 88 insertions(+), 66 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 860b9f8ddd3..707c4bbe46a 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -6328,7 +6328,7 @@ static void bnx2x_set_rx_mode(struct net_device *dev); static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) { u32 load_code; - int i, rc; + int i, rc = 0; #ifdef BNX2X_STOP_ON_ERROR if (unlikely(bp->panic)) return -EPERM; @@ -6336,48 +6336,6 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD; - /* Send LOAD_REQUEST command to MCP - Returns the type of LOAD command: - if it is the first port to be initialized - common blocks should be initialized, otherwise - not - */ - if (!BP_NOMCP(bp)) { - load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ); - if (!load_code) { - BNX2X_ERR("MCP response failure, aborting\n"); - return -EBUSY; - } - if (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED) - return -EBUSY; /* other port in diagnostic mode */ - - } else { - int port = BP_PORT(bp); - - DP(NETIF_MSG_IFUP, "NO MCP load counts before us %d, %d, %d\n", - load_count[0], load_count[1], load_count[2]); - load_count[0]++; - load_count[1 + port]++; - DP(NETIF_MSG_IFUP, "NO MCP new load counts %d, %d, %d\n", - load_count[0], load_count[1], load_count[2]); - if (load_count[0] == 1) - load_code = FW_MSG_CODE_DRV_LOAD_COMMON; - else if (load_count[1 + port] == 1) - load_code = FW_MSG_CODE_DRV_LOAD_PORT; - else - load_code = FW_MSG_CODE_DRV_LOAD_FUNCTION; - } - - if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) || - (load_code == FW_MSG_CODE_DRV_LOAD_PORT)) - bp->port.pmf = 1; - else - bp->port.pmf = 0; - DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf); - - /* if we can't use MSI-X we only need one fp, - * so try to enable MSI-X with the requested number of fp's - * and fallback to inta with one fp - */ if (use_inta) { bp->num_queues = 1; @@ -6392,7 +6350,15 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) else bp->num_queues = 1; - if (bnx2x_enable_msix(bp)) { + DP(NETIF_MSG_IFUP, + "set number of queues to %d\n", bp->num_queues); + + /* if we can't use MSI-X we only need one fp, + * so try to enable MSI-X with the requested number of fp's + * and fallback to MSI or legacy INTx with one fp + */ + rc = bnx2x_enable_msix(bp); + if (rc) { /* failed to enable MSI-X */ bp->num_queues = 1; if (use_multi) @@ -6400,8 +6366,6 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) " to enable MSI-X\n"); } } - DP(NETIF_MSG_IFUP, - "set number of queues to %d\n", bp->num_queues); if (bnx2x_alloc_mem(bp)) return -ENOMEM; @@ -6410,30 +6374,85 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) bnx2x_fp(bp, i, disable_tpa) = ((bp->flags & TPA_ENABLE_FLAG) == 0); + for_each_queue(bp, i) + netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), + bnx2x_poll, 128); + +#ifdef BNX2X_STOP_ON_ERROR + for_each_queue(bp, i) { + struct bnx2x_fastpath *fp = &bp->fp[i]; + + fp->poll_no_work = 0; + fp->poll_calls = 0; + fp->poll_max_calls = 0; + fp->poll_complete = 0; + fp->poll_exit = 0; + } +#endif + bnx2x_napi_enable(bp); + if (bp->flags & USING_MSIX_FLAG) { rc = bnx2x_req_msix_irqs(bp); if (rc) { pci_disable_msix(bp->pdev); - goto load_error; + goto load_error1; } + printk(KERN_INFO PFX "%s: using MSI-X\n", bp->dev->name); } else { bnx2x_ack_int(bp); rc = bnx2x_req_irq(bp); if (rc) { - BNX2X_ERR("IRQ request failed, aborting\n"); - goto load_error; + BNX2X_ERR("IRQ request failed rc %d, aborting\n", rc); + goto load_error1; } } - for_each_queue(bp, i) - netif_napi_add(bp->dev, &bnx2x_fp(bp, i, napi), - bnx2x_poll, 128); + /* Send LOAD_REQUEST command to MCP + Returns the type of LOAD command: + if it is the first port to be initialized + common blocks should be initialized, otherwise - not + */ + if (!BP_NOMCP(bp)) { + load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ); + if (!load_code) { + BNX2X_ERR("MCP response failure, aborting\n"); + rc = -EBUSY; + goto load_error2; + } + if (load_code == FW_MSG_CODE_DRV_LOAD_REFUSED) { + rc = -EBUSY; /* other port in diagnostic mode */ + goto load_error2; + } + + } else { + int port = BP_PORT(bp); + + DP(NETIF_MSG_IFUP, "NO MCP load counts before us %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + load_count[0]++; + load_count[1 + port]++; + DP(NETIF_MSG_IFUP, "NO MCP new load counts %d, %d, %d\n", + load_count[0], load_count[1], load_count[2]); + if (load_count[0] == 1) + load_code = FW_MSG_CODE_DRV_LOAD_COMMON; + else if (load_count[1 + port] == 1) + load_code = FW_MSG_CODE_DRV_LOAD_PORT; + else + load_code = FW_MSG_CODE_DRV_LOAD_FUNCTION; + } + + if ((load_code == FW_MSG_CODE_DRV_LOAD_COMMON) || + (load_code == FW_MSG_CODE_DRV_LOAD_PORT)) + bp->port.pmf = 1; + else + bp->port.pmf = 0; + DP(NETIF_MSG_LINK, "pmf %d\n", bp->port.pmf); /* Initialize HW */ rc = bnx2x_init_hw(bp, load_code); if (rc) { BNX2X_ERR("HW init failed, aborting\n"); - goto load_int_disable; + goto load_error2; } /* Setup NIC internals and enable interrupts */ @@ -6445,7 +6464,7 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) if (!load_code) { BNX2X_ERR("MCP response failure, aborting\n"); rc = -EBUSY; - goto load_rings_free; + goto load_error3; } } @@ -6454,7 +6473,7 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) rc = bnx2x_setup_leading(bp); if (rc) { BNX2X_ERR("Setup leading failed!\n"); - goto load_netif_stop; + goto load_error3; } if (CHIP_IS_E1H(bp)) @@ -6467,7 +6486,7 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) for_each_nondefault_queue(bp, i) { rc = bnx2x_setup_multi(bp, i); if (rc) - goto load_netif_stop; + goto load_error3; } if (CHIP_IS_E1(bp)) @@ -6483,18 +6502,18 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) case LOAD_NORMAL: /* Tx queue should be only reenabled */ netif_wake_queue(bp->dev); + /* Initialize the receive filter. */ bnx2x_set_rx_mode(bp->dev); break; case LOAD_OPEN: netif_start_queue(bp->dev); + /* Initialize the receive filter. */ bnx2x_set_rx_mode(bp->dev); - if (bp->flags & USING_MSIX_FLAG) - printk(KERN_INFO PFX "%s: using MSI-X\n", - bp->dev->name); break; case LOAD_DIAG: + /* Initialize the receive filter. */ bnx2x_set_rx_mode(bp->dev); bp->state = BNX2X_STATE_DIAG; break; @@ -6512,20 +6531,23 @@ static int bnx2x_nic_load(struct bnx2x *bp, int load_mode) return 0; -load_netif_stop: - bnx2x_napi_disable(bp); -load_rings_free: +load_error3: + bnx2x_int_disable_sync(bp, 1); + if (!BP_NOMCP(bp)) { + bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_REQ_WOL_MCP); + bnx2x_fw_command(bp, DRV_MSG_CODE_UNLOAD_DONE); + } + bp->port.pmf = 0; /* Free SKBs, SGEs, TPA pool and driver internals */ bnx2x_free_skbs(bp); for_each_queue(bp, i) bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE); -load_int_disable: - bnx2x_int_disable_sync(bp, 1); +load_error2: /* Release IRQs */ bnx2x_free_irq(bp); -load_error: +load_error1: + bnx2x_napi_disable(bp); bnx2x_free_mem(bp); - bp->port.pmf = 0; /* TBD we really need to reset the chip if we want to recover from this */ -- cgit v1.2.3 From 6eccabb301d442e6106ecc84b07a976c2816d9fb Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Thu, 22 Jan 2009 03:37:48 +0000 Subject: bnx2x: Carrier off first call Call carrier off should not be called after register_netdev since after register netdev open can be called at any time followed by an interrupt that will set it to carrier_on and the probe will resume control and set it to off Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 707c4bbe46a..fbd71659cca 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -9827,6 +9827,8 @@ static int bnx2x_open(struct net_device *dev) { struct bnx2x *bp = netdev_priv(dev); + netif_carrier_off(dev); + bnx2x_set_power_state(bp, PCI_D0); return bnx2x_nic_load(bp, LOAD_OPEN); @@ -10332,8 +10334,6 @@ static int __devinit bnx2x_init_one(struct pci_dev *pdev, goto init_one_exit; } - netif_carrier_off(dev); - bp->common.name = board_info[ent->driver_data].name; printk(KERN_INFO "%s: %s (%c%d) PCI-E x%d %s found at mem %lx," " IRQ %d, ", dev->name, bp->common.name, -- cgit v1.2.3 From 7cde1c8b79f913a0158bae4f4c612de2cb98e7e4 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Thu, 22 Jan 2009 06:01:25 +0000 Subject: bnx2x: Calling napi_del rmmod might hang without this patch since the reference counter is not going down Signed-off-by: Yitchak Gertner Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index fbd71659cca..71fbf2dbda3 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -6547,6 +6547,8 @@ load_error2: bnx2x_free_irq(bp); load_error1: bnx2x_napi_disable(bp); + for_each_queue(bp, i) + netif_napi_del(&bnx2x_fp(bp, i, napi)); bnx2x_free_mem(bp); /* TBD we really need to reset the chip @@ -6855,6 +6857,8 @@ unload_error: bnx2x_free_skbs(bp); for_each_queue(bp, i) bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE); + for_each_queue(bp, i) + netif_napi_del(&bnx2x_fp(bp, i, napi)); bnx2x_free_mem(bp); bp->state = BNX2X_STATE_CLOSED; @@ -10481,6 +10485,8 @@ static int bnx2x_eeh_nic_unload(struct bnx2x *bp) bnx2x_free_skbs(bp); for_each_queue(bp, i) bnx2x_free_rx_sge_range(bp, bp->fp + i, NUM_RX_SGE); + for_each_queue(bp, i) + netif_napi_del(&bnx2x_fp(bp, i, napi)); bnx2x_free_mem(bp); bp->state = BNX2X_STATE_CLOSED; -- cgit v1.2.3 From 5650d9d4cbf3898d3f9725ccad5dfca6bc086324 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Thu, 22 Jan 2009 06:01:29 +0000 Subject: bnx2x: Missing rmb when waiting for FW response Waiting for the FW to response requires a memory barrier Signed-off-by: Michal Kalderon Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 71fbf2dbda3..f71b8a5872b 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -6622,6 +6622,7 @@ static int bnx2x_stop_leading(struct bnx2x *bp) } cnt--; msleep(1); + rmb(); /* Refresh the dsb_sp_prod */ } bp->state = BNX2X_STATE_CLOSING_WAIT4_UNLOAD; bp->fp[0].state = BNX2X_FP_STATE_CLOSED; -- cgit v1.2.3 From 3910c8ae44c59cebed721e33aa496f0a385b4e03 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Thu, 22 Jan 2009 06:01:32 +0000 Subject: bnx2x: loopback test failure A link change interrupt might be queued and activated after the loopback was set and it will cause the loopback to fail. The PHY lock should be kept until the loopback test is over. That implies that the bnx2x_test_link should used within the loopback function and not bnx2x_wait_for_link since that function also takes the PHY link Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index f71b8a5872b..d95714f780f 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -8750,18 +8750,17 @@ static int bnx2x_run_loopback(struct bnx2x *bp, int loopback_mode, u8 link_up) if (loopback_mode == BNX2X_MAC_LOOPBACK) { bp->link_params.loopback_mode = LOOPBACK_BMAC; - bnx2x_acquire_phy_lock(bp); bnx2x_phy_init(&bp->link_params, &bp->link_vars); - bnx2x_release_phy_lock(bp); } else if (loopback_mode == BNX2X_PHY_LOOPBACK) { + u16 cnt = 1000; bp->link_params.loopback_mode = LOOPBACK_XGXS_10; - bnx2x_acquire_phy_lock(bp); bnx2x_phy_init(&bp->link_params, &bp->link_vars); - bnx2x_release_phy_lock(bp); /* wait until link state is restored */ - bnx2x_wait_for_link(bp, link_up); - + if (link_up) + while (cnt-- && bnx2x_test_link(&bp->link_params, + &bp->link_vars)) + msleep(10); } else return -EINVAL; @@ -8867,6 +8866,7 @@ static int bnx2x_test_loopback(struct bnx2x *bp, u8 link_up) return BNX2X_LOOPBACK_FAILED; bnx2x_netif_stop(bp, 1); + bnx2x_acquire_phy_lock(bp); if (bnx2x_run_loopback(bp, BNX2X_MAC_LOOPBACK, link_up)) { DP(NETIF_MSG_PROBE, "MAC loopback failed\n"); @@ -8878,6 +8878,7 @@ static int bnx2x_test_loopback(struct bnx2x *bp, u8 link_up) rc |= BNX2X_PHY_LOOPBACK_FAILED; } + bnx2x_release_phy_lock(bp); bnx2x_netif_start(bp); return rc; -- cgit v1.2.3 From 5422a2257350d984094e655b2361abed51a9ddc1 Mon Sep 17 00:00:00 2001 From: Eilon Greenstein Date: Thu, 22 Jan 2009 06:01:37 +0000 Subject: bnx2x: Version Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index d95714f780f..4b84f6ce5ed 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -57,8 +57,8 @@ #include "bnx2x.h" #include "bnx2x_init.h" -#define DRV_MODULE_VERSION "1.45.24" -#define DRV_MODULE_RELDATE "2009/01/14" +#define DRV_MODULE_VERSION "1.45.25" +#define DRV_MODULE_RELDATE "2009/01/22" #define BNX2X_BC_VER 0x040200 /* Time in jiffies before concluding the transmitter is hung */ -- cgit v1.2.3 From 6f051069d8a2045666055e3020ae8a7dec9762e0 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 22 Jan 2009 13:51:24 -0800 Subject: phylib: Fix oops in suspend/resume paths Suspend/resume routines check for phydrv != NULL, but that is wrong because "phydrv" comes from container_of(drv). If drv is NULL, then container_of(drv) will return non-NULL result, and the checks won't work. The Freescale TBI PHYs are driver-less, so "drv" is NULL, and that leads to the following oops: Unable to handle kernel paging request for data at address 0xffffffe4 Faulting instruction address: 0xc0215554 Oops: Kernel access of bad area, sig: 11 [#1] [...] NIP [c0215554] mdio_bus_suspend+0x34/0x70 LR [c01cc508] suspend_device+0x258/0x2bc Call Trace: [cfad3da0] [cfad3db8] 0xcfad3db8 (unreliable) [cfad3db0] [c01cc508] suspend_device+0x258/0x2bc [cfad3dd0] [c01cc62c] dpm_suspend+0xc0/0x140 [cfad3e20] [c01cc6f4] device_suspend+0x48/0x5c [cfad3e40] [c0068dd8] suspend_devices_and_enter+0x8c/0x148 [cfad3e60] [c00690f8] enter_state+0x100/0x118 [cfad3e80] [c00691c0] state_store+0xb0/0xe4 [cfad3ea0] [c018c938] kobj_attr_store+0x24/0x3c [cfad3eb0] [c00ea9a8] flush_write_buffer+0x58/0x7c [cfad3ed0] [c00eadf0] sysfs_write_file+0x58/0xa0 [cfad3ef0] [c009e810] vfs_write+0xb4/0x16c [cfad3f10] [c009ed40] sys_write+0x4c/0x90 [cfad3f40] [c0014954] ret_from_syscall+0x0/0x38 [...] This patch fixes the issue, plus removes unneeded parentheses and fixes indentation level in mdio_bus_suspend(). Signed-off-by: Anton Vorontsov Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 11adf6ed462..811a637695c 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -296,9 +296,8 @@ static int mdio_bus_suspend(struct device * dev, pm_message_t state) struct phy_driver *phydrv = to_phy_driver(drv); struct phy_device *phydev = to_phy_device(dev); - if ((!device_may_wakeup(phydev->dev.parent)) && - (phydrv && phydrv->suspend)) - ret = phydrv->suspend(phydev); + if (drv && phydrv->suspend && !device_may_wakeup(phydev->dev.parent)) + ret = phydrv->suspend(phydev); return ret; } @@ -310,8 +309,7 @@ static int mdio_bus_resume(struct device * dev) struct phy_driver *phydrv = to_phy_driver(drv); struct phy_device *phydev = to_phy_device(dev); - if ((!device_may_wakeup(phydev->dev.parent)) && - (phydrv && phydrv->resume)) + if (drv && phydrv->resume && !device_may_wakeup(phydev->dev.parent)) ret = phydrv->resume(phydev); return ret; -- cgit v1.2.3 From c64d2a9afbccd0aecb122d108770a407fe7b7e3f Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Thu, 22 Jan 2009 14:07:43 -0800 Subject: phy: Add suspend/resume support to SMSC PHYs All supported SMSC PHYs implement the standard "power down" bit 11 of BMCR, so this patch adds support using the generic genphy_{suspend,resume} functions. Signed-off-by: Steve Glendinning Signed-off-by: David S. Miller --- drivers/net/phy/smsc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index c05d38d4635..1387187543e 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -81,6 +81,9 @@ static struct phy_driver lan83c185_driver = { .ack_interrupt = smsc_phy_ack_interrupt, .config_intr = smsc_phy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, + .driver = { .owner = THIS_MODULE, } }; @@ -102,6 +105,9 @@ static struct phy_driver lan8187_driver = { .ack_interrupt = smsc_phy_ack_interrupt, .config_intr = smsc_phy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, + .driver = { .owner = THIS_MODULE, } }; @@ -123,6 +129,9 @@ static struct phy_driver lan8700_driver = { .ack_interrupt = smsc_phy_ack_interrupt, .config_intr = smsc_phy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, + .driver = { .owner = THIS_MODULE, } }; @@ -144,6 +153,9 @@ static struct phy_driver lan911x_int_driver = { .ack_interrupt = smsc_phy_ack_interrupt, .config_intr = smsc_phy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, + .driver = { .owner = THIS_MODULE, } }; -- cgit v1.2.3 From 1058a75f07b9bb8323fb5197be5526220f8b75cf Mon Sep 17 00:00:00 2001 From: Dan Magenheimer Date: Thu, 22 Jan 2009 14:36:08 -0800 Subject: xen: actually release memory when shrinking domain Fix this: > It appears that in the upstream balloon driver, > the call to HYPERVISOR_update_va_mapping is missing > from decrease_reservation. I think as a result, > the balloon driver is eating memory but not > releasing it to Xen, thus rendering the balloon > driver essentially useless. (Can be observed via xentop.) Signed-off-by: Dan Magenheimer Signed-off-by: Jeremy Fitzhardinge Signed-off-by: Ingo Molnar --- drivers/xen/balloon.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 8dc7109d61b..8069d520c46 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -298,6 +298,11 @@ static int decrease_reservation(unsigned long nr_pages) frame_list[i] = pfn_to_mfn(pfn); scrub_page(page); + + ret = HYPERVISOR_update_va_mapping( + (unsigned long)__va(pfn << PAGE_SHIFT), + __pte_ma(0), 0); + BUG_ON(ret); } /* Ensure that ballooned highmem pages don't have kmaps. */ -- cgit v1.2.3 From ff4ce8c332859508dc97826ab8b7f42bb9c212c9 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Fri, 23 Jan 2009 16:26:21 +0000 Subject: xen: handle highmem pages correctly when shrinking a domain Commit 1058a75f07b9bb8323fb5197be5526220f8b75cf ("xen: actually release memory when shrinking domain") causes a crash if the page being released is a highmem page. If a page is highmem then there is no need to unmap it. Signed-off-by: Ian Campbell Acked-by: Jeremy Fitzhardinge Signed-off-by: Ingo Molnar --- drivers/xen/balloon.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 8069d520c46..2ba8f95516a 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -299,10 +299,13 @@ static int decrease_reservation(unsigned long nr_pages) scrub_page(page); - ret = HYPERVISOR_update_va_mapping( - (unsigned long)__va(pfn << PAGE_SHIFT), - __pte_ma(0), 0); - BUG_ON(ret); + if (!PageHighMem(page)) { + ret = HYPERVISOR_update_va_mapping( + (unsigned long)__va(pfn << PAGE_SHIFT), + __pte_ma(0), 0); + BUG_ON(ret); + } + } /* Ensure that ballooned highmem pages don't have kmaps. */ -- cgit v1.2.3 From b4068a80492022848c11123bf485aff5c902c583 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Tue, 20 Jan 2009 23:11:21 +0100 Subject: p54usb: fix packet loss with first generation devices Artur Skawina confirmed that the first generation devices needs the same URB_ZERO_PACKET flag, in oder to finish the pending transfer properly. The second generation has been successfully fixed by "p54usb: fix random traffic stalls (LM87)" (43af18f06d5) Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54usb.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 4487cc5c928..5de2ebfb28c 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -229,6 +229,8 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb) usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), skb->data, skb->len, FREE_AFTER_TX(skb) ? p54u_tx_cb : p54u_tx_dummy_cb, skb); + addr_urb->transfer_flags |= URB_ZERO_PACKET; + data_urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(addr_urb, &priv->submitted); err = usb_submit_urb(addr_urb, GFP_ATOMIC); @@ -237,7 +239,7 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb) goto out; } - usb_anchor_urb(addr_urb, &priv->submitted); + usb_anchor_urb(data_urb, &priv->submitted); err = usb_submit_urb(data_urb, GFP_ATOMIC); if (err) usb_unanchor_urb(data_urb); @@ -332,12 +334,13 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) * free what's inside the transfer_buffer after the callback routine * has completed. */ - int_urb->transfer_flags |= URB_FREE_BUFFER; + int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET; usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? p54u_tx_cb : p54u_tx_dummy_cb, skb); + data_urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(int_urb, &priv->submitted); err = usb_submit_urb(int_urb, GFP_ATOMIC); -- cgit v1.2.3 From c338ba3ca5bef2df2082d9e8d336ff7b2880c326 Mon Sep 17 00:00:00 2001 From: "Abbas, Mohamed" Date: Wed, 21 Jan 2009 10:58:02 -0800 Subject: iwlwifi: fix rs_get_rate WARN_ON() In ieee80211_sta structure there is u64 supp_rates[IEEE80211_NUM_BANDS] this is filled with all support rate from assoc_resp. If we associate with G-band AP only supp_rates of G-band will be set the other band supp_rates will be set to 0. If the user type this command this will cause mac80211 to set to new channel, mac80211 does not disassociate in setting new channel, so the active band is now A-band. then in handling the new essid mac80211 will kick in the assoc steps which involve sending disassociation frame. in this mac80211 will WARN_ON sta->supp_rates[A_BAND] == 0. This fixes: http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=1822 http://www.kerneloops.org/searchweek.php?search=rs_get_rate Signed-off-by: mohamed abbas Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945-rs.c | 14 +++++++++++--- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 14 ++++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index 9b60a0c5de5..21c841847d8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -638,12 +638,16 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, s8 scale_action = 0; unsigned long flags; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - u16 fc, rate_mask; + u16 fc; + u16 rate_mask = 0; struct iwl3945_priv *priv = (struct iwl3945_priv *)priv_r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); IWL_DEBUG_RATE("enter\n"); + if (sta) + rate_mask = sta->supp_rates[sband->band]; + /* Send management frames and broadcast/multicast data using lowest * rate. */ fc = le16_to_cpu(hdr->frame_control); @@ -651,11 +655,15 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, is_multicast_ether_addr(hdr->addr1) || !sta || !priv_sta) { IWL_DEBUG_RATE("leave: No STA priv data to update!\n"); - info->control.rates[0].idx = rate_lowest_index(sband, sta); + if (!rate_mask) + info->control.rates[0].idx = + rate_lowest_index(sband, NULL); + else + info->control.rates[0].idx = + rate_lowest_index(sband, sta); return; } - rate_mask = sta->supp_rates[sband->band]; index = min(rs_sta->last_txrate_idx & 0xffff, IWL_RATE_COUNT - 1); if (sband->band == IEEE80211_BAND_5GHZ) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index f3f17929ca0..27f50471aed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -944,7 +944,8 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, } /* See if there's a better rate or modulation mode to try. */ - rs_rate_scale_perform(priv, hdr, sta, lq_sta); + if (sta && sta->supp_rates[sband->band]) + rs_rate_scale_perform(priv, hdr, sta, lq_sta); out: return; } @@ -2101,14 +2102,23 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = priv_sta; int rate_idx; + u64 mask_bit = 0; IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n"); + if (sta) + mask_bit = sta->supp_rates[sband->band]; + /* Send management frames and broadcast/multicast data using lowest * rate. */ if (!ieee80211_is_data(hdr->frame_control) || is_multicast_ether_addr(hdr->addr1) || !sta || !lq_sta) { - info->control.rates[0].idx = rate_lowest_index(sband, sta); + if (!mask_bit) + info->control.rates[0].idx = + rate_lowest_index(sband, NULL); + else + info->control.rates[0].idx = + rate_lowest_index(sband, sta); return; } -- cgit v1.2.3 From 2fcbab044a3faf4d4a6e269148dd1f188303b206 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Fri, 23 Jan 2009 11:46:32 -0600 Subject: rtl8187: Add termination packet to prevent stall The RTL8187 and RTL8187B devices can stall unless an explicit termination packet is sent. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187_dev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 6ad6bac3770..22bc07ef2f3 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -273,6 +273,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), buf, skb->len, rtl8187_tx_cb, skb); + urb->transfer_flags |= URB_ZERO_PACKET; usb_anchor_urb(urb, &priv->anchored); rc = usb_submit_urb(urb, GFP_ATOMIC); if (rc < 0) { -- cgit v1.2.3 From b006854955254a971096c120d4ef115a7c6145fb Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 5 Jan 2009 20:43:23 +0100 Subject: firewire: ohci: change "context_stop: still active" log message The present message is mostly just noise. We only need to be notified if the "active" flag does not go off before the retry loop terminates. Signed-off-by: Stefan Richter --- drivers/firewire/fw-ohci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index ab9c01e462e..9bedd6159f2 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -896,11 +896,11 @@ static void context_stop(struct context *ctx) for (i = 0; i < 10; i++) { reg = reg_read(ctx->ohci, CONTROL_SET(ctx->regs)); if ((reg & CONTEXT_ACTIVE) == 0) - break; + return; - fw_notify("context_stop: still active (0x%08x)\n", reg); mdelay(1); } + fw_error("Error: DMA context still active (0x%08x)\n", reg); } struct driver_data { -- cgit v1.2.3 From 8b7b6afaa84708d08139daa08538ca3e56c351f1 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 20 Jan 2009 19:10:58 +0100 Subject: firewire: ohci: increase AT req. retries, fix ack_busy_X from Panasonic camcorders and others Camcorders have a tendency to fail read requests to their config ROM and write request to their FCP command register with ack_busy_X. This has become a problem with newer kernels and especially Panasonic camcorders, causing AV/C in dvgrab and kino to fail. Dvgrab for example frequently logs "send oops"; kino reports loss of AV/C control. I suspect that lower CPU scheduling latencies in newer kernels made this issue more prominent now. According to https://sourceforge.net/tracker/?func=detail&atid=114103&aid=2492640&group_id=14103 this can be fixed by configuring the FireWire controller for more hardware retries for request transmission; these retries are evidently more successful than libavc1394's own retry loop (typically 3 tries on top of hardware retries). Presumably the same issue has been reported at https://bugzilla.redhat.com/show_bug.cgi?id=449252 and https://bugzilla.redhat.com/show_bug.cgi?id=477279 . In a quick test with a JVC camcorder (which didn't malfunction like the reported camcorders), this change decreased the number of ack_busy_X from 16 in three runs of dvgrab to 4 in three runs of the same capture duration. Signed-off-by: Stefan Richter --- drivers/firewire/fw-ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 9bedd6159f2..6d19828a93a 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -226,7 +226,7 @@ static inline struct fw_ohci *fw_ohci(struct fw_card *card) #define CONTEXT_DEAD 0x0800 #define CONTEXT_ACTIVE 0x0400 -#define OHCI1394_MAX_AT_REQ_RETRIES 0x2 +#define OHCI1394_MAX_AT_REQ_RETRIES 0xf #define OHCI1394_MAX_AT_RESP_RETRIES 0x2 #define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 -- cgit v1.2.3 From 64c634ef83991b390ec0503e61f16efb0ba3c60b Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Tue, 20 Jan 2009 19:09:58 +0100 Subject: ieee1394: ohci1394: increase AT req. retries, fix ack_busy_X from Panasonic camcorders and others Camcorders have a tendency to fail read requests to their config ROM and write request to their FCP command register with ack_busy_X. This has become a problem with newer kernels and especially Panasonic camcorders, causing AV/C in dvgrab and kino to fail. Dvgrab for example frequently logs "send oops"; kino reports loss of AV/C control. I suspect that lower CPU scheduling latencies in newer kernels made this issue more prominent now. According to https://sourceforge.net/tracker/?func=detail&atid=114103&aid=2492640&group_id=14103 this can be fixed by configuring the FireWire controller for more hardware retries for request transmission; these retries are evidently more successful than libavc1394's own retry loop (typically 3 tries on top of hardware retries). Presumably the same issue has been reported at https://bugzilla.redhat.com/show_bug.cgi?id=449252 and https://bugzilla.redhat.com/show_bug.cgi?id=477279 . Tested-by: Mathias Beilstein Signed-off-by: Stefan Richter --- drivers/ieee1394/ohci1394.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index 4320bf01049..7fb8ab9780a 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -26,7 +26,7 @@ #define OHCI1394_DRIVER_NAME "ohci1394" -#define OHCI1394_MAX_AT_REQ_RETRIES 0x2 +#define OHCI1394_MAX_AT_REQ_RETRIES 0xf #define OHCI1394_MAX_AT_RESP_RETRIES 0x2 #define OHCI1394_MAX_PHYS_RESP_RETRIES 0x8 #define OHCI1394_MAX_SELF_ID_ERRORS 16 -- cgit v1.2.3 From 953a7e8476bbd7367cebdb868c326ba42968bc13 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 18 Jan 2009 12:52:38 +0000 Subject: [ARM] omap: ensure OMAP drivers pass a struct device to clk_get() Signed-off-by: Russell King --- drivers/char/hw_random/omap-rng.c | 2 +- drivers/usb/host/ohci-omap.c | 6 +++--- drivers/video/omap/lcdc.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index d4e7dca06e4..ba68a4671cb 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -102,7 +102,7 @@ static int __init omap_rng_probe(struct platform_device *pdev) return -EBUSY; if (cpu_is_omap24xx()) { - rng_ick = clk_get(NULL, "rng_ick"); + rng_ick = clk_get(&pdev->dev, "rng_ick"); if (IS_ERR(rng_ick)) { dev_err(&pdev->dev, "Could not get rng_ick\n"); ret = PTR_ERR(rng_ick); diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 4bbddb73abd..f3aaba35e91 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -315,14 +315,14 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver, return -ENODEV; } - usb_host_ck = clk_get(0, "usb_hhc_ck"); + usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck"); if (IS_ERR(usb_host_ck)) return PTR_ERR(usb_host_ck); if (!cpu_is_omap15xx()) - usb_dc_ck = clk_get(0, "usb_dc_ck"); + usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck"); else - usb_dc_ck = clk_get(0, "lb_ck"); + usb_dc_ck = clk_get(&pdev->dev, "lb_ck"); if (IS_ERR(usb_dc_ck)) { clk_put(usb_host_ck); diff --git a/drivers/video/omap/lcdc.c b/drivers/video/omap/lcdc.c index 6e2ea751876..ab394925667 100644 --- a/drivers/video/omap/lcdc.c +++ b/drivers/video/omap/lcdc.c @@ -800,14 +800,14 @@ static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode, /* FIXME: * According to errata some platforms have a clock rate limitiation */ - lcdc.lcd_ck = clk_get(NULL, "lcd_ck"); + lcdc.lcd_ck = clk_get(fbdev->dev, "lcd_ck"); if (IS_ERR(lcdc.lcd_ck)) { dev_err(fbdev->dev, "unable to access LCD clock\n"); r = PTR_ERR(lcdc.lcd_ck); goto fail0; } - tc_ck = clk_get(NULL, "tc_ck"); + tc_ck = clk_get(fbdev->dev, "tc_ck"); if (IS_ERR(tc_ck)) { dev_err(fbdev->dev, "unable to access TC clock\n"); r = PTR_ERR(tc_ck); -- cgit v1.2.3 From 7ad14f83d335bc042baa21d710b4ea0918965ffe Mon Sep 17 00:00:00 2001 From: Ramax Lo Date: Wed, 14 Jan 2009 02:13:47 +0100 Subject: [ARM] 5365/1: s3cmci: Use new include path of dma.h Since dma.h has been moved to arch/arm/mach-s3c2410/include/mach, use the new include path. Signed-off-by: Ramax Lo Acked-by: Ben Dooks Signed-off-by: Russell King --- drivers/mmc/host/s3cmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index fcc98a4cce3..35a98eec741 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -20,7 +20,7 @@ #include #include -#include +#include #include #include -- cgit v1.2.3 From 02e0746ecc0e72482fe6f350cbb8b65d1d5fc40a Mon Sep 17 00:00:00 2001 From: Jean-Christop PLAGNIOL-VILLARD Date: Fri, 23 Jan 2009 10:06:07 +0100 Subject: [ARM] 5370/1: at91: fix rm9200 watchdog Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Russell King --- drivers/watchdog/at91rm9200_wdt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 993e5f52afe..5531691f46e 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From a45c6cb816474cefe56059fce422a9bdcd77e0dc Mon Sep 17 00:00:00 2001 From: Madhusudhan Chikkature Date: Fri, 23 Jan 2009 01:05:23 +0100 Subject: [ARM] 5369/1: omap mmc: Add new omap hsmmc controller for 2430 and 34xx, v3 Add omap hsmmc controller for 2430 and 34xx. Note that this controller has different registers compared to the earlier omap MMC controller, so sharing code currently is not possible. Various updates and fixes from linux-omap list have been merged into this patch. Signed-off-by: Madhusudhan Chikkature Acked-by: Pierre Ossman Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- drivers/mmc/host/Kconfig | 10 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/omap_hsmmc.c | 1242 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1253 insertions(+) create mode 100644 drivers/mmc/host/omap_hsmmc.c (limited to 'drivers') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index dfa585f7fea..0efa390978b 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -76,6 +76,16 @@ config MMC_OMAP If unsure, say N. +config MMC_OMAP_HS + tristate "TI OMAP High Speed Multimedia Card Interface support" + depends on ARCH_OMAP2430 || ARCH_OMAP3 + help + This selects the TI OMAP High Speed Multimedia card Interface. + If you have an OMAP2430 or OMAP3 board with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + config MMC_WBSD tristate "Winbond W83L51xD SD/MMC Card Interface support" depends on ISA_DMA_API diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f4853288bbb..98cab84829b 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o +obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c new file mode 100644 index 00000000000..db37490f67e --- /dev/null +++ b/drivers/mmc/host/omap_hsmmc.c @@ -0,0 +1,1242 @@ +/* + * drivers/mmc/host/omap_hsmmc.c + * + * Driver for OMAP2430/3430 MMC controller. + * + * Copyright (C) 2007 Texas Instruments. + * + * Authors: + * Syed Mohammed Khasim + * Madhusudhan + * Mohit Jalori + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* OMAP HSMMC Host Controller Registers */ +#define OMAP_HSMMC_SYSCONFIG 0x0010 +#define OMAP_HSMMC_CON 0x002C +#define OMAP_HSMMC_BLK 0x0104 +#define OMAP_HSMMC_ARG 0x0108 +#define OMAP_HSMMC_CMD 0x010C +#define OMAP_HSMMC_RSP10 0x0110 +#define OMAP_HSMMC_RSP32 0x0114 +#define OMAP_HSMMC_RSP54 0x0118 +#define OMAP_HSMMC_RSP76 0x011C +#define OMAP_HSMMC_DATA 0x0120 +#define OMAP_HSMMC_HCTL 0x0128 +#define OMAP_HSMMC_SYSCTL 0x012C +#define OMAP_HSMMC_STAT 0x0130 +#define OMAP_HSMMC_IE 0x0134 +#define OMAP_HSMMC_ISE 0x0138 +#define OMAP_HSMMC_CAPA 0x0140 + +#define VS18 (1 << 26) +#define VS30 (1 << 25) +#define SDVS18 (0x5 << 9) +#define SDVS30 (0x6 << 9) +#define SDVSCLR 0xFFFFF1FF +#define SDVSDET 0x00000400 +#define AUTOIDLE 0x1 +#define SDBP (1 << 8) +#define DTO 0xe +#define ICE 0x1 +#define ICS 0x2 +#define CEN (1 << 2) +#define CLKD_MASK 0x0000FFC0 +#define CLKD_SHIFT 6 +#define DTO_MASK 0x000F0000 +#define DTO_SHIFT 16 +#define INT_EN_MASK 0x307F0033 +#define INIT_STREAM (1 << 1) +#define DP_SELECT (1 << 21) +#define DDIR (1 << 4) +#define DMA_EN 0x1 +#define MSBS (1 << 5) +#define BCE (1 << 1) +#define FOUR_BIT (1 << 1) +#define CC 0x1 +#define TC 0x02 +#define OD 0x1 +#define ERR (1 << 15) +#define CMD_TIMEOUT (1 << 16) +#define DATA_TIMEOUT (1 << 20) +#define CMD_CRC (1 << 17) +#define DATA_CRC (1 << 21) +#define CARD_ERR (1 << 28) +#define STAT_CLEAR 0xFFFFFFFF +#define INIT_STREAM_CMD 0x00000000 +#define DUAL_VOLT_OCR_BIT 7 +#define SRC (1 << 25) +#define SRD (1 << 26) + +/* + * FIXME: Most likely all the data using these _DEVID defines should come + * from the platform_data, or implemented in controller and slot specific + * functions. + */ +#define OMAP_MMC1_DEVID 0 +#define OMAP_MMC2_DEVID 1 + +#define OMAP_MMC_DATADIR_NONE 0 +#define OMAP_MMC_DATADIR_READ 1 +#define OMAP_MMC_DATADIR_WRITE 2 +#define MMC_TIMEOUT_MS 20 +#define OMAP_MMC_MASTER_CLOCK 96000000 +#define DRIVER_NAME "mmci-omap-hs" + +/* + * One controller can have multiple slots, like on some omap boards using + * omap.c controller driver. Luckily this is not currently done on any known + * omap_hsmmc.c device. + */ +#define mmc_slot(host) (host->pdata->slots[host->slot_id]) + +/* + * MMC Host controller read/write API's + */ +#define OMAP_HSMMC_READ(base, reg) \ + __raw_readl((base) + OMAP_HSMMC_##reg) + +#define OMAP_HSMMC_WRITE(base, reg, val) \ + __raw_writel((val), (base) + OMAP_HSMMC_##reg) + +struct mmc_omap_host { + struct device *dev; + struct mmc_host *mmc; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + struct clk *fclk; + struct clk *iclk; + struct clk *dbclk; + struct semaphore sem; + struct work_struct mmc_carddetect_work; + void __iomem *base; + resource_size_t mapbase; + unsigned int id; + unsigned int dma_len; + unsigned int dma_dir; + unsigned char bus_mode; + unsigned char datadir; + u32 *buffer; + u32 bytesleft; + int suspended; + int irq; + int carddetect; + int use_dma, dma_ch; + int initstr; + int slot_id; + int dbclk_enabled; + struct omap_mmc_platform_data *pdata; +}; + +/* + * Stop clock to the card + */ +static void omap_mmc_stop_clock(struct mmc_omap_host *host) +{ + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN); + if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0) + dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n"); +} + +/* + * Send init stream sequence to card + * before sending IDLE command + */ +static void send_init_stream(struct mmc_omap_host *host) +{ + int reg = 0; + unsigned long timeout; + + disable_irq(host->irq); + OMAP_HSMMC_WRITE(host->base, CON, + OMAP_HSMMC_READ(host->base, CON) | INIT_STREAM); + OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD); + + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); + while ((reg != CC) && time_before(jiffies, timeout)) + reg = OMAP_HSMMC_READ(host->base, STAT) & CC; + + OMAP_HSMMC_WRITE(host->base, CON, + OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM); + enable_irq(host->irq); +} + +static inline +int mmc_omap_cover_is_closed(struct mmc_omap_host *host) +{ + int r = 1; + + if (host->pdata->slots[host->slot_id].get_cover_state) + r = host->pdata->slots[host->slot_id].get_cover_state(host->dev, + host->slot_id); + return r; +} + +static ssize_t +mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + struct mmc_omap_host *host = mmc_priv(mmc); + + return sprintf(buf, "%s\n", mmc_omap_cover_is_closed(host) ? "closed" : + "open"); +} + +static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); + +static ssize_t +mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); + struct mmc_omap_host *host = mmc_priv(mmc); + struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id]; + + return sprintf(buf, "slot:%s\n", slot.name); +} + +static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); + +/* + * Configure the response type and send the cmd. + */ +static void +mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd, + struct mmc_data *data) +{ + int cmdreg = 0, resptype = 0, cmdtype = 0; + + dev_dbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n", + mmc_hostname(host->mmc), cmd->opcode, cmd->arg); + host->cmd = cmd; + + /* + * Clear status bits and enable interrupts + */ + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); + OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) + resptype = 1; + else + resptype = 2; + } + + /* + * Unlike OMAP1 controller, the cmdtype does not seem to be based on + * ac, bc, adtc, bcr. Only commands ending an open ended transfer need + * a val of 0x3, rest 0x0. + */ + if (cmd == host->mrq->stop) + cmdtype = 0x3; + + cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22); + + if (data) { + cmdreg |= DP_SELECT | MSBS | BCE; + if (data->flags & MMC_DATA_READ) + cmdreg |= DDIR; + else + cmdreg &= ~(DDIR); + } + + if (host->use_dma) + cmdreg |= DMA_EN; + + OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg); + OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); +} + +/* + * Notify the transfer complete to MMC core + */ +static void +mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) +{ + host->data = NULL; + + if (host->use_dma && host->dma_ch != -1) + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, + host->dma_dir); + + host->datadir = OMAP_MMC_DATADIR_NONE; + + if (!data->error) + data->bytes_xfered += data->blocks * (data->blksz); + else + data->bytes_xfered = 0; + + if (!data->stop) { + host->mrq = NULL; + mmc_request_done(host->mmc, data->mrq); + return; + } + mmc_omap_start_command(host, data->stop, NULL); +} + +/* + * Notify the core about command completion + */ +static void +mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) +{ + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + /* response type 2 */ + cmd->resp[3] = OMAP_HSMMC_READ(host->base, RSP10); + cmd->resp[2] = OMAP_HSMMC_READ(host->base, RSP32); + cmd->resp[1] = OMAP_HSMMC_READ(host->base, RSP54); + cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP76); + } else { + /* response types 1, 1b, 3, 4, 5, 6 */ + cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10); + } + } + if (host->data == NULL || cmd->error) { + host->mrq = NULL; + mmc_request_done(host->mmc, cmd->mrq); + } +} + +/* + * DMA clean up for command errors + */ +static void mmc_dma_cleanup(struct mmc_omap_host *host) +{ + host->data->error = -ETIMEDOUT; + + if (host->use_dma && host->dma_ch != -1) { + dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len, + host->dma_dir); + omap_free_dma(host->dma_ch); + host->dma_ch = -1; + up(&host->sem); + } + host->data = NULL; + host->datadir = OMAP_MMC_DATADIR_NONE; +} + +/* + * Readable error output + */ +#ifdef CONFIG_MMC_DEBUG +static void mmc_omap_report_irq(struct mmc_omap_host *host, u32 status) +{ + /* --- means reserved bit without definition at documentation */ + static const char *mmc_omap_status_bits[] = { + "CC", "TC", "BGE", "---", "BWR", "BRR", "---", "---", "CIRQ", + "OBI", "---", "---", "---", "---", "---", "ERRI", "CTO", "CCRC", + "CEB", "CIE", "DTO", "DCRC", "DEB", "---", "ACE", "---", + "---", "---", "---", "CERR", "CERR", "BADA", "---", "---", "---" + }; + char res[256]; + char *buf = res; + int len, i; + + len = sprintf(buf, "MMC IRQ 0x%x :", status); + buf += len; + + for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) + if (status & (1 << i)) { + len = sprintf(buf, " %s", mmc_omap_status_bits[i]); + buf += len; + } + + dev_dbg(mmc_dev(host->mmc), "%s\n", res); +} +#endif /* CONFIG_MMC_DEBUG */ + + +/* + * MMC controller IRQ handler + */ +static irqreturn_t mmc_omap_irq(int irq, void *dev_id) +{ + struct mmc_omap_host *host = dev_id; + struct mmc_data *data; + int end_cmd = 0, end_trans = 0, status; + + if (host->cmd == NULL && host->data == NULL) { + OMAP_HSMMC_WRITE(host->base, STAT, + OMAP_HSMMC_READ(host->base, STAT)); + return IRQ_HANDLED; + } + + data = host->data; + status = OMAP_HSMMC_READ(host->base, STAT); + dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); + + if (status & ERR) { +#ifdef CONFIG_MMC_DEBUG + mmc_omap_report_irq(host, status); +#endif + if ((status & CMD_TIMEOUT) || + (status & CMD_CRC)) { + if (host->cmd) { + if (status & CMD_TIMEOUT) { + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, + SYSCTL) | SRC); + while (OMAP_HSMMC_READ(host->base, + SYSCTL) & SRC) + ; + + host->cmd->error = -ETIMEDOUT; + } else { + host->cmd->error = -EILSEQ; + } + end_cmd = 1; + } + if (host->data) + mmc_dma_cleanup(host); + } + if ((status & DATA_TIMEOUT) || + (status & DATA_CRC)) { + if (host->data) { + if (status & DATA_TIMEOUT) + mmc_dma_cleanup(host); + else + host->data->error = -EILSEQ; + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, + SYSCTL) | SRD); + while (OMAP_HSMMC_READ(host->base, + SYSCTL) & SRD) + ; + end_trans = 1; + } + } + if (status & CARD_ERR) { + dev_dbg(mmc_dev(host->mmc), + "Ignoring card err CMD%d\n", host->cmd->opcode); + if (host->cmd) + end_cmd = 1; + if (host->data) + end_trans = 1; + } + } + + OMAP_HSMMC_WRITE(host->base, STAT, status); + + if (end_cmd || (status & CC)) + mmc_omap_cmd_done(host, host->cmd); + if (end_trans || (status & TC)) + mmc_omap_xfer_done(host, data); + + return IRQ_HANDLED; +} + +/* + * Switch MMC operating voltage + */ +static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) +{ + u32 reg_val = 0; + int ret; + + /* Disable the clocks */ + clk_disable(host->fclk); + clk_disable(host->iclk); + clk_disable(host->dbclk); + + /* Turn the power off */ + ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); + if (ret != 0) + goto err; + + /* Turn the power ON with given VDD 1.8 or 3.0v */ + ret = mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd); + if (ret != 0) + goto err; + + clk_enable(host->fclk); + clk_enable(host->iclk); + clk_enable(host->dbclk); + + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) & SDVSCLR); + reg_val = OMAP_HSMMC_READ(host->base, HCTL); + /* + * If a MMC dual voltage card is detected, the set_ios fn calls + * this fn with VDD bit set for 1.8V. Upon card removal from the + * slot, omap_mmc_set_ios sets the VDD back to 3V on MMC_POWER_OFF. + * + * Only MMC1 supports 3.0V. MMC2 will not function if SDVS30 is + * set in HCTL. + */ + if (host->id == OMAP_MMC1_DEVID && (((1 << vdd) == MMC_VDD_32_33) || + ((1 << vdd) == MMC_VDD_33_34))) + reg_val |= SDVS30; + if ((1 << vdd) == MMC_VDD_165_195) + reg_val |= SDVS18; + + OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); + + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | SDBP); + + return 0; +err: + dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n"); + return ret; +} + +/* + * Work Item to notify the core about card insertion/removal + */ +static void mmc_omap_detect(struct work_struct *work) +{ + struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, + mmc_carddetect_work); + + sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); + if (host->carddetect) { + mmc_detect_change(host->mmc, (HZ * 200) / 1000); + } else { + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) | SRD); + while (OMAP_HSMMC_READ(host->base, SYSCTL) & SRD) + ; + + mmc_detect_change(host->mmc, (HZ * 50) / 1000); + } +} + +/* + * ISR for handling card insertion and removal + */ +static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *)dev_id; + + host->carddetect = mmc_slot(host).card_detect(irq); + schedule_work(&host->mmc_carddetect_work); + + return IRQ_HANDLED; +} + +/* + * DMA call back function + */ +static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) +{ + struct mmc_omap_host *host = data; + + if (ch_status & OMAP2_DMA_MISALIGNED_ERR_IRQ) + dev_dbg(mmc_dev(host->mmc), "MISALIGNED_ADRS_ERR\n"); + + if (host->dma_ch < 0) + return; + + omap_free_dma(host->dma_ch); + host->dma_ch = -1; + /* + * DMA Callback: run in interrupt context. + * mutex_unlock will through a kernel warning if used. + */ + up(&host->sem); +} + +/* + * Configure dma src and destination parameters + */ +static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host, + struct mmc_data *data) +{ + if (sync_dir == 0) { + omap_set_dma_dest_params(host->dma_ch, 0, + OMAP_DMA_AMODE_CONSTANT, + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + omap_set_dma_src_params(host->dma_ch, 0, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(&data->sg[0]), 0, 0); + } else { + omap_set_dma_src_params(host->dma_ch, 0, + OMAP_DMA_AMODE_CONSTANT, + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + omap_set_dma_dest_params(host->dma_ch, 0, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(&data->sg[0]), 0, 0); + } + return 0; +} +/* + * Routine to configure and start DMA for the MMC card + */ +static int +mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) +{ + int sync_dev, sync_dir = 0; + int dma_ch = 0, ret = 0, err = 1; + struct mmc_data *data = req->data; + + /* + * If for some reason the DMA transfer is still active, + * we wait for timeout period and free the dma + */ + if (host->dma_ch != -1) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(100); + if (down_trylock(&host->sem)) { + omap_free_dma(host->dma_ch); + host->dma_ch = -1; + up(&host->sem); + return err; + } + } else { + if (down_trylock(&host->sem)) + return err; + } + + if (!(data->flags & MMC_DATA_WRITE)) { + host->dma_dir = DMA_FROM_DEVICE; + if (host->id == OMAP_MMC1_DEVID) + sync_dev = OMAP24XX_DMA_MMC1_RX; + else + sync_dev = OMAP24XX_DMA_MMC2_RX; + } else { + host->dma_dir = DMA_TO_DEVICE; + if (host->id == OMAP_MMC1_DEVID) + sync_dev = OMAP24XX_DMA_MMC1_TX; + else + sync_dev = OMAP24XX_DMA_MMC2_TX; + } + + ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb, + host, &dma_ch); + if (ret != 0) { + dev_dbg(mmc_dev(host->mmc), + "%s: omap_request_dma() failed with %d\n", + mmc_hostname(host->mmc), ret); + return ret; + } + + host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma_dir); + host->dma_ch = dma_ch; + + if (!(data->flags & MMC_DATA_WRITE)) + mmc_omap_config_dma_param(1, host, data); + else + mmc_omap_config_dma_param(0, host, data); + + if ((data->blksz % 4) == 0) + omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, + (data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME, + sync_dev, sync_dir); + else + /* REVISIT: The MMC buffer increments only when MSB is written. + * Return error for blksz which is non multiple of four. + */ + return -EINVAL; + + omap_start_dma(dma_ch); + return 0; +} + +static void set_data_timeout(struct mmc_omap_host *host, + struct mmc_request *req) +{ + unsigned int timeout, cycle_ns; + uint32_t reg, clkd, dto = 0; + + reg = OMAP_HSMMC_READ(host->base, SYSCTL); + clkd = (reg & CLKD_MASK) >> CLKD_SHIFT; + if (clkd == 0) + clkd = 1; + + cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd); + timeout = req->data->timeout_ns / cycle_ns; + timeout += req->data->timeout_clks; + if (timeout) { + while ((timeout & 0x80000000) == 0) { + dto += 1; + timeout <<= 1; + } + dto = 31 - dto; + timeout <<= 1; + if (timeout && dto) + dto += 1; + if (dto >= 13) + dto -= 13; + else + dto = 0; + if (dto > 14) + dto = 14; + } + + reg &= ~DTO_MASK; + reg |= dto << DTO_SHIFT; + OMAP_HSMMC_WRITE(host->base, SYSCTL, reg); +} + +/* + * Configure block length for MMC/SD cards and initiate the transfer. + */ +static int +mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) +{ + int ret; + host->data = req->data; + + if (req->data == NULL) { + host->datadir = OMAP_MMC_DATADIR_NONE; + OMAP_HSMMC_WRITE(host->base, BLK, 0); + return 0; + } + + OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz) + | (req->data->blocks << 16)); + set_data_timeout(host, req); + + host->datadir = (req->data->flags & MMC_DATA_WRITE) ? + OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ; + + if (host->use_dma) { + ret = mmc_omap_start_dma_transfer(host, req); + if (ret != 0) { + dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n"); + return ret; + } + } + return 0; +} + +/* + * Request function. for read/write operation + */ +static void omap_mmc_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + + WARN_ON(host->mrq != NULL); + host->mrq = req; + mmc_omap_prepare_data(host, req); + mmc_omap_start_command(host, req->cmd, req->data); +} + + +/* Routine to configure clock values. Exposed API to core */ +static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + u16 dsor = 0; + unsigned long regval; + unsigned long timeout; + + switch (ios->power_mode) { + case MMC_POWER_OFF: + mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); + /* + * Reset bus voltage to 3V if it got set to 1.8V earlier. + * REVISIT: If we are able to detect cards after unplugging + * a 1.8V card, this code should not be needed. + */ + if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { + int vdd = fls(host->mmc->ocr_avail) - 1; + if (omap_mmc_switch_opcond(host, vdd) != 0) + host->mmc->ios.vdd = vdd; + } + break; + case MMC_POWER_UP: + mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd); + break; + } + + switch (mmc->ios.bus_width) { + case MMC_BUS_WIDTH_4: + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT); + break; + case MMC_BUS_WIDTH_1: + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); + break; + } + + if (host->id == OMAP_MMC1_DEVID) { + /* Only MMC1 can operate at 3V/1.8V */ + if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) && + (ios->vdd == DUAL_VOLT_OCR_BIT)) { + /* + * The mmc_select_voltage fn of the core does + * not seem to set the power_mode to + * MMC_POWER_UP upon recalculating the voltage. + * vdd 1.8v. + */ + if (omap_mmc_switch_opcond(host, ios->vdd) != 0) + dev_dbg(mmc_dev(host->mmc), + "Switch operation failed\n"); + } + } + + if (ios->clock) { + dsor = OMAP_MMC_MASTER_CLOCK / ios->clock; + if (dsor < 1) + dsor = 1; + + if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock) + dsor++; + + if (dsor > 250) + dsor = 250; + } + omap_mmc_stop_clock(host); + regval = OMAP_HSMMC_READ(host->base, SYSCTL); + regval = regval & ~(CLKD_MASK); + regval = regval | (dsor << 6) | (DTO << 16); + OMAP_HSMMC_WRITE(host->base, SYSCTL, regval); + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); + + /* Wait till the ICS bit is set */ + timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); + while ((OMAP_HSMMC_READ(host->base, SYSCTL) & ICS) != 0x2 + && time_before(jiffies, timeout)) + msleep(1); + + OMAP_HSMMC_WRITE(host->base, SYSCTL, + OMAP_HSMMC_READ(host->base, SYSCTL) | CEN); + + if (ios->power_mode == MMC_POWER_ON) + send_init_stream(host); + + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + OMAP_HSMMC_WRITE(host->base, CON, + OMAP_HSMMC_READ(host->base, CON) | OD); +} + +static int omap_hsmmc_get_cd(struct mmc_host *mmc) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + struct omap_mmc_platform_data *pdata = host->pdata; + + if (!pdata->slots[0].card_detect) + return -ENOSYS; + return pdata->slots[0].card_detect(pdata->slots[0].card_detect_irq); +} + +static int omap_hsmmc_get_ro(struct mmc_host *mmc) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + struct omap_mmc_platform_data *pdata = host->pdata; + + if (!pdata->slots[0].get_ro) + return -ENOSYS; + return pdata->slots[0].get_ro(host->dev, 0); +} + +static struct mmc_host_ops mmc_omap_ops = { + .request = omap_mmc_request, + .set_ios = omap_mmc_set_ios, + .get_cd = omap_hsmmc_get_cd, + .get_ro = omap_hsmmc_get_ro, + /* NYET -- enable_sdio_irq */ +}; + +static int __init omap_mmc_probe(struct platform_device *pdev) +{ + struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; + struct mmc_host *mmc; + struct mmc_omap_host *host = NULL; + struct resource *res; + int ret = 0, irq; + u32 hctl, capa; + + if (pdata == NULL) { + dev_err(&pdev->dev, "Platform Data is missing\n"); + return -ENXIO; + } + + if (pdata->nr_slots == 0) { + dev_err(&pdev->dev, "No Slots\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (res == NULL || irq < 0) + return -ENXIO; + + res = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (res == NULL) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto err; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdata = pdata; + host->dev = &pdev->dev; + host->use_dma = 1; + host->dev->dma_mask = &pdata->dma_mask; + host->dma_ch = -1; + host->irq = irq; + host->id = pdev->id; + host->slot_id = 0; + host->mapbase = res->start; + host->base = ioremap(host->mapbase, SZ_4K); + + platform_set_drvdata(pdev, host); + INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect); + + mmc->ops = &mmc_omap_ops; + mmc->f_min = 400000; + mmc->f_max = 52000000; + + sema_init(&host->sem, 1); + + host->iclk = clk_get(&pdev->dev, "mmchs_ick"); + if (IS_ERR(host->iclk)) { + ret = PTR_ERR(host->iclk); + host->iclk = NULL; + goto err1; + } + host->fclk = clk_get(&pdev->dev, "mmchs_fck"); + if (IS_ERR(host->fclk)) { + ret = PTR_ERR(host->fclk); + host->fclk = NULL; + clk_put(host->iclk); + goto err1; + } + + if (clk_enable(host->fclk) != 0) { + clk_put(host->iclk); + clk_put(host->fclk); + goto err1; + } + + if (clk_enable(host->iclk) != 0) { + clk_disable(host->fclk); + clk_put(host->iclk); + clk_put(host->fclk); + goto err1; + } + + host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); + /* + * MMC can still work without debounce clock. + */ + if (IS_ERR(host->dbclk)) + dev_warn(mmc_dev(host->mmc), "Failed to get debounce clock\n"); + else + if (clk_enable(host->dbclk) != 0) + dev_dbg(mmc_dev(host->mmc), "Enabling debounce" + " clk failed\n"); + else + host->dbclk_enabled = 1; + +#ifdef CONFIG_MMC_BLOCK_BOUNCE + mmc->max_phys_segs = 1; + mmc->max_hw_segs = 1; +#endif + mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ + mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; + + mmc->ocr_avail = mmc_slot(host).ocr_mask; + mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; + + if (pdata->slots[host->slot_id].wires >= 4) + mmc->caps |= MMC_CAP_4_BIT_DATA; + + /* Only MMC1 supports 3.0V */ + if (host->id == OMAP_MMC1_DEVID) { + hctl = SDVS30; + capa = VS30 | VS18; + } else { + hctl = SDVS18; + capa = VS18; + } + + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | hctl); + + OMAP_HSMMC_WRITE(host->base, CAPA, + OMAP_HSMMC_READ(host->base, CAPA) | capa); + + /* Set the controller to AUTO IDLE mode */ + OMAP_HSMMC_WRITE(host->base, SYSCONFIG, + OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); + + /* Set SD bus power bit */ + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | SDBP); + + /* Request IRQ for MMC operations */ + ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED, + mmc_hostname(mmc), host); + if (ret) { + dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); + goto err_irq; + } + + if (pdata->init != NULL) { + if (pdata->init(&pdev->dev) != 0) { + dev_dbg(mmc_dev(host->mmc), + "Unable to configure MMC IRQs\n"); + goto err_irq_cd_init; + } + } + + /* Request IRQ for card detect */ + if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) { + ret = request_irq(mmc_slot(host).card_detect_irq, + omap_mmc_cd_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING + | IRQF_DISABLED, + mmc_hostname(mmc), host); + if (ret) { + dev_dbg(mmc_dev(host->mmc), + "Unable to grab MMC CD IRQ\n"); + goto err_irq_cd; + } + } + + OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); + OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); + + mmc_add_host(mmc); + + if (host->pdata->slots[host->slot_id].name != NULL) { + ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name); + if (ret < 0) + goto err_slot_name; + } + if (mmc_slot(host).card_detect_irq && mmc_slot(host).card_detect && + host->pdata->slots[host->slot_id].get_cover_state) { + ret = device_create_file(&mmc->class_dev, + &dev_attr_cover_switch); + if (ret < 0) + goto err_cover_switch; + } + + return 0; + +err_cover_switch: + device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); +err_slot_name: + mmc_remove_host(mmc); +err_irq_cd: + free_irq(mmc_slot(host).card_detect_irq, host); +err_irq_cd_init: + free_irq(host->irq, host); +err_irq: + clk_disable(host->fclk); + clk_disable(host->iclk); + clk_put(host->fclk); + clk_put(host->iclk); + if (host->dbclk_enabled) { + clk_disable(host->dbclk); + clk_put(host->dbclk); + } + +err1: + iounmap(host->base); +err: + dev_dbg(mmc_dev(host->mmc), "Probe Failed\n"); + release_mem_region(res->start, res->end - res->start + 1); + if (host) + mmc_free_host(mmc); + return ret; +} + +static int omap_mmc_remove(struct platform_device *pdev) +{ + struct mmc_omap_host *host = platform_get_drvdata(pdev); + struct resource *res; + + if (host) { + mmc_remove_host(host->mmc); + if (host->pdata->cleanup) + host->pdata->cleanup(&pdev->dev); + free_irq(host->irq, host); + if (mmc_slot(host).card_detect_irq) + free_irq(mmc_slot(host).card_detect_irq, host); + flush_scheduled_work(); + + clk_disable(host->fclk); + clk_disable(host->iclk); + clk_put(host->fclk); + clk_put(host->iclk); + if (host->dbclk_enabled) { + clk_disable(host->dbclk); + clk_put(host->dbclk); + } + + mmc_free_host(host->mmc); + iounmap(host->base); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, res->end - res->start + 1); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) +{ + int ret = 0; + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + if (host && host->suspended) + return 0; + + if (host) { + ret = mmc_suspend_host(host->mmc, state); + if (ret == 0) { + host->suspended = 1; + + OMAP_HSMMC_WRITE(host->base, ISE, 0); + OMAP_HSMMC_WRITE(host->base, IE, 0); + + if (host->pdata->suspend) { + ret = host->pdata->suspend(&pdev->dev, + host->slot_id); + if (ret) + dev_dbg(mmc_dev(host->mmc), + "Unable to handle MMC board" + " level suspend\n"); + } + + if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) + & SDVSCLR); + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) + | SDVS30); + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) + | SDBP); + } + + clk_disable(host->fclk); + clk_disable(host->iclk); + clk_disable(host->dbclk); + } + + } + return ret; +} + +/* Routine to resume the MMC device */ +static int omap_mmc_resume(struct platform_device *pdev) +{ + int ret = 0; + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + if (host && !host->suspended) + return 0; + + if (host) { + + ret = clk_enable(host->fclk); + if (ret) + goto clk_en_err; + + ret = clk_enable(host->iclk); + if (ret) { + clk_disable(host->fclk); + clk_put(host->fclk); + goto clk_en_err; + } + + if (clk_enable(host->dbclk) != 0) + dev_dbg(mmc_dev(host->mmc), + "Enabling debounce clk failed\n"); + + if (host->pdata->resume) { + ret = host->pdata->resume(&pdev->dev, host->slot_id); + if (ret) + dev_dbg(mmc_dev(host->mmc), + "Unmask interrupt failed\n"); + } + + /* Notify the core to resume the host */ + ret = mmc_resume_host(host->mmc); + if (ret == 0) + host->suspended = 0; + } + + return ret; + +clk_en_err: + dev_dbg(mmc_dev(host->mmc), + "Failed to enable MMC clocks during resume\n"); + return ret; +} + +#else +#define omap_mmc_suspend NULL +#define omap_mmc_resume NULL +#endif + +static struct platform_driver omap_mmc_driver = { + .probe = omap_mmc_probe, + .remove = omap_mmc_remove, + .suspend = omap_mmc_suspend, + .resume = omap_mmc_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init omap_mmc_init(void) +{ + /* Register the MMC driver */ + return platform_driver_register(&omap_mmc_driver); +} + +static void __exit omap_mmc_cleanup(void) +{ + /* Unregister MMC driver */ + platform_driver_unregister(&omap_mmc_driver); +} + +module_init(omap_mmc_init); +module_exit(omap_mmc_cleanup); + +MODULE_DESCRIPTION("OMAP High Speed Multimedia Card driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Texas Instruments Inc"); -- cgit v1.2.3 From b7cfc9ca6a511acec529cad322eec2a6379f35f7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 24 Jan 2009 16:48:42 +0000 Subject: [ARM] omap: watchdog: allow OMAP watchdog driver on OMAP34xx platforms The driver was updated for OMAP34xx, but the Kconfig file was missed. So this adds the missing parts from d99241c in Tony Lindgren's tree: Add watchdog timer support for TI OMAP3430. Signed-off-by: Madhusudhan Chikkature Signed-off-by: Tony Lindgren Signed-off-by: Russell King --- drivers/watchdog/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 3efa12f9ee5..09a3d5522b4 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -187,10 +187,10 @@ config EP93XX_WATCHDOG config OMAP_WATCHDOG tristate "OMAP Watchdog" - depends on ARCH_OMAP16XX || ARCH_OMAP24XX + depends on ARCH_OMAP16XX || ARCH_OMAP24XX || ARCH_OMAP34XX help - Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to - enable the OMAP1610/OMAP1710 watchdog timer. + Support for TI OMAP1610/OMAP1710/OMAP2420/OMAP3430 watchdog. Say 'Y' + here to enable the OMAP1610/OMAP1710/OMAP2420/OMAP3430 watchdog timer. config PNX4008_WATCHDOG tristate "PNX4008 Watchdog" -- cgit v1.2.3 From e747a5c0be3efe5465e45c8e326bc766b1288be6 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 24 Jan 2009 20:35:38 +0100 Subject: firewire: core: optimize card shutdown This fixes a regression by "firewire: keep highlevel drivers attached during brief connection loss": There were 2 seconds unnecessary waiting added to the shutdown procedure of each controller. We use card->link as status flag to signal the device handler that there is no use to wait for a come-back. Signed-off-by: Stefan Richter --- drivers/firewire/fw-card.c | 2 +- drivers/firewire/fw-device.c | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index 17a80cecdc1..7be2cf3514e 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c @@ -512,7 +512,7 @@ fw_core_remove_card(struct fw_card *card) fw_core_initiate_bus_reset(card, 1); mutex_lock(&card_mutex); - list_del(&card->link); + list_del_init(&card->link); mutex_unlock(&card_mutex); /* Set up the dummy driver. */ diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 0925d91b261..bf53acb4565 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c @@ -657,7 +657,8 @@ static void fw_device_shutdown(struct work_struct *work) container_of(work, struct fw_device, work.work); int minor = MINOR(device->device.devt); - if (time_is_after_jiffies(device->card->reset_jiffies + SHUTDOWN_DELAY)) { + if (time_is_after_jiffies(device->card->reset_jiffies + SHUTDOWN_DELAY) + && !list_empty(&device->card->link)) { schedule_delayed_work(&device->work, SHUTDOWN_DELAY); return; } @@ -1074,7 +1075,8 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) if (atomic_xchg(&device->state, FW_DEVICE_GONE) == FW_DEVICE_RUNNING) { PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown); - schedule_delayed_work(&device->work, SHUTDOWN_DELAY); + schedule_delayed_work(&device->work, + list_empty(&card->link) ? 0 : SHUTDOWN_DELAY); } break; } -- cgit v1.2.3 From fb22d72782b023cda5e9876d3381f30932a64f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 22 Jan 2009 23:29:42 +0100 Subject: [NET] am79c961a: fix spin_lock usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spin_lock functions take a pointer to the lock, not the lock itself. This error was noticed by compiling ebsa110_defconfig for linux-rt where the locking functions obviously are more picky about their arguments. Signed-off-by: Uwe Kleine-König Cc: Roel Kluin <12o3l@tiscali.nl> Cc: Steven Rostedt Cc: netdev@vger.kernel.org Signed-off-by: Russell King --- drivers/net/arm/am79c961a.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c index 0c628a9e533..c2d012fcc29 100644 --- a/drivers/net/arm/am79c961a.c +++ b/drivers/net/arm/am79c961a.c @@ -208,9 +208,9 @@ am79c961_init_for_open(struct net_device *dev) /* * Stop the chip. */ - spin_lock_irqsave(priv->chip_lock, flags); + spin_lock_irqsave(&priv->chip_lock, flags); write_rreg (dev->base_addr, CSR0, CSR0_BABL|CSR0_CERR|CSR0_MISS|CSR0_MERR|CSR0_TINT|CSR0_RINT|CSR0_STOP); - spin_unlock_irqrestore(priv->chip_lock, flags); + spin_unlock_irqrestore(&priv->chip_lock, flags); write_ireg (dev->base_addr, 5, 0x00a0); /* Receive address LED */ write_ireg (dev->base_addr, 6, 0x0081); /* Collision LED */ @@ -332,10 +332,10 @@ am79c961_close(struct net_device *dev) netif_stop_queue(dev); netif_carrier_off(dev); - spin_lock_irqsave(priv->chip_lock, flags); + spin_lock_irqsave(&priv->chip_lock, flags); write_rreg (dev->base_addr, CSR0, CSR0_STOP); write_rreg (dev->base_addr, CSR3, CSR3_MASKALL); - spin_unlock_irqrestore(priv->chip_lock, flags); + spin_unlock_irqrestore(&priv->chip_lock, flags); free_irq (dev->irq, dev); @@ -391,7 +391,7 @@ static void am79c961_setmulticastlist (struct net_device *dev) am79c961_mc_hash(dmi, multi_hash); } - spin_lock_irqsave(priv->chip_lock, flags); + spin_lock_irqsave(&priv->chip_lock, flags); stopped = read_rreg(dev->base_addr, CSR0) & CSR0_STOP; @@ -405,9 +405,9 @@ static void am79c961_setmulticastlist (struct net_device *dev) * Spin waiting for chip to report suspend mode */ while ((read_rreg(dev->base_addr, CTRL1) & CTRL1_SPND) == 0) { - spin_unlock_irqrestore(priv->chip_lock, flags); + spin_unlock_irqrestore(&priv->chip_lock, flags); nop(); - spin_lock_irqsave(priv->chip_lock, flags); + spin_lock_irqsave(&priv->chip_lock, flags); } } @@ -429,7 +429,7 @@ static void am79c961_setmulticastlist (struct net_device *dev) write_rreg(dev->base_addr, CTRL1, 0); } - spin_unlock_irqrestore(priv->chip_lock, flags); + spin_unlock_irqrestore(&priv->chip_lock, flags); } static void am79c961_timeout(struct net_device *dev) @@ -467,10 +467,10 @@ am79c961_sendpacket(struct sk_buff *skb, struct net_device *dev) am_writeword (dev, hdraddr + 2, TMD_OWN|TMD_STP|TMD_ENP); priv->txhead = head; - spin_lock_irqsave(priv->chip_lock, flags); + spin_lock_irqsave(&priv->chip_lock, flags); write_rreg (dev->base_addr, CSR0, CSR0_TDMD|CSR0_IENA); dev->trans_start = jiffies; - spin_unlock_irqrestore(priv->chip_lock, flags); + spin_unlock_irqrestore(&priv->chip_lock, flags); /* * If the next packet is owned by the ethernet device, -- cgit v1.2.3 From 2f5899a39dcffb404c9a3d06ad438aff3e03bf04 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 16 Jan 2009 12:36:51 -0600 Subject: [SCSI] libiscsi: fix iscsi pool leak I am not sure what happened. It looks like we have always leaked the q->queue that is allocated from the kfifo_init call. nab finally noticed that we were leaking and this patch fixes it by adding a kfree call to iscsi_pool_free. kfifo_free is not used per kfifo_init's instructions to use kfree. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/libiscsi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 7225b6e2029..257c24115de 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -1981,6 +1981,7 @@ void iscsi_pool_free(struct iscsi_pool *q) kfree(q->pool[i]); if (q->pool) kfree(q->pool); + kfree(q->queue); } EXPORT_SYMBOL_GPL(iscsi_pool_free); -- cgit v1.2.3 From 41bbdbebbbe7e06871d25f51c2eb1d6466bb9e5f Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Fri, 16 Jan 2009 12:36:52 -0600 Subject: [SCSI] qla4xxx: do not reuse session when connecting to different target port qla4xxx does not check the I_T nexus values correctly so it ends up creating one session to the target. If a portal should disappear or they should be reported in different order the driver will think it is already logged in when it could now be speaking to a different target portal or accessing it through a different initiator port (iscsi initiator port is not tied to hardware and is just the initiator name plus isid so you could end up with multiple ports through one host). This patch has the driver check the iscsi scsi port values when matching sessions (we do not check the initiator name because that is static). It results in a portal from each target portal group getting logged into instead of just one per target. In the future the firmware should hopefully send us notification of other sessions that are created to other portals within the same tpgt and the sessions should have different isids. Signed-off-by: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/qla4xxx/ql4_def.h | 1 + drivers/scsi/qla4xxx/ql4_init.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h index d6be0762eb9..b586f27c3bd 100644 --- a/drivers/scsi/qla4xxx/ql4_def.h +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -244,6 +244,7 @@ struct ddb_entry { uint8_t ip_addr[ISCSI_IPADDR_SIZE]; uint8_t iscsi_name[ISCSI_NAME_SIZE]; /* 72 x48 */ uint8_t iscsi_alias[0x20]; + uint8_t isid[6]; }; /* diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 109c5f5985e..af8c3233e8a 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -342,8 +342,12 @@ static struct ddb_entry* qla4xxx_get_ddb_entry(struct scsi_qla_host *ha, DEBUG2(printk("scsi%ld: %s: Looking for ddb[%d]\n", ha->host_no, __func__, fw_ddb_index)); list_for_each_entry(ddb_entry, &ha->ddb_list, list) { - if (memcmp(ddb_entry->iscsi_name, fw_ddb_entry->iscsi_name, - ISCSI_NAME_SIZE) == 0) { + if ((memcmp(ddb_entry->iscsi_name, fw_ddb_entry->iscsi_name, + ISCSI_NAME_SIZE) == 0) && + (ddb_entry->tpgt == + le32_to_cpu(fw_ddb_entry->tgt_portal_grp)) && + (memcmp(ddb_entry->isid, fw_ddb_entry->isid, + sizeof(ddb_entry->isid)) == 0)) { found++; break; } @@ -430,6 +434,8 @@ static int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, ddb_entry->port = le16_to_cpu(fw_ddb_entry->port); ddb_entry->tpgt = le32_to_cpu(fw_ddb_entry->tgt_portal_grp); + memcpy(ddb_entry->isid, fw_ddb_entry->isid, sizeof(ddb_entry->isid)); + memcpy(&ddb_entry->iscsi_name[0], &fw_ddb_entry->iscsi_name[0], min(sizeof(ddb_entry->iscsi_name), sizeof(fw_ddb_entry->iscsi_name))); -- cgit v1.2.3 From 6e9f21f3d3d4933087d1e13b04667b6eb663b487 Mon Sep 17 00:00:00 2001 From: Anirban Chakraborty Date: Thu, 22 Jan 2009 09:45:28 -0800 Subject: [SCSI] qla2xxx: Fix memory leak in error path Reviewed-by: Hisashi Hifumi Signed-off-by: Anirban Chakraborty Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_os.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index cf32653fe01..185ea8bcb9e 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1888,6 +1888,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) "[ERROR] Failed to allocate memory for scsi_host\n"); ret = -ENOMEM; + qla2x00_mem_free(ha); + qla2x00_free_que(ha, req, rsp); goto probe_hw_failed; } @@ -1917,14 +1919,13 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) /* Set up the irqs */ ret = qla2x00_request_irqs(ha, rsp); if (ret) - goto probe_failed; - + goto probe_init_failed; /* Alloc arrays of request and response ring ptrs */ if (!qla2x00_alloc_queues(ha)) { qla_printk(KERN_WARNING, ha, "[ERROR] Failed to allocate memory for queue" " pointers\n"); - goto probe_failed; + goto probe_init_failed; } ha->rsp_q_map[0] = rsp; ha->req_q_map[0] = req; @@ -1997,6 +1998,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) return 0; +probe_init_failed: + qla2x00_free_que(ha, req, rsp); + ha->max_queues = 0; + probe_failed: qla2x00_free_device(base_vha); -- cgit v1.2.3 From 85d0acbb2e64cee4d3253ea9ce4650658e05d945 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 22 Jan 2009 09:45:29 -0800 Subject: [SCSI] qla2xxx: Simplify sector-mask calculation in preparation for larger flash parts. Also removes unneeded 'findex' local variable within routine. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_sup.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 303f8ee11f2..afec7f90f58 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -982,7 +982,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, int ret; uint32_t liter, miter; uint32_t sec_mask, rest_addr; - uint32_t fdata, findex; + uint32_t fdata; dma_addr_t optrom_dma; void *optrom = NULL; uint32_t *s, *d; @@ -1003,17 +1003,15 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, } rest_addr = (ha->fdt_block_size >> 2) - 1; - sec_mask = (ha->optrom_size >> 2) - (ha->fdt_block_size >> 2); + sec_mask = ~rest_addr; qla24xx_unprotect_flash(ha); for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) { - - findex = faddr; - fdata = (findex & sec_mask) << 2; + fdata = (faddr & sec_mask) << 2; /* Are we at the beginning of a sector? */ - if ((findex & rest_addr) == 0) { + if ((faddr & rest_addr) == 0) { /* Do sector unprotect. */ if (ha->fdt_unprotect_sec_cmd) qla24xx_write_flash_dword(ha, -- cgit v1.2.3 From 09ff36d30c27ee23b50ffb419c80a0aaef1db4a0 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 22 Jan 2009 09:45:30 -0800 Subject: [SCSI] qla2xxx: Ensure RISC-interrupt-enabled consistency for IS_NOPOLLING_TYPE() ISPs. Original code should work as well given qla24xx_reset_adapter() is only called in extreme cases where the HBA is taken offline. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_init.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 9ad4d0968e5..da9700fbc47 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -3562,6 +3562,9 @@ qla24xx_reset_adapter(scsi_qla_host_t *vha) WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); RD_REG_DWORD(®->hccr); spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (IS_NOPOLLING_TYPE(ha)) + ha->isp_ops->enable_intrs(ha); } /* On sparc systems, obtain port and node WWN from firmware -- cgit v1.2.3 From 8eca3f39c4b11320787f7b216f63214aee8415a9 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 22 Jan 2009 09:45:31 -0800 Subject: [SCSI] qla2xxx: Always serialize mailbox command execution. Original code would incorrectly bypass serialization if the DPC thread were performing a big-hammer operation (ISP abort). This short circuit, though rare, would subsequently stomp on a secondary thread's mailbox command execution. Found during ISP81XX testing. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_mbx.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index db4df45234a..f94ffbb98e9 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -58,14 +58,11 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) * seconds. This is to serialize actual issuing of mailbox cmds during * non ISP abort time. */ - if (!abort_active) { - if (!wait_for_completion_timeout(&ha->mbx_cmd_comp, - mcp->tov * HZ)) { - /* Timeout occurred. Return error. */ - DEBUG2_3_11(printk("%s(%ld): cmd access timeout. " - "Exiting.\n", __func__, base_vha->host_no)); - return QLA_FUNCTION_TIMEOUT; - } + if (!wait_for_completion_timeout(&ha->mbx_cmd_comp, mcp->tov * HZ)) { + /* Timeout occurred. Return error. */ + DEBUG2_3_11(printk("%s(%ld): cmd access timeout. " + "Exiting.\n", __func__, base_vha->host_no)); + return QLA_FUNCTION_TIMEOUT; } ha->flags.mbox_busy = 1; @@ -265,8 +262,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) } /* Allow next mbx cmd to come in. */ - if (!abort_active) - complete(&ha->mbx_cmd_comp); + complete(&ha->mbx_cmd_comp); if (rval) { DEBUG2_3_11(printk("%s(%ld): **** FAILED. mbx0=%x, mbx1=%x, " -- cgit v1.2.3 From eaac30be268b90e9288b3945fb5cc9ee8c5397c0 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 22 Jan 2009 09:45:32 -0800 Subject: [SCSI] qla2xxx: Modify firmware-load order precedence for ISP81XX parts. Pre-ISP81XX parts (including ISP24xx and ISP25xx) could contain a firmware image within a segment of flash, driver would fallback to loading this firmware if the request-firmware interface failed (userspace .bin file). Moving forward, all ISP81XX parts will ship with a suggested-to-be-used firmware image within flash which all driver should first attempt to load. If the flash firmware load fails, the driver will then fallback to loading firmware via the request-firmware interface (ql8100_fw.bin). Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_gbl.h | 1 + drivers/scsi/qla2xxx/qla_init.c | 50 ++++++++++++++++++++++++++++++++++++----- drivers/scsi/qla2xxx/qla_os.c | 2 +- 3 files changed, 46 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index ba491335375..a336b4bc81a 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -34,6 +34,7 @@ extern void qla24xx_update_fw_options(scsi_qla_host_t *); extern void qla81xx_update_fw_options(scsi_qla_host_t *); extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *); extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *); +extern int qla81xx_load_risc(scsi_qla_host_t *, uint32_t *); extern int qla2x00_loop_resync(scsi_qla_host_t *); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index da9700fbc47..f6368a1d302 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -3850,6 +3850,10 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr) uint32_t i; struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; + + qla_printk(KERN_INFO, ha, + "FW: Loading from flash (%x)...\n", ha->flt_region_fw); + rval = QLA_SUCCESS; segments = FA_RISC_CODE_SEGMENTS; @@ -4025,8 +4029,8 @@ fail_fw_integrity: return QLA_FUNCTION_FAILED; } -int -qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) +static int +qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) { int rval; int segments, fragment; @@ -4046,12 +4050,12 @@ qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) qla_printk(KERN_ERR, ha, "Firmware images can be retrieved " "from: " QLA_FW_URL ".\n"); - /* Try to load RISC code from flash. */ - qla_printk(KERN_ERR, ha, "Attempting to load (potentially " - "outdated) firmware from flash.\n"); - return qla24xx_load_risc_flash(vha, srisc_addr); + return QLA_FUNCTION_FAILED; } + qla_printk(KERN_INFO, ha, + "FW: Loading via request-firmware...\n"); + rval = QLA_SUCCESS; segments = FA_RISC_CODE_SEGMENTS; @@ -4136,6 +4140,40 @@ fail_fw_integrity: return QLA_FUNCTION_FAILED; } +int +qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) +{ + int rval; + + /* + * FW Load priority: + * 1) Firmware via request-firmware interface (.bin file). + * 2) Firmware residing in flash. + */ + rval = qla24xx_load_risc_blob(vha, srisc_addr); + if (rval == QLA_SUCCESS) + return rval; + + return qla24xx_load_risc_flash(vha, srisc_addr); +} + +int +qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) +{ + int rval; + + /* + * FW Load priority: + * 1) Firmware residing in flash. + * 2) Firmware via request-firmware interface (.bin file). + */ + rval = qla24xx_load_risc_flash(vha, srisc_addr); + if (rval == QLA_SUCCESS) + return rval; + + return qla24xx_load_risc_blob(vha, srisc_addr); +} + void qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 185ea8bcb9e..cbf377fddbd 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1480,7 +1480,7 @@ static struct isp_operations qla81xx_isp_ops = { .reset_adapter = qla24xx_reset_adapter, .nvram_config = qla81xx_nvram_config, .update_fw_options = qla81xx_update_fw_options, - .load_risc = qla24xx_load_risc, + .load_risc = qla81xx_load_risc, .pci_info_str = qla24xx_pci_info_str, .fw_version_str = qla24xx_fw_version_str, .intr_handler = qla24xx_intr_handler, -- cgit v1.2.3 From ad038fa8242a1f4547045f9213c3881a34bbcc21 Mon Sep 17 00:00:00 2001 From: Lalit Chandivade Date: Thu, 22 Jan 2009 09:45:33 -0800 Subject: [SCSI] qla2xxx: Correct MSI-X vector allocation for single queue mode. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_isr.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 789fc576f22..e28ad81baf1 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1868,6 +1868,7 @@ qla24xx_disable_msix(struct qla_hw_data *ha) static int qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) { +#define MIN_MSIX_COUNT 2 int i, ret; struct msix_entry *entries; struct qla_msix_entry *qentry; @@ -1883,12 +1884,16 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) ret = pci_enable_msix(ha->pdev, entries, ha->msix_count); if (ret) { + if (ret < MIN_MSIX_COUNT) + goto msix_failed; + qla_printk(KERN_WARNING, ha, "MSI-X: Failed to enable support -- %d/%d\n" " Retry with %d vectors\n", ha->msix_count, ret, ret); ha->msix_count = ret; ret = pci_enable_msix(ha->pdev, entries, ha->msix_count); if (ret) { +msix_failed: qla_printk(KERN_WARNING, ha, "MSI-X: Failed to enable" " support, giving up -- %d/%d\n", ha->msix_count, ret); -- cgit v1.2.3 From 7c283177fad8786afa1bbf7ef848038284084e41 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 22 Jan 2009 09:45:34 -0800 Subject: [SCSI] qla2xxx: Correct endianness issue during flash manipulation. The flash data was incorrectly being converted (cpu_to_le32()) when using the bulk-flash-write mailbox command (ISP25xx and above). Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_sup.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index afec7f90f58..ed9582603af 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -980,12 +980,11 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, uint32_t dwords) { int ret; - uint32_t liter, miter; + uint32_t liter; uint32_t sec_mask, rest_addr; uint32_t fdata; dma_addr_t optrom_dma; void *optrom = NULL; - uint32_t *s, *d; struct qla_hw_data *ha = vha->hw; ret = QLA_SUCCESS; @@ -1031,9 +1030,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, /* Go with burst-write. */ if (optrom && (liter + OPTROM_BURST_DWORDS) <= dwords) { /* Copy data to DMA'ble buffer. */ - for (miter = 0, s = optrom, d = dwptr; - miter < OPTROM_BURST_DWORDS; miter++, s++, d++) - *s = cpu_to_le32(*d); + memcpy(optrom, dwptr, OPTROM_BURST_SIZE); ret = qla2x00_load_ram(vha, optrom_dma, flash_data_addr(ha, faddr), -- cgit v1.2.3 From 2ac4b64f7483f3684a423b21ac4e687827f7eb62 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 22 Jan 2009 09:45:35 -0800 Subject: [SCSI] qla2xxx: Correct regression in EH abort handling. Commit 73208dfd7ab19f379d73e8a0fbf30f92c203e5e8 (qla2xxx: add support for multi-queue adapter) inadvertently backed-out the fix in 5bff55db3dc4d659f46b4d2fce2f61c1964c2762 (qla2xxx: Return a FAILED status when abort mailbox-command fails.). Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_os.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index cbf377fddbd..f344b6816fd 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -800,6 +800,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) if (ha->isp_ops->abort_command(vha, sp, req)) { DEBUG2(printk("%s(%ld): abort_command " "mbx failed.\n", __func__, vha->host_no)); + ret = FAILED; } else { DEBUG3(printk("%s(%ld): abort_command " "mbx success.\n", __func__, vha->host_no)); -- cgit v1.2.3 From b872ca4081c480e3d76443282ffd7f206321f50f Mon Sep 17 00:00:00 2001 From: Joe Carnuccio Date: Thu, 22 Jan 2009 09:45:36 -0800 Subject: [SCSI] qla2xxx: Correct descriptions in flash manipulation routines. When clearing the flash device's SR, the comment is incorrect... clearing the SR is 2 steps: 1. the SR protect bit is 1, so the first write zero clears only that bit, 2. the SR protect bit is now 0, so the next write zero clears the remaining bits. The sector erase debug print more correctly identifies that the erase failed. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_sup.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index ed9582603af..9c3b694c049 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -944,9 +944,9 @@ qla24xx_unprotect_flash(struct qla_hw_data *ha) if (!ha->fdt_wrt_disable) return; - /* Disable flash write-protection. */ + /* Disable flash write-protection, first clear SR protection bit */ qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0); - /* Some flash parts need an additional zero-write to clear bits.*/ + /* Then write zero again to clear remaining SR bits.*/ qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0); } @@ -1021,7 +1021,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, (fdata & 0xff00) |((fdata << 16) & 0xff0000) | ((fdata >> 16) & 0xff)); if (ret != QLA_SUCCESS) { - DEBUG9(qla_printk("Unable to flash sector: " + DEBUG9(qla_printk("Unable to erase sector: " "address=%x.\n", faddr)); break; } -- cgit v1.2.3 From 53303c42d5a148a73b201a04c89e371d4d5a150f Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 22 Jan 2009 09:45:37 -0800 Subject: [SCSI] qla2xxx: Correct regression in DMA-mask setting prior to allocations. Jeremy Higdon noted (http://marc.info/?l=linux-scsi&m=123262143131788&w=2) that the rework done in commit e315cd28b9ef0d7b71e462ac16e18dbaa2f5adfe was not setting the proper consistent and streaming DMA masks prior to memory allocations. Correct this and remove the unnecessary prototype. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_os.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index f344b6816fd..c11f872d3e1 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -65,8 +65,6 @@ MODULE_PARM_DESC(ql2xextended_error_logging, static void qla2x00_free_device(scsi_qla_host_t *); -static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha); - int ql2xfdmienable=1; module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xfdmienable, @@ -1242,9 +1240,8 @@ qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type) * supported addressing method. */ static void -qla2x00_config_dma_addressing(scsi_qla_host_t *vha) +qla2x00_config_dma_addressing(struct qla_hw_data *ha) { - struct qla_hw_data *ha = vha->hw; /* Assume a 32bit DMA mask. */ ha->flags.enable_64bit_addressing = 0; @@ -1870,6 +1867,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) set_bit(0, (unsigned long *) ha->vp_idx_map); + qla2x00_config_dma_addressing(ha); ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp); if (!ret) { qla_printk(KERN_WARNING, ha, @@ -1896,8 +1894,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, base_vha); - qla2x00_config_dma_addressing(base_vha); - host = base_vha->host; base_vha->req_ques[0] = req->id; host->can_queue = req->length + 128; -- cgit v1.2.3 From 3c01b4f9fbb43fc911acd33ea7a14ea7a4f9866b Mon Sep 17 00:00:00 2001 From: Seokmann Ju Date: Thu, 22 Jan 2009 09:45:38 -0800 Subject: [SCSI] qla2xxx: Add checks for a valid fcport in dev-loss-tmo/terminate_rport_io callbacks. Commit f78badb1ae07e7f8b835ab2ea0b456ed3fc4caf4 ([SCSI] fc transport: pre-emptively terminate i/o upon dev_loss_tmo timeout) changed the callback semantics of dev_loss_tmo and terminate_rport_io such that repeated calls could be made. This could result in the the driver using stale (NULLed-out, in dev_loss_tmo) data from the rport. Correct this by addint a simple check to ensure a valid fcport is attached. Signed-off-by: Seokmann Ju Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_attr.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index c7acef50d5d..33a3c13fd89 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1016,6 +1016,9 @@ qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport) struct Scsi_Host *host = rport_to_shost(rport); fc_port_t *fcport = *(fc_port_t **)rport->dd_data; + if (!fcport) + return; + qla2x00_abort_fcport_cmds(fcport); /* @@ -1033,6 +1036,9 @@ qla2x00_terminate_rport_io(struct fc_rport *rport) { fc_port_t *fcport = *(fc_port_t **)rport->dd_data; + if (!fcport) + return; + /* * At this point all fcport's software-states are cleared. Perform any * final cleanup of firmware resources (PCBs and XCBs). -- cgit v1.2.3 From f9932deb9900789ee0b5739c118f850d62e3b9b1 Mon Sep 17 00:00:00 2001 From: Andrew Vasquez Date: Thu, 22 Jan 2009 09:45:39 -0800 Subject: [SCSI] qla2xxx: Update version number to 8.03.00-k2. Signed-off-by: Andrew Vasquez Signed-off-by: James Bottomley --- drivers/scsi/qla2xxx/qla_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 808bab6ef06..cfa4c11a479 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.03.00-k1" +#define QLA2XXX_VERSION "8.03.00-k2" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 3 -- cgit v1.2.3 From 64b840dd88eb2054f86c72ed6d989cb8681f0058 Mon Sep 17 00:00:00 2001 From: Brian King Date: Thu, 22 Jan 2009 15:45:38 -0600 Subject: [SCSI] ibmvfc: Fix DMA mapping leak on memory allocation failure There is currently a DMA mapping leak that can occur in the ibmvfc driver if we fail to allocate a scatterlist. Fix this by unmapping the scatterlist in the failure path. Additionally, only log an error for a scatterlist allocation failure if the log level is greater than the default, since this can occur when running Active Memory Sharing and this is not considered an error. Signed-off-by: Brian King Signed-off-by: James Bottomley --- drivers/scsi/ibmvscsi/ibmvfc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index 91ef669d98f..a1a511bdec8 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -1322,7 +1322,9 @@ static int ibmvfc_map_sg_data(struct scsi_cmnd *scmd, &evt->ext_list_token); if (!evt->ext_list) { - scmd_printk(KERN_ERR, scmd, "Can't allocate memory for scatterlist\n"); + scsi_dma_unmap(scmd); + if (vhost->log_level > IBMVFC_DEFAULT_LOG_LEVEL) + scmd_printk(KERN_ERR, scmd, "Can't allocate memory for scatterlist\n"); return -ENOMEM; } } -- cgit v1.2.3 From 74194cc71074c8bc17690a5d826093fb6f6e9928 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Sun, 18 Jan 2009 14:46:21 +0100 Subject: power_supply: pda_power: Don't request shared IRQs w/ IRQF_DISABLED IRQF_DISABLED is not guaranteed for shared IRQs. I think power_changed_isr doesn't need it anyway, as it only fires a timer. This patch enables IRQF_SAMPLE_RANDOM instead. Signed-off-by: Philipp Zabel Signed-off-by: Anton Vorontsov --- drivers/power/pda_power.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c index d30bb766fce..b56a704409d 100644 --- a/drivers/power/pda_power.c +++ b/drivers/power/pda_power.c @@ -20,7 +20,7 @@ static inline unsigned int get_irq_flags(struct resource *res) { - unsigned int flags = IRQF_DISABLED | IRQF_SHARED; + unsigned int flags = IRQF_SAMPLE_RANDOM | IRQF_SHARED; flags |= res->flags & IRQF_TRIGGER_MASK; -- cgit v1.2.3 From 801599b0cd4c026a18fb9fce436eae4459f799a6 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 20 Jan 2009 06:14:32 +0000 Subject: lcs: fix compilation for !CONFIG_IP_MULTICAST drivers/s390/net/lcs.c: In function 'lcs_new_device': drivers/s390/net/lcs.c:2179: error: implicit declaration of function 'lcs_set_multicast_list' Reported-by: Kamalesh Babulal Signed-off-by: Heiko Carstens Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- drivers/s390/net/lcs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index acca6678cb2..49c3bfa1afd 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -70,7 +70,9 @@ static char debug_buffer[255]; static void lcs_tasklet(unsigned long); static void lcs_start_kernel_thread(struct work_struct *); static void lcs_get_frames_cb(struct lcs_channel *, struct lcs_buffer *); +#ifdef CONFIG_IP_MULTICAST static int lcs_send_delipm(struct lcs_card *, struct lcs_ipm_list *); +#endif /* CONFIG_IP_MULTICAST */ static int lcs_recovery(void *ptr); /** @@ -1285,6 +1287,8 @@ out: lcs_clear_thread_running_bit(card, LCS_SET_MC_THREAD); return 0; } +#endif /* CONFIG_IP_MULTICAST */ + /** * function called by net device to * handle multicast address relevant things @@ -1292,6 +1296,7 @@ out: static void lcs_set_multicast_list(struct net_device *dev) { +#ifdef CONFIG_IP_MULTICAST struct lcs_card *card; LCS_DBF_TEXT(4, trace, "setmulti"); @@ -1299,9 +1304,8 @@ lcs_set_multicast_list(struct net_device *dev) if (!lcs_set_thread_start_bit(card, LCS_SET_MC_THREAD)) schedule_work(&card->kernel_thread_starter); -} - #endif /* CONFIG_IP_MULTICAST */ +} static long lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb) -- cgit v1.2.3 From e918085aaff34086e265f825dd469926b1aec4a4 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Sun, 25 Jan 2009 18:06:26 -0800 Subject: virtio_net: Fix MAX_PACKET_LEN to support 802.1Q VLANs 802.1Q expanded the maximum ethernet frame size by 4 bytes for the VLAN tag. We're not taking this into account in virtio_net, which means the buffers we provide to the backend in the virtqueue RX ring aren't big enough to hold a full MTU VLAN packet. For QEMU/KVM, this results in the backend exiting with a packet truncation error. Signed-off-by: Alex Williamson Acked-by: Mark McLoughlin Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 43f6523c40b..63ef2a8905f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -24,6 +24,7 @@ #include #include #include +#include static int napi_weight = 128; module_param(napi_weight, int, 0444); @@ -33,7 +34,7 @@ module_param(csum, bool, 0444); module_param(gso, bool, 0444); /* FIXME: MTU in config. */ -#define MAX_PACKET_LEN (ETH_HLEN+ETH_DATA_LEN) +#define MAX_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) #define GOOD_COPY_LEN 128 struct virtnet_info -- cgit v1.2.3 From 80ee6f54f51ffc623843dd8955248d4fab064b99 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 23 Jan 2009 14:12:59 +0900 Subject: libata-sff: fix incorrect EH message The EH message for NODEV_HINT path was describing the opposite condition. Fix it. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-sff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 5a4aad123c4..0a8567c48d9 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1322,7 +1322,7 @@ fsm_start: * condition. Mark hint. */ ata_ehi_push_desc(ehi, "ST-ATA: " - "DRQ=1 with device error, " + "DRQ=0 without device error, " "dev_stat 0x%X", status); qc->err_mask |= AC_ERR_HSM | AC_ERR_NODEV_HINT; -- cgit v1.2.3 From b919930c34e99a48d6b13a5ec9db8c059ec44d72 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 25 Jan 2009 10:26:00 +0900 Subject: libata: set NODEV_HINT for 0x7f status Asus Pundit-R with atiixp controller has the second port missing and, very unusually, its status is stuck at 0x7f and all others at 0. This meanst that it fails TF access test but gets detected as a disk due to classification code check and then evades polling IDENTIFY presence detection thanks to the missing BSY in the status value causing excessive delays during boot. This patch makes libata-sff HSM set NODEV_HINT if the status is 0x7f to make polling IDENTIFY presence detection work for these machines. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/libata-sff.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 0a8567c48d9..0b299b0f817 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1358,6 +1358,16 @@ fsm_start: qc->err_mask |= AC_ERR_HSM; } + /* There are oddball controllers with + * status register stuck at 0x7f and + * lbal/m/h at zero which makes it + * pass all other presence detection + * mechanisms we have. Set NODEV_HINT + * for it. Kernel bz#7241. + */ + if (status == 0x7f) + qc->err_mask |= AC_ERR_NODEV_HINT; + /* ata_pio_sectors() might change the * state to HSM_ST_LAST. so, the state * is changed after ata_pio_sectors(). -- cgit v1.2.3 From e8caa3c70e94d867ca2efe9e53fd388b52d6d0c8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 25 Jan 2009 11:25:22 +0900 Subject: sata_nv: rename nv_nf2_hardreset() nv_nf2_hardreset() will be used by other flavors too. Rename it to nv_noclassify_hardreset(). Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/sata_nv.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 6f146061432..93bf5855a9b 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -305,10 +305,10 @@ static irqreturn_t nv_ck804_interrupt(int irq, void *dev_instance); static int nv_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val); static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val); +static int nv_noclassify_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline); static void nv_nf2_freeze(struct ata_port *ap); static void nv_nf2_thaw(struct ata_port *ap); -static int nv_nf2_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline); static void nv_ck804_freeze(struct ata_port *ap); static void nv_ck804_thaw(struct ata_port *ap); static int nv_adma_slave_config(struct scsi_device *sdev); @@ -432,7 +432,7 @@ static struct ata_port_operations nv_nf2_ops = { .inherits = &nv_common_ops, .freeze = nv_nf2_freeze, .thaw = nv_nf2_thaw, - .hardreset = nv_nf2_hardreset, + .hardreset = nv_noclassify_hardreset, }; /* CK804 finally gets hardreset right */ @@ -1530,6 +1530,17 @@ static int nv_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val) return 0; } +static int nv_noclassify_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + bool online; + int rc; + + rc = sata_link_hardreset(link, sata_deb_timing_hotplug, deadline, + &online, NULL); + return online ? -EAGAIN : rc; +} + static void nv_nf2_freeze(struct ata_port *ap) { void __iomem *scr_addr = ap->host->ports[0]->ioaddr.scr_addr; @@ -1554,17 +1565,6 @@ static void nv_nf2_thaw(struct ata_port *ap) iowrite8(mask, scr_addr + NV_INT_ENABLE); } -static int nv_nf2_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) -{ - bool online; - int rc; - - rc = sata_link_hardreset(link, sata_deb_timing_hotplug, deadline, - &online, NULL); - return online ? -EAGAIN : rc; -} - static void nv_ck804_freeze(struct ata_port *ap) { void __iomem *mmio_base = ap->host->iomap[NV_MMIO_BAR]; -- cgit v1.2.3 From 2d775708bc6613f1be47f1e720781343341ecc94 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 25 Jan 2009 11:29:38 +0900 Subject: sata_nv: fix MCP5x reset MCP5x family of controllers seem to share much more with nf2's as far as reset protocol is concerned. It requires heardreset to get the PHY going and classfication code report after hardreset is unreliable. Create a new board type MCP5x and use noclassify hardreset. SWNCQ is modified to inherit from this new type. This fixes hotplug regression reported in kernel bz#12351. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/sata_nv.c | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index 93bf5855a9b..c49ad0e61b6 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -352,6 +352,7 @@ enum nv_host_type NFORCE3 = NFORCE2, /* NF2 == NF3 as far as sata_nv is concerned */ CK804, ADMA, + MCP5x, SWNCQ, }; @@ -363,10 +364,10 @@ static const struct pci_device_id nv_pci_tbl[] = { { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2), CK804 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA), CK804 }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2), CK804 }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), SWNCQ }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), SWNCQ }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), SWNCQ }, - { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), SWNCQ }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA), MCP5x }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2), MCP5x }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA), MCP5x }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2), MCP5x }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA), GENERIC }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA2), GENERIC }, { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA3), GENERIC }, @@ -467,8 +468,19 @@ static struct ata_port_operations nv_adma_ops = { .host_stop = nv_adma_host_stop, }; +/* Kernel bz#12351 reports that when SWNCQ is enabled, for hotplug to + * work, hardreset should be used and hardreset can't report proper + * signature, which suggests that mcp5x is closer to nf2 as long as + * reset quirkiness is concerned. Define separate ops for mcp5x with + * nv_noclassify_hardreset(). + */ +static struct ata_port_operations nv_mcp5x_ops = { + .inherits = &nv_common_ops, + .hardreset = nv_noclassify_hardreset, +}; + static struct ata_port_operations nv_swncq_ops = { - .inherits = &nv_generic_ops, + .inherits = &nv_mcp5x_ops, .qc_defer = ata_std_qc_defer, .qc_prep = nv_swncq_qc_prep, @@ -531,6 +543,15 @@ static const struct ata_port_info nv_port_info[] = { .port_ops = &nv_adma_ops, .private_data = NV_PI_PRIV(nv_adma_interrupt, &nv_adma_sht), }, + /* MCP5x */ + { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .pio_mask = NV_PIO_MASK, + .mwdma_mask = NV_MWDMA_MASK, + .udma_mask = NV_UDMA_MASK, + .port_ops = &nv_mcp5x_ops, + .private_data = NV_PI_PRIV(nv_generic_interrupt, &nv_sht), + }, /* SWNCQ */ { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | @@ -2355,14 +2376,9 @@ static int nv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (type == CK804 && adma_enabled) { dev_printk(KERN_NOTICE, &pdev->dev, "Using ADMA mode\n"); type = ADMA; - } - - if (type == SWNCQ) { - if (swncq_enabled) - dev_printk(KERN_NOTICE, &pdev->dev, - "Using SWNCQ mode\n"); - else - type = GENERIC; + } else if (type == MCP5x && swncq_enabled) { + dev_printk(KERN_NOTICE, &pdev->dev, "Using SWNCQ mode\n"); + type = SWNCQ; } ppi[0] = &nv_port_info[type]; -- cgit v1.2.3 From b0bccb18bc523d1d5060d25958f12438062829a9 Mon Sep 17 00:00:00 2001 From: Mark Lord Date: Mon, 19 Jan 2009 18:04:37 -0500 Subject: sata_mv: fix 8-port timeouts on 508x/6081 chips Fix a longstanding bug for the 8-port Marvell Sata controllers (508x/6081), where accesses to the upper 4 ports would cause lost-interrupts / timeouts for the lower 4-ports. With this patch, the 6081 boards should finally be reliable enough for mainstream use with Linux. Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik --- drivers/ata/sata_mv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 86918634a4c..ce9d6ad5a64 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -883,7 +883,7 @@ static void mv_start_dma(struct ata_port *ap, void __iomem *port_mmio, struct mv_host_priv *hpriv = ap->host->private_data; int hardport = mv_hardport_from_port(ap->port_no); void __iomem *hc_mmio = mv_hc_base_from_port( - mv_host_base(ap->host), hardport); + mv_host_base(ap->host), ap->port_no); u32 hc_irq_cause, ipending; /* clear EDMA event indicators, if any */ -- cgit v1.2.3 From cae6edc3b5a536119374a5439d9b253cb26f05e1 Mon Sep 17 00:00:00 2001 From: Mark Lord Date: Mon, 19 Jan 2009 18:05:42 -0500 Subject: sata_mv: don't read hc_irq_cause Remove silly read-modify-write sequences when clearing interrupts in hc_irq_cause. This gets rid of unneeded MMIO reads, resulting in a slight performance boost when switching between EDMA and non-EDMA modes (eg. for cache flushes). Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik --- drivers/ata/sata_mv.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index ce9d6ad5a64..68096289074 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -35,8 +35,6 @@ * * --> Investigate problems with PCI Message Signalled Interrupts (MSI). * - * --> Cache frequently-accessed registers in mv_port_priv to reduce overhead. - * * --> Develop a low-power-consumption strategy, and implement it. * * --> [Experiment, low priority] Investigate interrupt coalescing. @@ -884,18 +882,14 @@ static void mv_start_dma(struct ata_port *ap, void __iomem *port_mmio, int hardport = mv_hardport_from_port(ap->port_no); void __iomem *hc_mmio = mv_hc_base_from_port( mv_host_base(ap->host), ap->port_no); - u32 hc_irq_cause, ipending; + u32 hc_irq_cause; /* clear EDMA event indicators, if any */ writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); - /* clear EDMA interrupt indicator, if any */ - hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS); - ipending = (DEV_IRQ | DMA_IRQ) << hardport; - if (hc_irq_cause & ipending) { - writelfl(hc_irq_cause & ~ipending, - hc_mmio + HC_IRQ_CAUSE_OFS); - } + /* clear pending irq events */ + hc_irq_cause = ~((DEV_IRQ | DMA_IRQ) << hardport); + writelfl(hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS); mv_edma_cfg(ap, want_ncq); @@ -2821,8 +2815,7 @@ static void mv_eh_thaw(struct ata_port *ap) writel(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); /* clear pending irq events */ - hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS); - hc_irq_cause &= ~((DEV_IRQ | DMA_IRQ) << hardport); + hc_irq_cause = ~((DEV_IRQ | DMA_IRQ) << hardport); writelfl(hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS); mv_enable_port_irqs(ap, ERR_IRQ); -- cgit v1.2.3 From cd12e1f7a2c28917c89d65c0d4a52d3919b4c125 Mon Sep 17 00:00:00 2001 From: Mark Lord Date: Mon, 19 Jan 2009 18:06:28 -0500 Subject: sata_mv: remove bogus nsect restriction Remove unneeded nsect restriction from GenII NCQ path, and improve comments to explain why this is not a problem. Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik --- drivers/ata/sata_mv.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 68096289074..874b60b2e20 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -349,8 +349,6 @@ enum { EDMA_HALTCOND_OFS = 0x60, /* GenIIe halt conditions */ - GEN_II_NCQ_MAX_SECTORS = 256, /* max sects/io on Gen2 w/NCQ */ - /* Host private flags (hp_flags) */ MV_HP_FLAG_MSI = (1 << 0), MV_HP_ERRATA_50XXB0 = (1 << 1), @@ -1093,20 +1091,12 @@ static void mv6_dev_config(struct ata_device *adev) * * Gen-II does not support NCQ over a port multiplier * (no FIS-based switching). - * - * We don't have hob_nsect when doing NCQ commands on Gen-II. - * See mv_qc_prep() for more info. */ if (adev->flags & ATA_DFLAG_NCQ) { if (sata_pmp_attached(adev->link->ap)) { adev->flags &= ~ATA_DFLAG_NCQ; ata_dev_printk(adev, KERN_INFO, "NCQ disabled for command-based switching\n"); - } else if (adev->max_sectors > GEN_II_NCQ_MAX_SECTORS) { - adev->max_sectors = GEN_II_NCQ_MAX_SECTORS; - ata_dev_printk(adev, KERN_INFO, - "max_sectors limited to %u for NCQ\n", - adev->max_sectors); } } } @@ -1444,7 +1434,8 @@ static void mv_qc_prep(struct ata_queued_cmd *qc) * only 11 bytes...so we must pick and choose required * registers based on the command. So, we drop feature and * hob_feature for [RW] DMA commands, but they are needed for - * NCQ. NCQ will drop hob_nsect. + * NCQ. NCQ will drop hob_nsect, which is not needed there + * (nsect is used only for the tag; feat/hob_feat hold true nsect). */ switch (tf->command) { case ATA_CMD_READ: -- cgit v1.2.3 From 5d0fb2e730e2085021cf5c8b6d14983e92aea75b Mon Sep 17 00:00:00 2001 From: Thomas Reitmayr Date: Sat, 24 Jan 2009 20:24:58 +0100 Subject: sata_mv: Properly initialize main irq mask I noticed that during initialization sata_mv.c assumes that the main interrupt mask has its default value of 0. The function mv_platform_probe(..) initializes a shadow irq mask with 0 assuming that's the value of the controller's register. Now mv_set_main_irq_mask(..) only writes the controller's register if the new value differs from the "shadowed" value. This is fatal when trying to disable all interrupts in mv_init_host(..), i.e. the following function call does not write anything to the main irq mask register: mv_set_main_irq_mask(host, ~0, 0); The effect I see on my machine (QNAP TS-109 II) with booting via kexec (with Linux as a 2nd-stage boot loader) is that if the sata_mv module was still loaded when performing kexec, then the new kernel's sata_mv module starts up with interrupts enabled. This results in an unhandled IRQ and breaks the boot process. The unhandled interrupt itself might also be fixed by Lennert's patch proposed at http://markmail.org/message/kwvzxstnlsa3s26w which I did not try yet. However I still propose to additionally initialize the shadow variable with the current contents of the main irq mask register to get both in sync and allow proper disabling the main irq mask. This fixes the unhandled irq on my machine. Signed-off-by: Thomas Reitmayr Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik --- drivers/ata/sata_mv.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 874b60b2e20..9e9fb959478 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -3059,6 +3059,9 @@ static int mv_init_host(struct ata_host *host, unsigned int board_idx) hpriv->main_irq_mask_addr = mmio + PCI_HC_MAIN_IRQ_MASK_OFS; } + /* initialize shadow irq mask with register's value */ + hpriv->main_irq_mask = readl(hpriv->main_irq_mask_addr); + /* global interrupt mask: 0 == mask everything */ mv_set_main_irq_mask(host, ~0, 0); -- cgit v1.2.3 From 6d3c30efc964fadf2e6270e6fceaeca3ce50027a Mon Sep 17 00:00:00 2001 From: Mark Lord Date: Wed, 21 Jan 2009 10:31:29 -0500 Subject: sata_mv: msi masking fix (v2) Enable reliable use of Message-Signaled Interrupts (MSI) in sata_mv by masking further chip interrupts within the main interrupt handler. Based upon a suggestion by Grant Grundler. MSI is working reliably in all of my test systems here now. Signed-off-by: Mark Lord Reviewed-by: Grant Grundler Signed-off-by: Jeff Garzik --- drivers/ata/sata_mv.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 9e9fb959478..f2d8a020ea5 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -33,8 +33,6 @@ * * --> ATAPI support (Marvell claims the 60xx/70xx chips can do it). * - * --> Investigate problems with PCI Message Signalled Interrupts (MSI). - * * --> Develop a low-power-consumption strategy, and implement it. * * --> [Experiment, low priority] Investigate interrupt coalescing. @@ -70,7 +68,7 @@ #include #define DRV_NAME "sata_mv" -#define DRV_VERSION "1.24" +#define DRV_VERSION "1.25" enum { /* BAR's are enumerated in terms of pci_resource_start() terms */ @@ -2199,9 +2197,15 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance) struct ata_host *host = dev_instance; struct mv_host_priv *hpriv = host->private_data; unsigned int handled = 0; + int using_msi = hpriv->hp_flags & MV_HP_FLAG_MSI; u32 main_irq_cause, pending_irqs; spin_lock(&host->lock); + + /* for MSI: block new interrupts while in here */ + if (using_msi) + writel(0, hpriv->main_irq_mask_addr); + main_irq_cause = readl(hpriv->main_irq_cause_addr); pending_irqs = main_irq_cause & hpriv->main_irq_mask; /* @@ -2215,6 +2219,11 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance) handled = mv_host_intr(host, pending_irqs); } spin_unlock(&host->lock); + + /* for MSI: unmask; interrupt cause bits will retrigger now */ + if (using_msi) + writel(hpriv->main_irq_mask, hpriv->main_irq_mask_addr); + return IRQ_RETVAL(handled); } @@ -3417,9 +3426,9 @@ static int mv_pci_init_one(struct pci_dev *pdev, if (rc) return rc; - /* Enable interrupts */ - if (msi && pci_enable_msi(pdev)) - pci_intx(pdev, 1); + /* Enable message-switched interrupts, if requested */ + if (msi && pci_enable_msi(pdev) == 0) + hpriv->hp_flags |= MV_HP_FLAG_MSI; mv_dump_pci_cfg(pdev, 0x68); mv_print_info(host); -- cgit v1.2.3 From f9228c7ffaaa37521c46239ccea623f10fa44a27 Mon Sep 17 00:00:00 2001 From: Mark Lord Date: Wed, 21 Jan 2009 10:34:17 -0500 Subject: sata_mv: no longer experimental (v2) Update Kconfig for sata_mv with full list of chips supported, and (finally!) remove the "EXPERIMENTAL" designations. Signed-off-by: Mark Lord Signed-off-by: Jeff Garzik --- drivers/ata/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 503a908afc8..0bcf2646467 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -112,11 +112,11 @@ config ATA_PIIX If unsure, say N. config SATA_MV - tristate "Marvell SATA support (HIGHLY EXPERIMENTAL)" - depends on EXPERIMENTAL + tristate "Marvell SATA support" help This option enables support for the Marvell Serial ATA family. - Currently supports 88SX[56]0[48][01] chips. + Currently supports 88SX[56]0[48][01] PCI(-X) chips, + as well as the newer [67]042 PCI-X/PCIe and SOC devices. If unsure, say N. -- cgit v1.2.3 From e4d866cdea24543ee16ce6d07d80c513e86ba983 Mon Sep 17 00:00:00 2001 From: "JosephChan@via.com.tw" Date: Fri, 23 Jan 2009 15:37:39 +0800 Subject: [libata] pata_via: support VX855, future chips whose IDE controller use 0x0571 It supports VX855 and future chips whose IDE controller uses PCI ID 0x0571. Signed-off-by: Joseph Chan Acked-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/ata/pata_via.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 681169c9c64..79a6c9a0b72 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -86,6 +86,10 @@ enum { VIA_SATA_PATA = 0x800, /* SATA/PATA combined configuration */ }; +enum { + VIA_IDFLAG_SINGLE = (1 << 0), /* single channel controller) */ +}; + /* * VIA SouthBridge chips. */ @@ -97,8 +101,12 @@ static const struct via_isa_bridge { u8 rev_max; u16 flags; } via_isa_bridges[] = { + { "vx855", PCI_DEVICE_ID_VIA_VX855, 0x00, 0x2f, + VIA_UDMA_133 | VIA_BAD_AST | VIA_SATA_PATA }, { "vx800", PCI_DEVICE_ID_VIA_VX800, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_SATA_PATA }, + { "vt8261", PCI_DEVICE_ID_VIA_8261, 0x00, 0x2f, + VIA_UDMA_133 | VIA_BAD_AST }, { "vt8237s", PCI_DEVICE_ID_VIA_8237S, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "vt8251", PCI_DEVICE_ID_VIA_8251, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST }, { "cx700", PCI_DEVICE_ID_VIA_CX700, 0x00, 0x2f, VIA_UDMA_133 | VIA_BAD_AST | VIA_SATA_PATA }, @@ -122,6 +130,8 @@ static const struct via_isa_bridge { { "vt82c586", PCI_DEVICE_ID_VIA_82C586_0, 0x00, 0x0f, VIA_UDMA_NONE | VIA_SET_FIFO }, { "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK }, { "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK | VIA_BAD_ID }, + { "vtxxxx", PCI_DEVICE_ID_VIA_ANON, 0x00, 0x2f, + VIA_UDMA_133 | VIA_BAD_AST }, { NULL } }; @@ -460,6 +470,7 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) static int printed_version; u8 enable; u32 timing; + unsigned long flags = id->driver_data; int rc; if (!printed_version++) @@ -469,9 +480,13 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) if (rc) return rc; + if (flags & VIA_IDFLAG_SINGLE) + ppi[1] = &ata_dummy_port_info; + /* To find out how the IDE will behave and what features we actually have to look at the bridge not the IDE controller */ - for (config = via_isa_bridges; config->id; config++) + for (config = via_isa_bridges; config->id != PCI_DEVICE_ID_VIA_ANON; + config++) if ((isa = pci_get_device(PCI_VENDOR_ID_VIA + !!(config->flags & VIA_BAD_ID), config->id, NULL))) { @@ -482,10 +497,6 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_dev_put(isa); } - if (!config->id) { - printk(KERN_WARNING "via: Unknown VIA SouthBridge, disabling.\n"); - return -ENODEV; - } pci_dev_put(isa); if (!(config->flags & VIA_NO_ENABLES)) { @@ -587,6 +598,7 @@ static const struct pci_device_id via[] = { { PCI_VDEVICE(VIA, 0x1571), }, { PCI_VDEVICE(VIA, 0x3164), }, { PCI_VDEVICE(VIA, 0x5324), }, + { PCI_VDEVICE(VIA, 0xC409), VIA_IDFLAG_SINGLE }, { }, }; -- cgit v1.2.3 From e88a0faae5baaaa3bdc6f23a55ad6bc7a7b4aa77 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Sat, 24 Jan 2009 08:22:47 +0000 Subject: xen: unitialised return value in xenbus_write_transaction The return value of xenbus_write_transaction can be uninitialised in the success case leading to the userspace xenstore utilities failing. Signed-off-by: Ian Campbell Signed-off-by: Ingo Molnar --- drivers/xen/xenfs/xenbus.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/xen/xenfs/xenbus.c b/drivers/xen/xenfs/xenbus.c index 875a4c59c59..a9592d981b1 100644 --- a/drivers/xen/xenfs/xenbus.c +++ b/drivers/xen/xenfs/xenbus.c @@ -291,7 +291,7 @@ static void watch_fired(struct xenbus_watch *watch, static int xenbus_write_transaction(unsigned msg_type, struct xenbus_file_priv *u) { - int rc, ret; + int rc; void *reply; struct xenbus_transaction_holder *trans = NULL; LIST_HEAD(staging_q); @@ -326,15 +326,14 @@ static int xenbus_write_transaction(unsigned msg_type, } mutex_lock(&u->reply_mutex); - ret = queue_reply(&staging_q, &u->u.msg, sizeof(u->u.msg)); - if (!ret) - ret = queue_reply(&staging_q, reply, u->u.msg.len); - if (!ret) { + rc = queue_reply(&staging_q, &u->u.msg, sizeof(u->u.msg)); + if (!rc) + rc = queue_reply(&staging_q, reply, u->u.msg.len); + if (!rc) { list_splice_tail(&staging_q, &u->read_buffers); wake_up(&u->read_waitq); } else { queue_cleanup(&staging_q); - rc = ret; } mutex_unlock(&u->reply_mutex); -- cgit v1.2.3 From aeb565dfc3ac4c8b47c5049085b4c7bfb2c7d5d7 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 26 Jan 2009 10:01:53 -0800 Subject: Fix annoying DRM_ERROR() string warning Use '%zu' to print out a size_t variable, not '%d'. Another case of the "let's keep at least Linus' defconfig compile warningless" rule. Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/i915_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 96316fd4723..33fbeb664f0 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3427,7 +3427,7 @@ i915_gem_attach_phys_object(struct drm_device *dev, ret = i915_gem_init_phys_object(dev, id, obj->size); if (ret) { - DRM_ERROR("failed to init phys object %d size: %d\n", id, obj->size); + DRM_ERROR("failed to init phys object %d size: %zu\n", id, obj->size); goto out; } } -- cgit v1.2.3 From 78272bbab895cc8f63bab5181dee55b72208e3b7 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Mon, 26 Jan 2009 12:16:26 -0800 Subject: e1000e: workaround hw errata There is a hardware errata in some revisions of the 82574 that needs to be worked around in the driver by setting a register bit at init. If this bit is not set A0 versions of the 82574 can generate tx hangs. Signed-off-by: Jesse Brandeburg Signed-off-by: Bruce Allan Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/e1000e/82571.c | 6 +++++- drivers/net/e1000e/hw.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/e1000e/82571.c b/drivers/net/e1000e/82571.c index cf43ee743b3..0890162953e 100644 --- a/drivers/net/e1000e/82571.c +++ b/drivers/net/e1000e/82571.c @@ -981,11 +981,15 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) ew32(PBA_ECC, reg); } - /* PCI-Ex Control Register */ + /* PCI-Ex Control Registers */ if (hw->mac.type == e1000_82574) { reg = er32(GCR); reg |= (1 << 22); ew32(GCR, reg); + + reg = er32(GCR2); + reg |= 1; + ew32(GCR2, reg); } return; diff --git a/drivers/net/e1000e/hw.h b/drivers/net/e1000e/hw.h index f25e961c6b3..2d4ce0492df 100644 --- a/drivers/net/e1000e/hw.h +++ b/drivers/net/e1000e/hw.h @@ -206,6 +206,7 @@ enum e1e_registers { E1000_MANC2H = 0x05860, /* Management Control To Host - RW */ E1000_SW_FW_SYNC = 0x05B5C, /* Software-Firmware Synchronization - RW */ E1000_GCR = 0x05B00, /* PCI-Ex Control */ + E1000_GCR2 = 0x05B64, /* PCI-Ex Control #2 */ E1000_FACTPS = 0x05B30, /* Function Active and Power State to MNG */ E1000_SWSM = 0x05B50, /* SW Semaphore */ E1000_FWSM = 0x05B54, /* FW Semaphore */ -- cgit v1.2.3 From 1745522ccbabd990bfc7511861aa9fa98287cba0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 26 Jan 2009 21:19:52 +0100 Subject: i2c: Delete many unused adapter IDs Signed-off-by: Jean Delvare --- drivers/gpu/drm/i915/intel_i2c.c | 4 ---- drivers/i2c/busses/i2c-acorn.c | 1 - drivers/i2c/busses/i2c-ali1535.c | 1 - drivers/i2c/busses/i2c-ali1563.c | 1 - drivers/i2c/busses/i2c-ali15x3.c | 1 - drivers/i2c/busses/i2c-amd756.c | 1 - drivers/i2c/busses/i2c-amd8111.c | 1 - drivers/i2c/busses/i2c-au1550.c | 1 - drivers/i2c/busses/i2c-bfin-twi.c | 1 - drivers/i2c/busses/i2c-elektor.c | 1 - drivers/i2c/busses/i2c-hydra.c | 1 - drivers/i2c/busses/i2c-i801.c | 1 - drivers/i2c/busses/i2c-ibm_iic.c | 1 - drivers/i2c/busses/i2c-iop3xx.c | 1 - drivers/i2c/busses/i2c-ixp2000.c | 1 - drivers/i2c/busses/i2c-mpc.c | 1 - drivers/i2c/busses/i2c-mv64xxx.c | 1 - drivers/i2c/busses/i2c-nforce2.c | 1 - drivers/i2c/busses/i2c-parport-light.c | 1 - drivers/i2c/busses/i2c-parport.c | 1 - drivers/i2c/busses/i2c-pca-isa.c | 1 - drivers/i2c/busses/i2c-piix4.c | 1 - drivers/i2c/busses/i2c-sibyte.c | 2 -- drivers/i2c/busses/i2c-sis5595.c | 1 - drivers/i2c/busses/i2c-sis630.c | 1 - drivers/i2c/busses/i2c-sis96x.c | 1 - drivers/i2c/busses/i2c-via.c | 1 - drivers/i2c/busses/i2c-viapro.c | 1 - drivers/i2c/busses/i2c-voodoo3.c | 2 -- drivers/i2c/busses/scx200_acb.c | 1 - drivers/i2c/busses/scx200_i2c.c | 1 - drivers/ieee1394/pcilynx.c | 1 - drivers/video/aty/radeon_i2c.c | 1 - drivers/video/i810/i810-i2c.c | 1 - drivers/video/intelfb/intelfb_i2c.c | 1 - drivers/video/nvidia/nv_i2c.c | 1 - drivers/video/savage/savagefb-i2c.c | 1 - 37 files changed, 42 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index a5a2f5339e9..5ee9d4c2575 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -137,10 +137,6 @@ struct intel_i2c_chan *intel_i2c_create(struct drm_device *dev, const u32 reg, chan->reg = reg; snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); chan->adapter.owner = THIS_MODULE; -#ifndef I2C_HW_B_INTELFB -#define I2C_HW_B_INTELFB I2C_HW_B_I810 -#endif - chan->adapter.id = I2C_HW_B_INTELFB; chan->adapter.algo_data = &chan->algo; chan->adapter.dev.parent = &dev->pdev->dev; chan->algo.setsda = set_data; diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c index 75089febbc1..9fee3ca1734 100644 --- a/drivers/i2c/busses/i2c-acorn.c +++ b/drivers/i2c/busses/i2c-acorn.c @@ -83,7 +83,6 @@ static struct i2c_algo_bit_data ioc_data = { }; static struct i2c_adapter ioc_ops = { - .id = I2C_HW_B_IOC, .algo_data = &ioc_data, }; diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index 9cead9b9458..981e080b32a 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -476,7 +476,6 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter ali1535_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_ALI1535, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index dd9e796fad6..f70f46582c6 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -386,7 +386,6 @@ static const struct i2c_algorithm ali1563_algorithm = { static struct i2c_adapter ali1563_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_ALI1563, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &ali1563_algorithm, }; diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index 234fdde7d40..39066dee46e 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -473,7 +473,6 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter ali15x3_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_ALI15X3, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index 36bee5b9c95..220f4a1eee1 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -298,7 +298,6 @@ static const struct i2c_algorithm smbus_algorithm = { struct i2c_adapter amd756_smbus = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_AMD756, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index 3972208876b..edab51973bf 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -387,7 +387,6 @@ static int __devinit amd8111_probe(struct pci_dev *dev, smbus->adapter.owner = THIS_MODULE; snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), "SMBus2 AMD8111 adapter at %04x", smbus->base); - smbus->adapter.id = I2C_HW_SMBUS_AMD8111; smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c index 66a04c2c660..f78ce523e3d 100644 --- a/drivers/i2c/busses/i2c-au1550.c +++ b/drivers/i2c/busses/i2c-au1550.c @@ -400,7 +400,6 @@ i2c_au1550_probe(struct platform_device *pdev) priv->xfer_timeout = 200; priv->ack_timeout = 200; - priv->adap.id = I2C_HW_AU1550_PSC; priv->adap.nr = pdev->id; priv->adap.algo = &au1550_algo; priv->adap.algo_data = priv; diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 3fd2c417c1e..fc548b3d002 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -651,7 +651,6 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) iface->timeout_timer.data = (unsigned long)iface; p_adap = &iface->adap; - p_adap->id = I2C_HW_BLACKFIN; p_adap->nr = pdev->id; strlcpy(p_adap->name, pdev->name, sizeof(p_adap->name)); p_adap->algo = &bfin_twi_algorithm; diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c index 0ed3ccb81b6..448b4bf35eb 100644 --- a/drivers/i2c/busses/i2c-elektor.c +++ b/drivers/i2c/busses/i2c-elektor.c @@ -202,7 +202,6 @@ static struct i2c_algo_pcf_data pcf_isa_data = { static struct i2c_adapter pcf_isa_ops = { .owner = THIS_MODULE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, - .id = I2C_HW_P_ELEK, .algo_data = &pcf_isa_data, .name = "i2c-elektor", }; diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c index 648aa7baff8..bec9b845dd1 100644 --- a/drivers/i2c/busses/i2c-hydra.c +++ b/drivers/i2c/busses/i2c-hydra.c @@ -102,7 +102,6 @@ static struct i2c_algo_bit_data hydra_bit_data = { static struct i2c_adapter hydra_adap = { .owner = THIS_MODULE, .name = "Hydra i2c", - .id = I2C_HW_B_HYDRA, .algo_data = &hydra_bit_data, }; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 526625eaa84..230238df56c 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -556,7 +556,6 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter i801_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_I801, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index 651f2f1ae5b..88f0db73b36 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -746,7 +746,6 @@ static int __devinit iic_probe(struct of_device *ofdev, adap->dev.parent = &ofdev->dev; strlcpy(adap->name, "IBM IIC", sizeof(adap->name)); i2c_set_adapdata(adap, dev); - adap->id = I2C_HW_OCP; adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->algo = &iic_algo; adap->timeout = 1; diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index fc2714ac0c0..3190690c26c 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -480,7 +480,6 @@ iop3xx_i2c_probe(struct platform_device *pdev) } memcpy(new_adapter->name, pdev->name, strlen(pdev->name)); - new_adapter->id = I2C_HW_IOP3XX; new_adapter->owner = THIS_MODULE; new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; new_adapter->dev.parent = &pdev->dev; diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c index 05d72e98135..8e846797048 100644 --- a/drivers/i2c/busses/i2c-ixp2000.c +++ b/drivers/i2c/busses/i2c-ixp2000.c @@ -116,7 +116,6 @@ static int ixp2000_i2c_probe(struct platform_device *plat_dev) drv_data->algo_data.udelay = 6; drv_data->algo_data.timeout = 100; - drv_data->adapter.id = I2C_HW_B_IXP2000, strlcpy(drv_data->adapter.name, plat_dev->dev.driver->name, sizeof(drv_data->adapter.name)); drv_data->adapter.algo_data = &drv_data->algo_data, diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index a9a45fcc854..aedbbe6618d 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -310,7 +310,6 @@ static const struct i2c_algorithm mpc_algo = { static struct i2c_adapter mpc_ops = { .owner = THIS_MODULE, .name = "MPC adapter", - .id = I2C_HW_MPC107, .algo = &mpc_algo, .timeout = 1, }; diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 9e8118d2fe6..eeda276f8f1 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -527,7 +527,6 @@ mv64xxx_i2c_probe(struct platform_device *pd) goto exit_unmap_regs; } drv_data->adapter.dev.parent = &pd->dev; - drv_data->adapter.id = I2C_HW_MV64XXX; drv_data->adapter.algo = &mv64xxx_i2c_algo; drv_data->adapter.owner = THIS_MODULE; drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 3b19bc41a60..05af6cd7f27 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -355,7 +355,6 @@ static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar, return -EBUSY; } smbus->adapter.owner = THIS_MODULE; - smbus->adapter.id = I2C_HW_SMBUS_NFORCE2; smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c index b2b8380f660..322c5691e38 100644 --- a/drivers/i2c/busses/i2c-parport-light.c +++ b/drivers/i2c/busses/i2c-parport-light.c @@ -115,7 +115,6 @@ static struct i2c_algo_bit_data parport_algo_data = { static struct i2c_adapter parport_adapter = { .owner = THIS_MODULE, .class = I2C_CLASS_HWMON, - .id = I2C_HW_B_LP, .algo_data = &parport_algo_data, .name = "Parallel port adapter (light)", }; diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c index a257cd5cd13..0d8998610c7 100644 --- a/drivers/i2c/busses/i2c-parport.c +++ b/drivers/i2c/busses/i2c-parport.c @@ -164,7 +164,6 @@ static void i2c_parport_attach (struct parport *port) /* Fill the rest of the structure */ adapter->adapter.owner = THIS_MODULE; adapter->adapter.class = I2C_CLASS_HWMON; - adapter->adapter.id = I2C_HW_B_LP; strlcpy(adapter->adapter.name, "Parallel port adapter", sizeof(adapter->adapter.name)); adapter->algo_data = parport_algo_data; diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c index 9eb76268ec7..4aa8138cb0a 100644 --- a/drivers/i2c/busses/i2c-pca-isa.c +++ b/drivers/i2c/busses/i2c-pca-isa.c @@ -101,7 +101,6 @@ static struct i2c_algo_pca_data pca_isa_data = { static struct i2c_adapter pca_isa_ops = { .owner = THIS_MODULE, - .id = I2C_HW_A_ISA, .algo_data = &pca_isa_data, .name = "PCA9564 ISA Adapter", .timeout = 100, diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index eaa9b387543..761f9dd5362 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -403,7 +403,6 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter piix4_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_PIIX4, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c index 4ddefbf238e..98b1ec48915 100644 --- a/drivers/i2c/busses/i2c-sibyte.c +++ b/drivers/i2c/busses/i2c-sibyte.c @@ -155,7 +155,6 @@ static struct i2c_algo_sibyte_data sibyte_board_data[2] = { static struct i2c_adapter sibyte_board_adapter[2] = { { .owner = THIS_MODULE, - .id = I2C_HW_SIBYTE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = NULL, .algo_data = &sibyte_board_data[0], @@ -164,7 +163,6 @@ static struct i2c_adapter sibyte_board_adapter[2] = { }, { .owner = THIS_MODULE, - .id = I2C_HW_SIBYTE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = NULL, .algo_data = &sibyte_board_data[1], diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index 8ce2daff985..f320ab27da4 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -365,7 +365,6 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis5595_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_SIS5595, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 9c9c016ff2b..50c3610e602 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -464,7 +464,6 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis630_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_SIS630, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index f1bba639664..7e1594b4057 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -241,7 +241,6 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter sis96x_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_SIS96X, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c index 29cef0433f3..8b24f192103 100644 --- a/drivers/i2c/busses/i2c-via.c +++ b/drivers/i2c/busses/i2c-via.c @@ -83,7 +83,6 @@ static struct i2c_algo_bit_data bit_data = { static struct i2c_adapter vt586b_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_B_VIA, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .name = "VIA i2c", .algo_data = &bit_data, diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c index 9f194d9efd9..02e6f724b05 100644 --- a/drivers/i2c/busses/i2c-viapro.c +++ b/drivers/i2c/busses/i2c-viapro.c @@ -321,7 +321,6 @@ static const struct i2c_algorithm smbus_algorithm = { static struct i2c_adapter vt596_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_SMBUS_VIA2, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, }; diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c index 1d4ae26ba73..1a474acc0dd 100644 --- a/drivers/i2c/busses/i2c-voodoo3.c +++ b/drivers/i2c/busses/i2c-voodoo3.c @@ -163,7 +163,6 @@ static struct i2c_algo_bit_data voo_i2c_bit_data = { static struct i2c_adapter voodoo3_i2c_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_B_VOO, .class = I2C_CLASS_TV_ANALOG, .name = "I2C Voodoo3/Banshee adapter", .algo_data = &voo_i2c_bit_data, @@ -180,7 +179,6 @@ static struct i2c_algo_bit_data voo_ddc_bit_data = { static struct i2c_adapter voodoo3_ddc_adapter = { .owner = THIS_MODULE, - .id = I2C_HW_B_VOO, .class = I2C_CLASS_DDC, .name = "DDC Voodoo3/Banshee adapter", .algo_data = &voo_ddc_bit_data, diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index ed794b145a1..648ecc6f60e 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c @@ -440,7 +440,6 @@ static __init struct scx200_acb_iface *scx200_create_iface(const char *text, i2c_set_adapdata(adapter, iface); snprintf(adapter->name, sizeof(adapter->name), "%s ACB%d", text, index); adapter->owner = THIS_MODULE; - adapter->id = I2C_HW_SMBUS_SCX200; adapter->algo = &scx200_acb_algorithm; adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adapter->dev.parent = dev; diff --git a/drivers/i2c/busses/scx200_i2c.c b/drivers/i2c/busses/scx200_i2c.c index e4c98539c51..162b74a0488 100644 --- a/drivers/i2c/busses/scx200_i2c.c +++ b/drivers/i2c/busses/scx200_i2c.c @@ -82,7 +82,6 @@ static struct i2c_algo_bit_data scx200_i2c_data = { static struct i2c_adapter scx200_i2c_ops = { .owner = THIS_MODULE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, - .id = I2C_HW_B_SCX200, .algo_data = &scx200_i2c_data, .name = "NatSemi SCx200 I2C", }; diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c index dc15cadb06e..38f71203620 100644 --- a/drivers/ieee1394/pcilynx.c +++ b/drivers/ieee1394/pcilynx.c @@ -1419,7 +1419,6 @@ static int __devinit add_card(struct pci_dev *dev, i2c_ad = kzalloc(sizeof(*i2c_ad), GFP_KERNEL); if (!i2c_ad) FAIL("failed to allocate I2C adapter memory"); - i2c_ad->id = I2C_HW_B_PCILYNX; strlcpy(i2c_ad->name, "PCILynx I2C", sizeof(i2c_ad->name)); i2c_adapter_data = bit_data; i2c_ad->algo_data = &i2c_adapter_data; diff --git a/drivers/video/aty/radeon_i2c.c b/drivers/video/aty/radeon_i2c.c index 2c5567175dc..359fc64e761 100644 --- a/drivers/video/aty/radeon_i2c.c +++ b/drivers/video/aty/radeon_i2c.c @@ -72,7 +72,6 @@ static int radeon_setup_i2c_bus(struct radeon_i2c_chan *chan, const char *name) snprintf(chan->adapter.name, sizeof(chan->adapter.name), "radeonfb %s", name); chan->adapter.owner = THIS_MODULE; - chan->adapter.id = I2C_HW_B_RADEON; chan->adapter.algo_data = &chan->algo; chan->adapter.dev.parent = &chan->rinfo->pdev->dev; chan->algo.setsda = radeon_gpio_setsda; diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c index 7787c3322ff..9dd55e5324a 100644 --- a/drivers/video/i810/i810-i2c.c +++ b/drivers/video/i810/i810-i2c.c @@ -90,7 +90,6 @@ static int i810_setup_i2c_bus(struct i810fb_i2c_chan *chan, const char *name) chan->adapter.owner = THIS_MODULE; chan->adapter.algo_data = &chan->algo; chan->adapter.dev.parent = &chan->par->dev->dev; - chan->adapter.id = I2C_HW_B_I810; chan->algo.setsda = i810i2c_setsda; chan->algo.setscl = i810i2c_setscl; chan->algo.getsda = i810i2c_getsda; diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c index 5d896b81f4e..b3065492bb2 100644 --- a/drivers/video/intelfb/intelfb_i2c.c +++ b/drivers/video/intelfb/intelfb_i2c.c @@ -111,7 +111,6 @@ static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo, "intelfb %s", name); chan->adapter.class = class; chan->adapter.owner = THIS_MODULE; - chan->adapter.id = I2C_HW_B_INTELFB; chan->adapter.algo_data = &chan->algo; chan->adapter.dev.parent = &chan->dinfo->pdev->dev; chan->algo.setsda = intelfb_gpio_setsda; diff --git a/drivers/video/nvidia/nv_i2c.c b/drivers/video/nvidia/nv_i2c.c index 6fd7cb8f9b8..6aaddb4f678 100644 --- a/drivers/video/nvidia/nv_i2c.c +++ b/drivers/video/nvidia/nv_i2c.c @@ -87,7 +87,6 @@ static int nvidia_setup_i2c_bus(struct nvidia_i2c_chan *chan, const char *name, strcpy(chan->adapter.name, name); chan->adapter.owner = THIS_MODULE; - chan->adapter.id = I2C_HW_B_NVIDIA; chan->adapter.class = i2c_class; chan->adapter.algo_data = &chan->algo; chan->adapter.dev.parent = &chan->par->pci_dev->dev; diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c index 783d4adffb9..574b29e9f8f 100644 --- a/drivers/video/savage/savagefb-i2c.c +++ b/drivers/video/savage/savagefb-i2c.c @@ -137,7 +137,6 @@ static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan, if (chan->par) { strcpy(chan->adapter.name, name); chan->adapter.owner = THIS_MODULE; - chan->adapter.id = I2C_HW_B_SAVAGE; chan->adapter.algo_data = &chan->algo; chan->adapter.dev.parent = &chan->par->pcidev->dev; chan->algo.udelay = 10; -- cgit v1.2.3 From 5195e5093bb7d30dbf057b260005cb4ab9761168 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 26 Jan 2009 21:19:53 +0100 Subject: i2c: Move at24 to drivers/misc/eeprom As drivers/i2c/chips is going to go away, move the driver to drivers/misc/eeprom. Other eeprom drivers may be moved here later, too. Update Kconfig text to specify this driver as I2C. Signed-off-by: Wolfram Sang Signed-off-by: Jean Delvare --- drivers/i2c/chips/Kconfig | 26 -- drivers/i2c/chips/Makefile | 1 - drivers/i2c/chips/at24.c | 582 ------------------------------------------- drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/eeprom/Kconfig | 29 +++ drivers/misc/eeprom/Makefile | 1 + drivers/misc/eeprom/at24.c | 582 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 614 insertions(+), 609 deletions(-) delete mode 100644 drivers/i2c/chips/at24.c create mode 100644 drivers/misc/eeprom/Kconfig create mode 100644 drivers/misc/eeprom/Makefile create mode 100644 drivers/misc/eeprom/at24.c (limited to 'drivers') diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index b9bef04b7be..b58e77056b2 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -16,32 +16,6 @@ config DS1682 This driver can also be built as a module. If so, the module will be called ds1682. -config AT24 - tristate "EEPROMs from most vendors" - depends on SYSFS && EXPERIMENTAL - help - Enable this driver to get read/write support to most I2C EEPROMs, - after you configure the driver to know about each EEPROM on - your target board. Use these generic chip names, instead of - vendor-specific ones like at24c64 or 24lc02: - - 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, - 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024 - - Unless you like data loss puzzles, always be sure that any chip - you configure as a 24c32 (32 kbit) or larger is NOT really a - 24c16 (16 kbit) or smaller, and vice versa. Marking the chip - as read-only won't help recover from this. Also, if your chip - has any software write-protect mechanism you may want to review the - code to make sure this driver won't turn it on by accident. - - If you use this with an SMBus adapter instead of an I2C adapter, - full functionality is not available. Only smaller devices are - supported (24c16 and below, max 4 kByte). - - This driver can also be built as a module. If so, the module - will be called at24. - config SENSORS_EEPROM tristate "EEPROM reader" depends on EXPERIMENTAL diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 00fcb5193ac..5c14776eb76 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -11,7 +11,6 @@ # obj-$(CONFIG_DS1682) += ds1682.o -obj-$(CONFIG_AT24) += at24.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o obj-$(CONFIG_SENSORS_MAX6875) += max6875.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o diff --git a/drivers/i2c/chips/at24.c b/drivers/i2c/chips/at24.c deleted file mode 100644 index d4775528abc..00000000000 --- a/drivers/i2c/chips/at24.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * at24.c - handle most I2C EEPROMs - * - * Copyright (C) 2005-2007 David Brownell - * Copyright (C) 2008 Wolfram Sang, Pengutronix - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. - * Differences between different vendor product lines (like Atmel AT24C or - * MicroChip 24LC, etc) won't much matter for typical read/write access. - * There are also I2C RAM chips, likewise interchangeable. One example - * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). - * - * However, misconfiguration can lose data. "Set 16-bit memory address" - * to a part with 8-bit addressing will overwrite data. Writing with too - * big a page size also loses data. And it's not safe to assume that the - * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC - * uses 0x51, for just one example. - * - * Accordingly, explicit board-specific configuration data should be used - * in almost all cases. (One partial exception is an SMBus used to access - * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.) - * - * So this driver uses "new style" I2C driver binding, expecting to be - * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or - * similar kernel-resident tables; or, configuration data coming from - * a bootloader. - * - * Other than binding model, current differences from "eeprom" driver are - * that this one handles write access and isn't restricted to 24c02 devices. - * It also handles larger devices (32 kbit and up) with two-byte addresses, - * which won't work on pure SMBus systems. - */ - -struct at24_data { - struct at24_platform_data chip; - bool use_smbus; - - /* - * Lock protects against activities from other Linux tasks, - * but not from changes by other I2C masters. - */ - struct mutex lock; - struct bin_attribute bin; - - u8 *writebuf; - unsigned write_max; - unsigned num_addresses; - - /* - * Some chips tie up multiple I2C addresses; dummy devices reserve - * them for us, and we'll use them with SMBus calls. - */ - struct i2c_client *client[]; -}; - -/* - * This parameter is to help this driver avoid blocking other drivers out - * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C - * clock, one 256 byte read takes about 1/43 second which is excessive; - * but the 1/170 second it takes at 400 kHz may be quite reasonable; and - * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. - * - * This value is forced to be a power of two so that writes align on pages. - */ -static unsigned io_limit = 128; -module_param(io_limit, uint, 0); -MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)"); - -/* - * Specs often allow 5 msec for a page write, sometimes 20 msec; - * it's important to recover from write timeouts. - */ -static unsigned write_timeout = 25; -module_param(write_timeout, uint, 0); -MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); - -#define AT24_SIZE_BYTELEN 5 -#define AT24_SIZE_FLAGS 8 - -#define AT24_BITMASK(x) (BIT(x) - 1) - -/* create non-zero magic value for given eeprom parameters */ -#define AT24_DEVICE_MAGIC(_len, _flags) \ - ((1 << AT24_SIZE_FLAGS | (_flags)) \ - << AT24_SIZE_BYTELEN | ilog2(_len)) - -static const struct i2c_device_id at24_ids[] = { - /* needs 8 addresses as A0-A2 are ignored */ - { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, - /* old variants can't be handled with this generic entry! */ - { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) }, - { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, - /* spd is a 24c02 in memory DIMMs */ - { "spd", AT24_DEVICE_MAGIC(2048 / 8, - AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, - { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, - /* 24rf08 quirk is handled at i2c-core */ - { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, - { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, - { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) }, - { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, - { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) }, - { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, - { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, - { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, - { "at24", 0 }, - { /* END OF LIST */ } -}; -MODULE_DEVICE_TABLE(i2c, at24_ids); - -/*-------------------------------------------------------------------------*/ - -/* - * This routine supports chips which consume multiple I2C addresses. It - * computes the addressing information to be used for a given r/w request. - * Assumes that sanity checks for offset happened at sysfs-layer. - */ -static struct i2c_client *at24_translate_offset(struct at24_data *at24, - unsigned *offset) -{ - unsigned i; - - if (at24->chip.flags & AT24_FLAG_ADDR16) { - i = *offset >> 16; - *offset &= 0xffff; - } else { - i = *offset >> 8; - *offset &= 0xff; - } - - return at24->client[i]; -} - -static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, - unsigned offset, size_t count) -{ - struct i2c_msg msg[2]; - u8 msgbuf[2]; - struct i2c_client *client; - int status, i; - - memset(msg, 0, sizeof(msg)); - - /* - * REVISIT some multi-address chips don't rollover page reads to - * the next slave address, so we may need to truncate the count. - * Those chips might need another quirk flag. - * - * If the real hardware used four adjacent 24c02 chips and that - * were misconfigured as one 24c08, that would be a similar effect: - * one "eeprom" file not four, but larger reads would fail when - * they crossed certain pages. - */ - - /* - * Slave address and byte offset derive from the offset. Always - * set the byte address; on a multi-master board, another master - * may have changed the chip's "current" address pointer. - */ - client = at24_translate_offset(at24, &offset); - - if (count > io_limit) - count = io_limit; - - /* Smaller eeproms can work given some SMBus extension calls */ - if (at24->use_smbus) { - if (count > I2C_SMBUS_BLOCK_MAX) - count = I2C_SMBUS_BLOCK_MAX; - status = i2c_smbus_read_i2c_block_data(client, offset, - count, buf); - dev_dbg(&client->dev, "smbus read %zu@%d --> %d\n", - count, offset, status); - return (status < 0) ? -EIO : status; - } - - /* - * When we have a better choice than SMBus calls, use a combined - * I2C message. Write address; then read up to io_limit data bytes. - * Note that read page rollover helps us here (unlike writes). - * msgbuf is u8 and will cast to our needs. - */ - i = 0; - if (at24->chip.flags & AT24_FLAG_ADDR16) - msgbuf[i++] = offset >> 8; - msgbuf[i++] = offset; - - msg[0].addr = client->addr; - msg[0].buf = msgbuf; - msg[0].len = i; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].buf = buf; - msg[1].len = count; - - status = i2c_transfer(client->adapter, msg, 2); - dev_dbg(&client->dev, "i2c read %zu@%d --> %d\n", - count, offset, status); - - if (status == 2) - return count; - else if (status >= 0) - return -EIO; - else - return status; -} - -static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - struct at24_data *at24; - ssize_t retval = 0; - - at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); - - if (unlikely(!count)) - return count; - - /* - * Read data from chip, protecting against concurrent updates - * from this host, but not from other I2C masters. - */ - mutex_lock(&at24->lock); - - while (count) { - ssize_t status; - - status = at24_eeprom_read(at24, buf, off, count); - if (status <= 0) { - if (retval == 0) - retval = status; - break; - } - buf += status; - off += status; - count -= status; - retval += status; - } - - mutex_unlock(&at24->lock); - - return retval; -} - - -/* - * REVISIT: export at24_bin{read,write}() to let other kernel code use - * eeprom data. For example, it might hold a board's Ethernet address, or - * board-specific calibration data generated on the manufacturing floor. - */ - - -/* - * Note that if the hardware write-protect pin is pulled high, the whole - * chip is normally write protected. But there are plenty of product - * variants here, including OTP fuses and partial chip protect. - * - * We only use page mode writes; the alternative is sloooow. This routine - * writes at most one page. - */ -static ssize_t at24_eeprom_write(struct at24_data *at24, char *buf, - unsigned offset, size_t count) -{ - struct i2c_client *client; - struct i2c_msg msg; - ssize_t status; - unsigned long timeout, write_time; - unsigned next_page; - - /* Get corresponding I2C address and adjust offset */ - client = at24_translate_offset(at24, &offset); - - /* write_max is at most a page */ - if (count > at24->write_max) - count = at24->write_max; - - /* Never roll over backwards, to the start of this page */ - next_page = roundup(offset + 1, at24->chip.page_size); - if (offset + count > next_page) - count = next_page - offset; - - /* If we'll use I2C calls for I/O, set up the message */ - if (!at24->use_smbus) { - int i = 0; - - msg.addr = client->addr; - msg.flags = 0; - - /* msg.buf is u8 and casts will mask the values */ - msg.buf = at24->writebuf; - if (at24->chip.flags & AT24_FLAG_ADDR16) - msg.buf[i++] = offset >> 8; - - msg.buf[i++] = offset; - memcpy(&msg.buf[i], buf, count); - msg.len = i + count; - } - - /* - * Writes fail if the previous one didn't complete yet. We may - * loop a few times until this one succeeds, waiting at least - * long enough for one entire page write to work. - */ - timeout = jiffies + msecs_to_jiffies(write_timeout); - do { - write_time = jiffies; - if (at24->use_smbus) { - status = i2c_smbus_write_i2c_block_data(client, - offset, count, buf); - if (status == 0) - status = count; - } else { - status = i2c_transfer(client->adapter, &msg, 1); - if (status == 1) - status = count; - } - dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", - count, offset, status, jiffies); - - if (status == count) - return count; - - /* REVISIT: at HZ=100, this is sloooow */ - msleep(1); - } while (time_before(write_time, timeout)); - - return -ETIMEDOUT; -} - -static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr, - char *buf, loff_t off, size_t count) -{ - struct at24_data *at24; - ssize_t retval = 0; - - at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); - - if (unlikely(!count)) - return count; - - /* - * Write data to chip, protecting against concurrent updates - * from this host, but not from other I2C masters. - */ - mutex_lock(&at24->lock); - - while (count) { - ssize_t status; - - status = at24_eeprom_write(at24, buf, off, count); - if (status <= 0) { - if (retval == 0) - retval = status; - break; - } - buf += status; - off += status; - count -= status; - retval += status; - } - - mutex_unlock(&at24->lock); - - return retval; -} - -/*-------------------------------------------------------------------------*/ - -static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct at24_platform_data chip; - bool writable; - bool use_smbus = false; - struct at24_data *at24; - int err; - unsigned i, num_addresses; - kernel_ulong_t magic; - - if (client->dev.platform_data) { - chip = *(struct at24_platform_data *)client->dev.platform_data; - } else { - if (!id->driver_data) { - err = -ENODEV; - goto err_out; - } - magic = id->driver_data; - chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); - magic >>= AT24_SIZE_BYTELEN; - chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); - /* - * This is slow, but we can't know all eeproms, so we better - * play safe. Specifying custom eeprom-types via platform_data - * is recommended anyhow. - */ - chip.page_size = 1; - } - - if (!is_power_of_2(chip.byte_len)) - dev_warn(&client->dev, - "byte_len looks suspicious (no power of 2)!\n"); - if (!is_power_of_2(chip.page_size)) - dev_warn(&client->dev, - "page_size looks suspicious (no power of 2)!\n"); - - /* Use I2C operations unless we're stuck with SMBus extensions. */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - if (chip.flags & AT24_FLAG_ADDR16) { - err = -EPFNOSUPPORT; - goto err_out; - } - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { - err = -EPFNOSUPPORT; - goto err_out; - } - use_smbus = true; - } - - if (chip.flags & AT24_FLAG_TAKE8ADDR) - num_addresses = 8; - else - num_addresses = DIV_ROUND_UP(chip.byte_len, - (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); - - at24 = kzalloc(sizeof(struct at24_data) + - num_addresses * sizeof(struct i2c_client *), GFP_KERNEL); - if (!at24) { - err = -ENOMEM; - goto err_out; - } - - mutex_init(&at24->lock); - at24->use_smbus = use_smbus; - at24->chip = chip; - at24->num_addresses = num_addresses; - - /* - * Export the EEPROM bytes through sysfs, since that's convenient. - * By default, only root should see the data (maybe passwords etc) - */ - at24->bin.attr.name = "eeprom"; - at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR; - at24->bin.read = at24_bin_read; - at24->bin.size = chip.byte_len; - - writable = !(chip.flags & AT24_FLAG_READONLY); - if (writable) { - if (!use_smbus || i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { - - unsigned write_max = chip.page_size; - - at24->bin.write = at24_bin_write; - at24->bin.attr.mode |= S_IWUSR; - - if (write_max > io_limit) - write_max = io_limit; - if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) - write_max = I2C_SMBUS_BLOCK_MAX; - at24->write_max = write_max; - - /* buffer (data + address at the beginning) */ - at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL); - if (!at24->writebuf) { - err = -ENOMEM; - goto err_struct; - } - } else { - dev_warn(&client->dev, - "cannot write due to controller restrictions."); - } - } - - at24->client[0] = client; - - /* use dummy devices for multiple-address chips */ - for (i = 1; i < num_addresses; i++) { - at24->client[i] = i2c_new_dummy(client->adapter, - client->addr + i); - if (!at24->client[i]) { - dev_err(&client->dev, "address 0x%02x unavailable\n", - client->addr + i); - err = -EADDRINUSE; - goto err_clients; - } - } - - err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin); - if (err) - goto err_clients; - - i2c_set_clientdata(client, at24); - - dev_info(&client->dev, "%zu byte %s EEPROM %s\n", - at24->bin.size, client->name, - writable ? "(writable)" : "(read-only)"); - dev_dbg(&client->dev, - "page_size %d, num_addresses %d, write_max %d%s\n", - chip.page_size, num_addresses, - at24->write_max, - use_smbus ? ", use_smbus" : ""); - - return 0; - -err_clients: - for (i = 1; i < num_addresses; i++) - if (at24->client[i]) - i2c_unregister_device(at24->client[i]); - - kfree(at24->writebuf); -err_struct: - kfree(at24); -err_out: - dev_dbg(&client->dev, "probe error %d\n", err); - return err; -} - -static int __devexit at24_remove(struct i2c_client *client) -{ - struct at24_data *at24; - int i; - - at24 = i2c_get_clientdata(client); - sysfs_remove_bin_file(&client->dev.kobj, &at24->bin); - - for (i = 1; i < at24->num_addresses; i++) - i2c_unregister_device(at24->client[i]); - - kfree(at24->writebuf); - kfree(at24); - i2c_set_clientdata(client, NULL); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct i2c_driver at24_driver = { - .driver = { - .name = "at24", - .owner = THIS_MODULE, - }, - .probe = at24_probe, - .remove = __devexit_p(at24_remove), - .id_table = at24_ids, -}; - -static int __init at24_init(void) -{ - io_limit = rounddown_pow_of_two(io_limit); - return i2c_add_driver(&at24_driver); -} -module_init(at24_init); - -static void __exit at24_exit(void) -{ - i2c_del_driver(&at24_driver); -} -module_exit(at24_exit); - -MODULE_DESCRIPTION("Driver for most I2C EEPROMs"); -MODULE_AUTHOR("David Brownell and Wolfram Sang"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 419c378bd24..6c9cd9d3008 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -231,5 +231,6 @@ config DELL_LAPTOP laptops. source "drivers/misc/c2port/Kconfig" +source "drivers/misc/eeprom/Kconfig" endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d5749a7bc77..0ec23203c99 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -20,3 +20,4 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o obj-$(CONFIG_C2PORT) += c2port/ +obj-y += eeprom/ diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig new file mode 100644 index 00000000000..0b21778b7d1 --- /dev/null +++ b/drivers/misc/eeprom/Kconfig @@ -0,0 +1,29 @@ +menu "EEPROM support" + +config AT24 + tristate "I2C EEPROMs from most vendors" + depends on I2C && SYSFS && EXPERIMENTAL + help + Enable this driver to get read/write support to most I2C EEPROMs, + after you configure the driver to know about each EEPROM on + your target board. Use these generic chip names, instead of + vendor-specific ones like at24c64 or 24lc02: + + 24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08, + 24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024 + + Unless you like data loss puzzles, always be sure that any chip + you configure as a 24c32 (32 kbit) or larger is NOT really a + 24c16 (16 kbit) or smaller, and vice versa. Marking the chip + as read-only won't help recover from this. Also, if your chip + has any software write-protect mechanism you may want to review the + code to make sure this driver won't turn it on by accident. + + If you use this with an SMBus adapter instead of an I2C adapter, + full functionality is not available. Only smaller devices are + supported (24c16 and below, max 4 kByte). + + This driver can also be built as a module. If so, the module + will be called at24. + +endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile new file mode 100644 index 00000000000..72cd478eb53 --- /dev/null +++ b/drivers/misc/eeprom/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AT24) += at24.o diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c new file mode 100644 index 00000000000..d4775528abc --- /dev/null +++ b/drivers/misc/eeprom/at24.c @@ -0,0 +1,582 @@ +/* + * at24.c - handle most I2C EEPROMs + * + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * I2C EEPROMs from most vendors are inexpensive and mostly interchangeable. + * Differences between different vendor product lines (like Atmel AT24C or + * MicroChip 24LC, etc) won't much matter for typical read/write access. + * There are also I2C RAM chips, likewise interchangeable. One example + * would be the PCF8570, which acts like a 24c02 EEPROM (256 bytes). + * + * However, misconfiguration can lose data. "Set 16-bit memory address" + * to a part with 8-bit addressing will overwrite data. Writing with too + * big a page size also loses data. And it's not safe to assume that the + * conventional addresses 0x50..0x57 only hold eeproms; a PCF8563 RTC + * uses 0x51, for just one example. + * + * Accordingly, explicit board-specific configuration data should be used + * in almost all cases. (One partial exception is an SMBus used to access + * "SPD" data for DRAM sticks. Those only use 24c02 EEPROMs.) + * + * So this driver uses "new style" I2C driver binding, expecting to be + * told what devices exist. That may be in arch/X/mach-Y/board-Z.c or + * similar kernel-resident tables; or, configuration data coming from + * a bootloader. + * + * Other than binding model, current differences from "eeprom" driver are + * that this one handles write access and isn't restricted to 24c02 devices. + * It also handles larger devices (32 kbit and up) with two-byte addresses, + * which won't work on pure SMBus systems. + */ + +struct at24_data { + struct at24_platform_data chip; + bool use_smbus; + + /* + * Lock protects against activities from other Linux tasks, + * but not from changes by other I2C masters. + */ + struct mutex lock; + struct bin_attribute bin; + + u8 *writebuf; + unsigned write_max; + unsigned num_addresses; + + /* + * Some chips tie up multiple I2C addresses; dummy devices reserve + * them for us, and we'll use them with SMBus calls. + */ + struct i2c_client *client[]; +}; + +/* + * This parameter is to help this driver avoid blocking other drivers out + * of I2C for potentially troublesome amounts of time. With a 100 kHz I2C + * clock, one 256 byte read takes about 1/43 second which is excessive; + * but the 1/170 second it takes at 400 kHz may be quite reasonable; and + * at 1 MHz (Fm+) a 1/430 second delay could easily be invisible. + * + * This value is forced to be a power of two so that writes align on pages. + */ +static unsigned io_limit = 128; +module_param(io_limit, uint, 0); +MODULE_PARM_DESC(io_limit, "Maximum bytes per I/O (default 128)"); + +/* + * Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +static unsigned write_timeout = 25; +module_param(write_timeout, uint, 0); +MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); + +#define AT24_SIZE_BYTELEN 5 +#define AT24_SIZE_FLAGS 8 + +#define AT24_BITMASK(x) (BIT(x) - 1) + +/* create non-zero magic value for given eeprom parameters */ +#define AT24_DEVICE_MAGIC(_len, _flags) \ + ((1 << AT24_SIZE_FLAGS | (_flags)) \ + << AT24_SIZE_BYTELEN | ilog2(_len)) + +static const struct i2c_device_id at24_ids[] = { + /* needs 8 addresses as A0-A2 are ignored */ + { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, + /* old variants can't be handled with this generic entry! */ + { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) }, + { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, + /* spd is a 24c02 in memory DIMMs */ + { "spd", AT24_DEVICE_MAGIC(2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, + { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, + /* 24rf08 quirk is handled at i2c-core */ + { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, + { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, + { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) }, + { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, + { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) }, + { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, + { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, + { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, + { "at24", 0 }, + { /* END OF LIST */ } +}; +MODULE_DEVICE_TABLE(i2c, at24_ids); + +/*-------------------------------------------------------------------------*/ + +/* + * This routine supports chips which consume multiple I2C addresses. It + * computes the addressing information to be used for a given r/w request. + * Assumes that sanity checks for offset happened at sysfs-layer. + */ +static struct i2c_client *at24_translate_offset(struct at24_data *at24, + unsigned *offset) +{ + unsigned i; + + if (at24->chip.flags & AT24_FLAG_ADDR16) { + i = *offset >> 16; + *offset &= 0xffff; + } else { + i = *offset >> 8; + *offset &= 0xff; + } + + return at24->client[i]; +} + +static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, + unsigned offset, size_t count) +{ + struct i2c_msg msg[2]; + u8 msgbuf[2]; + struct i2c_client *client; + int status, i; + + memset(msg, 0, sizeof(msg)); + + /* + * REVISIT some multi-address chips don't rollover page reads to + * the next slave address, so we may need to truncate the count. + * Those chips might need another quirk flag. + * + * If the real hardware used four adjacent 24c02 chips and that + * were misconfigured as one 24c08, that would be a similar effect: + * one "eeprom" file not four, but larger reads would fail when + * they crossed certain pages. + */ + + /* + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + */ + client = at24_translate_offset(at24, &offset); + + if (count > io_limit) + count = io_limit; + + /* Smaller eeproms can work given some SMBus extension calls */ + if (at24->use_smbus) { + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + status = i2c_smbus_read_i2c_block_data(client, offset, + count, buf); + dev_dbg(&client->dev, "smbus read %zu@%d --> %d\n", + count, offset, status); + return (status < 0) ? -EIO : status; + } + + /* + * When we have a better choice than SMBus calls, use a combined + * I2C message. Write address; then read up to io_limit data bytes. + * Note that read page rollover helps us here (unlike writes). + * msgbuf is u8 and will cast to our needs. + */ + i = 0; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msgbuf[i++] = offset >> 8; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + status = i2c_transfer(client->adapter, msg, 2); + dev_dbg(&client->dev, "i2c read %zu@%d --> %d\n", + count, offset, status); + + if (status == 2) + return count; + else if (status >= 0) + return -EIO; + else + return status; +} + +static ssize_t at24_bin_read(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct at24_data *at24; + ssize_t retval = 0; + + at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + if (unlikely(!count)) + return count; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + ssize_t status; + + status = at24_eeprom_read(at24, buf, off, count); + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&at24->lock); + + return retval; +} + + +/* + * REVISIT: export at24_bin{read,write}() to let other kernel code use + * eeprom data. For example, it might hold a board's Ethernet address, or + * board-specific calibration data generated on the manufacturing floor. + */ + + +/* + * Note that if the hardware write-protect pin is pulled high, the whole + * chip is normally write protected. But there are plenty of product + * variants here, including OTP fuses and partial chip protect. + * + * We only use page mode writes; the alternative is sloooow. This routine + * writes at most one page. + */ +static ssize_t at24_eeprom_write(struct at24_data *at24, char *buf, + unsigned offset, size_t count) +{ + struct i2c_client *client; + struct i2c_msg msg; + ssize_t status; + unsigned long timeout, write_time; + unsigned next_page; + + /* Get corresponding I2C address and adjust offset */ + client = at24_translate_offset(at24, &offset); + + /* write_max is at most a page */ + if (count > at24->write_max) + count = at24->write_max; + + /* Never roll over backwards, to the start of this page */ + next_page = roundup(offset + 1, at24->chip.page_size); + if (offset + count > next_page) + count = next_page - offset; + + /* If we'll use I2C calls for I/O, set up the message */ + if (!at24->use_smbus) { + int i = 0; + + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = at24->writebuf; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msg.buf[i++] = offset >> 8; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + } + + /* + * Writes fail if the previous one didn't complete yet. We may + * loop a few times until this one succeeds, waiting at least + * long enough for one entire page write to work. + */ + timeout = jiffies + msecs_to_jiffies(write_timeout); + do { + write_time = jiffies; + if (at24->use_smbus) { + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; + } else { + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + } + dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + + /* REVISIT: at HZ=100, this is sloooow */ + msleep(1); + } while (time_before(write_time, timeout)); + + return -ETIMEDOUT; +} + +static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct at24_data *at24; + ssize_t retval = 0; + + at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); + + if (unlikely(!count)) + return count; + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + ssize_t status; + + status = at24_eeprom_write(at24, buf, off, count); + if (status <= 0) { + if (retval == 0) + retval = status; + break; + } + buf += status; + off += status; + count -= status; + retval += status; + } + + mutex_unlock(&at24->lock); + + return retval; +} + +/*-------------------------------------------------------------------------*/ + +static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct at24_platform_data chip; + bool writable; + bool use_smbus = false; + struct at24_data *at24; + int err; + unsigned i, num_addresses; + kernel_ulong_t magic; + + if (client->dev.platform_data) { + chip = *(struct at24_platform_data *)client->dev.platform_data; + } else { + if (!id->driver_data) { + err = -ENODEV; + goto err_out; + } + magic = id->driver_data; + chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); + magic >>= AT24_SIZE_BYTELEN; + chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip.page_size = 1; + } + + if (!is_power_of_2(chip.byte_len)) + dev_warn(&client->dev, + "byte_len looks suspicious (no power of 2)!\n"); + if (!is_power_of_2(chip.page_size)) + dev_warn(&client->dev, + "page_size looks suspicious (no power of 2)!\n"); + + /* Use I2C operations unless we're stuck with SMBus extensions. */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + if (chip.flags & AT24_FLAG_ADDR16) { + err = -EPFNOSUPPORT; + goto err_out; + } + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + err = -EPFNOSUPPORT; + goto err_out; + } + use_smbus = true; + } + + if (chip.flags & AT24_FLAG_TAKE8ADDR) + num_addresses = 8; + else + num_addresses = DIV_ROUND_UP(chip.byte_len, + (chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256); + + at24 = kzalloc(sizeof(struct at24_data) + + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL); + if (!at24) { + err = -ENOMEM; + goto err_out; + } + + mutex_init(&at24->lock); + at24->use_smbus = use_smbus; + at24->chip = chip; + at24->num_addresses = num_addresses; + + /* + * Export the EEPROM bytes through sysfs, since that's convenient. + * By default, only root should see the data (maybe passwords etc) + */ + at24->bin.attr.name = "eeprom"; + at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR; + at24->bin.read = at24_bin_read; + at24->bin.size = chip.byte_len; + + writable = !(chip.flags & AT24_FLAG_READONLY); + if (writable) { + if (!use_smbus || i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { + + unsigned write_max = chip.page_size; + + at24->bin.write = at24_bin_write; + at24->bin.attr.mode |= S_IWUSR; + + if (write_max > io_limit) + write_max = io_limit; + if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) + write_max = I2C_SMBUS_BLOCK_MAX; + at24->write_max = write_max; + + /* buffer (data + address at the beginning) */ + at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL); + if (!at24->writebuf) { + err = -ENOMEM; + goto err_struct; + } + } else { + dev_warn(&client->dev, + "cannot write due to controller restrictions."); + } + } + + at24->client[0] = client; + + /* use dummy devices for multiple-address chips */ + for (i = 1; i < num_addresses; i++) { + at24->client[i] = i2c_new_dummy(client->adapter, + client->addr + i); + if (!at24->client[i]) { + dev_err(&client->dev, "address 0x%02x unavailable\n", + client->addr + i); + err = -EADDRINUSE; + goto err_clients; + } + } + + err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin); + if (err) + goto err_clients; + + i2c_set_clientdata(client, at24); + + dev_info(&client->dev, "%zu byte %s EEPROM %s\n", + at24->bin.size, client->name, + writable ? "(writable)" : "(read-only)"); + dev_dbg(&client->dev, + "page_size %d, num_addresses %d, write_max %d%s\n", + chip.page_size, num_addresses, + at24->write_max, + use_smbus ? ", use_smbus" : ""); + + return 0; + +err_clients: + for (i = 1; i < num_addresses; i++) + if (at24->client[i]) + i2c_unregister_device(at24->client[i]); + + kfree(at24->writebuf); +err_struct: + kfree(at24); +err_out: + dev_dbg(&client->dev, "probe error %d\n", err); + return err; +} + +static int __devexit at24_remove(struct i2c_client *client) +{ + struct at24_data *at24; + int i; + + at24 = i2c_get_clientdata(client); + sysfs_remove_bin_file(&client->dev.kobj, &at24->bin); + + for (i = 1; i < at24->num_addresses; i++) + i2c_unregister_device(at24->client[i]); + + kfree(at24->writebuf); + kfree(at24); + i2c_set_clientdata(client, NULL); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver at24_driver = { + .driver = { + .name = "at24", + .owner = THIS_MODULE, + }, + .probe = at24_probe, + .remove = __devexit_p(at24_remove), + .id_table = at24_ids, +}; + +static int __init at24_init(void) +{ + io_limit = rounddown_pow_of_two(io_limit); + return i2c_add_driver(&at24_driver); +} +module_init(at24_init); + +static void __exit at24_exit(void) +{ + i2c_del_driver(&at24_driver); +} +module_exit(at24_exit); + +MODULE_DESCRIPTION("Driver for most I2C EEPROMs"); +MODULE_AUTHOR("David Brownell and Wolfram Sang"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2e157888f132131f8877affd2785dcee4c227c1d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 26 Jan 2009 21:19:53 +0100 Subject: i2c: Move old eeprom driver to /drivers/misc/eeprom Update Kconfig text to specify this driver as I2C. Signed-off-by: Wolfram Sang Signed-off-by: Jean Delvare --- drivers/i2c/chips/Kconfig | 11 -- drivers/i2c/chips/Makefile | 1 - drivers/i2c/chips/eeprom.c | 257 ------------------------------------------- drivers/misc/eeprom/Kconfig | 11 ++ drivers/misc/eeprom/Makefile | 1 + drivers/misc/eeprom/eeprom.c | 257 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 269 insertions(+), 269 deletions(-) delete mode 100644 drivers/i2c/chips/eeprom.c create mode 100644 drivers/misc/eeprom/eeprom.c (limited to 'drivers') diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index b58e77056b2..c80312c1f38 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -16,17 +16,6 @@ config DS1682 This driver can also be built as a module. If so, the module will be called ds1682. -config SENSORS_EEPROM - tristate "EEPROM reader" - depends on EXPERIMENTAL - help - If you say yes here you get read-only access to the EEPROM data - available on modern memory DIMMs and Sony Vaio laptops. Such - EEPROMs could theoretically be available on other devices as well. - - This driver can also be built as a module. If so, the module - will be called eeprom. - config SENSORS_PCF8574 tristate "Philips PCF8574 and PCF8574A (DEPRECATED)" depends on EXPERIMENTAL && GPIO_PCF857X = "n" diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 5c14776eb76..d142f238a2d 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -11,7 +11,6 @@ # obj-$(CONFIG_DS1682) += ds1682.o -obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o obj-$(CONFIG_SENSORS_MAX6875) += max6875.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c deleted file mode 100644 index 2c27193aeaa..00000000000 --- a/drivers/i2c/chips/eeprom.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - Copyright (C) 1998, 1999 Frodo Looijaard and - Philip Edelbrock - Copyright (C) 2003 Greg Kroah-Hartman - Copyright (C) 2003 IBM Corp. - Copyright (C) 2004 Jean Delvare - - 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 program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - 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 -#include -#include -#include -#include -#include -#include - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, - 0x55, 0x56, 0x57, I2C_CLIENT_END }; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_1(eeprom); - - -/* Size of EEPROM in bytes */ -#define EEPROM_SIZE 256 - -/* possible types of eeprom devices */ -enum eeprom_nature { - UNKNOWN, - VAIO, -}; - -/* Each client has this additional data */ -struct eeprom_data { - struct mutex update_lock; - u8 valid; /* bitfield, bit!=0 if slice is valid */ - unsigned long last_updated[8]; /* In jiffies, 8 slices */ - u8 data[EEPROM_SIZE]; /* Register values */ - enum eeprom_nature nature; -}; - - -static void eeprom_update_client(struct i2c_client *client, u8 slice) -{ - struct eeprom_data *data = i2c_get_clientdata(client); - int i; - - mutex_lock(&data->update_lock); - - if (!(data->valid & (1 << slice)) || - time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { - dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); - - if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { - for (i = slice << 5; i < (slice + 1) << 5; i += 32) - if (i2c_smbus_read_i2c_block_data(client, i, - 32, data->data + i) - != 32) - goto exit; - } else { - for (i = slice << 5; i < (slice + 1) << 5; i += 2) { - int word = i2c_smbus_read_word_data(client, i); - if (word < 0) - goto exit; - data->data[i] = word & 0xff; - data->data[i + 1] = word >> 8; - } - } - data->last_updated[slice] = jiffies; - data->valid |= (1 << slice); - } -exit: - mutex_unlock(&data->update_lock); -} - -static ssize_t eeprom_read(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); - struct eeprom_data *data = i2c_get_clientdata(client); - u8 slice; - - if (off > EEPROM_SIZE) - return 0; - if (off + count > EEPROM_SIZE) - count = EEPROM_SIZE - off; - - /* Only refresh slices which contain requested bytes */ - for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++) - eeprom_update_client(client, slice); - - /* Hide Vaio private settings to regular users: - - BIOS passwords: bytes 0x00 to 0x0f - - UUID: bytes 0x10 to 0x1f - - Serial number: 0xc0 to 0xdf */ - if (data->nature == VAIO && !capable(CAP_SYS_ADMIN)) { - int i; - - for (i = 0; i < count; i++) { - if ((off + i <= 0x1f) || - (off + i >= 0xc0 && off + i <= 0xdf)) - buf[i] = 0; - else - buf[i] = data->data[off + i]; - } - } else { - memcpy(buf, &data->data[off], count); - } - - return count; -} - -static struct bin_attribute eeprom_attr = { - .attr = { - .name = "eeprom", - .mode = S_IRUGO, - }, - .size = EEPROM_SIZE, - .read = eeprom_read, -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int eeprom_detect(struct i2c_client *client, int kind, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - - /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all - addresses 0x50-0x57, but we only care about 0x50. So decline - attaching to addresses >= 0x51 on DDC buses */ - if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51) - return -ENODEV; - - /* There are four ways we can read the EEPROM data: - (1) I2C block reads (faster, but unsupported by most adapters) - (2) Word reads (128% overhead) - (3) Consecutive byte reads (88% overhead, unsafe) - (4) Regular byte data reads (265% overhead) - The third and fourth methods are not implemented by this driver - because all known adapters support one of the first two. */ - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA) - && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) - return -ENODEV; - - strlcpy(info->type, "eeprom", I2C_NAME_SIZE); - - return 0; -} - -static int eeprom_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct eeprom_data *data; - int err; - - if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - - memset(data->data, 0xff, EEPROM_SIZE); - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - data->nature = UNKNOWN; - - /* Detect the Vaio nature of EEPROMs. - We use the "PCG-" or "VGN-" prefix as the signature. */ - if (client->addr == 0x57 - && i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) { - char name[4]; - - name[0] = i2c_smbus_read_byte_data(client, 0x80); - name[1] = i2c_smbus_read_byte_data(client, 0x81); - name[2] = i2c_smbus_read_byte_data(client, 0x82); - name[3] = i2c_smbus_read_byte_data(client, 0x83); - - if (!memcmp(name, "PCG-", 4) || !memcmp(name, "VGN-", 4)) { - dev_info(&client->dev, "Vaio EEPROM detected, " - "enabling privacy protection\n"); - data->nature = VAIO; - } - } - - /* create the sysfs eeprom file */ - err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); - if (err) - goto exit_kfree; - - return 0; - -exit_kfree: - kfree(data); -exit: - return err; -} - -static int eeprom_remove(struct i2c_client *client) -{ - sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); - kfree(i2c_get_clientdata(client)); - - return 0; -} - -static const struct i2c_device_id eeprom_id[] = { - { "eeprom", 0 }, - { } -}; - -static struct i2c_driver eeprom_driver = { - .driver = { - .name = "eeprom", - }, - .probe = eeprom_probe, - .remove = eeprom_remove, - .id_table = eeprom_id, - - .class = I2C_CLASS_DDC | I2C_CLASS_SPD, - .detect = eeprom_detect, - .address_data = &addr_data, -}; - -static int __init eeprom_init(void) -{ - return i2c_add_driver(&eeprom_driver); -} - -static void __exit eeprom_exit(void) -{ - i2c_del_driver(&eeprom_driver); -} - - -MODULE_AUTHOR("Frodo Looijaard and " - "Philip Edelbrock and " - "Greg Kroah-Hartman "); -MODULE_DESCRIPTION("I2C EEPROM driver"); -MODULE_LICENSE("GPL"); - -module_init(eeprom_init); -module_exit(eeprom_exit); diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 0b21778b7d1..3d31b664424 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -26,4 +26,15 @@ config AT24 This driver can also be built as a module. If so, the module will be called at24. +config SENSORS_EEPROM + tristate "Old I2C EEPROM reader" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get read-only access to the EEPROM data + available on modern memory DIMMs and Sony Vaio laptops via I2C. Such + EEPROMs could theoretically be available on other devices as well. + + This driver can also be built as a module. If so, the module + will be called eeprom. + endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 72cd478eb53..a3dad28f272 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_AT24) += at24.o +obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c new file mode 100644 index 00000000000..2c27193aeaa --- /dev/null +++ b/drivers/misc/eeprom/eeprom.c @@ -0,0 +1,257 @@ +/* + Copyright (C) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + Copyright (C) 2003 Greg Kroah-Hartman + Copyright (C) 2003 IBM Corp. + Copyright (C) 2004 Jean Delvare + + 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 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + 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 +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(eeprom); + + +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 256 + +/* possible types of eeprom devices */ +enum eeprom_nature { + UNKNOWN, + VAIO, +}; + +/* Each client has this additional data */ +struct eeprom_data { + struct mutex update_lock; + u8 valid; /* bitfield, bit!=0 if slice is valid */ + unsigned long last_updated[8]; /* In jiffies, 8 slices */ + u8 data[EEPROM_SIZE]; /* Register values */ + enum eeprom_nature nature; +}; + + +static void eeprom_update_client(struct i2c_client *client, u8 slice) +{ + struct eeprom_data *data = i2c_get_clientdata(client); + int i; + + mutex_lock(&data->update_lock); + + if (!(data->valid & (1 << slice)) || + time_after(jiffies, data->last_updated[slice] + 300 * HZ)) { + dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice); + + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + for (i = slice << 5; i < (slice + 1) << 5; i += 32) + if (i2c_smbus_read_i2c_block_data(client, i, + 32, data->data + i) + != 32) + goto exit; + } else { + for (i = slice << 5; i < (slice + 1) << 5; i += 2) { + int word = i2c_smbus_read_word_data(client, i); + if (word < 0) + goto exit; + data->data[i] = word & 0xff; + data->data[i + 1] = word >> 8; + } + } + data->last_updated[slice] = jiffies; + data->valid |= (1 << slice); + } +exit: + mutex_unlock(&data->update_lock); +} + +static ssize_t eeprom_read(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); + struct eeprom_data *data = i2c_get_clientdata(client); + u8 slice; + + if (off > EEPROM_SIZE) + return 0; + if (off + count > EEPROM_SIZE) + count = EEPROM_SIZE - off; + + /* Only refresh slices which contain requested bytes */ + for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++) + eeprom_update_client(client, slice); + + /* Hide Vaio private settings to regular users: + - BIOS passwords: bytes 0x00 to 0x0f + - UUID: bytes 0x10 to 0x1f + - Serial number: 0xc0 to 0xdf */ + if (data->nature == VAIO && !capable(CAP_SYS_ADMIN)) { + int i; + + for (i = 0; i < count; i++) { + if ((off + i <= 0x1f) || + (off + i >= 0xc0 && off + i <= 0xdf)) + buf[i] = 0; + else + buf[i] = data->data[off + i]; + } + } else { + memcpy(buf, &data->data[off], count); + } + + return count; +} + +static struct bin_attribute eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = S_IRUGO, + }, + .size = EEPROM_SIZE, + .read = eeprom_read, +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int eeprom_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all + addresses 0x50-0x57, but we only care about 0x50. So decline + attaching to addresses >= 0x51 on DDC buses */ + if (!(adapter->class & I2C_CLASS_SPD) && client->addr >= 0x51) + return -ENODEV; + + /* There are four ways we can read the EEPROM data: + (1) I2C block reads (faster, but unsupported by most adapters) + (2) Word reads (128% overhead) + (3) Consecutive byte reads (88% overhead, unsafe) + (4) Regular byte data reads (265% overhead) + The third and fourth methods are not implemented by this driver + because all known adapters support one of the first two. */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA) + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -ENODEV; + + strlcpy(info->type, "eeprom", I2C_NAME_SIZE); + + return 0; +} + +static int eeprom_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct eeprom_data *data; + int err; + + if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data->data, 0xff, EEPROM_SIZE); + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + data->nature = UNKNOWN; + + /* Detect the Vaio nature of EEPROMs. + We use the "PCG-" or "VGN-" prefix as the signature. */ + if (client->addr == 0x57 + && i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) { + char name[4]; + + name[0] = i2c_smbus_read_byte_data(client, 0x80); + name[1] = i2c_smbus_read_byte_data(client, 0x81); + name[2] = i2c_smbus_read_byte_data(client, 0x82); + name[3] = i2c_smbus_read_byte_data(client, 0x83); + + if (!memcmp(name, "PCG-", 4) || !memcmp(name, "VGN-", 4)) { + dev_info(&client->dev, "Vaio EEPROM detected, " + "enabling privacy protection\n"); + data->nature = VAIO; + } + } + + /* create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); + if (err) + goto exit_kfree; + + return 0; + +exit_kfree: + kfree(data); +exit: + return err; +} + +static int eeprom_remove(struct i2c_client *client) +{ + sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id eeprom_id[] = { + { "eeprom", 0 }, + { } +}; + +static struct i2c_driver eeprom_driver = { + .driver = { + .name = "eeprom", + }, + .probe = eeprom_probe, + .remove = eeprom_remove, + .id_table = eeprom_id, + + .class = I2C_CLASS_DDC | I2C_CLASS_SPD, + .detect = eeprom_detect, + .address_data = &addr_data, +}; + +static int __init eeprom_init(void) +{ + return i2c_add_driver(&eeprom_driver); +} + +static void __exit eeprom_exit(void) +{ + i2c_del_driver(&eeprom_driver); +} + + +MODULE_AUTHOR("Frodo Looijaard and " + "Philip Edelbrock and " + "Greg Kroah-Hartman "); +MODULE_DESCRIPTION("I2C EEPROM driver"); +MODULE_LICENSE("GPL"); + +module_init(eeprom_init); +module_exit(eeprom_exit); -- cgit v1.2.3 From e51d565ff6bb1cedc10568425511badf0633a212 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 26 Jan 2009 21:19:54 +0100 Subject: spi: Move at25 (for SPI eeproms) to /drivers/misc/eeprom Signed-off-by: Wolfram Sang Signed-off-by: Jean Delvare --- drivers/misc/eeprom/Kconfig | 11 ++ drivers/misc/eeprom/Makefile | 1 + drivers/misc/eeprom/at25.c | 389 +++++++++++++++++++++++++++++++++++++++++++ drivers/spi/Kconfig | 11 -- drivers/spi/Makefile | 1 - drivers/spi/at25.c | 389 ------------------------------------------- 6 files changed, 401 insertions(+), 401 deletions(-) create mode 100644 drivers/misc/eeprom/at25.c delete mode 100644 drivers/spi/at25.c (limited to 'drivers') diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 3d31b664424..5bf3c92d716 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -26,6 +26,17 @@ config AT24 This driver can also be built as a module. If so, the module will be called at24. +config SPI_AT25 + tristate "SPI EEPROMs from most vendors" + depends on SPI && SYSFS + help + Enable this driver to get read/write support to most SPI EEPROMs, + after you configure the board init code to know about each eeprom + on your target board. + + This driver can also be built as a module. If so, the module + will be called at25. + config SENSORS_EEPROM tristate "Old I2C EEPROM reader" depends on I2C && EXPERIMENTAL diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index a3dad28f272..a4fb5cf8ffe 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_AT24) += at24.o +obj-$(CONFIG_SPI_AT25) += at25.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c new file mode 100644 index 00000000000..290dbe99647 --- /dev/null +++ b/drivers/misc/eeprom/at25.c @@ -0,0 +1,389 @@ +/* + * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models + * + * Copyright (C) 2006 David Brownell + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* + * NOTE: this is an *EEPROM* driver. The vagaries of product naming + * mean that some AT25 products are EEPROMs, and others are FLASH. + * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver, + * not this one! + */ + +struct at25_data { + struct spi_device *spi; + struct mutex lock; + struct spi_eeprom chip; + struct bin_attribute bin; + unsigned addrlen; +}; + +#define AT25_WREN 0x06 /* latch the write enable */ +#define AT25_WRDI 0x04 /* reset the write enable */ +#define AT25_RDSR 0x05 /* read status register */ +#define AT25_WRSR 0x01 /* write status register */ +#define AT25_READ 0x03 /* read byte(s) */ +#define AT25_WRITE 0x02 /* write byte(s)/sector */ + +#define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */ +#define AT25_SR_WEN 0x02 /* write enable (latched) */ +#define AT25_SR_BP0 0x04 /* BP for software writeprotect */ +#define AT25_SR_BP1 0x08 +#define AT25_SR_WPEN 0x80 /* writeprotect enable */ + + +#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ + +/* Specs often allow 5 msec for a page write, sometimes 20 msec; + * it's important to recover from write timeouts. + */ +#define EE_TIMEOUT 25 + +/*-------------------------------------------------------------------------*/ + +#define io_limit PAGE_SIZE /* bytes */ + +static ssize_t +at25_ee_read( + struct at25_data *at25, + char *buf, + unsigned offset, + size_t count +) +{ + u8 command[EE_MAXADDRLEN + 1]; + u8 *cp; + ssize_t status; + struct spi_transfer t[2]; + struct spi_message m; + + cp = command; + *cp++ = AT25_READ; + + /* 8/16/24-bit address is written MSB first */ + switch (at25->addrlen) { + default: /* case 3 */ + *cp++ = offset >> 16; + case 2: + *cp++ = offset >> 8; + case 1: + case 0: /* can't happen: for better codegen */ + *cp++ = offset >> 0; + } + + spi_message_init(&m); + memset(t, 0, sizeof t); + + t[0].tx_buf = command; + t[0].len = at25->addrlen + 1; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = count; + spi_message_add_tail(&t[1], &m); + + mutex_lock(&at25->lock); + + /* Read it all at once. + * + * REVISIT that's potentially a problem with large chips, if + * other devices on the bus need to be accessed regularly or + * this chip is clocked very slowly + */ + status = spi_sync(at25->spi, &m); + dev_dbg(&at25->spi->dev, + "read %Zd bytes at %d --> %d\n", + count, offset, (int) status); + + mutex_unlock(&at25->lock); + return status ? status : count; +} + +static ssize_t +at25_bin_read(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev; + struct at25_data *at25; + + dev = container_of(kobj, struct device, kobj); + at25 = dev_get_drvdata(dev); + + if (unlikely(off >= at25->bin.size)) + return 0; + if ((off + count) > at25->bin.size) + count = at25->bin.size - off; + if (unlikely(!count)) + return count; + + return at25_ee_read(at25, buf, off, count); +} + + +static ssize_t +at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count) +{ + ssize_t status = 0; + unsigned written = 0; + unsigned buf_size; + u8 *bounce; + + /* Temp buffer starts with command and address */ + buf_size = at25->chip.page_size; + if (buf_size > io_limit) + buf_size = io_limit; + bounce = kmalloc(buf_size + at25->addrlen + 1, GFP_KERNEL); + if (!bounce) + return -ENOMEM; + + /* For write, rollover is within the page ... so we write at + * most one page, then manually roll over to the next page. + */ + bounce[0] = AT25_WRITE; + mutex_lock(&at25->lock); + do { + unsigned long timeout, retries; + unsigned segment; + unsigned offset = (unsigned) off; + u8 *cp = bounce + 1; + + *cp = AT25_WREN; + status = spi_write(at25->spi, cp, 1); + if (status < 0) { + dev_dbg(&at25->spi->dev, "WREN --> %d\n", + (int) status); + break; + } + + /* 8/16/24-bit address is written MSB first */ + switch (at25->addrlen) { + default: /* case 3 */ + *cp++ = offset >> 16; + case 2: + *cp++ = offset >> 8; + case 1: + case 0: /* can't happen: for better codegen */ + *cp++ = offset >> 0; + } + + /* Write as much of a page as we can */ + segment = buf_size - (offset % buf_size); + if (segment > count) + segment = count; + memcpy(cp, buf, segment); + status = spi_write(at25->spi, bounce, + segment + at25->addrlen + 1); + dev_dbg(&at25->spi->dev, + "write %u bytes at %u --> %d\n", + segment, offset, (int) status); + if (status < 0) + break; + + /* REVISIT this should detect (or prevent) failed writes + * to readonly sections of the EEPROM... + */ + + /* Wait for non-busy status */ + timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT); + retries = 0; + do { + int sr; + + sr = spi_w8r8(at25->spi, AT25_RDSR); + if (sr < 0 || (sr & AT25_SR_nRDY)) { + dev_dbg(&at25->spi->dev, + "rdsr --> %d (%02x)\n", sr, sr); + /* at HZ=100, this is sloooow */ + msleep(1); + continue; + } + if (!(sr & AT25_SR_nRDY)) + break; + } while (retries++ < 3 || time_before_eq(jiffies, timeout)); + + if (time_after(jiffies, timeout)) { + dev_err(&at25->spi->dev, + "write %d bytes offset %d, " + "timeout after %u msecs\n", + segment, offset, + jiffies_to_msecs(jiffies - + (timeout - EE_TIMEOUT))); + status = -ETIMEDOUT; + break; + } + + off += segment; + buf += segment; + count -= segment; + written += segment; + + } while (count > 0); + + mutex_unlock(&at25->lock); + + kfree(bounce); + return written ? written : status; +} + +static ssize_t +at25_bin_write(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev; + struct at25_data *at25; + + dev = container_of(kobj, struct device, kobj); + at25 = dev_get_drvdata(dev); + + if (unlikely(off >= at25->bin.size)) + return -EFBIG; + if ((off + count) > at25->bin.size) + count = at25->bin.size - off; + if (unlikely(!count)) + return count; + + return at25_ee_write(at25, buf, off, count); +} + +/*-------------------------------------------------------------------------*/ + +static int at25_probe(struct spi_device *spi) +{ + struct at25_data *at25 = NULL; + const struct spi_eeprom *chip; + int err; + int sr; + int addrlen; + + /* Chip description */ + chip = spi->dev.platform_data; + if (!chip) { + dev_dbg(&spi->dev, "no chip description\n"); + err = -ENODEV; + goto fail; + } + + /* For now we only support 8/16/24 bit addressing */ + if (chip->flags & EE_ADDR1) + addrlen = 1; + else if (chip->flags & EE_ADDR2) + addrlen = 2; + else if (chip->flags & EE_ADDR3) + addrlen = 3; + else { + dev_dbg(&spi->dev, "unsupported address type\n"); + err = -EINVAL; + goto fail; + } + + /* Ping the chip ... the status register is pretty portable, + * unlike probing manufacturer IDs. We do expect that system + * firmware didn't write it in the past few milliseconds! + */ + sr = spi_w8r8(spi, AT25_RDSR); + if (sr < 0 || sr & AT25_SR_nRDY) { + dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr); + err = -ENXIO; + goto fail; + } + + if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) { + err = -ENOMEM; + goto fail; + } + + mutex_init(&at25->lock); + at25->chip = *chip; + at25->spi = spi_dev_get(spi); + dev_set_drvdata(&spi->dev, at25); + at25->addrlen = addrlen; + + /* Export the EEPROM bytes through sysfs, since that's convenient. + * Default to root-only access to the data; EEPROMs often hold data + * that's sensitive for read and/or write, like ethernet addresses, + * security codes, board-specific manufacturing calibrations, etc. + */ + at25->bin.attr.name = "eeprom"; + at25->bin.attr.mode = S_IRUSR; + at25->bin.read = at25_bin_read; + + at25->bin.size = at25->chip.byte_len; + if (!(chip->flags & EE_READONLY)) { + at25->bin.write = at25_bin_write; + at25->bin.attr.mode |= S_IWUSR; + } + + err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin); + if (err) + goto fail; + + dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n", + (at25->bin.size < 1024) + ? at25->bin.size + : (at25->bin.size / 1024), + (at25->bin.size < 1024) ? "Byte" : "KByte", + at25->chip.name, + (chip->flags & EE_READONLY) ? " (readonly)" : "", + at25->chip.page_size); + return 0; +fail: + dev_dbg(&spi->dev, "probe err %d\n", err); + kfree(at25); + return err; +} + +static int __devexit at25_remove(struct spi_device *spi) +{ + struct at25_data *at25; + + at25 = dev_get_drvdata(&spi->dev); + sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin); + kfree(at25); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct spi_driver at25_driver = { + .driver = { + .name = "at25", + .owner = THIS_MODULE, + }, + .probe = at25_probe, + .remove = __devexit_p(at25_remove), +}; + +static int __init at25_init(void) +{ + return spi_register_driver(&at25_driver); +} +module_init(at25_init); + +static void __exit at25_exit(void) +{ + spi_unregister_driver(&at25_driver); +} +module_exit(at25_exit); + +MODULE_DESCRIPTION("Driver for most SPI EEPROMs"); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4a6fe01831a..83a185d5296 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -230,17 +230,6 @@ config SPI_XILINX # comment "SPI Protocol Masters" -config SPI_AT25 - tristate "SPI EEPROMs from most vendors" - depends on SYSFS - help - Enable this driver to get read/write support to most SPI EEPROMs, - after you configure the board init code to know about each eeprom - on your target board. - - This driver can also be built as a module. If so, the module - will be called at25. - config SPI_SPIDEV tristate "User mode SPI device driver support" depends on EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 5e9f521b884..5d0451936d8 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -33,7 +33,6 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o # ... add above this line ... # SPI protocol drivers (device/link on bus) -obj-$(CONFIG_SPI_AT25) += at25.o obj-$(CONFIG_SPI_SPIDEV) += spidev.o obj-$(CONFIG_SPI_TLE62X0) += tle62x0.o # ... add above this line ... diff --git a/drivers/spi/at25.c b/drivers/spi/at25.c deleted file mode 100644 index 290dbe99647..00000000000 --- a/drivers/spi/at25.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models - * - * Copyright (C) 2006 David Brownell - * - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* - * NOTE: this is an *EEPROM* driver. The vagaries of product naming - * mean that some AT25 products are EEPROMs, and others are FLASH. - * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver, - * not this one! - */ - -struct at25_data { - struct spi_device *spi; - struct mutex lock; - struct spi_eeprom chip; - struct bin_attribute bin; - unsigned addrlen; -}; - -#define AT25_WREN 0x06 /* latch the write enable */ -#define AT25_WRDI 0x04 /* reset the write enable */ -#define AT25_RDSR 0x05 /* read status register */ -#define AT25_WRSR 0x01 /* write status register */ -#define AT25_READ 0x03 /* read byte(s) */ -#define AT25_WRITE 0x02 /* write byte(s)/sector */ - -#define AT25_SR_nRDY 0x01 /* nRDY = write-in-progress */ -#define AT25_SR_WEN 0x02 /* write enable (latched) */ -#define AT25_SR_BP0 0x04 /* BP for software writeprotect */ -#define AT25_SR_BP1 0x08 -#define AT25_SR_WPEN 0x80 /* writeprotect enable */ - - -#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ - -/* Specs often allow 5 msec for a page write, sometimes 20 msec; - * it's important to recover from write timeouts. - */ -#define EE_TIMEOUT 25 - -/*-------------------------------------------------------------------------*/ - -#define io_limit PAGE_SIZE /* bytes */ - -static ssize_t -at25_ee_read( - struct at25_data *at25, - char *buf, - unsigned offset, - size_t count -) -{ - u8 command[EE_MAXADDRLEN + 1]; - u8 *cp; - ssize_t status; - struct spi_transfer t[2]; - struct spi_message m; - - cp = command; - *cp++ = AT25_READ; - - /* 8/16/24-bit address is written MSB first */ - switch (at25->addrlen) { - default: /* case 3 */ - *cp++ = offset >> 16; - case 2: - *cp++ = offset >> 8; - case 1: - case 0: /* can't happen: for better codegen */ - *cp++ = offset >> 0; - } - - spi_message_init(&m); - memset(t, 0, sizeof t); - - t[0].tx_buf = command; - t[0].len = at25->addrlen + 1; - spi_message_add_tail(&t[0], &m); - - t[1].rx_buf = buf; - t[1].len = count; - spi_message_add_tail(&t[1], &m); - - mutex_lock(&at25->lock); - - /* Read it all at once. - * - * REVISIT that's potentially a problem with large chips, if - * other devices on the bus need to be accessed regularly or - * this chip is clocked very slowly - */ - status = spi_sync(at25->spi, &m); - dev_dbg(&at25->spi->dev, - "read %Zd bytes at %d --> %d\n", - count, offset, (int) status); - - mutex_unlock(&at25->lock); - return status ? status : count; -} - -static ssize_t -at25_bin_read(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - struct device *dev; - struct at25_data *at25; - - dev = container_of(kobj, struct device, kobj); - at25 = dev_get_drvdata(dev); - - if (unlikely(off >= at25->bin.size)) - return 0; - if ((off + count) > at25->bin.size) - count = at25->bin.size - off; - if (unlikely(!count)) - return count; - - return at25_ee_read(at25, buf, off, count); -} - - -static ssize_t -at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count) -{ - ssize_t status = 0; - unsigned written = 0; - unsigned buf_size; - u8 *bounce; - - /* Temp buffer starts with command and address */ - buf_size = at25->chip.page_size; - if (buf_size > io_limit) - buf_size = io_limit; - bounce = kmalloc(buf_size + at25->addrlen + 1, GFP_KERNEL); - if (!bounce) - return -ENOMEM; - - /* For write, rollover is within the page ... so we write at - * most one page, then manually roll over to the next page. - */ - bounce[0] = AT25_WRITE; - mutex_lock(&at25->lock); - do { - unsigned long timeout, retries; - unsigned segment; - unsigned offset = (unsigned) off; - u8 *cp = bounce + 1; - - *cp = AT25_WREN; - status = spi_write(at25->spi, cp, 1); - if (status < 0) { - dev_dbg(&at25->spi->dev, "WREN --> %d\n", - (int) status); - break; - } - - /* 8/16/24-bit address is written MSB first */ - switch (at25->addrlen) { - default: /* case 3 */ - *cp++ = offset >> 16; - case 2: - *cp++ = offset >> 8; - case 1: - case 0: /* can't happen: for better codegen */ - *cp++ = offset >> 0; - } - - /* Write as much of a page as we can */ - segment = buf_size - (offset % buf_size); - if (segment > count) - segment = count; - memcpy(cp, buf, segment); - status = spi_write(at25->spi, bounce, - segment + at25->addrlen + 1); - dev_dbg(&at25->spi->dev, - "write %u bytes at %u --> %d\n", - segment, offset, (int) status); - if (status < 0) - break; - - /* REVISIT this should detect (or prevent) failed writes - * to readonly sections of the EEPROM... - */ - - /* Wait for non-busy status */ - timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT); - retries = 0; - do { - int sr; - - sr = spi_w8r8(at25->spi, AT25_RDSR); - if (sr < 0 || (sr & AT25_SR_nRDY)) { - dev_dbg(&at25->spi->dev, - "rdsr --> %d (%02x)\n", sr, sr); - /* at HZ=100, this is sloooow */ - msleep(1); - continue; - } - if (!(sr & AT25_SR_nRDY)) - break; - } while (retries++ < 3 || time_before_eq(jiffies, timeout)); - - if (time_after(jiffies, timeout)) { - dev_err(&at25->spi->dev, - "write %d bytes offset %d, " - "timeout after %u msecs\n", - segment, offset, - jiffies_to_msecs(jiffies - - (timeout - EE_TIMEOUT))); - status = -ETIMEDOUT; - break; - } - - off += segment; - buf += segment; - count -= segment; - written += segment; - - } while (count > 0); - - mutex_unlock(&at25->lock); - - kfree(bounce); - return written ? written : status; -} - -static ssize_t -at25_bin_write(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - struct device *dev; - struct at25_data *at25; - - dev = container_of(kobj, struct device, kobj); - at25 = dev_get_drvdata(dev); - - if (unlikely(off >= at25->bin.size)) - return -EFBIG; - if ((off + count) > at25->bin.size) - count = at25->bin.size - off; - if (unlikely(!count)) - return count; - - return at25_ee_write(at25, buf, off, count); -} - -/*-------------------------------------------------------------------------*/ - -static int at25_probe(struct spi_device *spi) -{ - struct at25_data *at25 = NULL; - const struct spi_eeprom *chip; - int err; - int sr; - int addrlen; - - /* Chip description */ - chip = spi->dev.platform_data; - if (!chip) { - dev_dbg(&spi->dev, "no chip description\n"); - err = -ENODEV; - goto fail; - } - - /* For now we only support 8/16/24 bit addressing */ - if (chip->flags & EE_ADDR1) - addrlen = 1; - else if (chip->flags & EE_ADDR2) - addrlen = 2; - else if (chip->flags & EE_ADDR3) - addrlen = 3; - else { - dev_dbg(&spi->dev, "unsupported address type\n"); - err = -EINVAL; - goto fail; - } - - /* Ping the chip ... the status register is pretty portable, - * unlike probing manufacturer IDs. We do expect that system - * firmware didn't write it in the past few milliseconds! - */ - sr = spi_w8r8(spi, AT25_RDSR); - if (sr < 0 || sr & AT25_SR_nRDY) { - dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr); - err = -ENXIO; - goto fail; - } - - if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) { - err = -ENOMEM; - goto fail; - } - - mutex_init(&at25->lock); - at25->chip = *chip; - at25->spi = spi_dev_get(spi); - dev_set_drvdata(&spi->dev, at25); - at25->addrlen = addrlen; - - /* Export the EEPROM bytes through sysfs, since that's convenient. - * Default to root-only access to the data; EEPROMs often hold data - * that's sensitive for read and/or write, like ethernet addresses, - * security codes, board-specific manufacturing calibrations, etc. - */ - at25->bin.attr.name = "eeprom"; - at25->bin.attr.mode = S_IRUSR; - at25->bin.read = at25_bin_read; - - at25->bin.size = at25->chip.byte_len; - if (!(chip->flags & EE_READONLY)) { - at25->bin.write = at25_bin_write; - at25->bin.attr.mode |= S_IWUSR; - } - - err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin); - if (err) - goto fail; - - dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n", - (at25->bin.size < 1024) - ? at25->bin.size - : (at25->bin.size / 1024), - (at25->bin.size < 1024) ? "Byte" : "KByte", - at25->chip.name, - (chip->flags & EE_READONLY) ? " (readonly)" : "", - at25->chip.page_size); - return 0; -fail: - dev_dbg(&spi->dev, "probe err %d\n", err); - kfree(at25); - return err; -} - -static int __devexit at25_remove(struct spi_device *spi) -{ - struct at25_data *at25; - - at25 = dev_get_drvdata(&spi->dev); - sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin); - kfree(at25); - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static struct spi_driver at25_driver = { - .driver = { - .name = "at25", - .owner = THIS_MODULE, - }, - .probe = at25_probe, - .remove = __devexit_p(at25_remove), -}; - -static int __init at25_init(void) -{ - return spi_register_driver(&at25_driver); -} -module_init(at25_init); - -static void __exit at25_exit(void) -{ - spi_unregister_driver(&at25_driver); -} -module_exit(at25_exit); - -MODULE_DESCRIPTION("Driver for most SPI EEPROMs"); -MODULE_AUTHOR("David Brownell"); -MODULE_LICENSE("GPL"); - -- cgit v1.2.3 From 0eb6da20681db9b5d5769d3e1aca877f4a77d8fb Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 26 Jan 2009 21:19:54 +0100 Subject: eeprom: Move 93cx6 eeprom driver to /drivers/misc/eeprom Signed-off-by: Wolfram Sang Signed-off-by: Jean Delvare --- drivers/misc/Kconfig | 8 -- drivers/misc/Makefile | 1 - drivers/misc/eeprom/Kconfig | 8 ++ drivers/misc/eeprom/Makefile | 1 + drivers/misc/eeprom/eeprom_93cx6.c | 240 +++++++++++++++++++++++++++++++++++++ drivers/misc/eeprom_93cx6.c | 240 ------------------------------------- 6 files changed, 249 insertions(+), 249 deletions(-) create mode 100644 drivers/misc/eeprom/eeprom_93cx6.c delete mode 100644 drivers/misc/eeprom_93cx6.c (limited to 'drivers') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 6c9cd9d3008..56073199ceb 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -87,14 +87,6 @@ config PHANTOM If you choose to build module, its name will be phantom. If unsure, say N here. -config EEPROM_93CX6 - tristate "EEPROM 93CX6 support" - ---help--- - This is a driver for the EEPROM chipsets 93c46 and 93c66. - The driver supports both read as well as write commands. - - If unsure, say N. - config SGI_IOC4 tristate "SGI IOC4 Base IO support" depends on PCI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 0ec23203c99..bc119983055 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_SGI_IOC4) += ioc4.o -obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 5bf3c92d716..62aae334ee6 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -48,4 +48,12 @@ config SENSORS_EEPROM This driver can also be built as a module. If so, the module will be called eeprom. +config EEPROM_93CX6 + tristate "EEPROM 93CX6 support" + help + This is a driver for the EEPROM chipsets 93c46 and 93c66. + The driver supports both read as well as write commands. + + If unsure, say N. + endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index a4fb5cf8ffe..3b7af6df79a 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_AT24) += at24.o obj-$(CONFIG_SPI_AT25) += at25.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o +obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o diff --git a/drivers/misc/eeprom/eeprom_93cx6.c b/drivers/misc/eeprom/eeprom_93cx6.c new file mode 100644 index 00000000000..15b1780025c --- /dev/null +++ b/drivers/misc/eeprom/eeprom_93cx6.c @@ -0,0 +1,240 @@ +/* + Copyright (C) 2004 - 2006 rt2x00 SourceForge Project + + + 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 program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + 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., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* + Module: eeprom_93cx6 + Abstract: EEPROM reader routines for 93cx6 chipsets. + Supported chipsets: 93c46 & 93c66. + */ + +#include +#include +#include +#include + +MODULE_AUTHOR("http://rt2x00.serialmonkey.com"); +MODULE_VERSION("1.0"); +MODULE_DESCRIPTION("EEPROM 93cx6 chip driver"); +MODULE_LICENSE("GPL"); + +static inline void eeprom_93cx6_pulse_high(struct eeprom_93cx6 *eeprom) +{ + eeprom->reg_data_clock = 1; + eeprom->register_write(eeprom); + + /* + * Add a short delay for the pulse to work. + * According to the specifications the "maximum minimum" + * time should be 450ns. + */ + ndelay(450); +} + +static inline void eeprom_93cx6_pulse_low(struct eeprom_93cx6 *eeprom) +{ + eeprom->reg_data_clock = 0; + eeprom->register_write(eeprom); + + /* + * Add a short delay for the pulse to work. + * According to the specifications the "maximum minimum" + * time should be 450ns. + */ + ndelay(450); +} + +static void eeprom_93cx6_startup(struct eeprom_93cx6 *eeprom) +{ + /* + * Clear all flags, and enable chip select. + */ + eeprom->register_read(eeprom); + eeprom->reg_data_in = 0; + eeprom->reg_data_out = 0; + eeprom->reg_data_clock = 0; + eeprom->reg_chip_select = 1; + eeprom->register_write(eeprom); + + /* + * kick a pulse. + */ + eeprom_93cx6_pulse_high(eeprom); + eeprom_93cx6_pulse_low(eeprom); +} + +static void eeprom_93cx6_cleanup(struct eeprom_93cx6 *eeprom) +{ + /* + * Clear chip_select and data_in flags. + */ + eeprom->register_read(eeprom); + eeprom->reg_data_in = 0; + eeprom->reg_chip_select = 0; + eeprom->register_write(eeprom); + + /* + * kick a pulse. + */ + eeprom_93cx6_pulse_high(eeprom); + eeprom_93cx6_pulse_low(eeprom); +} + +static void eeprom_93cx6_write_bits(struct eeprom_93cx6 *eeprom, + const u16 data, const u16 count) +{ + unsigned int i; + + eeprom->register_read(eeprom); + + /* + * Clear data flags. + */ + eeprom->reg_data_in = 0; + eeprom->reg_data_out = 0; + + /* + * Start writing all bits. + */ + for (i = count; i > 0; i--) { + /* + * Check if this bit needs to be set. + */ + eeprom->reg_data_in = !!(data & (1 << (i - 1))); + + /* + * Write the bit to the eeprom register. + */ + eeprom->register_write(eeprom); + + /* + * Kick a pulse. + */ + eeprom_93cx6_pulse_high(eeprom); + eeprom_93cx6_pulse_low(eeprom); + } + + eeprom->reg_data_in = 0; + eeprom->register_write(eeprom); +} + +static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom, + u16 *data, const u16 count) +{ + unsigned int i; + u16 buf = 0; + + eeprom->register_read(eeprom); + + /* + * Clear data flags. + */ + eeprom->reg_data_in = 0; + eeprom->reg_data_out = 0; + + /* + * Start reading all bits. + */ + for (i = count; i > 0; i--) { + eeprom_93cx6_pulse_high(eeprom); + + eeprom->register_read(eeprom); + + /* + * Clear data_in flag. + */ + eeprom->reg_data_in = 0; + + /* + * Read if the bit has been set. + */ + if (eeprom->reg_data_out) + buf |= (1 << (i - 1)); + + eeprom_93cx6_pulse_low(eeprom); + } + + *data = buf; +} + +/** + * eeprom_93cx6_read - Read multiple words from eeprom + * @eeprom: Pointer to eeprom structure + * @word: Word index from where we should start reading + * @data: target pointer where the information will have to be stored + * + * This function will read the eeprom data as host-endian word + * into the given data pointer. + */ +void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, const u8 word, + u16 *data) +{ + u16 command; + + /* + * Initialize the eeprom register + */ + eeprom_93cx6_startup(eeprom); + + /* + * Select the read opcode and the word to be read. + */ + command = (PCI_EEPROM_READ_OPCODE << eeprom->width) | word; + eeprom_93cx6_write_bits(eeprom, command, + PCI_EEPROM_WIDTH_OPCODE + eeprom->width); + + /* + * Read the requested 16 bits. + */ + eeprom_93cx6_read_bits(eeprom, data, 16); + + /* + * Cleanup eeprom register. + */ + eeprom_93cx6_cleanup(eeprom); +} +EXPORT_SYMBOL_GPL(eeprom_93cx6_read); + +/** + * eeprom_93cx6_multiread - Read multiple words from eeprom + * @eeprom: Pointer to eeprom structure + * @word: Word index from where we should start reading + * @data: target pointer where the information will have to be stored + * @words: Number of words that should be read. + * + * This function will read all requested words from the eeprom, + * this is done by calling eeprom_93cx6_read() multiple times. + * But with the additional change that while the eeprom_93cx6_read + * will return host ordered bytes, this method will return little + * endian words. + */ +void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word, + __le16 *data, const u16 words) +{ + unsigned int i; + u16 tmp; + + for (i = 0; i < words; i++) { + tmp = 0; + eeprom_93cx6_read(eeprom, word + i, &tmp); + data[i] = cpu_to_le16(tmp); + } +} +EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread); + diff --git a/drivers/misc/eeprom_93cx6.c b/drivers/misc/eeprom_93cx6.c deleted file mode 100644 index 15b1780025c..00000000000 --- a/drivers/misc/eeprom_93cx6.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - Copyright (C) 2004 - 2006 rt2x00 SourceForge Project - - - 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 program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - Module: eeprom_93cx6 - Abstract: EEPROM reader routines for 93cx6 chipsets. - Supported chipsets: 93c46 & 93c66. - */ - -#include -#include -#include -#include - -MODULE_AUTHOR("http://rt2x00.serialmonkey.com"); -MODULE_VERSION("1.0"); -MODULE_DESCRIPTION("EEPROM 93cx6 chip driver"); -MODULE_LICENSE("GPL"); - -static inline void eeprom_93cx6_pulse_high(struct eeprom_93cx6 *eeprom) -{ - eeprom->reg_data_clock = 1; - eeprom->register_write(eeprom); - - /* - * Add a short delay for the pulse to work. - * According to the specifications the "maximum minimum" - * time should be 450ns. - */ - ndelay(450); -} - -static inline void eeprom_93cx6_pulse_low(struct eeprom_93cx6 *eeprom) -{ - eeprom->reg_data_clock = 0; - eeprom->register_write(eeprom); - - /* - * Add a short delay for the pulse to work. - * According to the specifications the "maximum minimum" - * time should be 450ns. - */ - ndelay(450); -} - -static void eeprom_93cx6_startup(struct eeprom_93cx6 *eeprom) -{ - /* - * Clear all flags, and enable chip select. - */ - eeprom->register_read(eeprom); - eeprom->reg_data_in = 0; - eeprom->reg_data_out = 0; - eeprom->reg_data_clock = 0; - eeprom->reg_chip_select = 1; - eeprom->register_write(eeprom); - - /* - * kick a pulse. - */ - eeprom_93cx6_pulse_high(eeprom); - eeprom_93cx6_pulse_low(eeprom); -} - -static void eeprom_93cx6_cleanup(struct eeprom_93cx6 *eeprom) -{ - /* - * Clear chip_select and data_in flags. - */ - eeprom->register_read(eeprom); - eeprom->reg_data_in = 0; - eeprom->reg_chip_select = 0; - eeprom->register_write(eeprom); - - /* - * kick a pulse. - */ - eeprom_93cx6_pulse_high(eeprom); - eeprom_93cx6_pulse_low(eeprom); -} - -static void eeprom_93cx6_write_bits(struct eeprom_93cx6 *eeprom, - const u16 data, const u16 count) -{ - unsigned int i; - - eeprom->register_read(eeprom); - - /* - * Clear data flags. - */ - eeprom->reg_data_in = 0; - eeprom->reg_data_out = 0; - - /* - * Start writing all bits. - */ - for (i = count; i > 0; i--) { - /* - * Check if this bit needs to be set. - */ - eeprom->reg_data_in = !!(data & (1 << (i - 1))); - - /* - * Write the bit to the eeprom register. - */ - eeprom->register_write(eeprom); - - /* - * Kick a pulse. - */ - eeprom_93cx6_pulse_high(eeprom); - eeprom_93cx6_pulse_low(eeprom); - } - - eeprom->reg_data_in = 0; - eeprom->register_write(eeprom); -} - -static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom, - u16 *data, const u16 count) -{ - unsigned int i; - u16 buf = 0; - - eeprom->register_read(eeprom); - - /* - * Clear data flags. - */ - eeprom->reg_data_in = 0; - eeprom->reg_data_out = 0; - - /* - * Start reading all bits. - */ - for (i = count; i > 0; i--) { - eeprom_93cx6_pulse_high(eeprom); - - eeprom->register_read(eeprom); - - /* - * Clear data_in flag. - */ - eeprom->reg_data_in = 0; - - /* - * Read if the bit has been set. - */ - if (eeprom->reg_data_out) - buf |= (1 << (i - 1)); - - eeprom_93cx6_pulse_low(eeprom); - } - - *data = buf; -} - -/** - * eeprom_93cx6_read - Read multiple words from eeprom - * @eeprom: Pointer to eeprom structure - * @word: Word index from where we should start reading - * @data: target pointer where the information will have to be stored - * - * This function will read the eeprom data as host-endian word - * into the given data pointer. - */ -void eeprom_93cx6_read(struct eeprom_93cx6 *eeprom, const u8 word, - u16 *data) -{ - u16 command; - - /* - * Initialize the eeprom register - */ - eeprom_93cx6_startup(eeprom); - - /* - * Select the read opcode and the word to be read. - */ - command = (PCI_EEPROM_READ_OPCODE << eeprom->width) | word; - eeprom_93cx6_write_bits(eeprom, command, - PCI_EEPROM_WIDTH_OPCODE + eeprom->width); - - /* - * Read the requested 16 bits. - */ - eeprom_93cx6_read_bits(eeprom, data, 16); - - /* - * Cleanup eeprom register. - */ - eeprom_93cx6_cleanup(eeprom); -} -EXPORT_SYMBOL_GPL(eeprom_93cx6_read); - -/** - * eeprom_93cx6_multiread - Read multiple words from eeprom - * @eeprom: Pointer to eeprom structure - * @word: Word index from where we should start reading - * @data: target pointer where the information will have to be stored - * @words: Number of words that should be read. - * - * This function will read all requested words from the eeprom, - * this is done by calling eeprom_93cx6_read() multiple times. - * But with the additional change that while the eeprom_93cx6_read - * will return host ordered bytes, this method will return little - * endian words. - */ -void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word, - __le16 *data, const u16 words) -{ - unsigned int i; - u16 tmp; - - for (i = 0; i < words; i++) { - tmp = 0; - eeprom_93cx6_read(eeprom, word + i, &tmp); - data[i] = cpu_to_le16(tmp); - } -} -EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread); - -- cgit v1.2.3 From dd7f8dbe2b3c0611ba969cd867c10cb63d163e25 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 26 Jan 2009 21:19:57 +0100 Subject: eeprom: More consistent symbol names Now that all EEPROM drivers live in the same place, let's harmonize their symbol names. Also fix eeprom's dependencies, it definitely needs sysfs, and is no longer experimental after many years in the kernel tree. Signed-off-by: Jean Delvare Acked-by: Wolfram Sang Cc: David Brownell --- drivers/misc/eeprom/Kconfig | 8 ++++---- drivers/misc/eeprom/Makefile | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 62aae334ee6..c76df8cda5e 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -1,6 +1,6 @@ menu "EEPROM support" -config AT24 +config EEPROM_AT24 tristate "I2C EEPROMs from most vendors" depends on I2C && SYSFS && EXPERIMENTAL help @@ -26,7 +26,7 @@ config AT24 This driver can also be built as a module. If so, the module will be called at24. -config SPI_AT25 +config EEPROM_AT25 tristate "SPI EEPROMs from most vendors" depends on SPI && SYSFS help @@ -37,9 +37,9 @@ config SPI_AT25 This driver can also be built as a module. If so, the module will be called at25. -config SENSORS_EEPROM +config EEPROM_LEGACY tristate "Old I2C EEPROM reader" - depends on I2C && EXPERIMENTAL + depends on I2C && SYSFS help If you say yes here you get read-only access to the EEPROM data available on modern memory DIMMs and Sony Vaio laptops via I2C. Such diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 3b7af6df79a..539dd8f8812 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_AT24) += at24.o -obj-$(CONFIG_SPI_AT25) += at25.o -obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o +obj-$(CONFIG_EEPROM_AT24) += at24.o +obj-$(CONFIG_EEPROM_AT25) += at25.o +obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o -- cgit v1.2.3 From 26285ba35813063ade9abd2c2eaaddba9354f587 Mon Sep 17 00:00:00 2001 From: Daniele Venzano Date: Mon, 26 Jan 2009 12:24:38 -0800 Subject: isdn: Fix missing ifdef in isdn_ppp The following patch fixes a warning caused by a missing ifdef in isdn_ppp.c. A function was defined, but never used if CONFIG_IPPP_FILTER was not defined. The warning was: 'get_filter' defined but not used Patch is against 2.6.28.1 Signed-off-by: Daniele Venzano Acked-by: Karsten Keil Signed-off-by: David S. Miller --- drivers/isdn/i4l/isdn_ppp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index a3551dd0324..aa30b5cb351 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -431,6 +431,7 @@ set_arg(void __user *b, void *val,int len) return 0; } +#ifdef CONFIG_IPPP_FILTER static int get_filter(void __user *arg, struct sock_filter **p) { struct sock_fprog uprog; @@ -465,6 +466,7 @@ static int get_filter(void __user *arg, struct sock_filter **p) *p = code; return uprog.len; } +#endif /* CONFIG_IPPP_FILTER */ /* * ippp device ioctl -- cgit v1.2.3 From cdff1036492ac97b4213aeab2546914a633a7de7 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Mon, 26 Jan 2009 12:34:57 -0800 Subject: netxen: fix vlan tso/checksum offload o set netdev->vlan_features appropriately. o fix tso descriptor initialization for vlan case. Signed-off-by: Dhananjay Phadke Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic_main.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index d854f07ef4d..645d384fe87 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -735,17 +735,18 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops); - /* ScatterGather support */ - netdev->features = NETIF_F_SG; - netdev->features |= NETIF_F_IP_CSUM; - netdev->features |= NETIF_F_TSO; + netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO); + netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO); + if (NX_IS_REVISION_P3(revision_id)) { - netdev->features |= NETIF_F_IPV6_CSUM; - netdev->features |= NETIF_F_TSO6; + netdev->features |= (NETIF_F_IPV6_CSUM | NETIF_F_TSO6); + netdev->vlan_features |= (NETIF_F_IPV6_CSUM | NETIF_F_TSO6); } - if (adapter->pci_using_dac) + if (adapter->pci_using_dac) { netdev->features |= NETIF_F_HIGHDMA; + netdev->vlan_features |= NETIF_F_HIGHDMA; + } /* * Set the CRB window to invalid. If any register in window 0 is @@ -1166,6 +1167,14 @@ static bool netxen_tso_check(struct net_device *netdev, { bool tso = false; u8 opcode = TX_ETHER_PKT; + __be16 protocol = skb->protocol; + u16 flags = 0; + + if (protocol == __constant_htons(ETH_P_8021Q)) { + struct vlan_ethhdr *vh = (struct vlan_ethhdr *)skb->data; + protocol = vh->h_vlan_encapsulated_proto; + flags = FLAGS_VLAN_TAGGED; + } if ((netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) && skb_shinfo(skb)->gso_size > 0) { @@ -1174,21 +1183,21 @@ static bool netxen_tso_check(struct net_device *netdev, desc->total_hdr_length = skb_transport_offset(skb) + tcp_hdrlen(skb); - opcode = (skb->protocol == htons(ETH_P_IPV6)) ? + opcode = (protocol == __constant_htons(ETH_P_IPV6)) ? TX_TCP_LSO6 : TX_TCP_LSO; tso = true; } else if (skb->ip_summed == CHECKSUM_PARTIAL) { u8 l4proto; - if (skb->protocol == htons(ETH_P_IP)) { + if (protocol == __constant_htons(ETH_P_IP)) { l4proto = ip_hdr(skb)->protocol; if (l4proto == IPPROTO_TCP) opcode = TX_TCP_PKT; else if(l4proto == IPPROTO_UDP) opcode = TX_UDP_PKT; - } else if (skb->protocol == htons(ETH_P_IPV6)) { + } else if (protocol == __constant_htons(ETH_P_IPV6)) { l4proto = ipv6_hdr(skb)->nexthdr; if (l4proto == IPPROTO_TCP) @@ -1199,7 +1208,7 @@ static bool netxen_tso_check(struct net_device *netdev, } desc->tcp_hdr_offset = skb_transport_offset(skb); desc->ip_hdr_offset = skb_network_offset(skb); - netxen_set_tx_flags_opcode(desc, 0, opcode); + netxen_set_tx_flags_opcode(desc, flags, opcode); return tso; } -- cgit v1.2.3 From 32ec803348b4d5f1353e1d7feae30880b8b3e342 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Mon, 26 Jan 2009 12:35:19 -0800 Subject: netxen: reduce memory footprint o reduce rx ring size from 8192 to 4096. o cut down old huge lro buffers. Signed-off-by: Dhananjay Phadke Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic.h | 12 ++++++------ drivers/net/netxen/netxen_nic_ethtool.c | 5 ++++- 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index c11c568fd7d..a75a31005fd 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -146,7 +146,7 @@ #define MAX_RX_BUFFER_LENGTH 1760 #define MAX_RX_JUMBO_BUFFER_LENGTH 8062 -#define MAX_RX_LRO_BUFFER_LENGTH ((48*1024)-512) +#define MAX_RX_LRO_BUFFER_LENGTH (8062) #define RX_DMA_MAP_LEN (MAX_RX_BUFFER_LENGTH - 2) #define RX_JUMBO_DMA_MAP_LEN \ (MAX_RX_JUMBO_BUFFER_LENGTH - 2) @@ -207,11 +207,11 @@ #define MAX_CMD_DESCRIPTORS 4096 #define MAX_RCV_DESCRIPTORS 16384 -#define MAX_CMD_DESCRIPTORS_HOST (MAX_CMD_DESCRIPTORS / 4) -#define MAX_RCV_DESCRIPTORS_1G (MAX_RCV_DESCRIPTORS / 4) -#define MAX_RCV_DESCRIPTORS_10G 8192 -#define MAX_JUMBO_RCV_DESCRIPTORS 1024 -#define MAX_LRO_RCV_DESCRIPTORS 64 +#define MAX_CMD_DESCRIPTORS_HOST 1024 +#define MAX_RCV_DESCRIPTORS_1G 2048 +#define MAX_RCV_DESCRIPTORS_10G 4096 +#define MAX_JUMBO_RCV_DESCRIPTORS 512 +#define MAX_LRO_RCV_DESCRIPTORS 8 #define MAX_RCVSTATUS_DESCRIPTORS MAX_RCV_DESCRIPTORS #define MAX_JUMBO_RCV_DESC MAX_JUMBO_RCV_DESCRIPTORS #define MAX_RCV_DESC MAX_RCV_DESCRIPTORS diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index c0bd40fcf70..0894a7be022 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -561,7 +561,10 @@ netxen_nic_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) } ring->tx_pending = adapter->max_tx_desc_count; - ring->rx_max_pending = MAX_RCV_DESCRIPTORS; + if (adapter->ahw.board_type == NETXEN_NIC_GBE) + ring->rx_max_pending = MAX_RCV_DESCRIPTORS_1G; + else + ring->rx_max_pending = MAX_RCV_DESCRIPTORS_10G; ring->tx_max_pending = MAX_CMD_DESCRIPTORS_HOST; ring->rx_jumbo_max_pending = MAX_JUMBO_RCV_DESCRIPTORS; ring->rx_mini_max_pending = 0; -- cgit v1.2.3 From e8b5fc514d1c7637cb4b8f77e7d8ac33ef66130c Mon Sep 17 00:00:00 2001 From: Vladislav Zolotarov Date: Mon, 26 Jan 2009 12:36:42 -0800 Subject: bnx2x: tx_has_work should not wait for FW The current tx_has_work waited until all packets sent by the driver are marked as completed by the FW. This is too greedy and it causes the bnx2x_poll to spin in vain. The driver should only check that all packets FW already completed are freed - only in unload flow the driver should make sure that transmit queue is empty Signed-off-by: Vladislav Zolotarov Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller --- drivers/net/bnx2x_main.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 4b84f6ce5ed..d3e7775a9cc 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -57,8 +57,8 @@ #include "bnx2x.h" #include "bnx2x_init.h" -#define DRV_MODULE_VERSION "1.45.25" -#define DRV_MODULE_RELDATE "2009/01/22" +#define DRV_MODULE_VERSION "1.45.26" +#define DRV_MODULE_RELDATE "2009/01/26" #define BNX2X_BC_VER 0x040200 /* Time in jiffies before concluding the transmitter is hung */ @@ -740,8 +740,15 @@ static inline int bnx2x_has_tx_work(struct bnx2x_fastpath *fp) /* Tell compiler that status block fields can change */ barrier(); tx_cons_sb = le16_to_cpu(*fp->tx_cons_sb); - return ((fp->tx_pkt_prod != tx_cons_sb) || - (fp->tx_pkt_prod != fp->tx_pkt_cons)); + return (fp->tx_pkt_cons != tx_cons_sb); +} + +static inline int bnx2x_has_tx_work_unload(struct bnx2x_fastpath *fp) +{ + /* Tell compiler that consumer and producer can change */ + barrier(); + return (fp->tx_pkt_prod != fp->tx_pkt_cons); + } /* free skb in the packet ring at pos idx @@ -6729,7 +6736,7 @@ static int bnx2x_nic_unload(struct bnx2x *bp, int unload_mode) cnt = 1000; smp_rmb(); - while (bnx2x_has_tx_work(fp)) { + while (bnx2x_has_tx_work_unload(fp)) { bnx2x_tx_int(fp, 1000); if (!cnt) { -- cgit v1.2.3 From cd1f55a5b49b74e13ed9e7bc74d005803aaa0da8 Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Mon, 26 Jan 2009 14:33:23 -0800 Subject: gianfar: Revive VLAN support commit 77ecaf2d5a8bfd548eed3f05c1c2e6573d5de4ba ("gianfar: Fix VLAN HW feature related frame/buffer size calculation") wrongly removed priv->vlgrp assignment, and now priv->vlgrp is always NULL. This patch fixes the issue, plus fixes following sparse warning introduced by the same commit: gianfar.c:1406:13: warning: context imbalance in 'gfar_vlan_rx_register' - wrong count at exit gfar_vlan_rx_register() checks for "if (old_grp == grp)" and tries to return w/o dropping the lock. According to net/8021q/vlan.c VLAN core issues rx_register() callback: 1. In register_vlan_dev() only on a newly created group; 2. In unregister_vlan_dev() only if the group becomes empty. Thus the check in the gianfar driver isn't needed. Signed-off-by: Anton Vorontsov Acked-by: Andy Fleming Signed-off-by: David S. Miller --- drivers/net/gianfar.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index ea530673236..3f7eab42aef 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -1423,15 +1423,11 @@ static void gfar_vlan_rx_register(struct net_device *dev, { struct gfar_private *priv = netdev_priv(dev); unsigned long flags; - struct vlan_group *old_grp; u32 tempval; spin_lock_irqsave(&priv->rxlock, flags); - old_grp = priv->vlgrp; - - if (old_grp == grp) - return; + priv->vlgrp = grp; if (grp) { /* Enable VLAN tag insertion */ -- cgit v1.2.3 From 8527bec548e01a29c6d1928d20d6d3be71861482 Mon Sep 17 00:00:00 2001 From: "Ira W. Snyder" Date: Mon, 26 Jan 2009 21:00:33 -0800 Subject: virtio_net: use correct accessors for scatterlists Without this fix, virtio_net makes incorrect usage of scatterlists. It sets the end of the scatterlist chain after the first element, despite the fact that more entries come after it. If you try to run dma_map_sg() on one of the scatterlists given to you by add_buf(), you will get a null pointer oops. Signed-off-by: Ira W. Snyder Signed-off-by: Rusty Russell Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 63ef2a8905f..c68808336c8 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -287,7 +287,7 @@ static void try_fill_recv_maxbufs(struct virtnet_info *vi) skb_put(skb, MAX_PACKET_LEN); hdr = skb_vnet_hdr(skb); - sg_init_one(sg, hdr, sizeof(*hdr)); + sg_set_buf(sg, hdr, sizeof(*hdr)); if (vi->big_packets) { for (i = 0; i < MAX_SKB_FRAGS; i++) { @@ -488,9 +488,9 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb) /* Encode metadata header at front. */ if (vi->mergeable_rx_bufs) - sg_init_one(sg, mhdr, sizeof(*mhdr)); + sg_set_buf(sg, mhdr, sizeof(*mhdr)); else - sg_init_one(sg, hdr, sizeof(*hdr)); + sg_set_buf(sg, hdr, sizeof(*hdr)); num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; -- cgit v1.2.3 From a7a41acf99d9150b424839b0d7b4f5ad9d211e2d Mon Sep 17 00:00:00 2001 From: Manish Katiyar Date: Mon, 26 Jan 2009 21:54:21 -0800 Subject: r6040: Remove unused variable pdev from drivers/net/r6040.c drivers/net/r6040.c:441: warning: unused variable 'pdev' Signed-off-by: Manish Katiyar Signed-off-by: David S. Miller --- drivers/net/r6040.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c index 72fd9e97c19..b2dcdb5ed8b 100644 --- a/drivers/net/r6040.c +++ b/drivers/net/r6040.c @@ -438,7 +438,6 @@ static void r6040_down(struct net_device *dev) { struct r6040_private *lp = netdev_priv(dev); void __iomem *ioaddr = lp->base; - struct pci_dev *pdev = lp->pdev; int limit = 2048; u16 *adrp; u16 cmd; -- cgit v1.2.3 From 26a4bc66a6f57299027e04d90b14fe56a44c6d2b Mon Sep 17 00:00:00 2001 From: John Adamson Date: Fri, 22 Aug 2008 16:43:49 +1000 Subject: m68knommu: fix ColdFire 5272 serial baud rates in mcf.c I noticed (the hard way) that the mcf.c driver doesn't support the fractional precision register on the MCF5272. This makes the console dicey at 115200 baud and a system clock of 66.0 MHz. On the other hand, if your hardware is running at 66.666 MHz, it probably isn't a problem. Patch submitted by John Adamson Signed-off-by: Greg Ungerer --- drivers/serial/mcf.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c index b2001c5b145..56841fe5f48 100644 --- a/drivers/serial/mcf.c +++ b/drivers/serial/mcf.c @@ -212,10 +212,18 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, { unsigned long flags; unsigned int baud, baudclk; +#if defined(CONFIG_M5272) + unsigned int baudfr; +#endif unsigned char mr1, mr2; baud = uart_get_baud_rate(port, termios, old, 0, 230400); +#if defined(CONFIG_M5272) + baudclk = (MCF_BUSCLK / baud) / 32; + baudfr = (((MCF_BUSCLK / baud) + 1) / 2) % 16; +#else baudclk = ((MCF_BUSCLK / baud) + 16) / 32; +#endif mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; mr2 = 0; @@ -262,6 +270,9 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, writeb(mr2, port->membase + MCFUART_UMR); writeb((baudclk & 0xff00) >> 8, port->membase + MCFUART_UBG1); writeb((baudclk & 0xff), port->membase + MCFUART_UBG2); +#if defined(CONFIG_M5272) + writeb((baudfr & 0x0f), port->membase + MCFUART_UFPD); +#endif writeb(MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER, port->membase + MCFUART_UCSR); writeb(MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE, -- cgit v1.2.3 From d4732d3c59b84bb093e11c8f755f32801b4bf86d Mon Sep 17 00:00:00 2001 From: Matt Waddel Date: Tue, 13 Jan 2009 17:13:06 +1000 Subject: m68knommu: add ColdFire M532x to the FEC configuration options Signed-off-by: Matt Waddel Signed-off-by: Greg Ungerer --- drivers/net/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 9fe8cb7d43a..6bdfd47d679 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1829,7 +1829,7 @@ config 68360_ENET config FEC bool "FEC ethernet controller (of ColdFire CPUs)" - depends on M523x || M527x || M5272 || M528x || M520x + depends on M523x || M527x || M5272 || M528x || M520x || M532x help Say Y here if you want to use the built-in 10/100 Fast ethernet controller on some Motorola ColdFire processors. -- cgit v1.2.3 From b9d57f94bb0ed56a5a2b58552a9ff4453013ff0b Mon Sep 17 00:00:00 2001 From: Matt Waddel Date: Tue, 13 Jan 2009 17:14:20 +1000 Subject: m68knommu: correct the mii calculations for 532x ColdFire FEC Signed-off-by: Matt Waddel Signed-off-by: Greg Ungerer --- drivers/net/fec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 7e33c129d51..2769083bfe8 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -1698,7 +1698,7 @@ static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_priva /* * Set MII speed to 2.5 MHz */ - fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2; + fep->phy_speed = (MCF_CLK / 3) / (2500000 * 2 ) * 2; fecp->fec_mii_speed = fep->phy_speed; fec_restart(dev, 0); -- cgit v1.2.3 From b98f5046397b9f4c5060e5b73e483bfd9e453dd6 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Tue, 20 Jan 2009 17:40:56 +0100 Subject: pata-rb532-cf: remove set_irq_type from finish_io The driver has been tested without the call to set_irq_type at this point and occurs to work fine, so it should be safe to remove it. Signed-off-by: Phil Sutter Signed-off-by: Jeff Garzik --- drivers/ata/pata_rb532_cf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/pata_rb532_cf.c b/drivers/ata/pata_rb532_cf.c index c2e6fb9f2ef..ebfcda26d63 100644 --- a/drivers/ata/pata_rb532_cf.c +++ b/drivers/ata/pata_rb532_cf.c @@ -63,8 +63,6 @@ static inline void rb532_pata_finish_io(struct ata_port *ap) ata_sff_sync might be sufficient. */ ata_sff_dma_pause(ap); ndelay(RB500_CF_IO_DELAY); - - set_irq_type(info->irq, IRQ_TYPE_LEVEL_HIGH); } static void rb532_pata_exec_command(struct ata_port *ap, -- cgit v1.2.3 From d7b1956fed33d30c4815e848fd7a143722916868 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 19 Jan 2009 20:55:50 +0100 Subject: DMI: Introduce dmi_first_match to make the interface more flexible Some notebooks from HP have the problem that their BIOSes attempt to spin down hard drives before entering ACPI system states S4 and S5. This leads to a yo-yo effect during system power-off shutdown and the last phase of hibernation when the disk is first spun down by the kernel and then almost immediately turned on and off by the BIOS. This, in turn, may result in shortening the disk's life times. To prevent this from happening we can blacklist the affected systems using DMI information. However, only the on-board controlles should be blacklisted and their PCI slot numbers can be used for this purpose. Unfortunately the existing interface for checking DMI information of the system is not very convenient for this purpose, because to use it, we would have to define special callback functions or create a separate struct dmi_system_id table for each blacklisted system. To overcome this difficulty introduce a new function dmi_first_match() returning a pointer to the first entry in an array of struct dmi_system_id elements that matches the system DMI information. Then, we can use this pointer to access the entry's .driver_data field containing the additional information, such as the PCI slot number, allowing us to do the desired blacklisting. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jeff Garzik --- drivers/firmware/dmi_scan.c | 74 +++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index d76adfea5df..8f0f7c44930 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -414,6 +414,29 @@ void __init dmi_scan_machine(void) dmi_initialized = 1; } +/** + * dmi_matches - check if dmi_system_id structure matches system DMI data + * @dmi: pointer to the dmi_system_id structure to check + */ +static bool dmi_matches(const struct dmi_system_id *dmi) +{ + int i; + + WARN(!dmi_initialized, KERN_ERR "dmi check: not initialized yet.\n"); + + for (i = 0; i < ARRAY_SIZE(dmi->matches); i++) { + int s = dmi->matches[i].slot; + if (s == DMI_NONE) + continue; + if (dmi_ident[s] + && strstr(dmi_ident[s], dmi->matches[i].substr)) + continue; + /* No match */ + return false; + } + return true; +} + /** * dmi_check_system - check system DMI data * @list: array of dmi_system_id structures to match against @@ -429,31 +452,44 @@ void __init dmi_scan_machine(void) */ int dmi_check_system(const struct dmi_system_id *list) { - int i, count = 0; - const struct dmi_system_id *d = list; - - WARN(!dmi_initialized, KERN_ERR "dmi check: not initialized yet.\n"); - - while (d->ident) { - for (i = 0; i < ARRAY_SIZE(d->matches); i++) { - int s = d->matches[i].slot; - if (s == DMI_NONE) - continue; - if (dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr)) - continue; - /* No match */ - goto fail; + int count = 0; + const struct dmi_system_id *d; + + for (d = list; d->ident; d++) + if (dmi_matches(d)) { + count++; + if (d->callback && d->callback(d)) + break; } - count++; - if (d->callback && d->callback(d)) - break; -fail: d++; - } return count; } EXPORT_SYMBOL(dmi_check_system); +/** + * dmi_first_match - find dmi_system_id structure matching system DMI data + * @list: array of dmi_system_id structures to match against + * All non-null elements of the list must match + * their slot's (field index's) data (i.e., each + * list string must be a substring of the specified + * DMI slot's string data) to be considered a + * successful match. + * + * Walk the blacklist table until the first match is found. Return the + * pointer to the matching entry or NULL if there's no match. + */ +const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list) +{ + const struct dmi_system_id *d; + + for (d = list; d->ident; d++) + if (dmi_matches(d)) + return d; + + return NULL; +} +EXPORT_SYMBOL(dmi_first_match); + /** * dmi_get_system_info - return DMI data value * @field: data index (see enum dmi_field) -- cgit v1.2.3 From 2a6e58d2731dcc05dafa7f976d935e0f0627fcd7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 19 Jan 2009 20:56:43 +0100 Subject: SATA: Blacklisting of systems that spin off disks during ACPI power off Introduce new libata flags ATA_FLAG_NO_POWEROFF_SPINDOWN and ATA_FLAG_NO_HIBERNATE_SPINDOWN that, if set, will prevent disks from being spun off during system power off and hibernation, respectively (to handle the hibernation case we need the new system state SYSTEM_HIBERNATE_ENTER that can be checked against by libata, in analogy with SYSTEM_POWER_OFF). Signed-off-by: Rafael J. Wysocki Signed-off-by: Jeff Garzik --- drivers/ata/libata-scsi.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index a1a6e6298c3..3c4c5ae277b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "libata.h" @@ -1303,6 +1304,17 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) tf->command = ATA_CMD_VERIFY; /* READ VERIFY */ } else { + /* Some odd clown BIOSen issue spindown on power off (ACPI S4 + * or S5) causing some drives to spin up and down again. + */ + if ((qc->ap->flags & ATA_FLAG_NO_POWEROFF_SPINDOWN) && + system_state == SYSTEM_POWER_OFF) + goto skip; + + if ((qc->ap->flags & ATA_FLAG_NO_HIBERNATE_SPINDOWN) && + system_entering_hibernation()) + goto skip; + /* XXX: This is for backward compatibility, will be * removed. Read Documentation/feature-removal-schedule.txt * for more info. @@ -1326,8 +1338,7 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) scmd->scsi_done = qc->scsidone; qc->scsidone = ata_delayed_done; } - scmd->result = SAM_STAT_GOOD; - return 1; + goto skip; } /* Issue ATA STANDBY IMMEDIATE command */ @@ -1343,10 +1354,13 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc) return 0; -invalid_fld: + invalid_fld: ata_scsi_set_sense(scmd, ILLEGAL_REQUEST, 0x24, 0x0); /* "Invalid field in cbd" */ return 1; + skip: + scmd->result = SAM_STAT_GOOD; + return 1; } -- cgit v1.2.3 From 1fd684346d41f6be2487c161f60d03a7feb68911 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 19 Jan 2009 20:57:36 +0100 Subject: SATA AHCI: Blacklist system that spins off disks during ACPI power off Some notebooks from HP have the problem that their BIOSes attempt to spin down hard drives before entering ACPI system states S4 and S5. This leads to a yo-yo effect during system power-off shutdown and the last phase of hibernation when the disk is first spun down by the kernel and then almost immediately turned on and off by the BIOS. This, in turn, may result in shortening the disk's life times. To prevent this from happening we can blacklist the affected systems using DMI information. Blacklist HP nx6310 that uses the AHCI driver. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jeff Garzik --- drivers/ata/ahci.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 96039671e3b..77bba4c083c 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -2548,6 +2548,32 @@ static void ahci_p5wdh_workaround(struct ata_host *host) } } +static bool ahci_broken_system_poweroff(struct pci_dev *pdev) +{ + static const struct dmi_system_id broken_systems[] = { + { + .ident = "HP Compaq nx6310", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6310"), + }, + /* PCI slot number of the controller */ + .driver_data = (void *)0x1FUL, + }, + + { } /* terminate list */ + }; + const struct dmi_system_id *dmi = dmi_first_match(broken_systems); + + if (dmi) { + unsigned long slot = (unsigned long)dmi->driver_data; + /* apply the quirk only to on-board controllers */ + return slot == PCI_SLOT(pdev->devfn); + } + + return false; +} + static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; @@ -2647,6 +2673,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } + if (ahci_broken_system_poweroff(pdev)) { + pi.flags |= ATA_FLAG_NO_POWEROFF_SPINDOWN; + dev_info(&pdev->dev, + "quirky BIOS, skipping spindown on poweroff\n"); + } + /* CAP.NP sometimes indicate the index of the last enabled * port, at other times, that of the last possible port, so * determining the maximum port number requires looking at -- cgit v1.2.3 From e57db7bde7bff95ae812736ca00c73bd5271455b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 19 Jan 2009 20:58:29 +0100 Subject: SATA Sil: Blacklist system that spins off disks during ACPI power off Some notebooks from HP have the problem that their BIOSes attempt to spin down hard drives before entering ACPI system states S4 and S5. This leads to a yo-yo effect during system power-off shutdown and the last phase of hibernation when the disk is first spun down by the kernel and then almost immediately turned on and off by the BIOS. This, in turn, may result in shortening the disk's life times. To prevent this from happening we can blacklist the affected systems using DMI information. Blacklist HP nx6325 that uses the sata_sil driver. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jeff Garzik --- drivers/ata/sata_sil.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index 564c142b03b..bfd55b085ae 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -695,11 +695,38 @@ static void sil_init_controller(struct ata_host *host) } } +static bool sil_broken_system_poweroff(struct pci_dev *pdev) +{ + static const struct dmi_system_id broken_systems[] = { + { + .ident = "HP Compaq nx6325", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq nx6325"), + }, + /* PCI slot number of the controller */ + .driver_data = (void *)0x12UL, + }, + + { } /* terminate list */ + }; + const struct dmi_system_id *dmi = dmi_first_match(broken_systems); + + if (dmi) { + unsigned long slot = (unsigned long)dmi->driver_data; + /* apply the quirk only to on-board controllers */ + return slot == PCI_SLOT(pdev->devfn); + } + + return false; +} + static int sil_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; int board_id = ent->driver_data; - const struct ata_port_info *ppi[] = { &sil_port_info[board_id], NULL }; + struct ata_port_info pi = sil_port_info[board_id]; + const struct ata_port_info *ppi[] = { &pi, NULL }; struct ata_host *host; void __iomem *mmio_base; int n_ports, rc; @@ -713,6 +740,13 @@ static int sil_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (board_id == sil_3114) n_ports = 4; + if (sil_broken_system_poweroff(pdev)) { + pi.flags |= ATA_FLAG_NO_POWEROFF_SPINDOWN | + ATA_FLAG_NO_HIBERNATE_SPINDOWN; + dev_info(&pdev->dev, "quirky BIOS, skipping spindown " + "on poweroff and hibernation\n"); + } + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); if (!host) return -ENOMEM; -- cgit v1.2.3 From 5f451fe1ab5d73b987051f0d23c85216c552e163 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 19 Jan 2009 20:59:22 +0100 Subject: SATA PIIX: Blacklist system that spins off disks during ACPI power off Some notebooks from HP have the problem that their BIOSes attempt to spin down hard drives before entering ACPI system states S4 and S5. This leads to a yo-yo effect during system power-off shutdown and the last phase of hibernation when the disk is first spun down by the kernel and then almost immediately turned on and off by the BIOS. This, in turn, may result in shortening the disk's life times. To prevent this from happening we can blacklist the affected systems using DMI information. Blacklist HP 2510p that uses the ata_piix driver. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jeff Garzik --- drivers/ata/ata_piix.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 887d8f46a28..54961c0b2c7 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -1387,6 +1387,32 @@ static void piix_iocfg_bit18_quirk(struct ata_host *host) } } +static bool piix_broken_system_poweroff(struct pci_dev *pdev) +{ + static const struct dmi_system_id broken_systems[] = { + { + .ident = "HP Compaq 2510p", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq 2510p"), + }, + /* PCI slot number of the controller */ + .driver_data = (void *)0x1FUL, + }, + + { } /* terminate list */ + }; + const struct dmi_system_id *dmi = dmi_first_match(broken_systems); + + if (dmi) { + unsigned long slot = (unsigned long)dmi->driver_data; + /* apply the quirk only to on-board controllers */ + return slot == PCI_SLOT(pdev->devfn); + } + + return false; +} + /** * piix_init_one - Register PIIX ATA PCI device with kernel services * @pdev: PCI device to register @@ -1422,6 +1448,14 @@ static int __devinit piix_init_one(struct pci_dev *pdev, if (!in_module_init) return -ENODEV; + if (piix_broken_system_poweroff(pdev)) { + piix_port_info[ent->driver_data].flags |= + ATA_FLAG_NO_POWEROFF_SPINDOWN | + ATA_FLAG_NO_HIBERNATE_SPINDOWN; + dev_info(&pdev->dev, "quirky BIOS, skipping spindown " + "on poweroff and hibernation\n"); + } + port_info[0] = piix_port_info[ent->driver_data]; port_info[1] = piix_port_info[ent->driver_data]; -- cgit v1.2.3 From 766fb95ba06e1bbf531d30dc05e21b2d4a0e8dd2 Mon Sep 17 00:00:00 2001 From: Sidney Amani Date: Tue, 27 Jan 2009 10:11:46 +0100 Subject: UBI: allow direct user-space I/O Introduce a new ioctl UBI_IOCSETPROP to set properties on a volume. Also add the first property: UBI_PROP_DIRECT_WRITE, this property is used to set the ability to use direct writes in userspace Signed-off-by: Sidney Amani Signed-off-by: Corentin Chary Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/Kconfig.debug | 10 ---------- drivers/mtd/ubi/cdev.c | 36 ++++++++++++++++++++++++++++-------- drivers/mtd/ubi/ubi.h | 5 ++++- 3 files changed, 32 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ubi/Kconfig.debug b/drivers/mtd/ubi/Kconfig.debug index 1e2ee22edef..2246f154e2f 100644 --- a/drivers/mtd/ubi/Kconfig.debug +++ b/drivers/mtd/ubi/Kconfig.debug @@ -33,16 +33,6 @@ config MTD_UBI_DEBUG_DISABLE_BGT This option switches the background thread off by default. The thread may be also be enabled/disabled via UBI sysfs. -config MTD_UBI_DEBUG_USERSPACE_IO - bool "Direct user-space write/erase support" - default n - depends on MTD_UBI_DEBUG - help - By default, users cannot directly write and erase individual - eraseblocks of dynamic volumes, and have to use update operation - instead. This option enables this capability - it is very useful for - debugging and testing. - config MTD_UBI_DEBUG_EMULATE_BITFLIPS bool "Emulate flash bit-flips" depends on MTD_UBI_DEBUG diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index f9631eb3fef..e63c8fc3df3 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -259,12 +259,9 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, return err ? err : count_save - count; } -#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO - /* * This function allows to directly write to dynamic UBI volumes, without - * issuing the volume update operation. Available only as a debugging feature. - * Very useful for testing UBI. + * issuing the volume update operation. */ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, size_t count, loff_t *offp) @@ -276,6 +273,9 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, size_t count_save = count; char *tbuf; + if (!vol->direct_writes) + return -EPERM; + dbg_gen("requested: write %zd bytes to offset %lld of volume %u", count, *offp, vol->vol_id); @@ -339,10 +339,6 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, return err ? err : count_save - count; } -#else -#define vol_cdev_direct_write(file, buf, count, offp) (-EPERM) -#endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */ - static ssize_t vol_cdev_write(struct file *file, const char __user *buf, size_t count, loff_t *offp) { @@ -552,6 +548,30 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, break; } + /* Set volume property command*/ + case UBI_IOCSETPROP: + { + struct ubi_set_prop_req req; + + err = copy_from_user(&req, argp, + sizeof(struct ubi_set_prop_req)); + if (err) { + err = -EFAULT; + break; + } + switch (req.property) { + case UBI_PROP_DIRECT_WRITE: + mutex_lock(&ubi->volumes_mutex); + desc->vol->direct_writes = !!req.value; + mutex_unlock(&ubi->volumes_mutex); + break; + default: + err = -EINVAL; + break; + } + break; + } + default: err = -ENOTTY; break; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 381f0e1d0a7..c055511bb1b 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -206,6 +206,7 @@ struct ubi_volume_desc; * @upd_marker: %1 if the update marker is set for this volume * @updating: %1 if the volume is being updated * @changing_leb: %1 if the atomic LEB change ioctl command is in progress + * @direct_writes: %1 if direct writes are enabled for this volume * * @gluebi_desc: gluebi UBI volume descriptor * @gluebi_refcount: reference count of the gluebi MTD device @@ -253,6 +254,7 @@ struct ubi_volume { unsigned int upd_marker:1; unsigned int updating:1; unsigned int changing_leb:1; + unsigned int direct_writes:1; #ifdef CONFIG_MTD_UBI_GLUEBI /* @@ -304,7 +306,8 @@ struct ubi_wl_entry; * @vtbl_size: size of the volume table in bytes * @vtbl: in-RAM volume table copy * @volumes_mutex: protects on-flash volume table and serializes volume - * changes, like creation, deletion, update, re-size and re-name + * changes, like creation, deletion, update, re-size, + * re-name and set property * * @max_ec: current highest erase counter value * @mean_ec: current mean erase counter value -- cgit v1.2.3 From 808ffa3d302257b9dc37b1412c1fcdf976fcddac Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 27 Jan 2009 11:50:37 +0000 Subject: tty_open can return to userspace holding tty_mutex __tty_open could return (to userspace) holding the tty_mutex thanks to a regression introduced by 4a2b5fddd53b80efcb3266ee36e23b8de28e761a ("Move tty lookup/reopen to caller"). This was found by bisecting an fsfuzzer problem. Admittedly I have no idea how it managed to tickle this 100% reliably, but it is clearly a regression and when hit leaves the box in a completely unusable state. This patch lets the fsfuzzer test complete every time. Signed-off-by: Eric Paris Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index d33e5ab0617..bc84e125c6b 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1817,8 +1817,10 @@ got_driver: /* check whether we're reopening an existing tty */ tty = tty_driver_lookup_tty(driver, inode, index); - if (IS_ERR(tty)) + if (IS_ERR(tty)) { + mutex_unlock(&tty_mutex); return PTR_ERR(tty); + } } if (tty) { -- cgit v1.2.3 From 11455be2a3874d405508d9d81157d0f8fb179f32 Mon Sep 17 00:00:00 2001 From: Ihar Hrachyshka Date: Tue, 27 Jan 2009 11:50:46 +0000 Subject: MIPS: enable serial UART support on PNX833X devices. Enabled serial UART driver for PNX833X devices. Signed-off-by: Ihar Hrachyshka Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 3e525e38a5d..7d7f576da20 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -982,7 +982,7 @@ config SERIAL_SH_SCI_CONSOLE config SERIAL_PNX8XXX bool "Enable PNX8XXX SoCs' UART Support" - depends on MIPS && SOC_PNX8550 + depends on MIPS && (SOC_PNX8550 || SOC_PNX833X) select SERIAL_CORE help If you have a MIPS-based Philips SoC such as PNX8550 or PNX8330 -- cgit v1.2.3 From e9fed5673949df33385091037f996f1b1a0e1908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 27 Jan 2009 11:51:06 +0000 Subject: Move jsm_remove_one to .devexit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function jsm_remove_one is used only wrapped by __devexit_p so define it using __devexit. Signed-off-by: Uwe Kleine-König Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/jsm/jsm_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c index 338cf8a08b4..92187e28608 100644 --- a/drivers/serial/jsm/jsm_driver.c +++ b/drivers/serial/jsm/jsm_driver.c @@ -180,7 +180,7 @@ static int jsm_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent) return rc; } -static void jsm_remove_one(struct pci_dev *pdev) +static void __devexit jsm_remove_one(struct pci_dev *pdev) { struct jsm_board *brd = pci_get_drvdata(pdev); int i = 0; -- cgit v1.2.3 From 78d70d48132ce4c678a95b771ffa1af4fb5a03ec Mon Sep 17 00:00:00 2001 From: Michael Bramer Date: Tue, 27 Jan 2009 11:51:16 +0000 Subject: Add support for '8-port RS-232 MIC-3620 from advantech' This Patch add the device information for the MIC-3620 8-port RS-232 cPCI card from Advantech Co. Ltd. Signed-off-by: Michael Bramer Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- drivers/serial/8250_pci.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index 2a3671233b1..536d8e510f6 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -806,6 +806,8 @@ pci_default_setup(struct serial_private *priv, #define PCI_SUBDEVICE_ID_OCTPRO422 0x0208 #define PCI_SUBDEVICE_ID_POCTAL232 0x0308 #define PCI_SUBDEVICE_ID_POCTAL422 0x0408 +#define PCI_VENDOR_ID_ADVANTECH 0x13fe +#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -2152,6 +2154,10 @@ static int pciserial_resume_one(struct pci_dev *dev) #endif static struct pci_device_id serial_pci_tbl[] = { + /* Advantech use PCI_DEVICE_ID_ADVANTECH_PCI3620 (0x3620) as 'PCI_SUBVENDOR_ID' */ + { PCI_VENDOR_ID_ADVANTECH, PCI_DEVICE_ID_ADVANTECH_PCI3620, + PCI_DEVICE_ID_ADVANTECH_PCI3620, 0x0001, 0, 0, + pbn_b2_8_921600 }, { PCI_VENDOR_ID_V3, PCI_DEVICE_ID_V3_V960, PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_BH8_232, 0, 0, -- cgit v1.2.3 From 95bec45d2051227ef037f1080d7cef003b88d852 Mon Sep 17 00:00:00 2001 From: Wolfgang Glas Date: Thu, 15 Jan 2009 23:11:53 +0100 Subject: USB: cp2101: add fasttrax GPS evaluation kit vendor/product ID This adds the vendor/product ID of the fasttrax GPS evaluation kit from: http://www.fastraxgps.com/products/evaluationtools/evaluationkit/ to the cp2101 module since this device is actually equipped with a CP210x USB to serial bridge. The vendor/product ID is: 0x10c4/0x826b. Signed-off-by: Wolfgang Glas Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp2101.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index cfaf1f08553..e97eb054e38 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -85,6 +85,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ + { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demostration module */ { USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ -- cgit v1.2.3 From ddeac4e75f2527a340f9dc655bde49bb2429b39b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 15 Jan 2009 17:03:33 -0500 Subject: USB: fix toggle mismatch in disable_endpoint paths This patch (as1200) finishes some fixes that were left incomplete by an earlier patch. Although nobody has addressed this issue in the past, it turns out that we need to distinguish between two different modes of disabling and enabling endpoints. In one mode only the data structures in usbcore are affected, and in the other mode the host controller and device hardware states are affected as well. The earlier patch added an extra argument to the routines in the enable_endpoint pathways to reflect this difference. This patch adds corresponding arguments to the disable_endpoint pathways. Without this change, the endpoint toggle state can get out of sync between the host and the device. The exact mechanism depends on the details of the host controller (whether or not it stores its own copy of the toggle values). Signed-off-by: Alan Stern Reported-by: Dan Streetman Tested-by: Dan Streetman Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 2 +- drivers/usb/core/hub.c | 4 ++-- drivers/usb/core/message.c | 40 ++++++++++++++++++++++++---------------- drivers/usb/core/usb.h | 5 +++-- 4 files changed, 30 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 98760553bc9..d0a21a5f820 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -284,7 +284,7 @@ static int usb_unbind_interface(struct device *dev) * supports "soft" unbinding. */ if (!driver->soft_unbind) - usb_disable_interface(udev, intf); + usb_disable_interface(udev, intf, false); driver->disconnect(intf); usb_cancel_queued_reset(intf); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 94d5ee263c2..cd50d86029e 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2382,8 +2382,8 @@ static int hub_port_debounce(struct usb_hub *hub, int port1) void usb_ep0_reinit(struct usb_device *udev) { - usb_disable_endpoint(udev, 0 + USB_DIR_IN); - usb_disable_endpoint(udev, 0 + USB_DIR_OUT); + usb_disable_endpoint(udev, 0 + USB_DIR_IN, true); + usb_disable_endpoint(udev, 0 + USB_DIR_OUT, true); usb_enable_endpoint(udev, &udev->ep0, true); } EXPORT_SYMBOL_GPL(usb_ep0_reinit); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index de51667dd64..31fb204f44c 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1039,14 +1039,15 @@ static void remove_intf_ep_devs(struct usb_interface *intf) * @dev: the device whose endpoint is being disabled * @epaddr: the endpoint's address. Endpoint number for output, * endpoint number + USB_DIR_IN for input + * @reset_hardware: flag to erase any endpoint state stored in the + * controller hardware * - * Deallocates hcd/hardware state for this endpoint ... and nukes all - * pending urbs. - * - * If the HCD hasn't registered a disable() function, this sets the - * endpoint's maxpacket size to 0 to prevent further submissions. + * Disables the endpoint for URB submission and nukes all pending URBs. + * If @reset_hardware is set then also deallocates hcd/hardware state + * for the endpoint. */ -void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) +void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, + bool reset_hardware) { unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK; struct usb_host_endpoint *ep; @@ -1056,15 +1057,18 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) if (usb_endpoint_out(epaddr)) { ep = dev->ep_out[epnum]; - dev->ep_out[epnum] = NULL; + if (reset_hardware) + dev->ep_out[epnum] = NULL; } else { ep = dev->ep_in[epnum]; - dev->ep_in[epnum] = NULL; + if (reset_hardware) + dev->ep_in[epnum] = NULL; } if (ep) { ep->enabled = 0; usb_hcd_flush_endpoint(dev, ep); - usb_hcd_disable_endpoint(dev, ep); + if (reset_hardware) + usb_hcd_disable_endpoint(dev, ep); } } @@ -1072,17 +1076,21 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr) * usb_disable_interface -- Disable all endpoints for an interface * @dev: the device whose interface is being disabled * @intf: pointer to the interface descriptor + * @reset_hardware: flag to erase any endpoint state stored in the + * controller hardware * * Disables all the endpoints for the interface's current altsetting. */ -void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf) +void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf, + bool reset_hardware) { struct usb_host_interface *alt = intf->cur_altsetting; int i; for (i = 0; i < alt->desc.bNumEndpoints; ++i) { usb_disable_endpoint(dev, - alt->endpoint[i].desc.bEndpointAddress); + alt->endpoint[i].desc.bEndpointAddress, + reset_hardware); } } @@ -1103,8 +1111,8 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) dev_dbg(&dev->dev, "%s nuking %s URBs\n", __func__, skip_ep0 ? "non-ep0" : "all"); for (i = skip_ep0; i < 16; ++i) { - usb_disable_endpoint(dev, i); - usb_disable_endpoint(dev, i + USB_DIR_IN); + usb_disable_endpoint(dev, i, true); + usb_disable_endpoint(dev, i + USB_DIR_IN, true); } dev->toggle[0] = dev->toggle[1] = 0; @@ -1274,7 +1282,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) remove_intf_ep_devs(iface); usb_remove_sysfs_intf_files(iface); } - usb_disable_interface(dev, iface); + usb_disable_interface(dev, iface, true); iface->cur_altsetting = alt; @@ -1353,8 +1361,8 @@ int usb_reset_configuration(struct usb_device *dev) */ for (i = 1; i < 16; ++i) { - usb_disable_endpoint(dev, i); - usb_disable_endpoint(dev, i + USB_DIR_IN); + usb_disable_endpoint(dev, i, true); + usb_disable_endpoint(dev, i + USB_DIR_IN, true); } config = dev->actconfig; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 386177867a8..9d0f33fe871 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -15,9 +15,10 @@ extern void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep, bool reset_toggle); extern void usb_enable_interface(struct usb_device *dev, struct usb_interface *intf, bool reset_toggles); -extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr); +extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, + bool reset_hardware); extern void usb_disable_interface(struct usb_device *dev, - struct usb_interface *intf); + struct usb_interface *intf, bool reset_hardware); extern void usb_release_interface_cache(struct kref *ref); extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern int usb_deauthorize_device(struct usb_device *); -- cgit v1.2.3 From b90de8aea36ae6fe8050a6e91b031369c4f251b2 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 14 Jan 2009 16:17:19 +0100 Subject: USB: storage: add unusual devs entry This adds an unusual devs entry for 2116:0320 Signed-off-by: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index a7f9513fa19..ec40c26bd54 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2041,6 +2041,12 @@ UNUSUAL_DEV( 0x19d2, 0x2000, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_DEVICE), +UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001, + "ST", + "2A", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* patch submitted by Davide Perini * and Renato Perini */ -- cgit v1.2.3 From bcca06efea883bdf3803a0bb0ffa60f26730387d Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 13 Jan 2009 11:35:54 -0500 Subject: USB: don't enable wakeup by default for PCI host controllers This patch (as1199) changes the initial wakeup settings for PCI USB host controllers. The controllers are marked as capable of waking the system, but wakeup is not enabled by default. It turns out that enabling wakeup for USB host controllers has a lot of bad consequences. As the simplest example, if a USB mouse or keyboard is unplugged immediately after the computer is put to sleep, the unplug will cause the system to wake back up again! We are better off marking them as wakeup-capable and leaving wakeup disabled. Signed-off-by: Alan Stern Reported-by: Rafael J. Wysocki CC: David Brownell Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 1 - drivers/usb/host/ehci-pci.c | 2 +- drivers/usb/host/ohci-hcd.c | 8 +++----- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 507741ed448..99432785f43 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -128,7 +128,6 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) } pci_set_master(dev); - device_set_wakeup_enable(&dev->dev, 1); retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index bdc6e86e1f8..9faa5c8fe02 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -230,7 +230,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) pci_read_config_word(pdev, 0x62, &port_wake); if (port_wake & 0x0001) { dev_warn(&pdev->dev, "Enabling legacy PCI PM\n"); - device_init_wakeup(&pdev->dev, 1); + device_set_wakeup_capable(&pdev->dev, 1); } } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 65a9609f4ad..5cf5f1eca4f 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -593,12 +593,10 @@ static int ohci_run (struct ohci_hcd *ohci) * to be checked in case boot firmware (BIOS/SMM/...) has set up * wakeup in a way the bus isn't aware of (e.g., legacy PCI PM). * If the bus glue detected wakeup capability then it should - * already be enabled. Either way, if wakeup should be enabled - * but isn't, we'll enable it now. + * already be enabled; if so we'll just enable it again. */ - if ((ohci->hc_control & OHCI_CTRL_RWC) != 0 - && !device_can_wakeup(hcd->self.controller)) - device_init_wakeup(hcd->self.controller, 1); + if ((ohci->hc_control & OHCI_CTRL_RWC) != 0) + device_set_wakeup_capable(hcd->self.controller, 1); switch (ohci->hc_control & OHCI_CTRL_HCFS) { case OHCI_USB_OPER: -- cgit v1.2.3 From a15d95a003fae154121733f049dd25e9c13dbef3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 20 Jan 2009 01:26:56 +0100 Subject: USB: Fix suspend-resume of PCI USB controllers Commit a0d4922da2e4ccb0973095d8d29f36f6b1b5f703 (USB: fix up suspend and resume for PCI host controllers) attempted to fix the suspend-resume of PCI USB controllers, but unfortunately it did that incorrectly and interrupts are left enabled by the USB controllers' ->suspend_late() callback as a result. This leads to serious problems during suspend which are very difficult to debug. Fix the issue by removing the ->suspend_late() callback of PCI USB controllers and moving the code from there to the ->suspend() callback executed with interrupts enabled. Additionally, make the ->resume() callback of PCI USB controllers execute pci_enable_wake(dev, PCI_D0, false) to disable wake-up from the full power state (PCI_D0). Signed-off-by: Rafael J. Wysocki Tested-by: Andrey Borzenkov Tested-by: "Jeff Chua" Cc: Alan Stern Cc: Andrew Morton Cc: Christian Borntraeger Cc: "Zdenek Kabelac" Cc: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 116 +++++++++++--------------------------------- drivers/usb/core/hcd.h | 1 - drivers/usb/host/ehci-pci.c | 1 - drivers/usb/host/ohci-pci.c | 1 - drivers/usb/host/uhci-hcd.c | 1 - 5 files changed, 27 insertions(+), 93 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 99432785f43..c54fc40458b 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -200,6 +200,7 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) struct usb_hcd *hcd = pci_get_drvdata(dev); int retval = 0; int wake, w; + int has_pci_pm; /* Root hub suspend should have stopped all downstream traffic, * and all bus master traffic. And done so for both the interface @@ -229,6 +230,15 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) synchronize_irq(dev->irq); + /* Downstream ports from this root hub should already be quiesced, so + * there will be no DMA activity. Now we can shut down the upstream + * link (except maybe for PME# resume signaling) and enter some PCI + * low power state, if the hardware allows. + */ + pci_disable_device(dev); + + pci_save_state(dev); + /* Don't fail on error to enable wakeup. We rely on pci code * to reject requests the hardware can't implement, rather * than coding the same thing. @@ -240,35 +250,6 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message) wake = w; dev_dbg(&dev->dev, "wakeup: %d\n", wake); - /* Downstream ports from this root hub should already be quiesced, so - * there will be no DMA activity. Now we can shut down the upstream - * link (except maybe for PME# resume signaling) and enter some PCI - * low power state, if the hardware allows. - */ - pci_disable_device(dev); - done: - return retval; -} -EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); - -/** - * usb_hcd_pci_suspend_late - suspend a PCI-based HCD after IRQs are disabled - * @dev: USB Host Controller being suspended - * @message: Power Management message describing this state transition - * - * Store this function in the HCD's struct pci_driver as .suspend_late. - */ -int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message) -{ - int retval = 0; - int has_pci_pm; - - /* We might already be suspended (runtime PM -- not yet written) */ - if (dev->current_state != PCI_D0) - goto done; - - pci_save_state(dev); - /* Don't change state if we don't need to */ if (message.event == PM_EVENT_FREEZE || message.event == PM_EVENT_PRETHAW) { @@ -314,7 +295,7 @@ int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message) done: return retval; } -EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late); +EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend); /** * usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled @@ -324,65 +305,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late); */ int usb_hcd_pci_resume_early(struct pci_dev *dev) { - int retval = 0; - pci_power_t state = dev->current_state; - -#ifdef CONFIG_PPC_PMAC - /* Reenable ASIC clocks for USB */ - if (machine_is(powermac)) { - struct device_node *of_node; - - of_node = pci_device_to_OF_node(dev); - if (of_node) - pmac_call_feature(PMAC_FTR_USB_ENABLE, - of_node, 0, 1); - } -#endif - - /* NOTE: chip docs cover clean "real suspend" cases (what Linux - * calls "standby", "suspend to RAM", and so on). There are also - * dirty cases when swsusp fakes a suspend in "shutdown" mode. - */ - if (state != PCI_D0) { -#ifdef DEBUG - int pci_pm; - u16 pmcr; - - pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM); - pci_read_config_word(dev, pci_pm + PCI_PM_CTRL, &pmcr); - pmcr &= PCI_PM_CTRL_STATE_MASK; - if (pmcr) { - /* Clean case: power to USB and to HC registers was - * maintained; remote wakeup is easy. - */ - dev_dbg(&dev->dev, "resume from PCI D%d\n", pmcr); - } else { - /* Clean: HC lost Vcc power, D0 uninitialized - * + Vaux may have preserved port and transceiver - * state ... for remote wakeup from D3cold - * + or not; HCD must reinit + re-enumerate - * - * Dirty: D0 semi-initialized cases with swsusp - * + after BIOS init - * + after Linux init (HCD statically linked) - */ - dev_dbg(&dev->dev, "resume from previous PCI D%d\n", - state); - } -#endif - - retval = pci_set_power_state(dev, PCI_D0); - } else { - /* Same basic cases: clean (powered/not), dirty */ - dev_dbg(&dev->dev, "PCI legacy resume\n"); - } - - if (retval < 0) - dev_err(&dev->dev, "can't resume: %d\n", retval); - else - pci_restore_state(dev); - - return retval; + pci_restore_state(dev); + return 0; } EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early); @@ -397,6 +321,18 @@ int usb_hcd_pci_resume(struct pci_dev *dev) struct usb_hcd *hcd; int retval; +#ifdef CONFIG_PPC_PMAC + /* Reenable ASIC clocks for USB */ + if (machine_is(powermac)) { + struct device_node *of_node; + + of_node = pci_device_to_OF_node(dev); + if (of_node) + pmac_call_feature(PMAC_FTR_USB_ENABLE, + of_node, 0, 1); + } +#endif + hcd = pci_get_drvdata(dev); if (hcd->state != HC_STATE_SUSPENDED) { dev_dbg(hcd->self.controller, @@ -404,6 +340,8 @@ int usb_hcd_pci_resume(struct pci_dev *dev) return 0; } + pci_enable_wake(dev, PCI_D0, false); + retval = pci_enable_device(dev); if (retval < 0) { dev_err(&dev->dev, "can't re-enable after resume, %d!\n", diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 572d2cf46e8..5b94a56bec2 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -257,7 +257,6 @@ extern void usb_hcd_pci_remove(struct pci_dev *dev); #ifdef CONFIG_PM extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg); -extern int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t msg); extern int usb_hcd_pci_resume_early(struct pci_dev *dev); extern int usb_hcd_pci_resume(struct pci_dev *dev); #endif /* CONFIG_PM */ diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 9faa5c8fe02..bb21fb0a496 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -432,7 +432,6 @@ static struct pci_driver ehci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .suspend_late = usb_hcd_pci_suspend_late, .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 8b28ae7865b..5d625c3fd42 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -487,7 +487,6 @@ static struct pci_driver ohci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .suspend_late = usb_hcd_pci_suspend_late, .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 4e221060f58..944f7e0ca4d 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -942,7 +942,6 @@ static struct pci_driver uhci_pci_driver = { #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, - .suspend_late = usb_hcd_pci_suspend_late, .resume_early = usb_hcd_pci_resume_early, .resume = usb_hcd_pci_resume, #endif /* PM */ -- cgit v1.2.3 From 501950d846218ed80a776d2aae5aed9c8b92e778 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 13 Jan 2009 11:33:42 -0500 Subject: USB: fix char-device disconnect handling This patch (as1198) fixes a conceptual bug: Somewhere along the line we managed to confuse USB class devices with USB char devices. As a result, the code to send a disconnect signal to userspace would not be built if both CONFIG_USB_DEVICE_CLASS and CONFIG_USB_DEVICEFS were disabled. The usb_fs_classdev_common_remove() routine has been renamed to usbdev_remove() and it is now called whenever any USB device is removed, not just when a class device is unregistered. The notifier registration and unregistration calls are no longer conditionally compiled. And since the common removal code will always be called as part of the char device interface, there's no need to call it again as part of the usbfs interface; thus the invocation of usb_fs_classdev_common_remove() has been taken out of usbfs_remove_device(). Signed-off-by: Alan Stern Reported-by: Alon Bar-Lev Tested-by: Alon Bar-Lev Cc: stable --- drivers/usb/core/devio.c | 20 ++++++++++++-------- drivers/usb/core/inode.c | 1 - drivers/usb/core/usb.h | 1 - 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 26fece124e0..7513bb083c1 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1700,7 +1700,7 @@ const struct file_operations usbdev_file_operations = { .release = usbdev_release, }; -void usb_fs_classdev_common_remove(struct usb_device *udev) +static void usbdev_remove(struct usb_device *udev) { struct dev_state *ps; struct siginfo sinfo; @@ -1742,10 +1742,15 @@ static void usb_classdev_remove(struct usb_device *dev) { if (dev->usb_classdev) device_unregister(dev->usb_classdev); - usb_fs_classdev_common_remove(dev); } -static int usb_classdev_notify(struct notifier_block *self, +#else +#define usb_classdev_add(dev) 0 +#define usb_classdev_remove(dev) do {} while (0) + +#endif + +static int usbdev_notify(struct notifier_block *self, unsigned long action, void *dev) { switch (action) { @@ -1755,15 +1760,15 @@ static int usb_classdev_notify(struct notifier_block *self, break; case USB_DEVICE_REMOVE: usb_classdev_remove(dev); + usbdev_remove(dev); break; } return NOTIFY_OK; } static struct notifier_block usbdev_nb = { - .notifier_call = usb_classdev_notify, + .notifier_call = usbdev_notify, }; -#endif static struct cdev usb_device_cdev; @@ -1798,9 +1803,8 @@ int __init usb_devio_init(void) * to /sys/dev */ usb_classdev_class->dev_kobj = NULL; - - usb_register_notify(&usbdev_nb); #endif + usb_register_notify(&usbdev_nb); out: return retval; @@ -1811,8 +1815,8 @@ error_cdev: void usb_devio_cleanup(void) { -#ifdef CONFIG_USB_DEVICE_CLASS usb_unregister_notify(&usbdev_nb); +#ifdef CONFIG_USB_DEVICE_CLASS class_destroy(usb_classdev_class); #endif cdev_del(&usb_device_cdev); diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 2a129cb7bb5..dff5760a37f 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -717,7 +717,6 @@ static void usbfs_remove_device(struct usb_device *dev) fs_remove_file (dev->usbfs_dentry); dev->usbfs_dentry = NULL; } - usb_fs_classdev_common_remove(dev); } static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev) diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 9d0f33fe871..79d8a9ea559 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -152,7 +152,6 @@ extern struct usb_driver usbfs_driver; extern const struct file_operations usbfs_devices_fops; extern const struct file_operations usbdev_file_operations; extern void usbfs_conn_disc_event(void); -extern void usb_fs_classdev_common_remove(struct usb_device *udev); extern int usb_devio_init(void); extern void usb_devio_cleanup(void); -- cgit v1.2.3 From 2bf5fa13fc8e34d7b86307b99f64a24cb7a83852 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 24 Jan 2009 17:55:57 -0800 Subject: USB: omap1 ohci buildfix (otg related) > > drivers/built-in.o: In function `ohci_omap_init': > > hid-quirks.c:(.text+0x6c608): undefined reference to `otg_get_transceiver' > > drivers/built-in.o: In function `omap_udc_probe': > > hid-quirks.c:(.init.text+0x34c0): undefined reference to `otg_get_transceiver' > > hid-quirks.c:(.init.text+0x3d40): undefined reference to `otg_put_transceiver' Reported-by: Russell King Signed-off-by: David Brownell Acked-by: Tony Lindgren Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 1 + drivers/usb/otg/Kconfig | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 2b476b6b3d4..5a988f4cdf8 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -140,6 +140,7 @@ config USB_OHCI_HCD tristate "OHCI HCD support" depends on USB && USB_ARCH_HAS_OHCI select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 + select USB_OTG_UTILS if ARCH_OMAP ---help--- The Open Host Controller Interface (OHCI) is a standard for accessing USB 1.1 host controller hardware. It does more in hardware than Intel's diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index 8e8dbdb9b39..ee55b449ffd 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -6,14 +6,14 @@ comment "OTG and related infrastructure" -if USB || USB_GADGET - config USB_OTG_UTILS bool help Select this to make sure the build includes objects from the OTG infrastructure directory. +if USB || USB_GADGET + # # USB Transceiver Drivers # -- cgit v1.2.3 From 10b4eadef140b09baf8b9ec1df37185e69773275 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 24 Jan 2009 17:56:17 -0800 Subject: USB: musb davinci buildfix Trying once more to get this merged. The original was submitted for 2.6.27-rc2 or so, and never got correctly merged. Neither were any of the numerous subsequent resends. Sigh. CC drivers/usb/musb/davinci.o drivers/usb/musb/davinci.c:35:32: error: mach/arch/hardware.h: No such file or directory drivers/usb/musb/davinci.c:36:30: error: mach/arch/memory.h: No such file or directory drivers/usb/musb/davinci.c:37:28: error: mach/arch/gpio.h: No such file or directory drivers/usb/musb/davinci.c:373: error: redefinition of 'musb_platform_set_mode' drivers/usb/musb/davinci.c:368: error: previous definition of 'musb_platform_set_mode' was here Signed-off-by: David Brownell Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/davinci.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 0d566dc5ce0..5a8fd5d57a1 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -32,9 +32,10 @@ #include #include -#include -#include -#include +#include +#include +#include + #include #include "musb_core.h" @@ -370,12 +371,6 @@ int musb_platform_set_mode(struct musb *musb, u8 mode) return -EIO; } -int musb_platform_set_mode(struct musb *musb, u8 mode) -{ - /* EVM can't do this (right?) */ - return -EIO; -} - int __init musb_platform_init(struct musb *musb) { void __iomem *tibase = musb->ctrl_base; -- cgit v1.2.3 From 37daa925cf0d4dfd2d1d9ca01e2e0d74fba3d64a Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sat, 24 Jan 2009 17:56:25 -0800 Subject: USB: musb_hdrc: another davinci buildfix (otg related) The DaVinci code had an implementation of the OTG transceiver glue too; make it use the new-standard one. Signed-off-by: David Brownell Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 5af7379cd9a..e1d81d8de22 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -11,6 +11,7 @@ config USB_MUSB_HDRC depends on (USB || USB_GADGET) && HAVE_CLK depends on !SUPERH select TWL4030_USB if MACH_OMAP_3430SDP + select USB_OTG_UTILS tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help Say Y here if your system has a dual role high speed USB -- cgit v1.2.3 From 97a39896816489fe9a67c223e782e8dda06f25c9 Mon Sep 17 00:00:00 2001 From: Ajay Kumar Gupta Date: Sat, 24 Jan 2009 17:56:39 -0800 Subject: USB: musb free_irq bugfix Fixes insert module failure as free_irq() was not done in previous rmmod. Signed-off-by: Ajay Kumar Gupta Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 6c7faacfb53..2cc34fa05b7 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1824,8 +1824,9 @@ static void musb_free(struct musb *musb) musb_gadget_cleanup(musb); #endif - if (musb->nIrq >= 0 && musb->irq_wake) { - disable_irq_wake(musb->nIrq); + if (musb->nIrq >= 0) { + if (musb->irq_wake) + disable_irq_wake(musb->nIrq); free_irq(musb->nIrq, musb); } if (is_dma_capable() && musb->dma_controller) { -- cgit v1.2.3 From af7e0c5f126677fe8e6c4fbea37637b9c0c2fe2a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Sat, 24 Jan 2009 17:57:15 -0800 Subject: USB: musb: tusb6010 buildfix drivers/usb/musb/tusb6010_omap.c:18:26: error: asm/arch/dma.h: No such file or directory drivers/usb/musb/tusb6010_omap.c:19:26: error: asm/arch/mux.h: No such file or directory Signed-off-by: Kalle Valo Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/tusb6010_omap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 52f7f29cebd..7e073a0d7ac 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -15,8 +15,8 @@ #include #include #include -#include -#include +#include +#include #include "musb_core.h" -- cgit v1.2.3 From 96bcd090fa434b4369e6e3a9cba937d1e513596d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 24 Jan 2009 17:57:24 -0800 Subject: USB: musb uses endpoint functions This set of patches introduces calls to the following set of functions: usb_endpoint_dir_in(epd) usb_endpoint_dir_out(epd) usb_endpoint_is_bulk_in(epd) usb_endpoint_is_bulk_out(epd) usb_endpoint_is_int_in(epd) usb_endpoint_is_int_out(epd) usb_endpoint_num(epd) usb_endpoint_type(epd) usb_endpoint_xfer_bulk(epd) usb_endpoint_xfer_control(epd) usb_endpoint_xfer_int(epd) usb_endpoint_xfer_isoc(epd) In some cases, introducing one of these functions is not possible, and it just replaces an explicit integer value by one of the following constants: USB_ENDPOINT_XFER_BULK USB_ENDPOINT_XFER_CONTROL USB_ENDPOINT_XFER_INT USB_ENDPOINT_XFER_ISOC An extract of the semantic patch that makes these changes is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r1@ struct usb_endpoint_descriptor *epd; @@ - ((epd->bmAttributes & \(USB_ENDPOINT_XFERTYPE_MASK\|3\)) == - \(USB_ENDPOINT_XFER_CONTROL\|0\)) + usb_endpoint_xfer_control(epd) @r5@ struct usb_endpoint_descriptor *epd; @@ - ((epd->bEndpointAddress & \(USB_ENDPOINT_DIR_MASK\|0x80\)) == - \(USB_DIR_IN\|0x80\)) + usb_endpoint_dir_in(epd) @inc@ @@ #include @depends on !inc && (r1||r5)@ @@ + #include #include // Signed-off-by: Julia Lawall Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 6 +++--- drivers/usb/musb/musb_host.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 6197daeab8f..4ea30538798 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -874,10 +874,10 @@ static int musb_gadget_enable(struct usb_ep *ep, status = -EBUSY; goto fail; } - musb_ep->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + musb_ep->type = usb_endpoint_type(desc); /* check direction and (later) maxpacket size against endpoint */ - if ((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) != epnum) + if (usb_endpoint_num(desc) != epnum) goto fail; /* REVISIT this rules out high bandwidth periodic transfers */ @@ -890,7 +890,7 @@ static int musb_gadget_enable(struct usb_ep *ep, * packet size (or fail), set the mode, clear the fifo */ musb_ep_select(mbase, epnum); - if (desc->bEndpointAddress & USB_DIR_IN) { + if (usb_endpoint_dir_in(desc)) { u16 int_txe = musb_readw(mbase, MUSB_INTRTXE); if (hw_ep->is_shared_fifo) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 99fa6123487..a035ceccf95 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1847,8 +1847,8 @@ static int musb_urb_enqueue( goto done; } - qh->epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - qh->type = epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + qh->epnum = usb_endpoint_num(epd); + qh->type = usb_endpoint_type(epd); /* NOTE: urb->dev->devnum is wrong during SET_ADDRESS */ qh->addr_reg = (u8) usb_pipedevice(urb->pipe); -- cgit v1.2.3 From 704a14854aaf9758a1248ea36a7d1b8cc42a4b3e Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sat, 24 Jan 2009 17:57:30 -0800 Subject: USB: musb cppi bugfixes These compilation errors are related to incorrect debugging macro and variable names and generated the following errors: drivers/usb/musb/cppi_dma.c:437:5: warning: "MUSB_DEBUG" is not defined drivers/usb/musb/cppi_dma.c: In function 'cppi_next_rx_segment': drivers/usb/musb/cppi_dma.c:884: error: 'debug' undeclared (first use in this function) Signed-off-by: Hugo Villeneuve Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/cppi_dma.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 5ad6d0893cb..d8d5345519c 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -9,6 +9,7 @@ #include #include "musb_core.h" +#include "musb_debug.h" #include "cppi_dma.h" @@ -423,6 +424,7 @@ cppi_rndis_update(struct cppi_channel *c, int is_rx, } } +#ifdef CONFIG_USB_MUSB_DEBUG static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd) { pr_debug("RXBD/%s %08x: " @@ -431,10 +433,11 @@ static void cppi_dump_rxbd(const char *tag, struct cppi_descriptor *bd) bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options); } +#endif static void cppi_dump_rxq(int level, const char *tag, struct cppi_channel *rx) { -#if MUSB_DEBUG > 0 +#ifdef CONFIG_USB_MUSB_DEBUG struct cppi_descriptor *bd; if (!_dbg_level(level)) @@ -881,12 +884,14 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) bd->hw_options |= CPPI_SOP_SET; tail->hw_options |= CPPI_EOP_SET; - if (debug >= 5) { +#ifdef CONFIG_USB_MUSB_DEBUG + if (_dbg_level(5)) { struct cppi_descriptor *d; for (d = rx->head; d; d = d->next) cppi_dump_rxbd("S", d); } +#endif /* in case the preceding transfer left some state... */ tail = rx->last_processed; -- cgit v1.2.3 From 191b776616838f035c2fe7eecc882b5c1f134353 Mon Sep 17 00:00:00 2001 From: Swaminathan S Date: Sat, 24 Jan 2009 17:57:37 -0800 Subject: USB: musb cppi dma fix Initializes the actual_len field to 0 before every DMA transaction. Signed-off-by: Swaminathan S Acked-by: Felipe Balbi Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/cppi_dma.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index d8d5345519c..569ef0fed0f 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -995,6 +995,7 @@ static int cppi_channel_program(struct dma_channel *ch, cppi_ch->offset = 0; cppi_ch->maxpacket = maxpacket; cppi_ch->buf_len = len; + cppi_ch->channel.actual_len = 0; /* TX channel? or RX? */ if (cppi_ch->transmit) -- cgit v1.2.3 From cd67435ef985d0d6279803f2ae48b5248a7178df Mon Sep 17 00:00:00 2001 From: David Brownell Date: Mon, 26 Jan 2009 02:05:43 -0800 Subject: USB: musb: Kconfig fix The Blackfin MUSB Kconfig text didn't properly parenthesise its dependencies. This was visible in non-Blackfin configs by the way the user interfaces lost track of dependencies, when doing a bunch of test builds. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index e1d81d8de22..9985db08e7d 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -50,7 +50,7 @@ comment "OMAP 343x high speed USB support" depends on USB_MUSB_HDRC && ARCH_OMAP34XX comment "Blackfin high speed USB Support" - depends on USB_MUSB_HDRC && (BF54x && !BF544) || (BF52x && !BF522 && !BF523) + depends on USB_MUSB_HDRC && ((BF54x && !BF544) || (BF52x && !BF522 && !BF523)) config USB_TUSB6010 boolean "TUSB 6010 support" -- cgit v1.2.3 From dd4dff8b035f6dda69ece98e20d4c2d76b9f97d1 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Thu, 8 Jan 2009 00:21:18 +0800 Subject: USB: composite: Fix bug: should test set_alt function pointer before use it Signed-off-by: Bryan Wu Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index f2da0269e1b..363951eb333 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -772,7 +772,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) f = cdev->config->interface[w_index]; if (!f) break; - if (w_value && !f->get_alt) + if (w_value && !f->set_alt) break; value = f->set_alt(f, w_index, w_value); break; -- cgit v1.2.3 From 08889517b3713926169d79d99782192e86acdc67 Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Thu, 8 Jan 2009 00:21:19 +0800 Subject: USB: composite: Fix bug: low byte of w_index is the usb interface number not the whole 2 bytes of w_index In some usb gadget driver, for example usb audio class device, the high byte of w_index is the entity id and low byte is the interface number. If we use the 2 bytes of w_index as the array number, we will get a wrong pointer or NULL pointer. This patch fixes this issue. Signed-off-by: Bryan Wu Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/composite.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 363951eb333..5d11c291f1a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -683,6 +683,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) struct usb_request *req = cdev->req; int value = -EOPNOTSUPP; u16 w_index = le16_to_cpu(ctrl->wIndex); + u8 intf = w_index & 0xFF; u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); struct usb_function *f = NULL; @@ -769,7 +770,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) goto unknown; if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) break; - f = cdev->config->interface[w_index]; + f = cdev->config->interface[intf]; if (!f) break; if (w_value && !f->set_alt) @@ -781,7 +782,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) goto unknown; if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) break; - f = cdev->config->interface[w_index]; + f = cdev->config->interface[intf]; if (!f) break; /* lots of interfaces only need altsetting zero... */ @@ -808,7 +809,7 @@ unknown: */ if ((ctrl->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { - f = cdev->config->interface[w_index]; + f = cdev->config->interface[intf]; if (f && f->setup) value = f->setup(f, ctrl); else -- cgit v1.2.3 From 837d84249611e9462dea6181a7ea30aa64e67d6a Mon Sep 17 00:00:00 2001 From: "James A. Treacy" Date: Sat, 24 Jan 2009 23:37:43 -0500 Subject: USB: cdc-acm: support some gps data loggers Below is a patch which allows a number of GPS loggers to work under linux. It is known to support the i-Blue 747 (all models), i-Blue 757, Qstarz BT-Q1000, i.Trek Z1, Konet BGL-32, and the Holux M-241. From: James A. Treacy Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 00b47ea24f8..5664b4afe56 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1349,6 +1349,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0e8d, 0x0003), /* FIREFLY, MediaTek Inc; andrey.arapov@gmail.com */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0e8d, 0x3329), /* i-blue 747, Qstarz BT-Q1000, Holux M-241 */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, -- cgit v1.2.3 From 0f9c7b4a1cc24d6f05a848f0acf72dbff7c5d42d Mon Sep 17 00:00:00 2001 From: Andrew Lunn Date: Tue, 23 Dec 2008 17:31:23 +0100 Subject: USB: CDC-ACM quirk for MTK GPS This patch adds a device quirk for a MediaTek Inc GPS chipset. The device implements USB CDC ACM, but is missing the union descriptor, so the ACM class driver fails to probe the device. I've tested this patch with an iBlue A+ GPS which uses this chipset and using kernel 2.6.28-rc9. Signed-off-by: Andrew Lunn, Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5664b4afe56..f2bfae7b398 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1352,6 +1352,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0e8d, 0x3329), /* i-blue 747, Qstarz BT-Q1000, Holux M-241 */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0e8d, 0x3329), /* MediaTek Inc GPS */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x0482, 0x0203), /* KYOCERA AH-K3001V */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, -- cgit v1.2.3 From 296361ec3abbba7621e9fff01a572ac0873da903 Mon Sep 17 00:00:00 2001 From: sware Date: Wed, 7 Jan 2009 15:35:55 -0800 Subject: USB: remove vernier labpro from ldusb Labpro device is in both ldusb and vstusb device tables. Should only be a vstusb device. Signed-off-by: stephen ware Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/ldusb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 189a9db0350..ad4fb15b5dc 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -57,7 +57,6 @@ #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 /* USB Product ID of Machine Test System */ #define USB_VENDOR_ID_VERNIER 0x08f7 -#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 #define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 #define USB_DEVICE_ID_VERNIER_SKIP 0x0003 #define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 @@ -85,7 +84,6 @@ static struct usb_device_id ld_usb_table [] = { { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, - { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, -- cgit v1.2.3 From 06a743bfc42660f27fde5f24d7471e1eb4c71218 Mon Sep 17 00:00:00 2001 From: Brandon Philips Date: Mon, 5 Jan 2009 08:30:39 -0800 Subject: USB: usblp.c: add USBLP_QUIRK_BIDIR to Brother HL-1440 My Brother HL-1440 would print one document before CUPS would stop printing with the error "Printer not connected; will retry in 30 seconds...". I traced this down to the CUPS usb backend getting an EIO out of usblp on the IOCNR_GET_DEVICE_ID IOCTL. Adding the USBLP_QUIRK_BIDIR fixes the problem but is it the right solution? output from strace /usr/lib/cups/backend/usb after printing a document (Note: SNDCTL_DSP_SYNC == IOCNR_GET_DEVICE_ID): before patch open("/dev/usb/lp0", O_RDWR|O_EXCL) = 3 ioctl(3, SNDCTL_DSP_SYNC, 0x7fff2478cef0) = -1 EIO (Input/output error) after patch open("/dev/usb/lp0", O_RDWR|O_EXCL) = 3 ioctl(3, SNDCTL_DSP_SYNC, 0x7fffb8d474c0) = 0 Possibly related bug: https://bugs.launchpad.net/ubuntu/+source/cupsys/+bug/35638 Signed-off-by: Brandon Philips Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/usblp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index b5775af3ba2..3f3ee135193 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -226,6 +226,7 @@ static const struct quirk_printer_struct quirk_printers[] = { { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */ { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */ { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, by zut */ + { 0x04f9, 0x000d, USBLP_QUIRK_BIDIR }, /* Brother Industries, Ltd HL-1440 Laser Printer */ { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt Printer M129C */ { 0, 0 } }; -- cgit v1.2.3 From 877e262c4e251352771cc391760a12665b5b210b Mon Sep 17 00:00:00 2001 From: Tomasz K Date: Sun, 4 Jan 2009 12:47:11 +0100 Subject: USB: cp2101 device My girl use modem GSM (EDGE) Commanader 2 on iPlus Polsih provider, PLEASE add this vendor=0x10C4 and product=0x822B to USB serial driver cp2101.c From: Tomasz K Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/cp2101.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index e97eb054e38..027f4b7dde8 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -85,6 +85,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ { USB_DEVICE(0x10C4, 0x81E7) }, /* Aerocomm Radio */ { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ + { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */ { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demostration module */ { USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ -- cgit v1.2.3 From 45eeff848bdfac96dc77aa722dda7c6cee6184f4 Mon Sep 17 00:00:00 2001 From: Robie Basak Date: Mon, 12 Jan 2009 23:05:59 +0000 Subject: USB: ftdi_sio: added Alti-2 VID and Neptune 3 PID This patch adds the vendor and product ID for the Alti-2 Neptune 3 (http://www.alti-2.com) which uses the FTDI chip. Signed-off-by: Robie Basak Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index c70a8f667d8..a85946b212d 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -660,6 +660,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, + { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 373ee09975b..e4305765198 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -854,6 +854,10 @@ #define FTDI_DOMINTELL_DGQG_PID 0xEF50 /* Master */ #define FTDI_DOMINTELL_DUSB_PID 0xEF51 /* DUSB01 module */ +/* Alti-2 products http://www.alti-2.com */ +#define ALTI2_VID 0x1BC9 +#define ALTI2_N3_PID 0x6001 /* Neptune 3 */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ -- cgit v1.2.3 From ca80801bfb24f7a41fe4fade4d2cf7c73f0b2f09 Mon Sep 17 00:00:00 2001 From: Mhayk Whandson Date: Fri, 9 Jan 2009 06:48:16 -0400 Subject: USB: ftdi_sio driver support of bar code scanner from Diebold Added the product id of bcs(bar code scanner) from Diebold Procomp Brazil. Signed-off-by: Mhayk Whandson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index a85946b212d..75597337583 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -661,6 +661,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) }, { USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) }, { USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) }, + { USB_DEVICE(FTDI_VID, DIEBOLD_BCS_SE923_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index e4305765198..1b62eff475d 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -884,6 +884,11 @@ #define RATOC_VENDOR_ID 0x0584 #define RATOC_PRODUCT_ID_USB60F 0xb020 +/* + * DIEBOLD BCS SE923 + */ +#define DIEBOLD_BCS_SE923_PID 0xfb99 + /* * BmRequestType: 1100 0000b * bRequest: FTDI_E2_READ -- cgit v1.2.3 From 7abce6bedc118eb39fe177c2c26be5d008505c14 Mon Sep 17 00:00:00 2001 From: Pete Zaitcev Date: Sat, 20 Dec 2008 12:56:08 -0700 Subject: USB: usbmon: Implement compat_ioctl Running a 32-bit usbmon(8) on 2.6.28-rc9 produces the following: ioctl32(usbmon:28563): Unknown cmd fd(3) cmd(400c9206){t:ffffff92;sz:12} arg(ffd3f458) on /dev/usbmon0 It happens because the compatibility mode was implemented for 2.6.18 and not updated for the fsops.compat_ioctl API. This patch relocates the pieces from under #ifdef CONFIG_COMPAT into compat_ioctl with no other changes except one new whitespace. Signed-off-by: Pete Zaitcev Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 105 +++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index e06810aef2d..4cf27c72423 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -37,6 +37,7 @@ #define MON_IOCX_GET _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get) #define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch) #define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8) + #ifdef CONFIG_COMPAT #define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32) #define MON_IOCX_MFETCH32 _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch32) @@ -921,21 +922,6 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, } break; -#ifdef CONFIG_COMPAT - case MON_IOCX_GET32: { - struct mon_bin_get32 getb; - - if (copy_from_user(&getb, (void __user *)arg, - sizeof(struct mon_bin_get32))) - return -EFAULT; - - ret = mon_bin_get_event(file, rp, - compat_ptr(getb.hdr32), compat_ptr(getb.data32), - getb.alloc32); - } - break; -#endif - case MON_IOCX_MFETCH: { struct mon_bin_mfetch mfetch; @@ -962,7 +948,57 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, } break; + case MON_IOCG_STATS: { + struct mon_bin_stats __user *sp; + unsigned int nevents; + unsigned int ndropped; + + spin_lock_irqsave(&rp->b_lock, flags); + ndropped = rp->cnt_lost; + rp->cnt_lost = 0; + spin_unlock_irqrestore(&rp->b_lock, flags); + nevents = mon_bin_queued(rp); + + sp = (struct mon_bin_stats __user *)arg; + if (put_user(rp->cnt_lost, &sp->dropped)) + return -EFAULT; + if (put_user(nevents, &sp->queued)) + return -EFAULT; + + } + break; + + default: + return -ENOTTY; + } + + return ret; +} + #ifdef CONFIG_COMPAT +static long mon_bin_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mon_reader_bin *rp = file->private_data; + int ret; + + switch (cmd) { + + case MON_IOCX_GET32: { + struct mon_bin_get32 getb; + + if (copy_from_user(&getb, (void __user *)arg, + sizeof(struct mon_bin_get32))) + return -EFAULT; + + ret = mon_bin_get_event(file, rp, + compat_ptr(getb.hdr32), compat_ptr(getb.data32), + getb.alloc32); + if (ret < 0) + return ret; + } + return 0; + case MON_IOCX_MFETCH32: { struct mon_bin_mfetch32 mfetch; @@ -986,37 +1022,25 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file, return ret; if (put_user(ret, &uptr->nfetch32)) return -EFAULT; - ret = 0; } - break; -#endif - - case MON_IOCG_STATS: { - struct mon_bin_stats __user *sp; - unsigned int nevents; - unsigned int ndropped; - - spin_lock_irqsave(&rp->b_lock, flags); - ndropped = rp->cnt_lost; - rp->cnt_lost = 0; - spin_unlock_irqrestore(&rp->b_lock, flags); - nevents = mon_bin_queued(rp); + return 0; - sp = (struct mon_bin_stats __user *)arg; - if (put_user(rp->cnt_lost, &sp->dropped)) - return -EFAULT; - if (put_user(nevents, &sp->queued)) - return -EFAULT; + case MON_IOCG_STATS: + return mon_bin_ioctl(NULL, file, cmd, + (unsigned long) compat_ptr(arg)); - } - break; + case MON_IOCQ_URB_LEN: + case MON_IOCQ_RING_SIZE: + case MON_IOCT_RING_SIZE: + case MON_IOCH_MFLUSH: + return mon_bin_ioctl(NULL, file, cmd, arg); default: - return -ENOTTY; + ; } - - return ret; + return -ENOTTY; } +#endif /* CONFIG_COMPAT */ static unsigned int mon_bin_poll(struct file *file, struct poll_table_struct *wait) @@ -1094,6 +1118,9 @@ static const struct file_operations mon_fops_binary = { /* .write = mon_text_write, */ .poll = mon_bin_poll, .ioctl = mon_bin_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mon_bin_compat_ioctl, +#endif .release = mon_bin_release, .mmap = mon_bin_mmap, }; -- cgit v1.2.3 From 649150926b01c57e45a0376cbc1d3aa98eabfde2 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Onofre Date: Sat, 20 Dec 2008 20:11:55 +0100 Subject: USB: storage: support of Dane-Elec MediaTouch USB device This adds another unusual_devs.h entry for a device that can't handle more than 64k reads/writes in a single command. Signed-off-by: Jean-Baptiste Onofre Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index ec40c26bd54..c6c435899d8 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -995,6 +995,16 @@ UNUSUAL_DEV( 0x071b, 0x3203, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NO_WP_DETECT | US_FL_MAX_SECTORS_64), +/* Reported by Jean-Baptiste Onofre + * Support the following product : + * "Dane-Elec MediaTouch" + */ +UNUSUAL_DEV( 0x071b, 0x32bb, 0x0000, 0x0000, + "RockChip", + "MTP", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_NO_WP_DETECT | US_FL_MAX_SECTORS_64), + /* Reported by Massimiliano Ghilardi * This USB MP3/AVI player device fails and disconnects if more than 128 * sectors (64kB) are read/written in a single command, and may be present -- cgit v1.2.3 From d547f13472adc99721d6eb756085276a8a342366 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 11 Jan 2009 18:46:20 +0100 Subject: USB: Remove ZTE modem from unusual_devices The ZTE modem entry causes usb-storage to ignore the device, but for some versions of the device, usb-storage mode is required to get to modem ode. For both kinds the tool: http://www.draisberghof.de/usb_modeswitch/ should work. Note that the various versions of the device have the same ProductId, VendorId, and bcdDevice number, so we cannot have the entry for some and not others. Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index c6c435899d8..ba16c9ed523 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -2041,16 +2041,6 @@ UNUSUAL_DEV( 0x1652, 0x6600, 0x0201, 0x0201, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), -/* Reported by Mauro Andreolini - * This entry is needed to bypass the ZeroCD mechanism - * and to properly load as a modem device. - */ -UNUSUAL_DEV( 0x19d2, 0x2000, 0x0000, 0x0000, - "Onda ET502HS", - "USB MMC Storage", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_DEVICE), - UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001, "ST", "2A", -- cgit v1.2.3 From 3b498a66a698c581535c0fcf1a8907f3fe9449cc Mon Sep 17 00:00:00 2001 From: Marcel Sebek Date: Sun, 28 Dec 2008 14:06:50 +0100 Subject: USB: 'option' driver - onda device MT503HS has wrong id While trying to make GSM modem Onda MT503HS working, I found a mismatch between device id in the driver code (0x0200) and id in the lsusb output (0x2000). This patch fixed it for me, but I don't know if the original device id was also correct and the new ID should be added instead of replacing the old one. Signed-off-by: Marcel Sebek Acked-by: Domenico Riccio Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 5ed183477aa..19f5f0ce2cc 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -224,7 +224,7 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define ONDA_VENDOR_ID 0x19d2 #define ONDA_PRODUCT_MSA501HS 0x0001 #define ONDA_PRODUCT_ET502HS 0x0002 -#define ONDA_PRODUCT_MT503HS 0x0200 +#define ONDA_PRODUCT_MT503HS 0x2000 #define BANDRICH_VENDOR_ID 0x1A8D #define BANDRICH_PRODUCT_C100_1 0x1002 -- cgit v1.2.3 From c89c60e9d6b306fb6963030abb3bd07cc3de66b2 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sun, 11 Jan 2009 19:53:10 +0000 Subject: USB: cdc-acm: Add another conexant modem to the quirks Another Conexant, another device with the same quirk Signed-off-by: Alan Cox Acked-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-acm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index f2bfae7b398..97ba4a985ed 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1376,6 +1376,9 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1321), /* Conexant USB MODEM CX93010 */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0572, 0x1324), /* Conexant USB MODEM RD02-D400 */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, -- cgit v1.2.3 From 1a1fab513734b3a4fca1bee8229e5ff7e1cb873c Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Mon, 12 Jan 2009 13:31:16 +0100 Subject: USB: new id for ti_usb_3410_5052 driver This adds a new device id Signed-off-by: Oliver Neukum Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ti_usb_3410_5052.c | 3 +++ drivers/usb/serial/ti_usb_3410_5052.h | 2 ++ 2 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 3cf41df302d..baf591137b8 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -184,6 +184,7 @@ static struct usb_device_id ti_id_table_3410[7+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_PRODUCT_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) }, { USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, }; static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { @@ -191,6 +192,7 @@ static struct usb_device_id ti_id_table_5052[4+TI_EXTRA_VID_PID_COUNT+1] = { { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, }; static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = { @@ -205,6 +207,7 @@ static struct usb_device_id ti_id_table_combined[6+2*TI_EXTRA_VID_PID_COUNT+1] = { USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) }, { USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) }, + { USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) }, { } }; diff --git a/drivers/usb/serial/ti_usb_3410_5052.h b/drivers/usb/serial/ti_usb_3410_5052.h index 7e4752fbf23..b7ea5dbadee 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.h +++ b/drivers/usb/serial/ti_usb_3410_5052.h @@ -27,7 +27,9 @@ /* Vendor and product ids */ #define TI_VENDOR_ID 0x0451 +#define IBM_VENDOR_ID 0x04b3 #define TI_3410_PRODUCT_ID 0x3410 +#define IBM_4543_PRODUCT_ID 0x4543 #define TI_3410_EZ430_ID 0xF430 /* TI ez430 development tool */ #define TI_5052_BOOT_PRODUCT_ID 0x5052 /* no EEPROM, no firmware */ #define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */ -- cgit v1.2.3 From 0df2479232eeea20c924350a11788c724b8c218d Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sat, 17 Jan 2009 16:52:17 +0100 Subject: USB: GADGET: fix !x & y ! has a higher precedence than & Signed-off-by: Roel Kluin Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/imx_udc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/imx_udc.c b/drivers/usb/gadget/imx_udc.c index cde8fdf15d5..77c5d0a8a06 100644 --- a/drivers/usb/gadget/imx_udc.c +++ b/drivers/usb/gadget/imx_udc.c @@ -297,7 +297,7 @@ void imx_ep_stall(struct imx_ep_struct *imx_ep) for (i = 0; i < 100; i ++) { temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); - if (!temp & EPSTAT_STALL) + if (!(temp & EPSTAT_STALL)) break; udelay(20); } -- cgit v1.2.3 From a83775b1465ce80af5610cbe80216432212bc7ee Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Tue, 20 Jan 2009 23:48:36 +0100 Subject: USB: unusual_dev: usb-storage needs to ignore a device This patch adds an unusual_devs entry for a Sony Ericsson modem. Like many other modems, we have to ignore the storage device in order to access the modem. At this time usb_modeswitch does not work with this device. Reported-by: The Solutor . Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index ba16c9ed523..b3353bf7a62 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1599,6 +1599,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NO_WP_DETECT ), +/* Reported by The Solutor */ +UNUSUAL_DEV( 0x0fce, 0xd0e1, 0x0000, 0x0000, + "Sony Ericsson", + "MD400", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_DEVICE), + /* Reported by Jan Mate * and by Soeren Sonnenburg */ UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, -- cgit v1.2.3 From aa23c8d616c33578fb99aa6a0effd6705b5d0fa1 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Tue, 20 Jan 2009 23:42:52 +0100 Subject: USB: storage: Add another unusual_dev for off-by-one bug Argosy has released another device with the off-by-one sector. This is a harddrive with an internal cardreader which is affected. Based on a patch written by Martijn Hijdra Signed-off-by: Phil Dibowitz Cc: Martijn Hijdra Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index b3353bf7a62..69269f73956 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1261,6 +1261,13 @@ UNUSUAL_DEV( 0x0840, 0x0084, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Reported by Martijn Hijdra */ +UNUSUAL_DEV( 0x0840, 0x0085, 0x0001, 0x0001, + "Argosy", + "Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Entry and supporting patch by Theodore Kilgore . * Flag will support Bulk devices which use a standards-violating 32-byte * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with -- cgit v1.2.3 From fc91be2ad03e0d243418414a854665274d560ca2 Mon Sep 17 00:00:00 2001 From: "Alex.Cheng@quantatw.com" Date: Thu, 22 Jan 2009 16:01:57 +0800 Subject: USB: option: add QUANTA HSDPA Data Card device ids This patch adds the support for the QUANTA Q101 series HSDPA Data Card. With the vendor and product IDs are set properly, the data card can be detected and works fine. Signed-off-by: Alex Cheng Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 19f5f0ce2cc..6c89da9c6fe 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -158,6 +158,13 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define HUAWEI_PRODUCT_E143E 0x143E #define HUAWEI_PRODUCT_E143F 0x143F +#define QUANTA_VENDOR_ID 0x0408 +#define QUANTA_PRODUCT_Q101 0xEA02 +#define QUANTA_PRODUCT_Q111 0xEA03 +#define QUANTA_PRODUCT_GLX 0xEA04 +#define QUANTA_PRODUCT_GKE 0xEA05 +#define QUANTA_PRODUCT_GLE 0xEA06 + #define NOVATELWIRELESS_VENDOR_ID 0x1410 /* YISO PRODUCTS */ @@ -298,6 +305,11 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q101) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_Q111) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) }, + { USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) }, -- cgit v1.2.3 From 236dd4d18f293e3c9798f35c08272196826a980d Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Sat, 10 Jan 2009 05:03:21 +0300 Subject: USB: Driver for Freescale QUICC Engine USB Host Controller This patch adds support for the FHCI USB controller, as found in the Freescale MPC836x and MPC832x processors. It can support Full or Low speed modes. Quite a lot the hardware is doing by itself (SOF generation, CRC generation and checking), though scheduling and retransmission is on software's shoulders. This controller does not integrate the root hub, so this driver also fakes one-port hub. External hub is required to support more than one device. Signed-off-by: Anton Vorontsov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 + drivers/usb/host/Kconfig | 17 + drivers/usb/host/Makefile | 6 + drivers/usb/host/fhci-dbg.c | 139 +++++++ drivers/usb/host/fhci-hcd.c | 836 +++++++++++++++++++++++++++++++++++++++ drivers/usb/host/fhci-hub.c | 345 ++++++++++++++++ drivers/usb/host/fhci-mem.c | 113 ++++++ drivers/usb/host/fhci-q.c | 284 ++++++++++++++ drivers/usb/host/fhci-sched.c | 888 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/fhci-tds.c | 626 +++++++++++++++++++++++++++++ drivers/usb/host/fhci.h | 607 +++++++++++++++++++++++++++++ 11 files changed, 3862 insertions(+) create mode 100644 drivers/usb/host/fhci-dbg.c create mode 100644 drivers/usb/host/fhci-hcd.c create mode 100644 drivers/usb/host/fhci-hub.c create mode 100644 drivers/usb/host/fhci-mem.c create mode 100644 drivers/usb/host/fhci-q.c create mode 100644 drivers/usb/host/fhci-sched.c create mode 100644 drivers/usb/host/fhci-tds.c create mode 100644 drivers/usb/host/fhci.h (limited to 'drivers') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 8b7c419b876..8bcde8cde55 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_ISP116X_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ +obj-$(CONFIG_USB_FHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 5a988f4cdf8..2c63bfb1f8d 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -239,6 +239,23 @@ config USB_UHCI_HCD To compile this driver as a module, choose M here: the module will be called uhci-hcd. +config USB_FHCI_HCD + tristate "Freescale QE USB Host Controller support" + depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE + select FSL_GTM + select QE_USB + help + This driver enables support for Freescale QE USB Host Controller + (as found on MPC8360 and MPC8323 processors), the driver supports + Full and Low Speed USB. + +config FHCI_DEBUG + bool "Freescale QE USB Host Controller debug support" + depends on USB_FHCI_HCD && DEBUG_FS + help + Say "y" to see some FHCI debug information and statistics + throught debugfs. + config USB_U132_HCD tristate "Elan U132 Adapter Host Controller" depends on USB && USB_FTDI_ELAN diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index e5f3f20787e..f163571e33d 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -7,6 +7,11 @@ ifeq ($(CONFIG_USB_DEBUG),y) endif isp1760-objs := isp1760-hcd.o isp1760-if.o +fhci-objs := fhci-hcd.o fhci-hub.o fhci-q.o fhci-mem.o \ + fhci-tds.o fhci-sched.o +ifeq ($(CONFIG_FHCI_DEBUG),y) +fhci-objs += fhci-dbg.o +endif obj-$(CONFIG_USB_WHCI_HCD) += whci/ @@ -17,6 +22,7 @@ obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o +obj-$(CONFIG_USB_FHCI_HCD) += fhci.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c new file mode 100644 index 00000000000..34e14edf390 --- /dev/null +++ b/drivers/usb/host/fhci-dbg.c @@ -0,0 +1,139 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) +{ + int i; + + if (usb_er == -1) { + fhci->usb_irq_stat[12]++; + return; + } + + for (i = 0; i < 12; ++i) { + if (usb_er & (1 << i)) + fhci->usb_irq_stat[i]++; + } +} + +static int fhci_dfs_regs_show(struct seq_file *s, void *v) +{ + struct fhci_hcd *fhci = s->private; + struct fhci_regs __iomem *regs = fhci->regs; + + seq_printf(s, + "mode: 0x%x\n" "addr: 0x%x\n" + "command: 0x%x\n" "ep0: 0x%x\n" + "event: 0x%x\n" "mask: 0x%x\n" + "status: 0x%x\n" "SOF timer: %d\n" + "frame number: %d\n" + "lines status: 0x%x\n", + in_8(®s->usb_mod), in_8(®s->usb_addr), + in_8(®s->usb_comm), in_be16(®s->usb_ep[0]), + in_be16(®s->usb_event), in_be16(®s->usb_mask), + in_8(®s->usb_status), in_be16(®s->usb_sof_tmr), + in_be16(®s->usb_frame_num), + fhci_ioports_check_bus_state(fhci)); + + return 0; +} + +static int fhci_dfs_irq_stat_show(struct seq_file *s, void *v) +{ + struct fhci_hcd *fhci = s->private; + int *usb_irq_stat = fhci->usb_irq_stat; + + seq_printf(s, + "RXB: %d\n" "TXB: %d\n" "BSY: %d\n" + "SOF: %d\n" "TXE0: %d\n" "TXE1: %d\n" + "TXE2: %d\n" "TXE3: %d\n" "IDLE: %d\n" + "RESET: %d\n" "SFT: %d\n" "MSF: %d\n" + "IDLE_ONLY: %d\n", + usb_irq_stat[0], usb_irq_stat[1], usb_irq_stat[2], + usb_irq_stat[3], usb_irq_stat[4], usb_irq_stat[5], + usb_irq_stat[6], usb_irq_stat[7], usb_irq_stat[8], + usb_irq_stat[9], usb_irq_stat[10], usb_irq_stat[11], + usb_irq_stat[12]); + + return 0; +} + +static int fhci_dfs_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, fhci_dfs_regs_show, inode->i_private); +} + +static int fhci_dfs_irq_stat_open(struct inode *inode, struct file *file) +{ + return single_open(file, fhci_dfs_irq_stat_show, inode->i_private); +} + +static const struct file_operations fhci_dfs_regs_fops = { + .open = fhci_dfs_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations fhci_dfs_irq_stat_fops = { + .open = fhci_dfs_irq_stat_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +void fhci_dfs_create(struct fhci_hcd *fhci) +{ + struct device *dev = fhci_to_hcd(fhci)->self.controller; + + fhci->dfs_root = debugfs_create_dir(dev->bus_id, NULL); + if (!fhci->dfs_root) { + WARN_ON(1); + return; + } + + fhci->dfs_regs = debugfs_create_file("regs", S_IFREG | S_IRUGO, + fhci->dfs_root, fhci, &fhci_dfs_regs_fops); + + fhci->dfs_irq_stat = debugfs_create_file("irq_stat", + S_IFREG | S_IRUGO, fhci->dfs_root, fhci, + &fhci_dfs_irq_stat_fops); + + WARN_ON(!fhci->dfs_regs || !fhci->dfs_irq_stat); +} + +void fhci_dfs_destroy(struct fhci_hcd *fhci) +{ + if (!fhci->dfs_root) + return; + + if (fhci->dfs_irq_stat) + debugfs_remove(fhci->dfs_irq_stat); + + if (fhci->dfs_regs) + debugfs_remove(fhci->dfs_regs); + + debugfs_remove(fhci->dfs_root); +} diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c new file mode 100644 index 00000000000..ba622cc8a9b --- /dev/null +++ b/drivers/usb/host/fhci-hcd.c @@ -0,0 +1,836 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +void fhci_start_sof_timer(struct fhci_hcd *fhci) +{ + fhci_dbg(fhci, "-> %s\n", __func__); + + /* clear frame_n */ + out_be16(&fhci->pram->frame_num, 0); + + out_be16(&fhci->regs->usb_sof_tmr, 0); + setbits8(&fhci->regs->usb_mod, USB_MODE_SFTE); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +void fhci_stop_sof_timer(struct fhci_hcd *fhci) +{ + fhci_dbg(fhci, "-> %s\n", __func__); + + clrbits8(&fhci->regs->usb_mod, USB_MODE_SFTE); + gtm_stop_timer16(fhci->timer); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +u16 fhci_get_sof_timer_count(struct fhci_usb *usb) +{ + return be16_to_cpu(in_be16(&usb->fhci->regs->usb_sof_tmr) / 12); +} + +/* initialize the endpoint zero */ +static u32 endpoint_zero_init(struct fhci_usb *usb, + enum fhci_mem_alloc data_mem, + u32 ring_len) +{ + u32 rc; + + rc = fhci_create_ep(usb, data_mem, ring_len); + if (rc) + return rc; + + /* inilialize endpoint registers */ + fhci_init_ep_registers(usb, usb->ep0, data_mem); + + return 0; +} + +/* enable the USB interrupts */ +void fhci_usb_enable_interrupt(struct fhci_usb *usb) +{ + struct fhci_hcd *fhci = usb->fhci; + + if (usb->intr_nesting_cnt == 1) { + /* initialize the USB interrupt */ + enable_irq(fhci_to_hcd(fhci)->irq); + + /* initialize the event register and mask register */ + out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + + /* enable the timer interrupts */ + enable_irq(fhci->timer->irq); + } else if (usb->intr_nesting_cnt > 1) + fhci_info(fhci, "unbalanced USB interrupts nesting\n"); + usb->intr_nesting_cnt--; +} + +/* diable the usb interrupt */ +void fhci_usb_disable_interrupt(struct fhci_usb *usb) +{ + struct fhci_hcd *fhci = usb->fhci; + + if (usb->intr_nesting_cnt == 0) { + /* diable the timer interrupt */ + disable_irq_nosync(fhci->timer->irq); + + /* disable the usb interrupt */ + disable_irq_nosync(fhci_to_hcd(fhci)->irq); + out_be16(&usb->fhci->regs->usb_mask, 0); + } + usb->intr_nesting_cnt++; +} + +/* enable the USB controller */ +static u32 fhci_usb_enable(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + out_be16(&usb->fhci->regs->usb_event, 0xffff); + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + setbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + + mdelay(100); + + return 0; +} + +/* disable the USB controller */ +static u32 fhci_usb_disable(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + fhci_usb_disable_interrupt(usb); + fhci_port_disable(fhci); + + /* disable the usb controller */ + if (usb->port_status == FHCI_PORT_FULL || + usb->port_status == FHCI_PORT_LOW) + fhci_device_disconnected_interrupt(fhci); + + clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + + return 0; +} + +/* check the bus state by polling the QE bit on the IO ports */ +int fhci_ioports_check_bus_state(struct fhci_hcd *fhci) +{ + u8 bits = 0; + + /* check USBOE,if transmitting,exit */ + if (!gpio_get_value(fhci->gpios[GPIO_USBOE])) + return -1; + + /* check USBRP */ + if (gpio_get_value(fhci->gpios[GPIO_USBRP])) + bits |= 0x2; + + /* check USBRN */ + if (gpio_get_value(fhci->gpios[GPIO_USBRN])) + bits |= 0x1; + + return bits; +} + +static void fhci_mem_free(struct fhci_hcd *fhci) +{ + struct ed *ed; + struct ed *next_ed; + struct td *td; + struct td *next_td; + + list_for_each_entry_safe(ed, next_ed, &fhci->empty_eds, node) { + list_del(&ed->node); + kfree(ed); + } + + list_for_each_entry_safe(td, next_td, &fhci->empty_tds, node) { + list_del(&td->node); + kfree(td); + } + + kfree(fhci->vroot_hub); + fhci->vroot_hub = NULL; + + kfree(fhci->hc_list); + fhci->hc_list = NULL; +} + +static int fhci_mem_init(struct fhci_hcd *fhci) +{ + int i; + + fhci->hc_list = kzalloc(sizeof(*fhci->hc_list), GFP_KERNEL); + if (!fhci->hc_list) + goto err; + + INIT_LIST_HEAD(&fhci->hc_list->ctrl_list); + INIT_LIST_HEAD(&fhci->hc_list->bulk_list); + INIT_LIST_HEAD(&fhci->hc_list->iso_list); + INIT_LIST_HEAD(&fhci->hc_list->intr_list); + INIT_LIST_HEAD(&fhci->hc_list->done_list); + + fhci->vroot_hub = kzalloc(sizeof(*fhci->vroot_hub), GFP_KERNEL); + if (!fhci->vroot_hub) + goto err; + + INIT_LIST_HEAD(&fhci->empty_eds); + INIT_LIST_HEAD(&fhci->empty_tds); + + /* initialize work queue to handle done list */ + fhci_tasklet.data = (unsigned long)fhci; + fhci->process_done_task = &fhci_tasklet; + + for (i = 0; i < MAX_TDS; i++) { + struct td *td; + + td = kmalloc(sizeof(*td), GFP_KERNEL); + if (!td) + goto err; + fhci_recycle_empty_td(fhci, td); + } + for (i = 0; i < MAX_EDS; i++) { + struct ed *ed; + + ed = kmalloc(sizeof(*ed), GFP_KERNEL); + if (!ed) + goto err; + fhci_recycle_empty_ed(fhci, ed); + } + + fhci->active_urbs = 0; + return 0; +err: + fhci_mem_free(fhci); + return -ENOMEM; +} + +/* destroy the fhci_usb structure */ +static void fhci_usb_free(void *lld) +{ + struct fhci_usb *usb = lld; + struct fhci_hcd *fhci = usb->fhci; + + if (usb) { + fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF); + fhci_ep0_free(usb); + kfree(usb->actual_frame); + kfree(usb); + } +} + +/* initialize the USB */ +static int fhci_usb_init(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + memset_io(usb->fhci->pram, 0, FHCI_PRAM_SIZE); + + usb->port_status = FHCI_PORT_DISABLED; + usb->max_frame_usage = FRAME_TIME_USAGE; + usb->sw_transaction_time = SW_FIX_TIME_BETWEEN_TRANSACTION; + + usb->actual_frame = kzalloc(sizeof(*usb->actual_frame), GFP_KERNEL); + if (!usb->actual_frame) { + fhci_usb_free(usb); + return -ENOMEM; + } + + INIT_LIST_HEAD(&usb->actual_frame->tds_list); + + /* initializing registers on chip, clear frame number */ + out_be16(&fhci->pram->frame_num, 0); + + /* clear rx state */ + out_be32(&fhci->pram->rx_state, 0); + + /* set mask register */ + usb->saved_msk = (USB_E_TXB_MASK | + USB_E_TXE1_MASK | + USB_E_IDLE_MASK | + USB_E_RESET_MASK | USB_E_SFT_MASK | USB_E_MSF_MASK); + + out_8(&usb->fhci->regs->usb_mod, USB_MODE_HOST | USB_MODE_EN); + + /* clearing the mask register */ + out_be16(&usb->fhci->regs->usb_mask, 0); + + /* initialing the event register */ + out_be16(&usb->fhci->regs->usb_event, 0xffff); + + if (endpoint_zero_init(usb, DEFAULT_DATA_MEM, DEFAULT_RING_LEN) != 0) { + fhci_usb_free(usb); + return -EINVAL; + } + + return 0; +} + +/* initialize the fhci_usb struct and the corresponding data staruct */ +static struct fhci_usb *fhci_create_lld(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb; + + /* allocate memory for SCC data structure */ + usb = kzalloc(sizeof(*usb), GFP_KERNEL); + if (!usb) { + fhci_err(fhci, "no memory for SCC data struct\n"); + return NULL; + } + + usb->fhci = fhci; + usb->hc_list = fhci->hc_list; + usb->vroot_hub = fhci->vroot_hub; + + usb->transfer_confirm = fhci_transfer_confirm_callback; + + return usb; +} + +static int fhci_start(struct usb_hcd *hcd) +{ + int ret; + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + + ret = fhci_mem_init(fhci); + if (ret) { + fhci_err(fhci, "failed to allocate memory\n"); + goto err; + } + + fhci->usb_lld = fhci_create_lld(fhci); + if (!fhci->usb_lld) { + fhci_err(fhci, "low level driver config failed\n"); + ret = -ENOMEM; + goto err; + } + + ret = fhci_usb_init(fhci); + if (ret) { + fhci_err(fhci, "low level driver initialize failed\n"); + goto err; + } + + spin_lock_init(&fhci->lock); + + /* connect the virtual root hub */ + fhci->vroot_hub->dev_num = 1; /* this field may be needed to fix */ + fhci->vroot_hub->hub.wHubStatus = 0; + fhci->vroot_hub->hub.wHubChange = 0; + fhci->vroot_hub->port.wPortStatus = 0; + fhci->vroot_hub->port.wPortChange = 0; + + hcd->state = HC_STATE_RUNNING; + + /* + * From here on, khubd concurrently accesses the root + * hub; drivers will be talking to enumerated devices. + * (On restart paths, khubd already knows about the root + * hub and could find work as soon as we wrote FLAG_CF.) + * + * Before this point the HC was idle/ready. After, khubd + * and device drivers may start it running. + */ + fhci_usb_enable(fhci); + return 0; +err: + fhci_mem_free(fhci); + return ret; +} + +static void fhci_stop(struct usb_hcd *hcd) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + + fhci_usb_disable_interrupt(fhci->usb_lld); + fhci_usb_disable(fhci); + + fhci_usb_free(fhci->usb_lld); + fhci->usb_lld = NULL; + fhci_mem_free(fhci); +} + +static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + u32 pipe = urb->pipe; + int ret; + int i; + int size = 0; + struct urb_priv *urb_priv; + unsigned long flags; + + switch (usb_pipetype(pipe)) { + case PIPE_CONTROL: + /* 1 td fro setup,1 for ack */ + size = 2; + case PIPE_BULK: + /* one td for every 4096 bytes(can be upto 8k) */ + size += urb->transfer_buffer_length / 4096; + /* ...add for any remaining bytes... */ + if ((urb->transfer_buffer_length % 4096) != 0) + size++; + /* ..and maybe a zero length packet to wrap it up */ + if (size == 0) + size++; + else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0 + && (urb->transfer_buffer_length + % usb_maxpacket(urb->dev, pipe, + usb_pipeout(pipe))) != 0) + size++; + break; + case PIPE_ISOCHRONOUS: + size = urb->number_of_packets; + if (size <= 0) + return -EINVAL; + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = (u32) (-EXDEV); + } + break; + case PIPE_INTERRUPT: + size = 1; + } + + /* allocate the private part of the URB */ + urb_priv = kzalloc(sizeof(*urb_priv), mem_flags); + if (!urb_priv) + return -ENOMEM; + + /* allocate the private part of the URB */ + urb_priv->tds = kzalloc(size * sizeof(struct td), mem_flags); + if (!urb_priv->tds) { + kfree(urb_priv); + return -ENOMEM; + } + + spin_lock_irqsave(&fhci->lock, flags); + + ret = usb_hcd_link_urb_to_ep(hcd, urb); + if (ret) + goto err; + + /* fill the private part of the URB */ + urb_priv->num_of_tds = size; + + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->error_count = 0; + urb->hcpriv = urb_priv; + + fhci_queue_urb(fhci, urb); +err: + if (ret) { + kfree(urb_priv->tds); + kfree(urb_priv); + } + spin_unlock_irqrestore(&fhci->lock, flags); + return ret; +} + +/* dequeue FHCI URB */ +static int fhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + struct fhci_usb *usb = fhci->usb_lld; + int ret = -EINVAL; + unsigned long flags; + + if (!urb || !urb->dev || !urb->dev->bus) + goto out; + + spin_lock_irqsave(&fhci->lock, flags); + + ret = usb_hcd_check_unlink_urb(hcd, urb, status); + if (ret) + goto out2; + + if (usb->port_status != FHCI_PORT_DISABLED) { + struct urb_priv *urb_priv; + + /* + * flag the urb's data for deletion in some upcoming + * SF interrupt's delete list processing + */ + urb_priv = urb->hcpriv; + + if (!urb_priv || (urb_priv->state == URB_DEL)) + goto out2; + + urb_priv->state = URB_DEL; + + /* already pending? */ + urb_priv->ed->state = FHCI_ED_URB_DEL; + } else { + fhci_urb_complete_free(fhci, urb); + } + +out2: + spin_unlock_irqrestore(&fhci->lock, flags); +out: + return ret; +} + +static void fhci_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) +{ + struct fhci_hcd *fhci; + struct ed *ed; + unsigned long flags; + + fhci = hcd_to_fhci(hcd); + spin_lock_irqsave(&fhci->lock, flags); + ed = ep->hcpriv; + if (ed) { + while (ed->td_head != NULL) { + struct td *td = fhci_remove_td_from_ed(ed); + fhci_urb_complete_free(fhci, td->urb); + } + fhci_recycle_empty_ed(fhci, ed); + ep->hcpriv = NULL; + } + spin_unlock_irqrestore(&fhci->lock, flags); +} + +static int fhci_get_frame_number(struct usb_hcd *hcd) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + + return get_frame_num(fhci); +} + +static const struct hc_driver fhci_driver = { + .description = "fsl,usb-fhci", + .product_desc = "FHCI HOST Controller", + .hcd_priv_size = sizeof(struct fhci_hcd), + + /* generic hardware linkage */ + .irq = fhci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* basic lifecycle operation */ + .start = fhci_start, + .stop = fhci_stop, + + /* managing i/o requests and associated device resources */ + .urb_enqueue = fhci_urb_enqueue, + .urb_dequeue = fhci_urb_dequeue, + .endpoint_disable = fhci_endpoint_disable, + + /* scheduling support */ + .get_frame_number = fhci_get_frame_number, + + /* root hub support */ + .hub_status_data = fhci_hub_status_data, + .hub_control = fhci_hub_control, +}; + +static int __devinit of_fhci_probe(struct of_device *ofdev, + const struct of_device_id *ofid) +{ + struct device *dev = &ofdev->dev; + struct device_node *node = ofdev->node; + struct usb_hcd *hcd; + struct fhci_hcd *fhci; + struct resource usb_regs; + unsigned long pram_addr; + unsigned int usb_irq; + const char *sprop; + const u32 *iprop; + int size; + int ret; + int i; + int j; + + if (usb_disabled()) + return -ENODEV; + + sprop = of_get_property(node, "mode", NULL); + if (sprop && strcmp(sprop, "host")) + return -ENODEV; + + hcd = usb_create_hcd(&fhci_driver, dev, dev->bus_id); + if (!hcd) { + dev_err(dev, "could not create hcd\n"); + return -ENOMEM; + } + + fhci = hcd_to_fhci(hcd); + hcd->self.controller = dev; + dev_set_drvdata(dev, hcd); + + iprop = of_get_property(node, "hub-power-budget", &size); + if (iprop && size == sizeof(*iprop)) + hcd->power_budget = *iprop; + + /* FHCI registers. */ + ret = of_address_to_resource(node, 0, &usb_regs); + if (ret) { + dev_err(dev, "could not get regs\n"); + goto err_regs; + } + + hcd->regs = ioremap(usb_regs.start, usb_regs.end - usb_regs.start + 1); + if (!hcd->regs) { + dev_err(dev, "could not ioremap regs\n"); + ret = -ENOMEM; + goto err_regs; + } + fhci->regs = hcd->regs; + + /* Parameter RAM. */ + iprop = of_get_property(node, "reg", &size); + if (!iprop || size < sizeof(*iprop) * 4) { + dev_err(dev, "can't get pram offset\n"); + ret = -EINVAL; + goto err_pram; + } + + pram_addr = cpm_muram_alloc_fixed(iprop[2], FHCI_PRAM_SIZE); + if (IS_ERR_VALUE(pram_addr)) { + dev_err(dev, "failed to allocate usb pram\n"); + ret = -ENOMEM; + goto err_pram; + } + fhci->pram = cpm_muram_addr(pram_addr); + + /* GPIOs and pins */ + for (i = 0; i < NUM_GPIOS; i++) { + int gpio; + enum of_gpio_flags flags; + + gpio = of_get_gpio_flags(node, i, &flags); + fhci->gpios[i] = gpio; + fhci->alow_gpios[i] = flags & OF_GPIO_ACTIVE_LOW; + + if (!gpio_is_valid(gpio)) { + if (i < GPIO_SPEED) { + dev_err(dev, "incorrect GPIO%d: %d\n", + i, gpio); + goto err_gpios; + } else { + dev_info(dev, "assuming board doesn't have " + "%s gpio\n", i == GPIO_SPEED ? + "speed" : "power"); + continue; + } + } + + ret = gpio_request(gpio, dev->bus_id); + if (ret) { + dev_err(dev, "failed to request gpio %d", i); + goto err_gpios; + } + + if (i >= GPIO_SPEED) { + ret = gpio_direction_output(gpio, 0); + if (ret) { + dev_err(dev, "failed to set gpio %d as " + "an output\n", i); + i++; + goto err_gpios; + } + } + } + + for (j = 0; j < NUM_PINS; j++) { + fhci->pins[j] = qe_pin_request(ofdev->node, j); + if (IS_ERR(fhci->pins[j])) { + ret = PTR_ERR(fhci->pins[j]); + dev_err(dev, "can't get pin %d: %d\n", j, ret); + goto err_pins; + } + } + + /* Frame limit timer and its interrupt. */ + fhci->timer = gtm_get_timer16(); + if (IS_ERR(fhci->timer)) { + ret = PTR_ERR(fhci->timer); + dev_err(dev, "failed to request qe timer: %i", ret); + goto err_get_timer; + } + + ret = request_irq(fhci->timer->irq, fhci_frame_limit_timer_irq, + IRQF_DISABLED, "qe timer (usb)", hcd); + if (ret) { + dev_err(dev, "failed to request timer irq"); + goto err_timer_irq; + } + + /* USB Host interrupt. */ + usb_irq = irq_of_parse_and_map(node, 0); + if (usb_irq == NO_IRQ) { + dev_err(dev, "could not get usb irq\n"); + ret = -EINVAL; + goto err_usb_irq; + } + + /* Clocks. */ + sprop = of_get_property(node, "fsl,fullspeed-clock", NULL); + if (sprop) { + fhci->fullspeed_clk = qe_clock_source(sprop); + if (fhci->fullspeed_clk == QE_CLK_DUMMY) { + dev_err(dev, "wrong fullspeed-clock\n"); + ret = -EINVAL; + goto err_clocks; + } + } + + sprop = of_get_property(node, "fsl,lowspeed-clock", NULL); + if (sprop) { + fhci->lowspeed_clk = qe_clock_source(sprop); + if (fhci->lowspeed_clk == QE_CLK_DUMMY) { + dev_err(dev, "wrong lowspeed-clock\n"); + ret = -EINVAL; + goto err_clocks; + } + } + + if (fhci->fullspeed_clk == QE_CLK_NONE && + fhci->lowspeed_clk == QE_CLK_NONE) { + dev_err(dev, "no clocks specified\n"); + ret = -EINVAL; + goto err_clocks; + } + + dev_info(dev, "at 0x%p, irq %d\n", hcd->regs, usb_irq); + + fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF); + + /* Start with full-speed, if possible. */ + if (fhci->fullspeed_clk != QE_CLK_NONE) { + fhci_config_transceiver(fhci, FHCI_PORT_FULL); + qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK); + } else { + fhci_config_transceiver(fhci, FHCI_PORT_LOW); + qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3); + } + + /* Clear and disable any pending interrupts. */ + out_be16(&fhci->regs->usb_event, 0xffff); + out_be16(&fhci->regs->usb_mask, 0); + + ret = usb_add_hcd(hcd, usb_irq, IRQF_DISABLED); + if (ret < 0) + goto err_add_hcd; + + fhci_dfs_create(fhci); + + return 0; + +err_add_hcd: +err_clocks: + irq_dispose_mapping(usb_irq); +err_usb_irq: + free_irq(fhci->timer->irq, hcd); +err_timer_irq: + gtm_put_timer16(fhci->timer); +err_get_timer: +err_pins: + while (--j >= 0) + qe_pin_free(fhci->pins[j]); +err_gpios: + while (--i >= 0) { + if (gpio_is_valid(fhci->gpios[i])) + gpio_free(fhci->gpios[i]); + } + cpm_muram_free(pram_addr); +err_pram: + iounmap(hcd->regs); +err_regs: + usb_put_hcd(hcd); + return ret; +} + +static int __devexit fhci_remove(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + int i; + int j; + + usb_remove_hcd(hcd); + free_irq(fhci->timer->irq, hcd); + gtm_put_timer16(fhci->timer); + cpm_muram_free(cpm_muram_offset(fhci->pram)); + for (i = 0; i < NUM_GPIOS; i++) { + if (!gpio_is_valid(fhci->gpios[i])) + continue; + gpio_free(fhci->gpios[i]); + } + for (j = 0; j < NUM_PINS; j++) + qe_pin_free(fhci->pins[j]); + fhci_dfs_destroy(fhci); + usb_put_hcd(hcd); + return 0; +} + +static int __devexit of_fhci_remove(struct of_device *ofdev) +{ + return fhci_remove(&ofdev->dev); +} + +static struct of_device_id of_fhci_match[] = { + { .compatible = "fsl,mpc8323-qe-usb", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_fhci_match); + +static struct of_platform_driver of_fhci_driver = { + .name = "fsl,usb-fhci", + .match_table = of_fhci_match, + .probe = of_fhci_probe, + .remove = __devexit_p(of_fhci_remove), +}; + +static int __init fhci_module_init(void) +{ + return of_register_platform_driver(&of_fhci_driver); +} +module_init(fhci_module_init); + +static void __exit fhci_module_exit(void) +{ + of_unregister_platform_driver(&of_fhci_driver); +} +module_exit(fhci_module_exit); + +MODULE_DESCRIPTION("USB Freescale Host Controller Interface Driver"); +MODULE_AUTHOR("Shlomi Gridish , " + "Jerry Huang , " + "Anton Vorontsov "); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c new file mode 100644 index 00000000000..0cfaedc3e12 --- /dev/null +++ b/drivers/usb/host/fhci-hub.c @@ -0,0 +1,345 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +/* virtual root hub specific descriptor */ +static u8 root_hub_des[] = { + 0x09, /* blength */ + 0x29, /* bDescriptorType;hub-descriptor */ + 0x01, /* bNbrPorts */ + 0x00, /* wHubCharacteristics */ + 0x00, + 0x01, /* bPwrOn2pwrGood;2ms */ + 0x00, /* bHubContrCurrent;0mA */ + 0x00, /* DeviceRemoveable */ + 0xff, /* PortPwrCtrlMask */ +}; + +static void fhci_gpio_set_value(struct fhci_hcd *fhci, int gpio_nr, bool on) +{ + int gpio = fhci->gpios[gpio_nr]; + bool alow = fhci->alow_gpios[gpio_nr]; + + if (!gpio_is_valid(gpio)) + return; + + gpio_set_value(gpio, on ^ alow); + mdelay(5); +} + +void fhci_config_transceiver(struct fhci_hcd *fhci, + enum fhci_port_status status) +{ + fhci_dbg(fhci, "-> %s: %d\n", __func__, status); + + switch (status) { + case FHCI_PORT_POWER_OFF: + fhci_gpio_set_value(fhci, GPIO_POWER, false); + break; + case FHCI_PORT_DISABLED: + case FHCI_PORT_WAITING: + fhci_gpio_set_value(fhci, GPIO_POWER, true); + break; + case FHCI_PORT_LOW: + fhci_gpio_set_value(fhci, GPIO_SPEED, false); + break; + case FHCI_PORT_FULL: + fhci_gpio_set_value(fhci, GPIO_SPEED, true); + break; + default: + WARN_ON(1); + break; + } + + fhci_dbg(fhci, "<- %s: %d\n", __func__, status); +} + +/* disable the USB port by clearing the EN bit in the USBMOD register */ +void fhci_port_disable(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = (struct fhci_usb *)fhci->usb_lld; + enum fhci_port_status port_status; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_stop_sof_timer(fhci); + + fhci_flush_all_transmissions(usb); + + fhci_usb_disable_interrupt((struct fhci_usb *)fhci->usb_lld); + port_status = usb->port_status; + usb->port_status = FHCI_PORT_DISABLED; + + /* Enable IDLE since we want to know if something comes along */ + usb->saved_msk |= USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + + /* check if during the disconnection process attached new device */ + if (port_status == FHCI_PORT_WAITING) + fhci_device_connected_interrupt(fhci); + usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_ENABLE; + usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE; + fhci_usb_enable_interrupt((struct fhci_usb *)fhci->usb_lld); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +/* enable the USB port by setting the EN bit in the USBMOD register */ +void fhci_port_enable(void *lld) +{ + struct fhci_usb *usb = (struct fhci_usb *)lld; + struct fhci_hcd *fhci = usb->fhci; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_config_transceiver(fhci, usb->port_status); + + if ((usb->port_status != FHCI_PORT_FULL) && + (usb->port_status != FHCI_PORT_LOW)) + fhci_start_sof_timer(fhci); + + usb->vroot_hub->port.wPortStatus |= USB_PORT_STAT_ENABLE; + usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_ENABLE; + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +void fhci_io_port_generate_reset(struct fhci_hcd *fhci) +{ + fhci_dbg(fhci, "-> %s\n", __func__); + + gpio_direction_output(fhci->gpios[GPIO_USBOE], 0); + gpio_direction_output(fhci->gpios[GPIO_USBTP], 0); + gpio_direction_output(fhci->gpios[GPIO_USBTN], 0); + + mdelay(5); + + qe_pin_set_dedicated(fhci->pins[PIN_USBOE]); + qe_pin_set_dedicated(fhci->pins[PIN_USBTP]); + qe_pin_set_dedicated(fhci->pins[PIN_USBTN]); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +/* generate the RESET condition on the bus */ +void fhci_port_reset(void *lld) +{ + struct fhci_usb *usb = (struct fhci_usb *)lld; + struct fhci_hcd *fhci = usb->fhci; + u8 mode; + u16 mask; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_stop_sof_timer(fhci); + /* disable the USB controller */ + mode = in_8(&fhci->regs->usb_mod); + out_8(&fhci->regs->usb_mod, mode & (~USB_MODE_EN)); + + /* disable idle interrupts */ + mask = in_be16(&fhci->regs->usb_mask); + out_be16(&fhci->regs->usb_mask, mask & (~USB_E_IDLE_MASK)); + + fhci_io_port_generate_reset(fhci); + + /* enable interrupt on this endpoint */ + out_be16(&fhci->regs->usb_mask, mask); + + /* enable the USB controller */ + mode = in_8(&fhci->regs->usb_mod); + out_8(&fhci->regs->usb_mod, mode | USB_MODE_EN); + fhci_start_sof_timer(fhci); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +int fhci_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + int ret = 0; + unsigned long flags; + + fhci_dbg(fhci, "-> %s\n", __func__); + + spin_lock_irqsave(&fhci->lock, flags); + + if (fhci->vroot_hub->port.wPortChange & (USB_PORT_STAT_C_CONNECTION | + USB_PORT_STAT_C_ENABLE | USB_PORT_STAT_C_SUSPEND | + USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_OVERCURRENT)) { + *buf = 1 << 1; + ret = 1; + fhci_dbg(fhci, "-- %s\n", __func__); + } + + spin_unlock_irqrestore(&fhci->lock, flags); + + fhci_dbg(fhci, "<- %s\n", __func__); + + return ret; +} + +int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + int retval = 0; + int len = 0; + struct usb_hub_status *hub_status; + struct usb_port_status *port_status; + unsigned long flags; + + spin_lock_irqsave(&fhci->lock, flags); + + fhci_dbg(fhci, "-> %s\n", __func__); + + switch (typeReq) { + case ClearHubFeature: + switch (wValue) { + case C_HUB_LOCAL_POWER: + case C_HUB_OVER_CURRENT: + break; + default: + goto error; + } + break; + case ClearPortFeature: + fhci->vroot_hub->feature &= (1 << wValue); + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + fhci->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_ENABLE; + fhci_port_disable(fhci); + break; + case USB_PORT_FEAT_C_ENABLE: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_ENABLE; + break; + case USB_PORT_FEAT_SUSPEND: + fhci->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_SUSPEND; + fhci_stop_sof_timer(fhci); + break; + case USB_PORT_FEAT_C_SUSPEND: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_SUSPEND; + break; + case USB_PORT_FEAT_POWER: + fhci->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_POWER; + fhci_config_transceiver(fhci, FHCI_PORT_POWER_OFF); + break; + case USB_PORT_FEAT_C_CONNECTION: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_CONNECTION; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_OVERCURRENT; + break; + case USB_PORT_FEAT_C_RESET: + fhci->vroot_hub->port.wPortChange &= + ~USB_PORT_STAT_C_RESET; + break; + default: + goto error; + } + break; + case GetHubDescriptor: + memcpy(buf, root_hub_des, sizeof(root_hub_des)); + buf[3] = 0x11; /* per-port power, no ovrcrnt */ + len = (buf[0] < wLength) ? buf[0] : wLength; + break; + case GetHubStatus: + hub_status = (struct usb_hub_status *)buf; + hub_status->wHubStatus = + cpu_to_le16(fhci->vroot_hub->hub.wHubStatus); + hub_status->wHubChange = + cpu_to_le16(fhci->vroot_hub->hub.wHubChange); + len = 4; + break; + case GetPortStatus: + port_status = (struct usb_port_status *)buf; + port_status->wPortStatus = + cpu_to_le16(fhci->vroot_hub->port.wPortStatus); + port_status->wPortChange = + cpu_to_le16(fhci->vroot_hub->port.wPortChange); + len = 4; + break; + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case SetPortFeature: + fhci->vroot_hub->feature |= (1 << wValue); + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_ENABLE; + fhci_port_enable(fhci->usb_lld); + break; + case USB_PORT_FEAT_SUSPEND: + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_SUSPEND; + fhci_stop_sof_timer(fhci); + break; + case USB_PORT_FEAT_RESET: + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_RESET; + fhci_port_reset(fhci->usb_lld); + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_ENABLE; + fhci->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_RESET; + break; + case USB_PORT_FEAT_POWER: + fhci->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_POWER; + fhci_config_transceiver(fhci, FHCI_PORT_WAITING); + break; + default: + goto error; + } + break; + default: +error: + retval = -EPIPE; + } + + fhci_dbg(fhci, "<- %s\n", __func__); + + spin_unlock_irqrestore(&fhci->lock, flags); + + return retval; +} diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c new file mode 100644 index 00000000000..2c0736c9971 --- /dev/null +++ b/drivers/usb/host/fhci-mem.c @@ -0,0 +1,113 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +static void init_td(struct td *td) +{ + memset(td, 0, sizeof(*td)); + INIT_LIST_HEAD(&td->node); + INIT_LIST_HEAD(&td->frame_lh); +} + +static void init_ed(struct ed *ed) +{ + memset(ed, 0, sizeof(*ed)); + INIT_LIST_HEAD(&ed->td_list); + INIT_LIST_HEAD(&ed->node); +} + +static struct td *get_empty_td(struct fhci_hcd *fhci) +{ + struct td *td; + + if (!list_empty(&fhci->empty_tds)) { + td = list_entry(fhci->empty_tds.next, struct td, node); + list_del(fhci->empty_tds.next); + } else { + td = kmalloc(sizeof(*td), GFP_ATOMIC); + if (!td) + fhci_err(fhci, "No memory to allocate to TD\n"); + else + init_td(td); + } + + return td; +} + +void fhci_recycle_empty_td(struct fhci_hcd *fhci, struct td *td) +{ + init_td(td); + list_add(&td->node, &fhci->empty_tds); +} + +struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci) +{ + struct ed *ed; + + if (!list_empty(&fhci->empty_eds)) { + ed = list_entry(fhci->empty_eds.next, struct ed, node); + list_del(fhci->empty_eds.next); + } else { + ed = kmalloc(sizeof(*ed), GFP_ATOMIC); + if (!ed) + fhci_err(fhci, "No memory to allocate to ED\n"); + else + init_ed(ed); + } + + return ed; +} + +void fhci_recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed) +{ + init_ed(ed); + list_add(&ed->node, &fhci->empty_eds); +} + +struct td *fhci_td_fill(struct fhci_hcd *fhci, struct urb *urb, + struct urb_priv *urb_priv, struct ed *ed, u16 index, + enum fhci_ta_type type, int toggle, u8 *data, u32 len, + u16 interval, u16 start_frame, bool ioc) +{ + struct td *td = get_empty_td(fhci); + + if (!td) + return NULL; + + td->urb = urb; + td->ed = ed; + td->type = type; + td->toggle = toggle; + td->data = data; + td->len = len; + td->iso_index = index; + td->interval = interval; + td->start_frame = start_frame; + td->ioc = ioc; + td->status = USB_TD_OK; + + urb_priv->tds[index] = td; + + return td; +} diff --git a/drivers/usb/host/fhci-q.c b/drivers/usb/host/fhci-q.c new file mode 100644 index 00000000000..b0a1446ba29 --- /dev/null +++ b/drivers/usb/host/fhci-q.c @@ -0,0 +1,284 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +/* maps the hardware error code to the USB error code */ +static int status_to_error(u32 status) +{ + if (status == USB_TD_OK) + return 0; + else if (status & USB_TD_RX_ER_CRC) + return -EILSEQ; + else if (status & USB_TD_RX_ER_NONOCT) + return -EPROTO; + else if (status & USB_TD_RX_ER_OVERUN) + return -ECOMM; + else if (status & USB_TD_RX_ER_BITSTUFF) + return -EPROTO; + else if (status & USB_TD_RX_ER_PID) + return -EILSEQ; + else if (status & (USB_TD_TX_ER_NAK | USB_TD_TX_ER_TIMEOUT)) + return -ETIMEDOUT; + else if (status & USB_TD_TX_ER_STALL) + return -EPIPE; + else if (status & USB_TD_TX_ER_UNDERUN) + return -ENOSR; + else if (status & USB_TD_RX_DATA_UNDERUN) + return -EREMOTEIO; + else if (status & USB_TD_RX_DATA_OVERUN) + return -EOVERFLOW; + else + return -EINVAL; +} + +void fhci_add_td_to_frame(struct fhci_time_frame *frame, struct td *td) +{ + list_add_tail(&td->frame_lh, &frame->tds_list); +} + +void fhci_add_tds_to_ed(struct ed *ed, struct td **td_list, int number) +{ + int i; + + for (i = 0; i < number; i++) { + struct td *td = td_list[i]; + list_add_tail(&td->node, &ed->td_list); + } + if (ed->td_head == NULL) + ed->td_head = td_list[0]; +} + +static struct td *peek_td_from_ed(struct ed *ed) +{ + struct td *td; + + if (!list_empty(&ed->td_list)) + td = list_entry(ed->td_list.next, struct td, node); + else + td = NULL; + + return td; +} + +struct td *fhci_remove_td_from_frame(struct fhci_time_frame *frame) +{ + struct td *td; + + if (!list_empty(&frame->tds_list)) { + td = list_entry(frame->tds_list.next, struct td, frame_lh); + list_del_init(frame->tds_list.next); + } else + td = NULL; + + return td; +} + +struct td *fhci_peek_td_from_frame(struct fhci_time_frame *frame) +{ + struct td *td; + + if (!list_empty(&frame->tds_list)) + td = list_entry(frame->tds_list.next, struct td, frame_lh); + else + td = NULL; + + return td; +} + +struct td *fhci_remove_td_from_ed(struct ed *ed) +{ + struct td *td; + + if (!list_empty(&ed->td_list)) { + td = list_entry(ed->td_list.next, struct td, node); + list_del_init(ed->td_list.next); + + /* if this TD was the ED's head, find next TD */ + if (!list_empty(&ed->td_list)) + ed->td_head = list_entry(ed->td_list.next, struct td, + node); + else + ed->td_head = NULL; + } else + td = NULL; + + return td; +} + +struct td *fhci_remove_td_from_done_list(struct fhci_controller_list *p_list) +{ + struct td *td; + + if (!list_empty(&p_list->done_list)) { + td = list_entry(p_list->done_list.next, struct td, node); + list_del_init(p_list->done_list.next); + } else + td = NULL; + + return td; +} + +void fhci_move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed) +{ + struct td *td; + + td = ed->td_head; + list_del_init(&td->node); + + /* If this TD was the ED's head,find next TD */ + if (!list_empty(&ed->td_list)) + ed->td_head = list_entry(ed->td_list.next, struct td, node); + else { + ed->td_head = NULL; + ed->state = FHCI_ED_SKIP; + } + ed->toggle_carry = td->toggle; + list_add_tail(&td->node, &usb->hc_list->done_list); + if (td->ioc) + usb->transfer_confirm(usb->fhci); +} + +/* free done FHCI URB resource such as ED and TD */ +static void free_urb_priv(struct fhci_hcd *fhci, struct urb *urb) +{ + int i; + struct urb_priv *urb_priv = urb->hcpriv; + struct ed *ed = urb_priv->ed; + + for (i = 0; i < urb_priv->num_of_tds; i++) { + list_del_init(&urb_priv->tds[i]->node); + fhci_recycle_empty_td(fhci, urb_priv->tds[i]); + } + + /* if this TD was the ED's head,find the next TD */ + if (!list_empty(&ed->td_list)) + ed->td_head = list_entry(ed->td_list.next, struct td, node); + else + ed->td_head = NULL; + + kfree(urb_priv->tds); + kfree(urb_priv); + urb->hcpriv = NULL; + + /* if this TD was the ED's head,find next TD */ + if (ed->td_head == NULL) + list_del_init(&ed->node); + fhci->active_urbs--; +} + +/* this routine called to complete and free done URB */ +void fhci_urb_complete_free(struct fhci_hcd *fhci, struct urb *urb) +{ + free_urb_priv(fhci, urb); + + if (urb->status == -EINPROGRESS) { + if (urb->actual_length != urb->transfer_buffer_length && + urb->transfer_flags & URB_SHORT_NOT_OK) + urb->status = -EREMOTEIO; + else + urb->status = 0; + } + + usb_hcd_unlink_urb_from_ep(fhci_to_hcd(fhci), urb); + + spin_unlock(&fhci->lock); + + usb_hcd_giveback_urb(fhci_to_hcd(fhci), urb, urb->status); + + spin_lock(&fhci->lock); +} + +/* + * caculate transfer length/stats and update the urb + * Precondition: irqsafe(only for urb-?status locking) + */ +void fhci_done_td(struct urb *urb, struct td *td) +{ + struct ed *ed = td->ed; + u32 cc = td->status; + + /* ISO...drivers see per-TD length/status */ + if (ed->mode == FHCI_TF_ISO) { + u32 len; + if (!(urb->transfer_flags & URB_SHORT_NOT_OK && + cc == USB_TD_RX_DATA_UNDERUN)) + cc = USB_TD_OK; + + if (usb_pipeout(urb->pipe)) + len = urb->iso_frame_desc[td->iso_index].length; + else + len = td->actual_len; + + urb->actual_length += len; + urb->iso_frame_desc[td->iso_index].actual_length = len; + urb->iso_frame_desc[td->iso_index].status = + status_to_error(cc); + } + + /* BULK,INT,CONTROL... drivers see aggregate length/status, + * except that "setup" bytes aren't counted and "short" transfers + * might not be reported as errors. + */ + else { + if (td->error_cnt >= 3) + urb->error_count = 3; + + /* control endpoint only have soft stalls */ + + /* update packet status if needed(short may be ok) */ + if (!(urb->transfer_flags & URB_SHORT_NOT_OK) && + cc == USB_TD_RX_DATA_UNDERUN) { + ed->state = FHCI_ED_OPER; + cc = USB_TD_OK; + } + if (cc != USB_TD_OK) { + if (urb->status == -EINPROGRESS) + urb->status = status_to_error(cc); + } + + /* count all non-empty packets except control SETUP packet */ + if (td->type != FHCI_TA_SETUP || td->iso_index != 0) + urb->actual_length += td->actual_len; + } +} + +/* there are some pedning request to unlink */ +void fhci_del_ed_list(struct fhci_hcd *fhci, struct ed *ed) +{ + struct td *td = peek_td_from_ed(ed); + struct urb *urb = td->urb; + struct urb_priv *urb_priv = urb->hcpriv; + + if (urb_priv->state == URB_DEL) { + td = fhci_remove_td_from_ed(ed); + /* HC may have partly processed this TD */ + if (td->status != USB_TD_INPROGRESS) + fhci_done_td(urb, td); + + /* URB is done;clean up */ + if (++(urb_priv->tds_cnt) == urb_priv->num_of_tds) + fhci_urb_complete_free(fhci, urb); + } +} diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c new file mode 100644 index 00000000000..bb63b68ddb7 --- /dev/null +++ b/drivers/usb/host/fhci-sched.c @@ -0,0 +1,888 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +static void recycle_frame(struct fhci_usb *usb, struct packet *pkt) +{ + pkt->data = NULL; + pkt->len = 0; + pkt->status = USB_TD_OK; + pkt->info = 0; + pkt->priv_data = NULL; + + cq_put(usb->ep0->empty_frame_Q, pkt); +} + +/* confirm submitted packet */ +void fhci_transaction_confirm(struct fhci_usb *usb, struct packet *pkt) +{ + struct td *td; + struct packet *td_pkt; + struct ed *ed; + u32 trans_len; + bool td_done = false; + + td = fhci_remove_td_from_frame(usb->actual_frame); + td_pkt = td->pkt; + trans_len = pkt->len; + td->status = pkt->status; + if (td->type == FHCI_TA_IN && td_pkt->info & PKT_DUMMY_PACKET) { + if ((td->data + td->actual_len) && trans_len) + memcpy(td->data + td->actual_len, pkt->data, + trans_len); + cq_put(usb->ep0->dummy_packets_Q, pkt->data); + } + + recycle_frame(usb, pkt); + + ed = td->ed; + if (ed->mode == FHCI_TF_ISO) { + if (ed->td_list.next->next != &ed->td_list) { + struct td *td_next = + list_entry(ed->td_list.next->next, struct td, + node); + + td_next->start_frame = usb->actual_frame->frame_num; + } + td->actual_len = trans_len; + td_done = true; + } else if ((td->status & USB_TD_ERROR) && + !(td->status & USB_TD_TX_ER_NAK)) { + /* + * There was an error on the transaction (but not NAK). + * If it is fatal error (data underrun, stall, bad pid or 3 + * errors exceeded), mark this TD as done. + */ + if ((td->status & USB_TD_RX_DATA_UNDERUN) || + (td->status & USB_TD_TX_ER_STALL) || + (td->status & USB_TD_RX_ER_PID) || + (++td->error_cnt >= 3)) { + ed->state = FHCI_ED_HALTED; + td_done = true; + + if (td->status & USB_TD_RX_DATA_UNDERUN) { + fhci_dbg(usb->fhci, "td err fu\n"); + td->toggle = !td->toggle; + td->actual_len += trans_len; + } else { + fhci_dbg(usb->fhci, "td err f!u\n"); + } + } else { + fhci_dbg(usb->fhci, "td err !f\n"); + /* it is not a fatal error -retry this transaction */ + td->nak_cnt = 0; + td->error_cnt++; + td->status = USB_TD_OK; + } + } else if (td->status & USB_TD_TX_ER_NAK) { + /* there was a NAK response */ + fhci_vdbg(usb->fhci, "td nack\n"); + td->nak_cnt++; + td->error_cnt = 0; + td->status = USB_TD_OK; + } else { + /* there was no error on transaction */ + td->error_cnt = 0; + td->nak_cnt = 0; + td->toggle = !td->toggle; + td->actual_len += trans_len; + + if (td->len == td->actual_len) + td_done = true; + } + + if (td_done) + fhci_move_td_from_ed_to_done_list(usb, ed); +} + +/* + * Flush all transmitted packets from BDs + * This routine is called when disabling the USB port to flush all + * transmissions that are allready scheduled in the BDs + */ +void fhci_flush_all_transmissions(struct fhci_usb *usb) +{ + u8 mode; + struct td *td; + + mode = in_8(&usb->fhci->regs->usb_mod); + clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_EN); + + fhci_flush_bds(usb); + + while ((td = fhci_peek_td_from_frame(usb->actual_frame)) != NULL) { + struct packet *pkt = td->pkt; + + pkt->status = USB_TD_TX_ER_TIMEOUT; + fhci_transaction_confirm(usb, pkt); + } + + usb->actual_frame->frame_status = FRAME_END_TRANSMISSION; + + /* reset the event register */ + out_be16(&usb->fhci->regs->usb_event, 0xffff); + /* enable the USB controller */ + out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN); +} + +/* + * This function forms the packet and transmit the packet. This function + * will handle all endpoint type:ISO,interrupt,control and bulk + */ +static int add_packet(struct fhci_usb *usb, struct ed *ed, struct td *td) +{ + u32 fw_transaction_time, len = 0; + struct packet *pkt; + u8 *data = NULL; + + /* calcalate data address,len and toggle and then add the transaction */ + if (td->toggle == USB_TD_TOGGLE_CARRY) + td->toggle = ed->toggle_carry; + + switch (ed->mode) { + case FHCI_TF_ISO: + len = td->len; + if (td->type != FHCI_TA_IN) + data = td->data; + break; + case FHCI_TF_CTRL: + case FHCI_TF_BULK: + len = min(td->len - td->actual_len, ed->max_pkt_size); + if (!((td->type == FHCI_TA_IN) && + ((len + td->actual_len) == td->len))) + data = td->data + td->actual_len; + break; + case FHCI_TF_INTR: + len = min(td->len, ed->max_pkt_size); + if (!((td->type == FHCI_TA_IN) && + ((td->len + CRC_SIZE) >= ed->max_pkt_size))) + data = td->data; + break; + default: + break; + } + + if (usb->port_status == FHCI_PORT_FULL) + fw_transaction_time = (((len + PROTOCOL_OVERHEAD) * 11) >> 4); + else + fw_transaction_time = ((len + PROTOCOL_OVERHEAD) * 6); + + /* check if there's enough space in this frame to submit this TD */ + if (usb->actual_frame->total_bytes + len + PROTOCOL_OVERHEAD >= + usb->max_bytes_per_frame) { + fhci_vdbg(usb->fhci, "not enough space in this frame: " + "%d %d %d\n", usb->actual_frame->total_bytes, len, + usb->max_bytes_per_frame); + return -1; + } + + /* check if there's enough time in this frame to submit this TD */ + if (usb->actual_frame->frame_status != FRAME_IS_PREPARED && + (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION || + (fw_transaction_time + usb->sw_transaction_time >= + 1000 - fhci_get_sof_timer_count(usb)))) { + fhci_dbg(usb->fhci, "not enough time in this frame\n"); + return -1; + } + + /* update frame object fields before transmitting */ + pkt = cq_get(usb->ep0->empty_frame_Q); + if (!pkt) { + fhci_dbg(usb->fhci, "there is no empty frame\n"); + return -1; + } + td->pkt = pkt; + + pkt->info = 0; + if (data == NULL) { + data = cq_get(usb->ep0->dummy_packets_Q); + BUG_ON(!data); + pkt->info = PKT_DUMMY_PACKET; + } + pkt->data = data; + pkt->len = len; + pkt->status = USB_TD_OK; + /* update TD status field before transmitting */ + td->status = USB_TD_INPROGRESS; + /* update actual frame time object with the actual transmission */ + usb->actual_frame->total_bytes += (len + PROTOCOL_OVERHEAD); + fhci_add_td_to_frame(usb->actual_frame, td); + + if (usb->port_status != FHCI_PORT_FULL && + usb->port_status != FHCI_PORT_LOW) { + pkt->status = USB_TD_TX_ER_TIMEOUT; + pkt->len = 0; + fhci_transaction_confirm(usb, pkt); + } else if (fhci_host_transaction(usb, pkt, td->type, ed->dev_addr, + ed->ep_addr, ed->mode, ed->speed, td->toggle)) { + /* remove TD from actual frame */ + list_del_init(&td->frame_lh); + td->status = USB_TD_OK; + if (pkt->info & PKT_DUMMY_PACKET) + cq_put(usb->ep0->dummy_packets_Q, pkt->data); + recycle_frame(usb, pkt); + usb->actual_frame->total_bytes -= (len + PROTOCOL_OVERHEAD); + fhci_err(usb->fhci, "host transaction failed\n"); + return -1; + } + + return len; +} + +static void move_head_to_tail(struct list_head *list) +{ + struct list_head *node = list->next; + + if (!list_empty(list)) { + list_del(node); + list_add_tail(node, list); + } +} + +/* + * This function goes through the endpoint list and schedules the + * transactions within this list + */ +static int scan_ed_list(struct fhci_usb *usb, + struct list_head *list, enum fhci_tf_mode list_type) +{ + static const int frame_part[4] = { + [FHCI_TF_CTRL] = MAX_BYTES_PER_FRAME, + [FHCI_TF_ISO] = (MAX_BYTES_PER_FRAME * + MAX_PERIODIC_FRAME_USAGE) / 100, + [FHCI_TF_BULK] = MAX_BYTES_PER_FRAME, + [FHCI_TF_INTR] = (MAX_BYTES_PER_FRAME * + MAX_PERIODIC_FRAME_USAGE) / 100 + }; + struct ed *ed; + struct td *td; + int ans = 1; + u32 save_transaction_time = usb->sw_transaction_time; + + list_for_each_entry(ed, list, node) { + td = ed->td_head; + + if (!td || (td && td->status == USB_TD_INPROGRESS)) + continue; + + if (ed->state != FHCI_ED_OPER) { + if (ed->state == FHCI_ED_URB_DEL) { + td->status = USB_TD_OK; + fhci_move_td_from_ed_to_done_list(usb, ed); + ed->state = FHCI_ED_SKIP; + } + continue; + } + + /* + * if it isn't interrupt pipe or it is not iso pipe and the + * interval time passed + */ + if ((list_type == FHCI_TF_INTR || list_type == FHCI_TF_ISO) && + (((usb->actual_frame->frame_num - + td->start_frame) & 0x7ff) < td->interval)) + continue; + + if (add_packet(usb, ed, td) < 0) + continue; + + /* update time stamps in the TD */ + td->start_frame = usb->actual_frame->frame_num; + usb->sw_transaction_time += save_transaction_time; + + if (usb->actual_frame->total_bytes >= + usb->max_bytes_per_frame) { + usb->actual_frame->frame_status = + FRAME_DATA_END_TRANSMISSION; + fhci_push_dummy_bd(usb->ep0); + ans = 0; + break; + } + + if (usb->actual_frame->total_bytes >= frame_part[list_type]) + break; + } + + /* be fair to each ED(move list head around) */ + move_head_to_tail(list); + usb->sw_transaction_time = save_transaction_time; + + return ans; +} + +static u32 rotate_frames(struct fhci_usb *usb) +{ + struct fhci_hcd *fhci = usb->fhci; + + if (!list_empty(&usb->actual_frame->tds_list)) { + if ((((in_be16(&fhci->pram->frame_num) & 0x07ff) - + usb->actual_frame->frame_num) & 0x7ff) > 5) + fhci_flush_actual_frame(usb); + else + return -EINVAL; + } + + usb->actual_frame->frame_status = FRAME_IS_PREPARED; + usb->actual_frame->frame_num = in_be16(&fhci->pram->frame_num) & 0x7ff; + usb->actual_frame->total_bytes = 0; + + return 0; +} + +/* + * This function schedule the USB transaction and will process the + * endpoint in the following order: iso, interrupt, control and bulk. + */ +void fhci_schedule_transactions(struct fhci_usb *usb) +{ + int left = 1; + + if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION) + if (rotate_frames(usb) != 0) + return; + + if (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION) + return; + + if (usb->actual_frame->total_bytes == 0) { + /* + * schedule the next available ISO transfer + *or next stage of the ISO transfer + */ + scan_ed_list(usb, &usb->hc_list->iso_list, FHCI_TF_ISO); + + /* + * schedule the next available interrupt transfer or + * the next stage of the interrupt transfer + */ + scan_ed_list(usb, &usb->hc_list->intr_list, FHCI_TF_INTR); + + /* + * schedule the next available control transfer + * or the next stage of the control transfer + */ + left = scan_ed_list(usb, &usb->hc_list->ctrl_list, + FHCI_TF_CTRL); + } + + /* + * schedule the next available bulk transfer or the next stage of the + * bulk transfer + */ + if (left > 0) + scan_ed_list(usb, &usb->hc_list->bulk_list, FHCI_TF_BULK); +} + +/* Handles SOF interrupt */ +static void sof_interrupt(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + if ((usb->port_status == FHCI_PORT_DISABLED) && + (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_CONNECTION) && + !(usb->vroot_hub->port.wPortChange & USB_PORT_STAT_C_CONNECTION)) { + if (usb->vroot_hub->port.wPortStatus & USB_PORT_STAT_LOW_SPEED) + usb->port_status = FHCI_PORT_LOW; + else + usb->port_status = FHCI_PORT_FULL; + /* Disable IDLE */ + usb->saved_msk &= ~USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + } + + gtm_set_exact_timer16(fhci->timer, usb->max_frame_usage, false); + + fhci_host_transmit_actual_frame(usb); + usb->actual_frame->frame_status = FRAME_IS_TRANSMITTED; + + fhci_schedule_transactions(usb); +} + +/* Handles device disconnected interrupt on port */ +void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci) +{ + struct fhci_usb *usb = fhci->usb_lld; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_usb_disable_interrupt(usb); + clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + usb->port_status = FHCI_PORT_DISABLED; + + fhci_stop_sof_timer(fhci); + + /* Enable IDLE since we want to know if something comes along */ + usb->saved_msk |= USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_mask, usb->saved_msk); + + usb->vroot_hub->port.wPortStatus &= ~USB_PORT_STAT_CONNECTION; + usb->vroot_hub->port.wPortChange |= USB_PORT_STAT_C_CONNECTION; + usb->max_bytes_per_frame = 0; + fhci_usb_enable_interrupt(usb); + + fhci_dbg(fhci, "<- %s\n", __func__); +} + +/* detect a new device connected on the USB port */ +void fhci_device_connected_interrupt(struct fhci_hcd *fhci) +{ + + struct fhci_usb *usb = fhci->usb_lld; + int state; + int ret; + + fhci_dbg(fhci, "-> %s\n", __func__); + + fhci_usb_disable_interrupt(usb); + state = fhci_ioports_check_bus_state(fhci); + + /* low-speed device was connected to the USB port */ + if (state == 1) { + ret = qe_usb_clock_set(fhci->lowspeed_clk, USB_CLOCK >> 3); + if (ret) { + fhci_warn(fhci, "Low-Speed device is not supported, " + "try use BRGx\n"); + goto out; + } + + usb->port_status = FHCI_PORT_LOW; + setbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + usb->vroot_hub->port.wPortStatus |= + (USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_CONNECTION); + usb->vroot_hub->port.wPortChange |= + USB_PORT_STAT_C_CONNECTION; + usb->max_bytes_per_frame = + (MAX_BYTES_PER_FRAME >> 3) - 7; + fhci_port_enable(usb); + } else if (state == 2) { + ret = qe_usb_clock_set(fhci->fullspeed_clk, USB_CLOCK); + if (ret) { + fhci_warn(fhci, "Full-Speed device is not supported, " + "try use CLKx\n"); + goto out; + } + + usb->port_status = FHCI_PORT_FULL; + clrbits8(&usb->fhci->regs->usb_mod, USB_MODE_LSS); + usb->vroot_hub->port.wPortStatus &= + ~USB_PORT_STAT_LOW_SPEED; + usb->vroot_hub->port.wPortStatus |= + USB_PORT_STAT_CONNECTION; + usb->vroot_hub->port.wPortChange |= + USB_PORT_STAT_C_CONNECTION; + usb->max_bytes_per_frame = (MAX_BYTES_PER_FRAME - 15); + fhci_port_enable(usb); + } +out: + fhci_usb_enable_interrupt(usb); + fhci_dbg(fhci, "<- %s\n", __func__); +} + +irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd) +{ + struct usb_hcd *hcd = _hcd; + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + struct fhci_usb *usb = fhci->usb_lld; + + spin_lock(&fhci->lock); + + gtm_set_exact_timer16(fhci->timer, 1000, false); + + if (usb->actual_frame->frame_status == FRAME_IS_TRANSMITTED) { + usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION; + fhci_push_dummy_bd(usb->ep0); + } + + fhci_schedule_transactions(usb); + + spin_unlock(&fhci->lock); + + return IRQ_HANDLED; +} + +/* Cancel transmission on the USB endpoint */ +static void abort_transmission(struct fhci_usb *usb) +{ + fhci_dbg(usb->fhci, "-> %s\n", __func__); + /* issue stop Tx command */ + qe_issue_cmd(QE_USB_STOP_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0); + /* flush Tx FIFOs */ + out_8(&usb->fhci->regs->usb_comm, USB_CMD_FLUSH_FIFO | EP_ZERO); + udelay(1000); + /* reset Tx BDs */ + fhci_flush_bds(usb); + /* issue restart Tx command */ + qe_issue_cmd(QE_USB_RESTART_TX, QE_CR_SUBBLOCK_USB, EP_ZERO, 0); + fhci_dbg(usb->fhci, "<- %s\n", __func__); +} + +irqreturn_t fhci_irq(struct usb_hcd *hcd) +{ + struct fhci_hcd *fhci = hcd_to_fhci(hcd); + struct fhci_usb *usb; + u16 usb_er = 0; + unsigned long flags; + + spin_lock_irqsave(&fhci->lock, flags); + + usb = fhci->usb_lld; + + usb_er |= in_be16(&usb->fhci->regs->usb_event) & + in_be16(&usb->fhci->regs->usb_mask); + + /* clear event bits for next time */ + out_be16(&usb->fhci->regs->usb_event, usb_er); + + fhci_dbg_isr(fhci, usb_er); + + if (usb_er & USB_E_RESET_MASK) { + if ((usb->port_status == FHCI_PORT_FULL) || + (usb->port_status == FHCI_PORT_LOW)) { + fhci_device_disconnected_interrupt(fhci); + usb_er &= ~USB_E_IDLE_MASK; + } else if (usb->port_status == FHCI_PORT_WAITING) { + usb->port_status = FHCI_PORT_DISCONNECTING; + + /* Turn on IDLE since we want to disconnect */ + usb->saved_msk |= USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_event, + usb->saved_msk); + } else if (usb->port_status == FHCI_PORT_DISABLED) { + if (fhci_ioports_check_bus_state(fhci) == 1 && + usb->port_status != FHCI_PORT_LOW && + usb->port_status != FHCI_PORT_FULL) + fhci_device_connected_interrupt(fhci); + } + usb_er &= ~USB_E_RESET_MASK; + } + + if (usb_er & USB_E_MSF_MASK) { + abort_transmission(fhci->usb_lld); + usb_er &= ~USB_E_MSF_MASK; + } + + if (usb_er & (USB_E_SOF_MASK | USB_E_SFT_MASK)) { + sof_interrupt(fhci); + usb_er &= ~(USB_E_SOF_MASK | USB_E_SFT_MASK); + } + + if (usb_er & USB_E_TXB_MASK) { + fhci_tx_conf_interrupt(fhci->usb_lld); + usb_er &= ~USB_E_TXB_MASK; + } + + if (usb_er & USB_E_TXE1_MASK) { + fhci_tx_conf_interrupt(fhci->usb_lld); + usb_er &= ~USB_E_TXE1_MASK; + } + + if (usb_er & USB_E_IDLE_MASK) { + if (usb->port_status == FHCI_PORT_DISABLED && + usb->port_status != FHCI_PORT_LOW && + usb->port_status != FHCI_PORT_FULL) { + usb_er &= ~USB_E_RESET_MASK; + fhci_device_connected_interrupt(fhci); + } else if (usb->port_status == + FHCI_PORT_DISCONNECTING) { + /* XXX usb->port_status = FHCI_PORT_WAITING; */ + /* Disable IDLE */ + usb->saved_msk &= ~USB_E_IDLE_MASK; + out_be16(&usb->fhci->regs->usb_mask, + usb->saved_msk); + } else { + fhci_dbg_isr(fhci, -1); + } + + usb_er &= ~USB_E_IDLE_MASK; + } + + spin_unlock_irqrestore(&fhci->lock, flags); + + return IRQ_HANDLED; +} + + +/* + * Process normal completions(error or sucess) and clean the schedule. + * + * This is the main path for handing urbs back to drivers. The only other patth + * is process_del_list(),which unlinks URBs by scanning EDs,instead of scanning + * the (re-reversed) done list as this does. + */ +static void process_done_list(unsigned long data) +{ + struct urb *urb; + struct ed *ed; + struct td *td; + struct urb_priv *urb_priv; + struct fhci_hcd *fhci = (struct fhci_hcd *)data; + + disable_irq(fhci->timer->irq); + disable_irq(fhci_to_hcd(fhci)->irq); + spin_lock(&fhci->lock); + + td = fhci_remove_td_from_done_list(fhci->hc_list); + while (td != NULL) { + urb = td->urb; + urb_priv = urb->hcpriv; + ed = td->ed; + + /* update URB's length and status from TD */ + fhci_done_td(urb, td); + urb_priv->tds_cnt++; + + /* + * if all this urb's TDs are done, call complete() + * Interrupt transfers are the onley special case: + * they are reissued,until "deleted" by usb_unlink_urb + * (real work done in a SOF intr, by process_del_list) + */ + if (urb_priv->tds_cnt == urb_priv->num_of_tds) { + fhci_urb_complete_free(fhci, urb); + } else if (urb_priv->state == URB_DEL && + ed->state == FHCI_ED_SKIP) { + fhci_del_ed_list(fhci, ed); + ed->state = FHCI_ED_OPER; + } else if (ed->state == FHCI_ED_HALTED) { + urb_priv->state = URB_DEL; + ed->state = FHCI_ED_URB_DEL; + fhci_del_ed_list(fhci, ed); + ed->state = FHCI_ED_OPER; + } + + td = fhci_remove_td_from_done_list(fhci->hc_list); + } + + spin_unlock(&fhci->lock); + enable_irq(fhci->timer->irq); + enable_irq(fhci_to_hcd(fhci)->irq); +} + +DECLARE_TASKLET(fhci_tasklet, process_done_list, 0); + +/* transfer complted callback */ +u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci) +{ + if (!fhci->process_done_task->state) + tasklet_schedule(fhci->process_done_task); + return 0; +} + +/* + * adds urb to the endpoint descriptor list + * arguments: + * fhci data structure for the Low level host controller + * ep USB Host endpoint data structure + * urb USB request block data structure + */ +void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb) +{ + struct ed *ed = urb->ep->hcpriv; + struct urb_priv *urb_priv = urb->hcpriv; + u32 data_len = urb->transfer_buffer_length; + int urb_state = 0; + int toggle = 0; + struct td *td; + u8 *data; + u16 cnt = 0; + + if (ed == NULL) { + ed = fhci_get_empty_ed(fhci); + ed->dev_addr = usb_pipedevice(urb->pipe); + ed->ep_addr = usb_pipeendpoint(urb->pipe); + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ed->mode = FHCI_TF_CTRL; + break; + case PIPE_BULK: + ed->mode = FHCI_TF_BULK; + break; + case PIPE_INTERRUPT: + ed->mode = FHCI_TF_INTR; + break; + case PIPE_ISOCHRONOUS: + ed->mode = FHCI_TF_ISO; + break; + default: + break; + } + ed->speed = (urb->dev->speed == USB_SPEED_LOW) ? + FHCI_LOW_SPEED : FHCI_FULL_SPEED; + ed->max_pkt_size = usb_maxpacket(urb->dev, + urb->pipe, usb_pipeout(urb->pipe)); + urb->ep->hcpriv = ed; + fhci_dbg(fhci, "new ep speed=%d max_pkt_size=%d\n", + ed->speed, ed->max_pkt_size); + } + + /* for ISO transfer calculate start frame index */ + if (ed->mode == FHCI_TF_ISO && urb->transfer_flags & URB_ISO_ASAP) + urb->start_frame = ed->td_head ? ed->last_iso + 1 : + get_frame_num(fhci); + + /* + * OHCI handles the DATA toggle itself,we just use the USB + * toggle bits + */ + if (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe))) + toggle = USB_TD_TOGGLE_CARRY; + else { + toggle = USB_TD_TOGGLE_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe), 1); + } + + urb_priv->tds_cnt = 0; + urb_priv->ed = ed; + if (data_len > 0) + data = urb->transfer_buffer; + else + data = NULL; + + switch (ed->mode) { + case FHCI_TF_BULK: + if (urb->transfer_flags & URB_ZERO_PACKET && + urb->transfer_buffer_length > 0 && + ((urb->transfer_buffer_length % + usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe))) == 0)) + urb_state = US_BULK0; + while (data_len > 4096) { + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : + FHCI_TA_IN, + cnt ? USB_TD_TOGGLE_CARRY : + toggle, + data, 4096, 0, 0, true); + data += 4096; + data_len -= 4096; + cnt++; + } + + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN, + cnt ? USB_TD_TOGGLE_CARRY : toggle, + data, data_len, 0, 0, true); + cnt++; + + if (urb->transfer_flags & URB_ZERO_PACKET && + cnt < urb_priv->num_of_tds) { + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : + FHCI_TA_IN, + USB_TD_TOGGLE_CARRY, NULL, 0, 0, 0, true); + cnt++; + } + break; + case FHCI_TF_INTR: + urb->start_frame = get_frame_num(fhci) + 1; + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : FHCI_TA_IN, + USB_TD_TOGGLE_DATA0, data, data_len, + urb->interval, urb->start_frame, true); + break; + case FHCI_TF_CTRL: + ed->dev_addr = usb_pipedevice(urb->pipe); + ed->max_pkt_size = usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe)); + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, FHCI_TA_SETUP, + USB_TD_TOGGLE_DATA0, urb->setup_packet, 8, 0, 0, true); + + if (data_len > 0) { + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : + FHCI_TA_IN, + USB_TD_TOGGLE_DATA1, data, data_len, 0, 0, + true); + } + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt++, + usb_pipeout(urb->pipe) ? FHCI_TA_IN : FHCI_TA_OUT, + USB_TD_TOGGLE_DATA1, data, 0, 0, 0, true); + urb_state = US_CTRL_SETUP; + break; + case FHCI_TF_ISO: + for (cnt = 0; cnt < urb->number_of_packets; cnt++) { + u16 frame = urb->start_frame; + + /* + * FIXME scheduling should handle frame counter + * roll-around ... exotic case (and OHCI has + * a 2^16 iso range, vs other HCs max of 2^10) + */ + frame += cnt * urb->interval; + frame &= 0x07ff; + td = fhci_td_fill(fhci, urb, urb_priv, ed, cnt, + usb_pipeout(urb->pipe) ? FHCI_TA_OUT : + FHCI_TA_IN, + USB_TD_TOGGLE_DATA0, + data + urb->iso_frame_desc[cnt].offset, + urb->iso_frame_desc[cnt].length, + urb->interval, frame, true); + } + break; + default: + break; + } + + /* + * set the state of URB + * control pipe:3 states -- setup,data,status + * interrupt and bulk pipe:1 state -- data + */ + urb->pipe &= ~0x1f; + urb->pipe |= urb_state & 0x1f; + + urb_priv->state = URB_INPROGRESS; + + if (!ed->td_head) { + ed->state = FHCI_ED_OPER; + switch (ed->mode) { + case FHCI_TF_CTRL: + list_add(&ed->node, &fhci->hc_list->ctrl_list); + break; + case FHCI_TF_BULK: + list_add(&ed->node, &fhci->hc_list->bulk_list); + break; + case FHCI_TF_INTR: + list_add(&ed->node, &fhci->hc_list->intr_list); + break; + case FHCI_TF_ISO: + list_add(&ed->node, &fhci->hc_list->iso_list); + break; + default: + break; + } + } + + fhci_add_tds_to_ed(ed, urb_priv->tds, urb_priv->num_of_tds); + fhci->active_urbs++; +} diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c new file mode 100644 index 00000000000..b4033229031 --- /dev/null +++ b/drivers/usb/host/fhci-tds.c @@ -0,0 +1,626 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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 +#include +#include +#include +#include +#include +#include "../core/hcd.h" +#include "fhci.h" + +#define DUMMY_BD_BUFFER 0xdeadbeef +#define DUMMY2_BD_BUFFER 0xbaadf00d + +/* Transaction Descriptors bits */ +#define TD_R 0x8000 /* ready bit */ +#define TD_W 0x2000 /* wrap bit */ +#define TD_I 0x1000 /* interrupt on completion */ +#define TD_L 0x0800 /* last */ +#define TD_TC 0x0400 /* transmit CRC */ +#define TD_CNF 0x0200 /* CNF - Must be always 1 */ +#define TD_LSP 0x0100 /* Low-speed transaction */ +#define TD_PID 0x00c0 /* packet id */ +#define TD_RXER 0x0020 /* Rx error or not */ + +#define TD_NAK 0x0010 /* No ack. */ +#define TD_STAL 0x0008 /* Stall recieved */ +#define TD_TO 0x0004 /* time out */ +#define TD_UN 0x0002 /* underrun */ +#define TD_NO 0x0010 /* Rx Non Octet Aligned Packet */ +#define TD_AB 0x0008 /* Frame Aborted */ +#define TD_CR 0x0004 /* CRC Error */ +#define TD_OV 0x0002 /* Overrun */ +#define TD_BOV 0x0001 /* Buffer Overrun */ + +#define TD_ERRORS (TD_NAK | TD_STAL | TD_TO | TD_UN | \ + TD_NO | TD_AB | TD_CR | TD_OV | TD_BOV) + +#define TD_PID_DATA0 0x0080 /* Data 0 toggle */ +#define TD_PID_DATA1 0x00c0 /* Data 1 toggle */ +#define TD_PID_TOGGLE 0x00c0 /* Data 0/1 toggle mask */ + +#define TD_TOK_SETUP 0x0000 +#define TD_TOK_OUT 0x4000 +#define TD_TOK_IN 0x8000 +#define TD_ISO 0x1000 +#define TD_ENDP 0x0780 +#define TD_ADDR 0x007f + +#define TD_ENDP_SHIFT 7 + +struct usb_td { + __be16 status; + __be16 length; + __be32 buf_ptr; + __be16 extra; + __be16 reserved; +}; + +static struct usb_td __iomem *next_bd(struct usb_td __iomem *base, + struct usb_td __iomem *td, + u16 status) +{ + if (status & TD_W) + return base; + else + return ++td; +} + +void fhci_push_dummy_bd(struct endpoint *ep) +{ + if (ep->already_pushed_dummy_bd == false) { + u16 td_status = in_be16(&ep->empty_td->status); + + out_be32(&ep->empty_td->buf_ptr, DUMMY_BD_BUFFER); + /* get the next TD in the ring */ + ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status); + ep->already_pushed_dummy_bd = true; + } +} + +/* destroy an USB endpoint */ +void fhci_ep0_free(struct fhci_usb *usb) +{ + struct endpoint *ep; + int size; + + ep = usb->ep0; + if (ep) { + if (ep->td_base) + cpm_muram_free(cpm_muram_offset(ep->td_base)); + + if (ep->conf_frame_Q) { + size = cq_howmany(ep->conf_frame_Q); + for (; size; size--) { + struct packet *pkt = cq_get(ep->conf_frame_Q); + + kfree(pkt); + } + cq_delete(ep->conf_frame_Q); + } + + if (ep->empty_frame_Q) { + size = cq_howmany(ep->empty_frame_Q); + for (; size; size--) { + struct packet *pkt = cq_get(ep->empty_frame_Q); + + kfree(pkt); + } + cq_delete(ep->empty_frame_Q); + } + + if (ep->dummy_packets_Q) { + size = cq_howmany(ep->dummy_packets_Q); + for (; size; size--) { + u8 *buff = cq_get(ep->dummy_packets_Q); + + kfree(buff); + } + cq_delete(ep->dummy_packets_Q); + } + + kfree(ep); + usb->ep0 = NULL; + } +} + +/* + * create the endpoint structure + * + * arguments: + * usb A pointer to the data structure of the USB + * data_mem The data memory partition(BUS) + * ring_len TD ring length + */ +u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem, + u32 ring_len) +{ + struct endpoint *ep; + struct usb_td __iomem *td; + unsigned long ep_offset; + char *err_for = "enpoint PRAM"; + int ep_mem_size; + u32 i; + + /* we need at least 3 TDs in the ring */ + if (!(ring_len > 2)) { + fhci_err(usb->fhci, "illegal TD ring length parameters\n"); + return -EINVAL; + } + + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + ep_mem_size = ring_len * sizeof(*td) + sizeof(struct fhci_ep_pram); + ep_offset = cpm_muram_alloc(ep_mem_size, 32); + if (IS_ERR_VALUE(ep_offset)) + goto err; + ep->td_base = cpm_muram_addr(ep_offset); + + /* zero all queue pointers */ + ep->conf_frame_Q = cq_new(ring_len + 2); + ep->empty_frame_Q = cq_new(ring_len + 2); + ep->dummy_packets_Q = cq_new(ring_len + 2); + if (!ep->conf_frame_Q || !ep->empty_frame_Q || !ep->dummy_packets_Q) { + err_for = "frame_queues"; + goto err; + } + + for (i = 0; i < (ring_len + 1); i++) { + struct packet *pkt; + u8 *buff; + + pkt = kmalloc(sizeof(*pkt), GFP_KERNEL); + if (!pkt) { + err_for = "frame"; + goto err; + } + + buff = kmalloc(1028 * sizeof(*buff), GFP_KERNEL); + if (!buff) { + kfree(pkt); + err_for = "buffer"; + goto err; + } + cq_put(ep->empty_frame_Q, pkt); + cq_put(ep->dummy_packets_Q, buff); + } + + /* we put the endpoint parameter RAM right behind the TD ring */ + ep->ep_pram_ptr = (void __iomem *)ep->td_base + sizeof(*td) * ring_len; + + ep->conf_td = ep->td_base; + ep->empty_td = ep->td_base; + + ep->already_pushed_dummy_bd = false; + + /* initialize tds */ + td = ep->td_base; + for (i = 0; i < ring_len; i++) { + out_be32(&td->buf_ptr, 0); + out_be16(&td->status, 0); + out_be16(&td->length, 0); + out_be16(&td->extra, 0); + td++; + } + td--; + out_be16(&td->status, TD_W); /* for last TD set Wrap bit */ + out_be16(&td->length, 0); + + /* endpoint structure has been created */ + usb->ep0 = ep; + + return 0; +err: + fhci_ep0_free(usb); + kfree(ep); + fhci_err(usb->fhci, "no memory for the %s\n", err_for); + return -ENOMEM; +} + +/* + * initialize the endpoint register according to the given parameters + * + * artuments: + * usb A pointer to the data strucutre of the USB + * ep A pointer to the endpoint structre + * data_mem The data memory partition(BUS) + */ +void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep, + enum fhci_mem_alloc data_mem) +{ + u8 rt; + + /* set the endpoint registers according to the endpoint */ + out_be16(&usb->fhci->regs->usb_ep[0], + USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE); + out_be16(&usb->fhci->pram->ep_ptr[0], + cpm_muram_offset(ep->ep_pram_ptr)); + + rt = (BUS_MODE_BO_BE | BUS_MODE_GBL); +#ifdef MULTI_DATA_BUS + if (data_mem == MEM_SECONDARY) + rt |= BUS_MODE_DTB; +#endif + out_8(&ep->ep_pram_ptr->rx_func_code, rt); + out_8(&ep->ep_pram_ptr->tx_func_code, rt); + out_be16(&ep->ep_pram_ptr->rx_buff_len, 1028); + out_be16(&ep->ep_pram_ptr->rx_base, 0); + out_be16(&ep->ep_pram_ptr->tx_base, cpm_muram_offset(ep->td_base)); + out_be16(&ep->ep_pram_ptr->rx_bd_ptr, 0); + out_be16(&ep->ep_pram_ptr->tx_bd_ptr, cpm_muram_offset(ep->td_base)); + out_be32(&ep->ep_pram_ptr->tx_state, 0); +} + +/* + * Collect the submitted frames and inform the application about them + * It is also prepearing the TDs for new frames. If the Tx interrupts + * are diabled, the application should call that routine to get + * confirmation about the submitted frames. Otherwise, the routine is + * called frome the interrupt service routine during the Tx interrupt. + * In that case the application is informed by calling the application + * specific 'fhci_transaction_confirm' routine + */ +static void fhci_td_transaction_confirm(struct fhci_usb *usb) +{ + struct endpoint *ep = usb->ep0; + struct packet *pkt; + struct usb_td __iomem *td; + u16 extra_data; + u16 td_status; + u16 td_length; + u32 buf; + + /* + * collect transmitted BDs from the chip. The routine clears all BDs + * with R bit = 0 and the pointer to data buffer is not NULL, that is + * BDs which point to the transmitted data buffer + */ + while (1) { + td = ep->conf_td; + td_status = in_be16(&td->status); + td_length = in_be16(&td->length); + buf = in_be32(&td->buf_ptr); + extra_data = in_be16(&td->extra); + + /* check if the TD is empty */ + if (!(!(td_status & TD_R) && ((td_status & ~TD_W) || buf))) + break; + /* check if it is a dummy buffer */ + else if ((buf == DUMMY_BD_BUFFER) && !(td_status & ~TD_W)) + break; + + /* mark TD as empty */ + clrbits16(&td->status, ~TD_W); + out_be16(&td->length, 0); + out_be32(&td->buf_ptr, 0); + out_be16(&td->extra, 0); + /* advance the TD pointer */ + ep->conf_td = next_bd(ep->td_base, ep->conf_td, td_status); + + /* check if it is a dummy buffer(type2) */ + if ((buf == DUMMY2_BD_BUFFER) && !(td_status & ~TD_W)) + continue; + + pkt = cq_get(ep->conf_frame_Q); + if (!pkt) + fhci_err(usb->fhci, "no frame to confirm\n"); + + if (td_status & TD_ERRORS) { + if (td_status & TD_RXER) { + if (td_status & TD_CR) + pkt->status = USB_TD_RX_ER_CRC; + else if (td_status & TD_AB) + pkt->status = USB_TD_RX_ER_BITSTUFF; + else if (td_status & TD_OV) + pkt->status = USB_TD_RX_ER_OVERUN; + else if (td_status & TD_BOV) + pkt->status = USB_TD_RX_DATA_OVERUN; + else if (td_status & TD_NO) + pkt->status = USB_TD_RX_ER_NONOCT; + else + fhci_err(usb->fhci, "illegal error " + "occured\n"); + } else if (td_status & TD_NAK) + pkt->status = USB_TD_TX_ER_NAK; + else if (td_status & TD_TO) + pkt->status = USB_TD_TX_ER_TIMEOUT; + else if (td_status & TD_UN) + pkt->status = USB_TD_TX_ER_UNDERUN; + else if (td_status & TD_STAL) + pkt->status = USB_TD_TX_ER_STALL; + else + fhci_err(usb->fhci, "illegal error occured\n"); + } else if ((extra_data & TD_TOK_IN) && + pkt->len > td_length - CRC_SIZE) { + pkt->status = USB_TD_RX_DATA_UNDERUN; + } + + if (extra_data & TD_TOK_IN) + pkt->len = td_length - CRC_SIZE; + else if (pkt->info & PKT_ZLP) + pkt->len = 0; + else + pkt->len = td_length; + + fhci_transaction_confirm(usb, pkt); + } +} + +/* + * Submitting a data frame to a specified endpoint of a USB device + * The frame is put in the driver's transmit queue for this endpoint + * + * Arguments: + * usb A pointer to the USB structure + * pkt A pointer to the user frame structure + * trans_type Transaction tyep - IN,OUT or SETUP + * dest_addr Device address - 0~127 + * dest_ep Endpoint number of the device - 0~16 + * trans_mode Pipe type - ISO,Interrupt,bulk or control + * dest_speed USB speed - Low speed or FULL speed + * data_toggle Data sequence toggle - 0 or 1 + */ +u32 fhci_host_transaction(struct fhci_usb *usb, + struct packet *pkt, + enum fhci_ta_type trans_type, + u8 dest_addr, + u8 dest_ep, + enum fhci_tf_mode trans_mode, + enum fhci_speed dest_speed, u8 data_toggle) +{ + struct endpoint *ep = usb->ep0; + struct usb_td __iomem *td; + u16 extra_data; + u16 td_status; + + fhci_usb_disable_interrupt(usb); + /* start from the next BD that should be filled */ + td = ep->empty_td; + td_status = in_be16(&td->status); + + if (td_status & TD_R && in_be16(&td->length)) { + /* if the TD is not free */ + fhci_usb_enable_interrupt(usb); + return -1; + } + + /* get the next TD in the ring */ + ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status); + fhci_usb_enable_interrupt(usb); + pkt->priv_data = td; + out_be32(&td->buf_ptr, virt_to_phys(pkt->data)); + /* sets up transaction parameters - addr,endp,dir,and type */ + extra_data = (dest_ep << TD_ENDP_SHIFT) | dest_addr; + switch (trans_type) { + case FHCI_TA_IN: + extra_data |= TD_TOK_IN; + break; + case FHCI_TA_OUT: + extra_data |= TD_TOK_OUT; + break; + case FHCI_TA_SETUP: + extra_data |= TD_TOK_SETUP; + break; + } + if (trans_mode == FHCI_TF_ISO) + extra_data |= TD_ISO; + out_be16(&td->extra, extra_data); + + /* sets up the buffer descriptor */ + td_status = ((td_status & TD_W) | TD_R | TD_L | TD_I | TD_CNF); + if (!(pkt->info & PKT_NO_CRC)) + td_status |= TD_TC; + + switch (trans_type) { + case FHCI_TA_IN: + if (data_toggle) + pkt->info |= PKT_PID_DATA1; + else + pkt->info |= PKT_PID_DATA0; + break; + default: + if (data_toggle) { + td_status |= TD_PID_DATA1; + pkt->info |= PKT_PID_DATA1; + } else { + td_status |= TD_PID_DATA0; + pkt->info |= PKT_PID_DATA0; + } + break; + } + + if ((dest_speed == FHCI_LOW_SPEED) && + (usb->port_status == FHCI_PORT_FULL)) + td_status |= TD_LSP; + + out_be16(&td->status, td_status); + + /* set up buffer length */ + if (trans_type == FHCI_TA_IN) + out_be16(&td->length, pkt->len + CRC_SIZE); + else + out_be16(&td->length, pkt->len); + + /* put the frame to the confirmation queue */ + cq_put(ep->conf_frame_Q, pkt); + + if (cq_howmany(ep->conf_frame_Q) == 1) + out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO); + + return 0; +} + +/* Reset the Tx BD ring */ +void fhci_flush_bds(struct fhci_usb *usb) +{ + u16 extra_data; + u16 td_status; + u32 buf; + struct usb_td __iomem *td; + struct endpoint *ep = usb->ep0; + + td = ep->td_base; + while (1) { + td_status = in_be16(&td->status); + buf = in_be32(&td->buf_ptr); + extra_data = in_be16(&td->extra); + + /* if the TD is not empty - we'll confirm it as Timeout */ + if (td_status & TD_R) + out_be16(&td->status, (td_status & ~TD_R) | TD_TO); + /* if this TD is dummy - let's skip this TD */ + else if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) + out_be32(&td->buf_ptr, DUMMY2_BD_BUFFER); + /* if this is the last TD - break */ + if (td_status & TD_W) + break; + + td++; + } + + fhci_td_transaction_confirm(usb); + + td = ep->td_base; + do { + out_be16(&td->status, 0); + out_be16(&td->length, 0); + out_be32(&td->buf_ptr, 0); + out_be16(&td->extra, 0); + td++; + } while (!(in_be16(&td->status) & TD_W)); + out_be16(&td->status, TD_W); /* for last TD set Wrap bit */ + out_be16(&td->length, 0); + out_be32(&td->buf_ptr, 0); + out_be16(&td->extra, 0); + + out_be16(&ep->ep_pram_ptr->tx_bd_ptr, + in_be16(&ep->ep_pram_ptr->tx_base)); + out_be32(&ep->ep_pram_ptr->tx_state, 0); + out_be16(&ep->ep_pram_ptr->tx_cnt, 0); + ep->empty_td = ep->td_base; + ep->conf_td = ep->td_base; +} + +/* + * Flush all transmitted packets from TDs in the actual frame. + * This routine is called when something wrong with the controller and + * we want to get rid of the actual frame and start again next frame + */ +void fhci_flush_actual_frame(struct fhci_usb *usb) +{ + u8 mode; + u16 tb_ptr; + u16 extra_data; + u16 td_status; + u32 buf_ptr; + struct usb_td __iomem *td; + struct endpoint *ep = usb->ep0; + + /* disable the USB controller */ + mode = in_8(&usb->fhci->regs->usb_mod); + out_8(&usb->fhci->regs->usb_mod, mode & ~USB_MODE_EN); + + tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr); + td = cpm_muram_addr(tb_ptr); + td_status = in_be16(&td->status); + buf_ptr = in_be32(&td->buf_ptr); + extra_data = in_be16(&td->extra); + do { + if (td_status & TD_R) { + out_be16(&td->status, (td_status & ~TD_R) | TD_TO); + } else { + out_be32(&td->buf_ptr, 0); + ep->already_pushed_dummy_bd = false; + break; + } + + /* advance the TD pointer */ + td = next_bd(ep->td_base, td, td_status); + td_status = in_be16(&td->status); + buf_ptr = in_be32(&td->buf_ptr); + extra_data = in_be16(&td->extra); + } while ((td_status & TD_R) || buf_ptr); + + fhci_td_transaction_confirm(usb); + + out_be16(&ep->ep_pram_ptr->tx_bd_ptr, + in_be16(&ep->ep_pram_ptr->tx_base)); + out_be32(&ep->ep_pram_ptr->tx_state, 0); + out_be16(&ep->ep_pram_ptr->tx_cnt, 0); + ep->empty_td = ep->td_base; + ep->conf_td = ep->td_base; + + usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION; + + /* reset the event register */ + out_be16(&usb->fhci->regs->usb_event, 0xffff); + /* enable the USB controller */ + out_8(&usb->fhci->regs->usb_mod, mode | USB_MODE_EN); +} + +/* handles Tx confirm and Tx error interrupt */ +void fhci_tx_conf_interrupt(struct fhci_usb *usb) +{ + fhci_td_transaction_confirm(usb); + + /* + * Schedule another transaction to this frame only if we have + * already confirmed all transaction in the frame. + */ + if (((fhci_get_sof_timer_count(usb) < usb->max_frame_usage) || + (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)) && + (list_empty(&usb->actual_frame->tds_list))) + fhci_schedule_transactions(usb); +} + +void fhci_host_transmit_actual_frame(struct fhci_usb *usb) +{ + u16 tb_ptr; + u16 td_status; + struct usb_td __iomem *td; + struct endpoint *ep = usb->ep0; + + tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr); + td = cpm_muram_addr(tb_ptr); + + if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) { + struct usb_td __iomem *old_td = td; + + ep->already_pushed_dummy_bd = false; + td_status = in_be16(&td->status); + /* gets the next TD in the ring */ + td = next_bd(ep->td_base, td, td_status); + tb_ptr = cpm_muram_offset(td); + out_be16(&ep->ep_pram_ptr->tx_bd_ptr, tb_ptr); + + /* start transmit only if we have something in the TDs */ + if (in_be16(&td->status) & TD_R) + out_8(&usb->fhci->regs->usb_comm, USB_CMD_STR_FIFO); + + if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) { + out_be32(&old_td->buf_ptr, 0); + ep->conf_td = next_bd(ep->td_base, ep->conf_td, + td_status); + } else { + out_be32(&old_td->buf_ptr, DUMMY2_BD_BUFFER); + } + } +} diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h new file mode 100644 index 00000000000..7116284ed21 --- /dev/null +++ b/drivers/usb/host/fhci.h @@ -0,0 +1,607 @@ +/* + * Freescale QUICC Engine USB Host Controller Driver + * + * Copyright (c) Freescale Semicondutor, Inc. 2006. + * Shlomi Gridish + * Jerry Huang + * Copyright (c) Logic Product Development, Inc. 2007 + * Peter Barada + * Copyright (c) MontaVista Software, Inc. 2008. + * Anton Vorontsov + * + * 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. + */ + +#ifndef __FHCI_H +#define __FHCI_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/hcd.h" + +#define USB_CLOCK 48000000 + +#define FHCI_PRAM_SIZE 0x100 + +#define MAX_EDS 32 +#define MAX_TDS 32 + + +/* CRC16 field size */ +#define CRC_SIZE 2 + +/* USB protocol overhead for each frame transmitted from the host */ +#define PROTOCOL_OVERHEAD 7 + +/* Packet structure, info field */ +#define PKT_PID_DATA0 0x80000000 /* PID - Data toggle zero */ +#define PKT_PID_DATA1 0x40000000 /* PID - Data toggle one */ +#define PKT_PID_SETUP 0x20000000 /* PID - Setup bit */ +#define PKT_SETUP_STATUS 0x10000000 /* Setup status bit */ +#define PKT_SETADDR_STATUS 0x08000000 /* Set address status bit */ +#define PKT_SET_HOST_LAST 0x04000000 /* Last data packet */ +#define PKT_HOST_DATA 0x02000000 /* Data packet */ +#define PKT_FIRST_IN_FRAME 0x01000000 /* First packet in the frame */ +#define PKT_TOKEN_FRAME 0x00800000 /* Token packet */ +#define PKT_ZLP 0x00400000 /* Zero length packet */ +#define PKT_IN_TOKEN_FRAME 0x00200000 /* IN token packet */ +#define PKT_OUT_TOKEN_FRAME 0x00100000 /* OUT token packet */ +#define PKT_SETUP_TOKEN_FRAME 0x00080000 /* SETUP token packet */ +#define PKT_STALL_FRAME 0x00040000 /* STALL packet */ +#define PKT_NACK_FRAME 0x00020000 /* NACK packet */ +#define PKT_NO_PID 0x00010000 /* No PID */ +#define PKT_NO_CRC 0x00008000 /* don't append CRC */ +#define PKT_HOST_COMMAND 0x00004000 /* Host command packet */ +#define PKT_DUMMY_PACKET 0x00002000 /* Dummy packet, used for mmm */ +#define PKT_LOW_SPEED_PACKET 0x00001000 /* Low-Speed packet */ + +#define TRANS_OK (0) +#define TRANS_INPROGRESS (-1) +#define TRANS_DISCARD (-2) +#define TRANS_FAIL (-3) + +#define PS_INT 0 +#define PS_DISCONNECTED 1 +#define PS_CONNECTED 2 +#define PS_READY 3 +#define PS_MISSING 4 + +/* Transfer Descriptor status field */ +#define USB_TD_OK 0x00000000 /* TD transmited or received ok */ +#define USB_TD_INPROGRESS 0x80000000 /* TD is being transmitted */ +#define USB_TD_RX_ER_NONOCT 0x40000000 /* Tx Non Octet Aligned Packet */ +#define USB_TD_RX_ER_BITSTUFF 0x20000000 /* Frame Aborted-Received pkt */ +#define USB_TD_RX_ER_CRC 0x10000000 /* CRC error */ +#define USB_TD_RX_ER_OVERUN 0x08000000 /* Over - run occured */ +#define USB_TD_RX_ER_PID 0x04000000 /* wrong PID received */ +#define USB_TD_RX_DATA_UNDERUN 0x02000000 /* shorter than expected */ +#define USB_TD_RX_DATA_OVERUN 0x01000000 /* longer than expected */ +#define USB_TD_TX_ER_NAK 0x00800000 /* NAK handshake */ +#define USB_TD_TX_ER_STALL 0x00400000 /* STALL handshake */ +#define USB_TD_TX_ER_TIMEOUT 0x00200000 /* transmit time out */ +#define USB_TD_TX_ER_UNDERUN 0x00100000 /* transmit underrun */ + +#define USB_TD_ERROR (USB_TD_RX_ER_NONOCT | USB_TD_RX_ER_BITSTUFF | \ + USB_TD_RX_ER_CRC | USB_TD_RX_ER_OVERUN | USB_TD_RX_ER_PID | \ + USB_TD_RX_DATA_UNDERUN | USB_TD_RX_DATA_OVERUN | \ + USB_TD_TX_ER_NAK | USB_TD_TX_ER_STALL | \ + USB_TD_TX_ER_TIMEOUT | USB_TD_TX_ER_UNDERUN) + +/* Transfer Descriptor toggle field */ +#define USB_TD_TOGGLE_DATA0 0 +#define USB_TD_TOGGLE_DATA1 1 +#define USB_TD_TOGGLE_CARRY 2 + +/* #define MULTI_DATA_BUS */ + +/* Bus mode register RBMR/TBMR */ +#define BUS_MODE_GBL 0x20 /* Global snooping */ +#define BUS_MODE_BO 0x18 /* Byte ordering */ +#define BUS_MODE_BO_BE 0x10 /* Byte ordering - Big-endian */ +#define BUS_MODE_DTB 0x02 /* Data bus */ + +/* FHCI QE USB Register Description */ + +/* USB Mode Register bit define */ +#define USB_MODE_EN 0x01 +#define USB_MODE_HOST 0x02 +#define USB_MODE_TEST 0x04 +#define USB_MODE_SFTE 0x08 +#define USB_MODE_RESUME 0x40 +#define USB_MODE_LSS 0x80 + +/* USB Slave Address Register Mask */ +#define USB_SLVADDR_MASK 0x7F + +/* USB Endpoint register define */ +#define USB_EPNUM_MASK 0xF000 +#define USB_EPNUM_SHIFT 12 + +#define USB_TRANS_MODE_SHIFT 8 +#define USB_TRANS_CTR 0x0000 +#define USB_TRANS_INT 0x0100 +#define USB_TRANS_BULK 0x0200 +#define USB_TRANS_ISO 0x0300 + +#define USB_EP_MF 0x0020 +#define USB_EP_RTE 0x0010 + +#define USB_THS_SHIFT 2 +#define USB_THS_MASK 0x000c +#define USB_THS_NORMAL 0x0 +#define USB_THS_IGNORE_IN 0x0004 +#define USB_THS_NACK 0x0008 +#define USB_THS_STALL 0x000c + +#define USB_RHS_SHIFT 0 +#define USB_RHS_MASK 0x0003 +#define USB_RHS_NORMAL 0x0 +#define USB_RHS_IGNORE_OUT 0x0001 +#define USB_RHS_NACK 0x0002 +#define USB_RHS_STALL 0x0003 + +#define USB_RTHS_MASK 0x000f + +/* USB Command Register define */ +#define USB_CMD_STR_FIFO 0x80 +#define USB_CMD_FLUSH_FIFO 0x40 +#define USB_CMD_ISFT 0x20 +#define USB_CMD_DSFT 0x10 +#define USB_CMD_EP_MASK 0x03 + +/* USB Event and Mask Register define */ +#define USB_E_MSF_MASK 0x0800 +#define USB_E_SFT_MASK 0x0400 +#define USB_E_RESET_MASK 0x0200 +#define USB_E_IDLE_MASK 0x0100 +#define USB_E_TXE4_MASK 0x0080 +#define USB_E_TXE3_MASK 0x0040 +#define USB_E_TXE2_MASK 0x0020 +#define USB_E_TXE1_MASK 0x0010 +#define USB_E_SOF_MASK 0x0008 +#define USB_E_BSY_MASK 0x0004 +#define USB_E_TXB_MASK 0x0002 +#define USB_E_RXB_MASK 0x0001 + +/* Freescale USB Host controller registers */ +struct fhci_regs { + u8 usb_mod; /* mode register */ + u8 usb_addr; /* address register */ + u8 usb_comm; /* command register */ + u8 reserved1[1]; + __be16 usb_ep[4]; /* endpoint register */ + u8 reserved2[4]; + __be16 usb_event; /* event register */ + u8 reserved3[2]; + __be16 usb_mask; /* mask register */ + u8 reserved4[1]; + u8 usb_status; /* status register */ + __be16 usb_sof_tmr; /* Start Of Frame timer */ + u8 reserved5[2]; + __be16 usb_frame_num; /* frame number register */ + u8 reserved6[1]; +}; + +/* Freescale USB HOST */ +struct fhci_pram { + __be16 ep_ptr[4]; /* Endpoint porter reg */ + __be32 rx_state; /* Rx internal state */ + __be32 rx_ptr; /* Rx internal data pointer */ + __be16 frame_num; /* Frame number */ + __be16 rx_cnt; /* Rx byte count */ + __be32 rx_temp; /* Rx temp */ + __be32 rx_data_temp; /* Rx data temp */ + __be16 rx_u_ptr; /* Rx microcode return address temp */ + u8 reserved1[2]; /* reserved area */ + __be32 sof_tbl; /* SOF lookup table pointer */ + u8 sof_u_crc_temp; /* SOF micorcode CRC5 temp reg */ + u8 reserved2[0xdb]; +}; + +/* Freescale USB Endpoint*/ +struct fhci_ep_pram { + __be16 rx_base; /* Rx BD base address */ + __be16 tx_base; /* Tx BD base address */ + u8 rx_func_code; /* Rx function code */ + u8 tx_func_code; /* Tx function code */ + __be16 rx_buff_len; /* Rx buffer length */ + __be16 rx_bd_ptr; /* Rx BD pointer */ + __be16 tx_bd_ptr; /* Tx BD pointer */ + __be32 tx_state; /* Tx internal state */ + __be32 tx_ptr; /* Tx internal data pointer */ + __be16 tx_crc; /* temp transmit CRC */ + __be16 tx_cnt; /* Tx byte count */ + __be32 tx_temp; /* Tx temp */ + __be16 tx_u_ptr; /* Tx microcode return address temp */ + __be16 reserved; +}; + +struct fhci_controller_list { + struct list_head ctrl_list; /* control endpoints */ + struct list_head bulk_list; /* bulk endpoints */ + struct list_head iso_list; /* isochronous endpoints */ + struct list_head intr_list; /* interruput endpoints */ + struct list_head done_list; /* done transfers */ +}; + +struct virtual_root_hub { + int dev_num; /* USB address of the root hub */ + u32 feature; /* indicates what feature has been set */ + struct usb_hub_status hub; + struct usb_port_status port; +}; + +enum fhci_gpios { + GPIO_USBOE = 0, + GPIO_USBTP, + GPIO_USBTN, + GPIO_USBRP, + GPIO_USBRN, + /* these are optional */ + GPIO_SPEED, + GPIO_POWER, + NUM_GPIOS, +}; + +enum fhci_pins { + PIN_USBOE = 0, + PIN_USBTP, + PIN_USBTN, + NUM_PINS, +}; + +struct fhci_hcd { + enum qe_clock fullspeed_clk; + enum qe_clock lowspeed_clk; + struct qe_pin *pins[NUM_PINS]; + int gpios[NUM_GPIOS]; + bool alow_gpios[NUM_GPIOS]; + + struct fhci_regs __iomem *regs; /* I/O memory used to communicate */ + struct fhci_pram __iomem *pram; /* Parameter RAM */ + struct gtm_timer *timer; + + spinlock_t lock; + struct fhci_usb *usb_lld; /* Low-level driver */ + struct virtual_root_hub *vroot_hub; /* the virtual root hub */ + int active_urbs; + struct fhci_controller_list *hc_list; + struct tasklet_struct *process_done_task; /* tasklet for done list */ + + struct list_head empty_eds; + struct list_head empty_tds; + +#ifdef CONFIG_FHCI_DEBUG + int usb_irq_stat[13]; + struct dentry *dfs_root; + struct dentry *dfs_regs; + struct dentry *dfs_irq_stat; +#endif +}; + +#define USB_FRAME_USAGE 90 +#define FRAME_TIME_USAGE (USB_FRAME_USAGE*10) /* frame time usage */ +#define SW_FIX_TIME_BETWEEN_TRANSACTION 150 /* SW */ +#define MAX_BYTES_PER_FRAME (USB_FRAME_USAGE*15) +#define MAX_PERIODIC_FRAME_USAGE 90 + +/* transaction type */ +enum fhci_ta_type { + FHCI_TA_IN = 0, /* input transaction */ + FHCI_TA_OUT, /* output transaction */ + FHCI_TA_SETUP, /* setup transaction */ +}; + +/* transfer mode */ +enum fhci_tf_mode { + FHCI_TF_CTRL = 0, + FHCI_TF_ISO, + FHCI_TF_BULK, + FHCI_TF_INTR, +}; + +enum fhci_speed { + FHCI_FULL_SPEED, + FHCI_LOW_SPEED, +}; + +/* endpoint state */ +enum fhci_ed_state { + FHCI_ED_NEW = 0, /* pipe is new */ + FHCI_ED_OPER, /* pipe is operating */ + FHCI_ED_URB_DEL, /* pipe is in hold because urb is being deleted */ + FHCI_ED_SKIP, /* skip this pipe */ + FHCI_ED_HALTED, /* pipe is halted */ +}; + +enum fhci_port_status { + FHCI_PORT_POWER_OFF = 0, + FHCI_PORT_DISABLED, + FHCI_PORT_DISCONNECTING, + FHCI_PORT_WAITING, /* waiting for connection */ + FHCI_PORT_FULL, /* full speed connected */ + FHCI_PORT_LOW, /* low speed connected */ +}; + +enum fhci_mem_alloc { + MEM_CACHABLE_SYS = 0x00000001, /* primary DDR,cachable */ + MEM_NOCACHE_SYS = 0x00000004, /* primary DDR,non-cachable */ + MEM_SECONDARY = 0x00000002, /* either secondary DDR or SDRAM */ + MEM_PRAM = 0x00000008, /* multi-user RAM identifier */ +}; + +/* USB default parameters*/ +#define DEFAULT_RING_LEN 8 +#define DEFAULT_DATA_MEM MEM_CACHABLE_SYS + +struct ed { + u8 dev_addr; /* device address */ + u8 ep_addr; /* endpoint address */ + enum fhci_tf_mode mode; /* USB transfer mode */ + enum fhci_speed speed; + unsigned int max_pkt_size; + enum fhci_ed_state state; + struct list_head td_list; /* a list of all queued TD to this pipe */ + struct list_head node; + + /* read only parameters, should be cleared upon initialization */ + u8 toggle_carry; /* toggle carry from the last TD submitted */ + u32 last_iso; /* time stamp of last queued ISO transfer */ + struct td *td_head; /* a pointer to the current TD handled */ +}; + +struct td { + void *data; /* a pointer to the data buffer */ + unsigned int len; /* length of the data to be submitted */ + unsigned int actual_len; /* actual bytes transfered on this td */ + enum fhci_ta_type type; /* transaction type */ + u8 toggle; /* toggle for next trans. within this TD */ + u16 iso_index; /* ISO transaction index */ + u16 start_frame; /* start frame time stamp */ + u16 interval; /* interval between trans. (for ISO/Intr) */ + u32 status; /* status of the TD */ + struct ed *ed; /* a handle to the corresponding ED */ + struct urb *urb; /* a handle to the corresponding URB */ + bool ioc; /* Inform On Completion */ + struct list_head node; + + /* read only parameters should be cleared upon initialization */ + struct packet *pkt; + int nak_cnt; + int error_cnt; + struct list_head frame_lh; +}; + +struct packet { + u8 *data; /* packet data */ + u32 len; /* packet length */ + u32 status; /* status of the packet - equivalent to the status + * field for the corresponding structure td */ + u32 info; /* packet information */ + void __iomem *priv_data; /* private data of the driver (TDs or BDs) */ +}; + +/* struct for each URB */ +#define URB_INPROGRESS 0 +#define URB_DEL 1 + +/* URB states (state field) */ +#define US_BULK 0 +#define US_BULK0 1 + +/* three setup states */ +#define US_CTRL_SETUP 2 +#define US_CTRL_DATA 1 +#define US_CTRL_ACK 0 + +#define EP_ZERO 0 + +struct urb_priv { + int num_of_tds; + int tds_cnt; + int state; + + struct td **tds; + struct ed *ed; + struct timer_list time_out; +}; + +struct endpoint { + /* Pointer to ep parameter RAM */ + struct fhci_ep_pram __iomem *ep_pram_ptr; + + /* Host transactions */ + struct usb_td __iomem *td_base; /* first TD in the ring */ + struct usb_td __iomem *conf_td; /* next TD for confirm after transac */ + struct usb_td __iomem *empty_td;/* next TD for new transaction req. */ + struct kfifo *empty_frame_Q; /* Empty frames list to use */ + struct kfifo *conf_frame_Q; /* frames passed to TDs,waiting for tx */ + struct kfifo *dummy_packets_Q;/* dummy packets for the CRC overun */ + + bool already_pushed_dummy_bd; +}; + +/* struct for each 1mSec frame time */ +#define FRAME_IS_TRANSMITTED 0x00 +#define FRAME_TIMER_END_TRANSMISSION 0x01 +#define FRAME_DATA_END_TRANSMISSION 0x02 +#define FRAME_END_TRANSMISSION 0x03 +#define FRAME_IS_PREPARED 0x04 + +struct fhci_time_frame { + u16 frame_num; /* frame number */ + u16 total_bytes; /* total bytes submitted within this frame */ + u8 frame_status; /* flag that indicates to stop fill this frame */ + struct list_head tds_list; /* all tds of this frame */ +}; + +/* internal driver structure*/ +struct fhci_usb { + u16 saved_msk; /* saving of the USB mask register */ + struct endpoint *ep0; /* pointer for endpoint0 structure */ + int intr_nesting_cnt; /* interrupt nesting counter */ + u16 max_frame_usage; /* max frame time usage,in micro-sec */ + u16 max_bytes_per_frame; /* max byte can be tx in one time frame */ + u32 sw_transaction_time; /* sw complete trans time,in micro-sec */ + struct fhci_time_frame *actual_frame; + struct fhci_controller_list *hc_list; /* main structure for hc */ + struct virtual_root_hub *vroot_hub; + enum fhci_port_status port_status; /* v_rh port status */ + + u32 (*transfer_confirm)(struct fhci_hcd *fhci); + + struct fhci_hcd *fhci; +}; + +/* + * Various helpers and prototypes below. + */ + +static inline u16 get_frame_num(struct fhci_hcd *fhci) +{ + return in_be16(&fhci->pram->frame_num) & 0x07ff; +} + +#define fhci_dbg(fhci, fmt, args...) \ + dev_dbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args) +#define fhci_vdbg(fhci, fmt, args...) \ + dev_vdbg(fhci_to_hcd(fhci)->self.controller, fmt, ##args) +#define fhci_err(fhci, fmt, args...) \ + dev_err(fhci_to_hcd(fhci)->self.controller, fmt, ##args) +#define fhci_info(fhci, fmt, args...) \ + dev_info(fhci_to_hcd(fhci)->self.controller, fmt, ##args) +#define fhci_warn(fhci, fmt, args...) \ + dev_warn(fhci_to_hcd(fhci)->self.controller, fmt, ##args) + +static inline struct fhci_hcd *hcd_to_fhci(struct usb_hcd *hcd) +{ + return (struct fhci_hcd *)hcd->hcd_priv; +} + +static inline struct usb_hcd *fhci_to_hcd(struct fhci_hcd *fhci) +{ + return container_of((void *)fhci, struct usb_hcd, hcd_priv); +} + +/* fifo of pointers */ +static inline struct kfifo *cq_new(int size) +{ + return kfifo_alloc(size * sizeof(void *), GFP_KERNEL, NULL); +} + +static inline void cq_delete(struct kfifo *kfifo) +{ + kfifo_free(kfifo); +} + +static inline unsigned int cq_howmany(struct kfifo *kfifo) +{ + return __kfifo_len(kfifo) / sizeof(void *); +} + +static inline int cq_put(struct kfifo *kfifo, void *p) +{ + return __kfifo_put(kfifo, (void *)&p, sizeof(p)); +} + +static inline void *cq_get(struct kfifo *kfifo) +{ + void *p = NULL; + + __kfifo_get(kfifo, (void *)&p, sizeof(p)); + return p; +} + +/* fhci-hcd.c */ +void fhci_start_sof_timer(struct fhci_hcd *fhci); +void fhci_stop_sof_timer(struct fhci_hcd *fhci); +u16 fhci_get_sof_timer_count(struct fhci_usb *usb); +void fhci_usb_enable_interrupt(struct fhci_usb *usb); +void fhci_usb_disable_interrupt(struct fhci_usb *usb); +int fhci_ioports_check_bus_state(struct fhci_hcd *fhci); + +/* fhci-mem.c */ +void fhci_recycle_empty_td(struct fhci_hcd *fhci, struct td *td); +void fhci_recycle_empty_ed(struct fhci_hcd *fhci, struct ed *ed); +struct ed *fhci_get_empty_ed(struct fhci_hcd *fhci); +struct td *fhci_td_fill(struct fhci_hcd *fhci, struct urb *urb, + struct urb_priv *urb_priv, struct ed *ed, u16 index, + enum fhci_ta_type type, int toggle, u8 *data, u32 len, + u16 interval, u16 start_frame, bool ioc); +void fhci_add_tds_to_ed(struct ed *ed, struct td **td_list, int number); + +/* fhci-hub.c */ +void fhci_config_transceiver(struct fhci_hcd *fhci, + enum fhci_port_status status); +void fhci_port_disable(struct fhci_hcd *fhci); +void fhci_port_enable(void *lld); +void fhci_io_port_generate_reset(struct fhci_hcd *fhci); +void fhci_port_reset(void *lld); +int fhci_hub_status_data(struct usb_hcd *hcd, char *buf); +int fhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength); + +/* fhci-tds.c */ +void fhci_flush_bds(struct fhci_usb *usb); +void fhci_flush_actual_frame(struct fhci_usb *usb); +u32 fhci_host_transaction(struct fhci_usb *usb, struct packet *pkt, + enum fhci_ta_type trans_type, u8 dest_addr, + u8 dest_ep, enum fhci_tf_mode trans_mode, + enum fhci_speed dest_speed, u8 data_toggle); +void fhci_host_transmit_actual_frame(struct fhci_usb *usb); +void fhci_tx_conf_interrupt(struct fhci_usb *usb); +void fhci_push_dummy_bd(struct endpoint *ep); +u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem, + u32 ring_len); +void fhci_init_ep_registers(struct fhci_usb *usb, + struct endpoint *ep, + enum fhci_mem_alloc data_mem); +void fhci_ep0_free(struct fhci_usb *usb); + +/* fhci-sched.c */ +extern struct tasklet_struct fhci_tasklet; +void fhci_transaction_confirm(struct fhci_usb *usb, struct packet *pkt); +void fhci_flush_all_transmissions(struct fhci_usb *usb); +void fhci_schedule_transactions(struct fhci_usb *usb); +void fhci_device_connected_interrupt(struct fhci_hcd *fhci); +void fhci_device_disconnected_interrupt(struct fhci_hcd *fhci); +void fhci_queue_urb(struct fhci_hcd *fhci, struct urb *urb); +u32 fhci_transfer_confirm_callback(struct fhci_hcd *fhci); +irqreturn_t fhci_irq(struct usb_hcd *hcd); +irqreturn_t fhci_frame_limit_timer_irq(int irq, void *_hcd); + +/* fhci-q.h */ +void fhci_urb_complete_free(struct fhci_hcd *fhci, struct urb *urb); +struct td *fhci_remove_td_from_ed(struct ed *ed); +struct td *fhci_remove_td_from_frame(struct fhci_time_frame *frame); +void fhci_move_td_from_ed_to_done_list(struct fhci_usb *usb, struct ed *ed); +struct td *fhci_peek_td_from_frame(struct fhci_time_frame *frame); +void fhci_add_td_to_frame(struct fhci_time_frame *frame, struct td *td); +struct td *fhci_remove_td_from_done_list(struct fhci_controller_list *p_list); +void fhci_done_td(struct urb *urb, struct td *td); +void fhci_del_ed_list(struct fhci_hcd *fhci, struct ed *ed); + +#ifdef CONFIG_FHCI_DEBUG + +void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er); +void fhci_dfs_destroy(struct fhci_hcd *fhci); +void fhci_dfs_create(struct fhci_hcd *fhci); + +#else + +static inline void fhci_dbg_isr(struct fhci_hcd *fhci, int usb_er) {} +static inline void fhci_dfs_destroy(struct fhci_hcd *fhci) {} +static inline void fhci_dfs_create(struct fhci_hcd *fhci) {} + +#endif /* CONFIG_FHCI_DEBUG */ + +#endif /* __FHCI_H */ -- cgit v1.2.3 From 15b2bee22a0390d951301b53e83df88d0350c499 Mon Sep 17 00:00:00 2001 From: Jesse Brandeburg Date: Tue, 27 Jan 2009 16:41:58 -0800 Subject: e1000: fix bug with shared interrupt during reset A nasty bug was found where an MTU change (or anything else that caused a reset) could race with the interrupt code. The interrupt code was entered by a shared interrupt during the MTU change. This change prevents the interrupt code from running while the driver is in the middle of its reset path. Signed-off-by: Jesse Brandeburg Signed-off-by: David S. Miller --- drivers/net/e1000/e1000_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 26474c92193..c986978ce76 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -31,7 +31,7 @@ char e1000_driver_name[] = "e1000"; static char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; -#define DRV_VERSION "7.3.20-k3-NAPI" +#define DRV_VERSION "7.3.21-k3-NAPI" const char e1000_driver_version[] = DRV_VERSION; static const char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation."; @@ -3712,7 +3712,7 @@ static irqreturn_t e1000_intr(int irq, void *data) struct e1000_hw *hw = &adapter->hw; u32 rctl, icr = er32(ICR); - if (unlikely(!icr)) + if (unlikely((!icr) || test_bit(__E1000_RESETTING, &adapter->flags))) return IRQ_NONE; /* Not our interrupt */ /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is -- cgit v1.2.3 From 4712fff9be0f4a41f7add146cee88a9b945215d7 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 21 Jan 2009 13:16:28 +0000 Subject: powerpc: More printing warning fixes for the l64 to ll64 conversion These are all powerpc specific drivers. res.start in fsl_elbc_nand.c needs to be cast since it may be either 32 or 64 bit. Thanks to Scott Wood for noticing. Signed-off-by: Stephen Rothwell Acked-by: Arnd Bergmann call_edac bits in particular Acked-by: Olof Johansson pasemi_nand peices Acked-by: Scott Wood fsl_elbc fixes Signed-off-by: Benjamin Herrenschmidt --- drivers/edac/cell_edac.c | 8 ++++---- drivers/mtd/nand/fsl_elbc_nand.c | 8 ++++---- drivers/mtd/nand/pasemi_nand.c | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index cd2e3b8087e..24f3ca85152 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -36,7 +36,7 @@ static void cell_edac_count_ce(struct mem_ctl_info *mci, int chan, u64 ar) struct csrow_info *csrow = &mci->csrows[0]; unsigned long address, pfn, offset, syndrome; - dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016lx\n", + dev_dbg(mci->dev, "ECC CE err on node %d, channel %d, ar = 0x%016llx\n", priv->node, chan, ar); /* Address decoding is likely a bit bogus, to dbl check */ @@ -58,7 +58,7 @@ static void cell_edac_count_ue(struct mem_ctl_info *mci, int chan, u64 ar) struct csrow_info *csrow = &mci->csrows[0]; unsigned long address, pfn, offset; - dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016lx\n", + dev_dbg(mci->dev, "ECC UE err on node %d, channel %d, ar = 0x%016llx\n", priv->node, chan, ar); /* Address decoding is likely a bit bogus, to dbl check */ @@ -169,7 +169,7 @@ static int __devinit cell_edac_probe(struct platform_device *pdev) /* Get channel population */ reg = in_be64(®s->mic_mnt_cfg); - dev_dbg(&pdev->dev, "MIC_MNT_CFG = 0x%016lx\n", reg); + dev_dbg(&pdev->dev, "MIC_MNT_CFG = 0x%016llx\n", reg); chanmask = 0; if (reg & CBE_MIC_MNT_CFG_CHAN_0_POP) chanmask |= 0x1; @@ -180,7 +180,7 @@ static int __devinit cell_edac_probe(struct platform_device *pdev) "Yuck ! No channel populated ? Aborting !\n"); return -ENODEV; } - dev_dbg(&pdev->dev, "Initial FIR = 0x%016lx\n", + dev_dbg(&pdev->dev, "Initial FIR = 0x%016llx\n", in_be64(®s->mic_fir)); /* Allocate & init EDAC MC data structure */ diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 65929db2944..1f6eb257871 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -676,7 +676,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n", chip->numchips); - dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %ld\n", + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %lld\n", chip->chipsize); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n", chip->pagemask); @@ -703,7 +703,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n", chip->ecc.layout); dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); - dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %d\n", mtd->size); + dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n", mtd->erasesize); dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n", @@ -932,8 +932,8 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, #endif add_mtd_device(&priv->mtd); - printk(KERN_INFO "eLBC NAND device at 0x%zx, bank %d\n", - res.start, priv->bank); + printk(KERN_INFO "eLBC NAND device at 0x%llx, bank %d\n", + (unsigned long long)res.start, priv->bank); return 0; err: diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 9bd6c9ac844..a8b9376cf32 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -107,7 +107,7 @@ static int __devinit pasemi_nand_probe(struct of_device *ofdev, if (pasemi_nand_mtd) return -ENODEV; - pr_debug("pasemi_nand at %lx-%lx\n", res.start, res.end); + pr_debug("pasemi_nand at %llx-%llx\n", res.start, res.end); /* Allocate memory for MTD device structure and private data */ pasemi_nand_mtd = kzalloc(sizeof(struct mtd_info) + @@ -170,7 +170,7 @@ static int __devinit pasemi_nand_probe(struct of_device *ofdev, goto out_lpc; } - printk(KERN_INFO "PA Semi NAND flash at %08lx, control at I/O %x\n", + printk(KERN_INFO "PA Semi NAND flash at %08llx, control at I/O %x\n", res.start, lpcctl); return 0; -- cgit v1.2.3 From 30b23634084d95781f7611c0713cb551a0c0a152 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 27 Jan 2009 21:19:41 -0800 Subject: drm: Rip out the racy, unused vblank signal code. Schedule a vblank signal, kill the process, and we'll go walking over freed memory. Given that no open-source userland exists using this, nor have I ever heard of a consumer, just let this code die. Signed-off-by: Eric Anholt Requested-by: Linus Torvalds Acked-by: Dave Airlie Signed-off-by: Linus Torvalds --- drivers/gpu/drm/drm_irq.c | 161 +++++++--------------------------------------- 1 file changed, 23 insertions(+), 138 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 477caa1b1e4..69aa0ab2840 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -106,8 +106,6 @@ void drm_vblank_cleanup(struct drm_device *dev) drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, DRM_MEM_DRIVER); - drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, - DRM_MEM_DRIVER); drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * dev->num_crtcs, DRM_MEM_DRIVER); drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * @@ -132,7 +130,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, (unsigned long)dev); spin_lock_init(&dev->vbl_lock); - atomic_set(&dev->vbl_signal_pending, 0); dev->num_crtcs = num_crtcs; dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs, @@ -140,11 +137,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->vbl_queue) goto err; - dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs, - DRM_MEM_DRIVER); - if (!dev->vbl_sigs) - goto err; - dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, DRM_MEM_DRIVER); if (!dev->_vblank_count) @@ -177,7 +169,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) /* Zero per-crtc vblank stuff */ for (i = 0; i < num_crtcs; i++) { init_waitqueue_head(&dev->vbl_queue[i]); - INIT_LIST_HEAD(&dev->vbl_sigs[i]); atomic_set(&dev->_vblank_count[i], 0); atomic_set(&dev->vblank_refcount[i], 0); } @@ -540,15 +531,10 @@ out: * \param data user argument, pointing to a drm_wait_vblank structure. * \return zero on success or a negative number on failure. * - * Verifies the IRQ is installed. - * - * If a signal is requested checks if this task has already scheduled the same signal - * for the same vblank sequence number - nothing to be done in - * that case. If the number of tasks waiting for the interrupt exceeds 100 the - * function fails. Otherwise adds a new entry to drm_device::vbl_sigs for this - * task. - * - * If a signal is not requested, then calls vblank_wait(). + * This function enables the vblank interrupt on the pipe requested, then + * sleeps waiting for the requested sequence number to occur, and drops + * the vblank interrupt refcount afterwards. (vblank irq disable follows that + * after a timeout with no further vblank waits scheduled). */ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -560,6 +546,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data, if ((!dev->pdev->irq) || (!dev->irq_enabled)) return -EINVAL; + if (vblwait->request.type & _DRM_VBLANK_SIGNAL) + return -EINVAL; + if (vblwait->request.type & ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", @@ -597,89 +586,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data, vblwait->request.sequence = seq + 1; } - if (flags & _DRM_VBLANK_SIGNAL) { - unsigned long irqflags; - struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; - struct drm_vbl_sig *vbl_sig; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - - /* Check if this task has already scheduled the same signal - * for the same vblank sequence number; nothing to be done in - * that case - */ - list_for_each_entry(vbl_sig, vbl_sigs, head) { - if (vbl_sig->sequence == vblwait->request.sequence - && vbl_sig->info.si_signo == - vblwait->request.signal - && vbl_sig->task == current) { - spin_unlock_irqrestore(&dev->vbl_lock, - irqflags); - vblwait->reply.sequence = seq; - goto done; - } - } - - if (atomic_read(&dev->vbl_signal_pending) >= 100) { - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - ret = -EBUSY; - goto done; - } - - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - - vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig), - DRM_MEM_DRIVER); - if (!vbl_sig) { - ret = -ENOMEM; - goto done; - } - - /* Get a refcount on the vblank, which will be released by - * drm_vbl_send_signals(). - */ - ret = drm_vblank_get(dev, crtc); - if (ret) { - drm_free(vbl_sig, sizeof(struct drm_vbl_sig), - DRM_MEM_DRIVER); - goto done; - } - - atomic_inc(&dev->vbl_signal_pending); - - vbl_sig->sequence = vblwait->request.sequence; - vbl_sig->info.si_signo = vblwait->request.signal; - vbl_sig->task = current; + DRM_DEBUG("waiting on vblank count %d, crtc %d\n", + vblwait->request.sequence, crtc); + dev->last_vblank_wait[crtc] = vblwait->request.sequence; + DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, + (((drm_vblank_count(dev, crtc) - + vblwait->request.sequence) <= (1 << 23)) || + !dev->irq_enabled)); - spin_lock_irqsave(&dev->vbl_lock, irqflags); - - list_add_tail(&vbl_sig->head, vbl_sigs); + if (ret != -EINTR) { + struct timeval now; - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + do_gettimeofday(&now); - vblwait->reply.sequence = seq; + vblwait->reply.tval_sec = now.tv_sec; + vblwait->reply.tval_usec = now.tv_usec; + vblwait->reply.sequence = drm_vblank_count(dev, crtc); + DRM_DEBUG("returning %d to client\n", + vblwait->reply.sequence); } else { - DRM_DEBUG("waiting on vblank count %d, crtc %d\n", - vblwait->request.sequence, crtc); - dev->last_vblank_wait[crtc] = vblwait->request.sequence; - DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, - (((drm_vblank_count(dev, crtc) - - vblwait->request.sequence) <= (1 << 23)) || - !dev->irq_enabled)); - - if (ret != -EINTR) { - struct timeval now; - - do_gettimeofday(&now); - - vblwait->reply.tval_sec = now.tv_sec; - vblwait->reply.tval_usec = now.tv_usec; - vblwait->reply.sequence = drm_vblank_count(dev, crtc); - DRM_DEBUG("returning %d to client\n", - vblwait->reply.sequence); - } else { - DRM_DEBUG("vblank wait interrupted by signal\n"); - } + DRM_DEBUG("vblank wait interrupted by signal\n"); } done: @@ -687,46 +613,6 @@ done: return ret; } -/** - * Send the VBLANK signals. - * - * \param dev DRM device. - * \param crtc CRTC where the vblank event occurred - * - * Sends a signal for each task in drm_device::vbl_sigs and empties the list. - * - * If a signal is not requested, then calls vblank_wait(). - */ -static void drm_vbl_send_signals(struct drm_device *dev, int crtc) -{ - struct drm_vbl_sig *vbl_sig, *tmp; - struct list_head *vbl_sigs; - unsigned int vbl_seq; - unsigned long flags; - - spin_lock_irqsave(&dev->vbl_lock, flags); - - vbl_sigs = &dev->vbl_sigs[crtc]; - vbl_seq = drm_vblank_count(dev, crtc); - - list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { - if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { - vbl_sig->info.si_code = vbl_seq; - send_sig_info(vbl_sig->info.si_signo, - &vbl_sig->info, vbl_sig->task); - - list_del(&vbl_sig->head); - - drm_free(vbl_sig, sizeof(*vbl_sig), - DRM_MEM_DRIVER); - atomic_dec(&dev->vbl_signal_pending); - drm_vblank_put(dev, crtc); - } - } - - spin_unlock_irqrestore(&dev->vbl_lock, flags); -} - /** * drm_handle_vblank - handle a vblank event * @dev: DRM device @@ -739,6 +625,5 @@ void drm_handle_vblank(struct drm_device *dev, int crtc) { atomic_inc(&dev->_vblank_count[crtc]); DRM_WAKEUP(&dev->vbl_queue[crtc]); - drm_vbl_send_signals(dev, crtc); } EXPORT_SYMBOL(drm_handle_vblank); -- cgit v1.2.3 From 82d4b90debaa7ab3590335c1b641eb3d2ebb164e Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 19 Jan 2009 19:19:55 +0100 Subject: ieee1394: support for speeds greater than S800 The hard-wired configuration of the top speed (until now S800) was unnecessary, remove it. If the local link layer controller supports S1600 or S3200, we now assume this speed for all present 1394b PHYs (except if they are behind 1394a repeaters) until nodemgr figured out the actual speed while fetching the config ROM. Signed-off-by: Stefan Richter --- drivers/ieee1394/ieee1394.h | 4 +--- drivers/ieee1394/ieee1394_core.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h index e0ae0d3d747..af320e2c507 100644 --- a/drivers/ieee1394/ieee1394.h +++ b/drivers/ieee1394/ieee1394.h @@ -54,9 +54,7 @@ #define IEEE1394_SPEED_800 0x03 #define IEEE1394_SPEED_1600 0x04 #define IEEE1394_SPEED_3200 0x05 - -/* The current highest tested speed supported by the subsystem */ -#define IEEE1394_SPEED_MAX IEEE1394_SPEED_800 +#define IEEE1394_SPEED_MAX IEEE1394_SPEED_3200 /* Maps speed values above to a string representation */ extern const char *hpsb_speedto_str[]; diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index dcdb71a7718..2beb8d94f7b 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -338,6 +338,7 @@ static void build_speed_map(struct hpsb_host *host, int nodecount) u8 cldcnt[nodecount]; u8 *map = host->speed_map; u8 *speedcap = host->speed; + u8 local_link_speed = host->csr.lnk_spd; struct selfid *sid; struct ext_selfid *esid; int i, j, n; @@ -373,8 +374,8 @@ static void build_speed_map(struct hpsb_host *host, int nodecount) if (sid->port2 == SELFID_PORT_CHILD) cldcnt[n]++; speedcap[n] = sid->speed; - if (speedcap[n] > host->csr.lnk_spd) - speedcap[n] = host->csr.lnk_spd; + if (speedcap[n] > local_link_speed) + speedcap[n] = local_link_speed; n--; } } @@ -407,12 +408,11 @@ static void build_speed_map(struct hpsb_host *host, int nodecount) } } -#if SELFID_SPEED_UNKNOWN != IEEE1394_SPEED_MAX - /* assume maximum speed for 1394b PHYs, nodemgr will correct it */ - for (n = 0; n < nodecount; n++) - if (speedcap[n] == SELFID_SPEED_UNKNOWN) - speedcap[n] = IEEE1394_SPEED_MAX; -#endif + /* assume a maximum speed for 1394b PHYs, nodemgr will correct it */ + if (local_link_speed > SELFID_SPEED_UNKNOWN) + for (i = 0; i < nodecount; i++) + if (speedcap[i] == SELFID_SPEED_UNKNOWN) + speedcap[i] = local_link_speed; } -- cgit v1.2.3 From 4106ceff15495a7df1617e78bbf3e852fe6601c9 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Mon, 19 Jan 2009 19:20:31 +0100 Subject: ieee1394: sbp2: update a help string Signed-off-by: Stefan Richter --- drivers/ieee1394/sbp2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index ab1034ccb7f..3f8082d3104 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -115,8 +115,8 @@ */ static int sbp2_max_speed = IEEE1394_SPEED_MAX; module_param_named(max_speed, sbp2_max_speed, int, 0644); -MODULE_PARM_DESC(max_speed, "Force max speed " - "(3 = 800Mb/s, 2 = 400Mb/s, 1 = 200Mb/s, 0 = 100Mb/s)"); +MODULE_PARM_DESC(max_speed, "Limit data transfer speed (5 <= 3200, " + "4 <= 1600, 3 <= 800, 2 <= 400, 1 <= 200, 0 = 100 Mb/s)"); /* * Set serialize_io to 0 or N to use dynamically appended lists of command ORBs. -- cgit v1.2.3 From d3e3e970e3722c51e3fd3b042b6065d4bfaf6f81 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 24 Jan 2009 19:41:46 +0100 Subject: ieee1394: sbp2: fix payload limit at S1600 and S3200 1394-2008 clause 16.3.4.1 (1394b-2002 clause 16.3.1.1) defines tighter limits than 1394-2008 clause 6.2.2.3 (1394a-2000 clause 6.2.2.3). Our previously too large limit doesn't matter though if the controller reports its max_receive correctly. Signed-off-by: Stefan Richter --- drivers/ieee1394/sbp2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 3f8082d3104..a2984a091ae 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -256,7 +256,7 @@ static int sbp2_set_busy_timeout(struct sbp2_lu *); static int sbp2_max_speed_and_size(struct sbp2_lu *); -static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xA, 0xB, 0xC }; +static const u8 sbp2_speedto_max_payload[] = { 0x7, 0x8, 0x9, 0xa, 0xa, 0xa }; static DEFINE_RWLOCK(sbp2_hi_logical_units_lock); -- cgit v1.2.3 From c1fbdd78517a9323ea5f5767c8ceb10aabc40fc2 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 24 Jan 2009 19:41:46 +0100 Subject: ieee1394: sbp2: don't assume zero model_id or firmware_revision if there is none This makes sbp2 behave more like firewire-sbp2 which reports 0xff000000 as immediate value if there are no unit directory entries for model_id or firmware_revision. It does not reduce matches with the currently existing quirks table; the only zero entry there is for a device which actually does have a zero model_id. It only changes how model_id and firmware_revision are logged if they are missing. Other functionally unrelated changes: The model_id member of quirks list entries is renamed to model; the value (but not the effect) of SBP2_ROM_VALUE_WILDCARD is changed. Now this part of the source is identical with firewire-sbp2 for easier maintenance. Signed-off-by: Stefan Richter --- drivers/ieee1394/sbp2.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index a2984a091ae..fb8f9f4430a 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -347,8 +347,8 @@ static struct scsi_host_template sbp2_shost_template = { .sdev_attrs = sbp2_sysfs_sdev_attrs, }; -/* for match-all entries in sbp2_workarounds_table */ -#define SBP2_ROM_VALUE_WILDCARD 0x1000000 +#define SBP2_ROM_VALUE_WILDCARD ~0 /* match all */ +#define SBP2_ROM_VALUE_MISSING 0xff000000 /* not present in the unit dir. */ /* * List of devices with known bugs. @@ -359,60 +359,60 @@ static struct scsi_host_template sbp2_shost_template = { */ static const struct { u32 firmware_revision; - u32 model_id; + u32 model; unsigned workarounds; } sbp2_workarounds_table[] = { /* DViCO Momobay CX-1 with TSB42AA9 bridge */ { .firmware_revision = 0x002800, - .model_id = 0x001010, + .model = 0x001010, .workarounds = SBP2_WORKAROUND_INQUIRY_36 | SBP2_WORKAROUND_MODE_SENSE_8 | SBP2_WORKAROUND_POWER_CONDITION, }, /* DViCO Momobay FX-3A with TSB42AA9A bridge */ { .firmware_revision = 0x002800, - .model_id = 0x000000, + .model = 0x000000, .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY | SBP2_WORKAROUND_POWER_CONDITION, }, /* Initio bridges, actually only needed for some older ones */ { .firmware_revision = 0x000200, - .model_id = SBP2_ROM_VALUE_WILDCARD, + .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_INQUIRY_36, }, /* PL-3507 bridge with Prolific firmware */ { .firmware_revision = 0x012800, - .model_id = SBP2_ROM_VALUE_WILDCARD, + .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_POWER_CONDITION, }, /* Symbios bridge */ { .firmware_revision = 0xa0b800, - .model_id = SBP2_ROM_VALUE_WILDCARD, + .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, }, /* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ { .firmware_revision = 0x002600, - .model_id = SBP2_ROM_VALUE_WILDCARD, + .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, }, /* iPod 4th generation */ { .firmware_revision = 0x0a2700, - .model_id = 0x000021, + .model = 0x000021, .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, }, /* iPod mini */ { .firmware_revision = 0x0a2700, - .model_id = 0x000022, + .model = 0x000022, .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, }, /* iPod mini */ { .firmware_revision = 0x0a2700, - .model_id = 0x000023, + .model = 0x000023, .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, }, /* iPod Photo */ { .firmware_revision = 0x0a2700, - .model_id = 0x00007e, + .model = 0x00007e, .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, } }; @@ -1341,13 +1341,15 @@ static void sbp2_parse_unit_directory(struct sbp2_lu *lu, struct csr1212_keyval *kv; struct csr1212_dentry *dentry; u64 management_agent_addr; - u32 unit_characteristics, firmware_revision; + u32 unit_characteristics, firmware_revision, model; unsigned workarounds; int i; management_agent_addr = 0; unit_characteristics = 0; - firmware_revision = 0; + firmware_revision = SBP2_ROM_VALUE_MISSING; + model = ud->flags & UNIT_DIRECTORY_MODEL_ID ? + ud->model_id : SBP2_ROM_VALUE_MISSING; csr1212_for_each_dir_entry(ud->ne->csr, kv, ud->ud_kv, dentry) { switch (kv->key.id) { @@ -1388,9 +1390,9 @@ static void sbp2_parse_unit_directory(struct sbp2_lu *lu, sbp2_workarounds_table[i].firmware_revision != (firmware_revision & 0xffff00)) continue; - if (sbp2_workarounds_table[i].model_id != + if (sbp2_workarounds_table[i].model != SBP2_ROM_VALUE_WILDCARD && - sbp2_workarounds_table[i].model_id != ud->model_id) + sbp2_workarounds_table[i].model != model) continue; workarounds |= sbp2_workarounds_table[i].workarounds; break; @@ -1403,7 +1405,7 @@ static void sbp2_parse_unit_directory(struct sbp2_lu *lu, NODE_BUS_ARGS(ud->ne->host, ud->ne->nodeid), workarounds, firmware_revision, ud->vendor_id ? ud->vendor_id : ud->ne->vendor_id, - ud->model_id); + model); /* We would need one SCSI host template for each target to adjust * max_sectors on the fly, therefore warn only. */ -- cgit v1.2.3 From a08e100aece16e33a45b82924ad85f4066c4ed1c Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 24 Jan 2009 19:41:46 +0100 Subject: firewire: sbp2: fix payload limit at S1600 and S3200 1394-2008 clause 16.3.4.1 (1394b-2002 clause 16.3.1.1) defines tighter limits than 1394-2008 clause 6.2.2.3 (1394a-2000 clause 6.2.2.3). Our previously too large limit doesn't matter though if the controller reports its max_receive correctly. Signed-off-by: Stefan Richter --- drivers/firewire/fw-sbp2.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index e88d5067448..ac803843158 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -168,6 +168,7 @@ struct sbp2_target { int address_high; unsigned int workarounds; unsigned int mgt_orb_timeout; + unsigned int max_payload; int dont_block; /* counter for each logical unit */ int blocked; /* ditto */ @@ -1156,6 +1157,15 @@ static int sbp2_probe(struct device *dev) sbp2_init_workarounds(tgt, model, firmware_revision); + /* + * At S100 we can do 512 bytes per packet, at S200 1024 bytes, + * and so on up to 4096 bytes. The SBP-2 max_payload field + * specifies the max payload size as 2 ^ (max_payload + 2), so + * if we set this to max_speed + 7, we get the right value. + */ + tgt->max_payload = min(device->max_speed + 7, 10U); + tgt->max_payload = min(tgt->max_payload, device->card->max_receive - 1); + /* Do the login in a workqueue so we can easily reschedule retries. */ list_for_each_entry(lu, &tgt->lu_list, link) sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5)); @@ -1434,7 +1444,6 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) struct sbp2_logical_unit *lu = cmd->device->hostdata; struct fw_device *device = fw_device(lu->tgt->unit->device.parent); struct sbp2_command_orb *orb; - unsigned int max_payload; int generation, retval = SCSI_MLQUEUE_HOST_BUSY; /* @@ -1462,17 +1471,9 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) orb->done = done; orb->cmd = cmd; - orb->request.next.high = cpu_to_be32(SBP2_ORB_NULL); - /* - * At speed 100 we can do 512 bytes per packet, at speed 200, - * 1024 bytes per packet etc. The SBP-2 max_payload field - * specifies the max payload size as 2 ^ (max_payload + 2), so - * if we set this to max_speed + 7, we get the right value. - */ - max_payload = min(device->max_speed + 7, - device->card->max_receive - 1); + orb->request.next.high = cpu_to_be32(SBP2_ORB_NULL); orb->request.misc = cpu_to_be32( - COMMAND_ORB_MAX_PAYLOAD(max_payload) | + COMMAND_ORB_MAX_PAYLOAD(lu->tgt->max_payload) | COMMAND_ORB_SPEED(device->max_speed) | COMMAND_ORB_NOTIFY); -- cgit v1.2.3 From f746072abc12d0e10ecd7847f1846157fde15987 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sat, 24 Jan 2009 19:41:46 +0100 Subject: firewire: sbp2: define some magic numbers as macros Signed-off-by: Stefan Richter --- drivers/firewire/fw-sbp2.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index ac803843158..5739caaaec7 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -311,14 +311,16 @@ struct sbp2_command_orb { dma_addr_t page_table_bus; }; +#define SBP2_ROM_VALUE_WILDCARD ~0 /* match all */ +#define SBP2_ROM_VALUE_MISSING 0xff000000 /* not present in the unit dir. */ + /* * List of devices with known bugs. * * The firmware_revision field, masked with 0xffff00, is the best * indicator for the type of bridge chip of a device. It yields a few * false positives but this did not break correctly behaving devices - * so far. We use ~0 as a wildcard, since the 24 bit values we get - * from the config rom can never match that. + * so far. */ static const struct { u32 firmware_revision; @@ -340,22 +342,22 @@ static const struct { }, /* Initio bridges, actually only needed for some older ones */ { .firmware_revision = 0x000200, - .model = ~0, + .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_INQUIRY_36, }, /* PL-3507 bridge with Prolific firmware */ { .firmware_revision = 0x012800, - .model = ~0, + .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_POWER_CONDITION, }, /* Symbios bridge */ { .firmware_revision = 0xa0b800, - .model = ~0, + .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, }, /* Datafab MD2-FW2 with Symbios/LSILogic SYM13FW500 bridge */ { .firmware_revision = 0x002600, - .model = ~0, + .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, }, @@ -1093,7 +1095,7 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model, continue; if (sbp2_workarounds_table[i].model != model && - sbp2_workarounds_table[i].model != ~0) + sbp2_workarounds_table[i].model != SBP2_ROM_VALUE_WILDCARD) continue; w |= sbp2_workarounds_table[i].workarounds; @@ -1143,14 +1145,13 @@ static int sbp2_probe(struct device *dev) fw_device_get(device); fw_unit_get(unit); - /* Initialize to values that won't match anything in our table. */ - firmware_revision = 0xff000000; - model = 0xff000000; - /* implicit directory ID */ tgt->directory_id = ((unit->directory - device->config_rom) * 4 + CSR_CONFIG_ROM) & 0xffffff; + firmware_revision = SBP2_ROM_VALUE_MISSING; + model = SBP2_ROM_VALUE_MISSING; + if (sbp2_scan_unit_dir(tgt, unit->directory, &model, &firmware_revision) < 0) goto fail_tgt_put; -- cgit v1.2.3 From 5e2125677fd72d36396cc537466e07ffcbbd4b2b Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 28 Jan 2009 01:03:34 +0100 Subject: firewire: sbp2: fix DMA mapping leak on the failure path Reported-by: FUJITA Tomonori who also provided a first version of the fix. Signed-off-by: Stefan Richter --- drivers/firewire/fw-sbp2.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 5739caaaec7..6635925a375 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -1284,6 +1284,19 @@ static struct fw_driver sbp2_driver = { .id_table = sbp2_id_table, }; +static void sbp2_unmap_scatterlist(struct device *card_device, + struct sbp2_command_orb *orb) +{ + if (scsi_sg_count(orb->cmd)) + dma_unmap_sg(card_device, scsi_sglist(orb->cmd), + scsi_sg_count(orb->cmd), + orb->cmd->sc_data_direction); + + if (orb->request.misc & cpu_to_be32(COMMAND_ORB_PAGE_TABLE_PRESENT)) + dma_unmap_single(card_device, orb->page_table_bus, + sizeof(orb->page_table), DMA_TO_DEVICE); +} + static unsigned int sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data) { @@ -1363,15 +1376,7 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) dma_unmap_single(device->card->device, orb->base.request_bus, sizeof(orb->request), DMA_TO_DEVICE); - - if (scsi_sg_count(orb->cmd) > 0) - dma_unmap_sg(device->card->device, scsi_sglist(orb->cmd), - scsi_sg_count(orb->cmd), - orb->cmd->sc_data_direction); - - if (orb->page_table_bus != 0) - dma_unmap_single(device->card->device, orb->page_table_bus, - sizeof(orb->page_table), DMA_TO_DEVICE); + sbp2_unmap_scatterlist(device->card->device, orb); orb->cmd->result = result; orb->done(orb->cmd); @@ -1493,8 +1498,10 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) orb->base.request_bus = dma_map_single(device->card->device, &orb->request, sizeof(orb->request), DMA_TO_DEVICE); - if (dma_mapping_error(device->card->device, orb->base.request_bus)) + if (dma_mapping_error(device->card->device, orb->base.request_bus)) { + sbp2_unmap_scatterlist(device->card->device, orb); goto out; + } sbp2_send_orb(&orb->base, lu, lu->tgt->node_id, generation, lu->command_block_agent_address + SBP2_ORB_POINTER); -- cgit v1.2.3 From c69a1f09430c7a62b87af89383998256fcf07685 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Jan 2009 17:59:15 -0800 Subject: Staging: comedi: fix Kbuild comedi doesn't like being built into the kernel right now, so force it to be a module. Reported-by: Kamalesh Babulal Reported-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig index b501bfb9c75..b47ca1e7e38 100644 --- a/drivers/staging/comedi/Kconfig +++ b/drivers/staging/comedi/Kconfig @@ -1,6 +1,7 @@ config COMEDI tristate "Data Acquision support (comedi)" default N + depends on m ---help--- Enable support a wide range of data acquision devices for Linux. -- cgit v1.2.3 From 7c5151fbf134e082bc7f2c0ed02684ed12578b3b Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 16 Jan 2009 18:01:57 -0800 Subject: Staging: meilhaus: fix Kbuild The Meilhaus drivers do not like being built into the kernel right now, so force them to be a module. Reported-by: Kamalesh Babulal Reported-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- drivers/staging/meilhaus/Kconfig | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/meilhaus/Kconfig b/drivers/staging/meilhaus/Kconfig index 6def83fa2c9..923af22a468 100644 --- a/drivers/staging/meilhaus/Kconfig +++ b/drivers/staging/meilhaus/Kconfig @@ -4,6 +4,7 @@ menuconfig MEILHAUS tristate "Meilhaus support" + depends on m ---help--- If you have a Meilhaus card, say Y (or M) here. @@ -18,7 +19,7 @@ if MEILHAUS config ME0600 tristate "Meilhaus ME-600 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-600 family of boards that do data collection and multipurpose I/O. @@ -29,7 +30,7 @@ config ME0600 config ME0900 tristate "Meilhaus ME-900 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-900 family of boards that do data collection and multipurpose I/O. @@ -40,7 +41,7 @@ config ME0900 config ME1000 tristate "Meilhaus ME-1000 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-1000 family of boards that do data collection and multipurpose I/O. @@ -51,7 +52,7 @@ config ME1000 config ME1400 tristate "Meilhaus ME-1400 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-1400 family of boards that do data collection and multipurpose I/O. @@ -62,7 +63,7 @@ config ME1400 config ME1600 tristate "Meilhaus ME-1600 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-1600 family of boards that do data collection and multipurpose I/O. @@ -73,7 +74,7 @@ config ME1600 config ME4600 tristate "Meilhaus ME-4600 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-4600 family of boards that do data collection and multipurpose I/O. @@ -84,7 +85,7 @@ config ME4600 config ME6000 tristate "Meilhaus ME-6000 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-6000 family of boards that do data collection and multipurpose I/O. @@ -95,7 +96,7 @@ config ME6000 config ME8100 tristate "Meilhaus ME-8100 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-8100 family of boards that do data collection and multipurpose I/O. @@ -106,7 +107,7 @@ config ME8100 config ME8200 tristate "Meilhaus ME-8200 support" default n - depends on PCI + depends on PCI && m help This driver supports the Meilhaus ME-8200 family of boards that do data collection and multipurpose I/O. @@ -117,7 +118,7 @@ config ME8200 config MEDUMMY tristate "Meilhaus dummy driver" default n - depends on PCI + depends on PCI && m help This provides a dummy driver for the Meilhaus driver package -- cgit v1.2.3 From c171ac36b74f6c90bc7a03c309136ba175314b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 8 Jan 2009 15:28:50 -0800 Subject: Staging: android: binder: fix arm build errors Reported-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/binder.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index 6a4ceacb33f..ab014bc9683 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -2649,14 +2649,14 @@ static void binder_vma_open(struct vm_area_struct *vma) { struct binder_proc *proc = vma->vm_private_data; if (binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE) - printk(KERN_INFO "binder: %d open vm area %lx-%lx (%ld K) vma %lx pagep %lx\n", proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, vma->vm_page_prot.pgprot); + printk(KERN_INFO "binder: %d open vm area %lx-%lx (%ld K) vma %lx pagep %lx\n", proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, pgprot_val(vma->vm_page_prot)); dump_stack(); } static void binder_vma_close(struct vm_area_struct *vma) { struct binder_proc *proc = vma->vm_private_data; if (binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE) - printk(KERN_INFO "binder: %d close vm area %lx-%lx (%ld K) vma %lx pagep %lx\n", proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, vma->vm_page_prot.pgprot); + printk(KERN_INFO "binder: %d close vm area %lx-%lx (%ld K) vma %lx pagep %lx\n", proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, pgprot_val(vma->vm_page_prot)); proc->vma = NULL; } @@ -2677,7 +2677,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) vma->vm_end = vma->vm_start + SZ_4M; if (binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE) - printk(KERN_INFO "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, vma->vm_page_prot.pgprot); + printk(KERN_INFO "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, pgprot_val(vma->vm_page_prot)); if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) { ret = -EPERM; -- cgit v1.2.3 From 2d0db6bf5010c26beb1ccbd4ee50991fd2c05d90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Thu, 8 Jan 2009 16:48:46 -0800 Subject: Staging: android: timed_gpio: Fix build to build on kernels after 2.6.25. Reported-by: Randy Dunlap Cc: Mike Lockwood Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/timed_gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c index bea68c9fc94..b41b20e3462 100644 --- a/drivers/staging/android/timed_gpio.c +++ b/drivers/staging/android/timed_gpio.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include "timed_gpio.h" -- cgit v1.2.3 From 07960058f0ce77ddc3027d3e45a5de1fb977334f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 28 Jan 2009 15:42:43 -0800 Subject: Staging: android: fix build error on 64bit boxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ktime_t isn't ment to directly access on all arches, so use the proper conversion functions instead to figure out what time is remaining. Reported-by: Randy Dunlap Cc: Arve HjønnevÃ¥g Cc: Mike Lockwood Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/timed_gpio.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c index b41b20e3462..903270cbbe0 100644 --- a/drivers/staging/android/timed_gpio.c +++ b/drivers/staging/android/timed_gpio.c @@ -49,7 +49,8 @@ static ssize_t gpio_enable_show(struct device *dev, struct device_attribute *att if (hrtimer_active(&gpio_data->timer)) { ktime_t r = hrtimer_get_remaining(&gpio_data->timer); - remaining = r.tv.sec * 1000 + r.tv.nsec / 1000000; + struct timeval t = ktime_to_timeval(r); + remaining = t.tv_sec * 1000 + t.tv_usec; } else remaining = 0; -- cgit v1.2.3 From 191805ac41a63929003faa33365027d3fb924d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Wed, 14 Jan 2009 16:54:16 -0800 Subject: Staging: android: Add lowmemorykiller documentation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Arve HjønnevÃ¥g Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/lowmemorykiller.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 drivers/staging/android/lowmemorykiller.txt (limited to 'drivers') diff --git a/drivers/staging/android/lowmemorykiller.txt b/drivers/staging/android/lowmemorykiller.txt new file mode 100644 index 00000000000..bd5c0c02896 --- /dev/null +++ b/drivers/staging/android/lowmemorykiller.txt @@ -0,0 +1,16 @@ +The lowmemorykiller driver lets user-space specify a set of memory thresholds +where processes with a range of oom_adj values will get killed. Specify the +minimum oom_adj values in /sys/module/lowmemorykiller/parameters/adj and the +number of free pages in /sys/module/lowmemorykiller/parameters/minfree. Both +files take a comma separated list of numbers in ascending order. + +For example, write "0,8" to /sys/module/lowmemorykiller/parameters/adj and +"1024,4096" to /sys/module/lowmemorykiller/parameters/minfree to kill processes +with a oom_adj value of 8 or higher when the free memory drops below 4096 pages +and kill processes with a oom_adj value of 0 or higher when the free memory +drops below 1024 pages. + +The driver considers memory used for caches to be free, but if a large +percentage of the cached memory is locked this can be very inaccurate +and processes may not get killed until the normal oom killer is triggered. + -- cgit v1.2.3 From 1176e83aff6f15b6ae4d1b53c16124884ad29363 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Sun, 18 Jan 2009 18:17:20 +0100 Subject: Staging: android: task_get_unused_fd_flags: fix the wrong usage of tsk->signal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compile tested. task_struct->signal is not protected by RCU, the code is bogus. Change the code to take ->siglock to pin ->signal. Signed-off-by: Oleg Nesterov Cc: Arve HjønnevÃ¥g Cc: Brian Swetland Signed-off-by: Greg Kroah-Hartman --- drivers/staging/android/binder.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c index ab014bc9683..758131cad08 100644 --- a/drivers/staging/android/binder.c +++ b/drivers/staging/android/binder.c @@ -319,6 +319,7 @@ int task_get_unused_fd_flags(struct task_struct *tsk, int flags) int fd, error; struct fdtable *fdt; unsigned long rlim_cur; + unsigned long irqs; if (files == NULL) return -ESRCH; @@ -335,12 +336,11 @@ repeat: * N.B. For clone tasks sharing a files structure, this test * will limit the total number of files that can be opened. */ - rcu_read_lock(); - if (tsk->signal) + rlim_cur = 0; + if (lock_task_sighand(tsk, &irqs)) { rlim_cur = tsk->signal->rlim[RLIMIT_NOFILE].rlim_cur; - else - rlim_cur = 0; - rcu_read_unlock(); + unlock_task_sighand(tsk, &irqs); + } if (fd >= rlim_cur) goto out; -- cgit v1.2.3 From e48d94dac7eef16b4a4f246bf7b8df0f00cc0aec Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 12 Jan 2009 09:19:42 +0100 Subject: staging: agnx: drivers/staging/agnx/agnx.h needs On m68k: drivers/staging/agnx/agnx.h: In function 'agnx_read32': drivers/staging/agnx/agnx.h:10: error: implicit declaration of function 'ioread32' drivers/staging/agnx/agnx.h: In function 'agnx_write32': drivers/staging/agnx/agnx.h:15: error: implicit declaration of function 'iowrite32' drivers/staging/agnx/sta.c: In function 'get_sta_power': drivers/staging/agnx/sta.c:94: error: implicit declaration of function 'memcpy_fromio' drivers/staging/agnx/sta.c: In function 'set_sta_power': drivers/staging/agnx/sta.c:103: error: implicit declaration of function 'memcpy_toio' Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/staging/agnx/agnx.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/staging/agnx/agnx.h b/drivers/staging/agnx/agnx.h index a75b0db3726..20f36da6247 100644 --- a/drivers/staging/agnx/agnx.h +++ b/drivers/staging/agnx/agnx.h @@ -1,6 +1,8 @@ #ifndef AGNX_H_ #define AGNX_H_ +#include + #include "xmit.h" #define PFX KBUILD_MODNAME ": " -- cgit v1.2.3 From 05d6d677ab4b975697c6a987f1dffdc55d61a160 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 19 Dec 2008 23:37:30 +0100 Subject: Staging: usbip: usbip_start_threads(): handle kernel_thread failure kernel_thread may fail, notice this. Signed-off-by: Roel Kluin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/usbip/usbip_common.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 72e209276ea..22f93dd0ba0 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -406,8 +406,20 @@ void usbip_start_threads(struct usbip_device *ud) /* * threads are invoked per one device (per one connection). */ - kernel_thread(usbip_thread, (void *)&ud->tcp_rx, 0); - kernel_thread(usbip_thread, (void *)&ud->tcp_tx, 0); + int retval; + + retval = kernel_thread(usbip_thread, (void *)&ud->tcp_rx, 0); + if (retval < 0) { + printk(KERN_ERR "Creating tcp_rx thread for ud %p failed.\n", + ud); + return; + } + retval = kernel_thread(usbip_thread, (void *)&ud->tcp_tx, 0); + if (retval < 0) { + printk(KERN_ERR "Creating tcp_tx thread for ud %p failed.\n", + ud); + return; + } /* confirm threads are starting */ wait_for_completion(&ud->tcp_rx.thread_done); -- cgit v1.2.3 From dcbbcefb6a6d540b605421e85fbaa4cea3fef5a2 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 28 Jan 2009 22:14:17 +0100 Subject: Staging: poch: fix verification of memory area fix verification of memory area Signed-off-by: Roel Kluin Signed-off-by: Greg Kroah-Hartman --- drivers/staging/poch/poch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/staging/poch/poch.c b/drivers/staging/poch/poch.c index ec343ef53a8..0d111ddfabb 100644 --- a/drivers/staging/poch/poch.c +++ b/drivers/staging/poch/poch.c @@ -1026,7 +1026,7 @@ static int poch_ioctl(struct inode *inode, struct file *filp, } break; case POCH_IOC_GET_COUNTERS: - if (access_ok(VERIFY_WRITE, argp, sizeof(struct poch_counters))) + if (!access_ok(VERIFY_WRITE, argp, sizeof(struct poch_counters))) return -EFAULT; spin_lock_irq(&channel->counters_lock); -- cgit v1.2.3 From 7cbcf22548df1f1df7c6b0d0bda579b92efca63c Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 20 Jan 2009 16:29:13 -0800 Subject: driver-core: fix kernel-doc parameter name Fix function parameter name in kernel-doc: Warning(linux-next-20090120//drivers/base/core.c:1289): No description found for parameter 'dev' Warning(linux-next-20090120//drivers/base/core.c:1289): Excess function parameter 'root' description in 'root_device_unregister' Signed-off-by: Randy Dunlap Acked-by: Mark McLoughlin Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/base/core.c b/drivers/base/core.c index 55e530942ab..f3eae630e58 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1280,7 +1280,7 @@ EXPORT_SYMBOL_GPL(__root_device_register); /** * root_device_unregister - unregister and free a root device - * @root: device going away. + * @dev: device going away * * This function unregisters and cleans up a device that was created by * root_device_register(). -- cgit v1.2.3 From 0fb21de0799a985d2da3da14ae5625d724256638 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 14 Jan 2009 03:03:21 +0100 Subject: HID: adjust report descriptor fixup for MS 1028 receiver Report descriptor fixup for MS 1028 receiver changes also values for Keyboard and Consumer, which incorrectly trims the range, causing correct events being thrown away before passing to userspace. We need to keep the GenDesk usage fixup though, as it reports totally bogus values about axis. Reported-by: Lucas Gadani Signed-off-by: Jiri Kosina --- drivers/hid/hid-microsoft.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index d718b1607d0..25b10dcad90 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -30,7 +30,7 @@ #define MS_NOGET 0x10 /* - * Microsoft Wireless Desktop Receiver (Model 1028) has several + * Microsoft Wireless Desktop Receiver (Model 1028) has * 'Usage Min/Max' where it ought to have 'Physical Min/Max' */ static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, @@ -38,17 +38,12 @@ static void ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, { unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); - if ((quirks & MS_RDESC) && rsize == 571 && rdesc[284] == 0x19 && - rdesc[286] == 0x2a && rdesc[304] == 0x19 && - rdesc[306] == 0x29 && rdesc[352] == 0x1a && - rdesc[355] == 0x2a && rdesc[557] == 0x19 && + if ((quirks & MS_RDESC) && rsize == 571 && rdesc[557] == 0x19 && rdesc[559] == 0x29) { dev_info(&hdev->dev, "fixing up Microsoft Wireless Receiver " "Model 1028 report descriptor\n"); - rdesc[284] = rdesc[304] = rdesc[557] = 0x35; - rdesc[352] = 0x36; - rdesc[286] = rdesc[355] = 0x46; - rdesc[306] = rdesc[559] = 0x45; + rdesc[557] = 0x35; + rdesc[559] = 0x45; } } -- cgit v1.2.3 From be5d0c837cf8e43458c5757be5df4837a2803d08 Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 28 Jan 2009 09:36:18 +0100 Subject: HID: fix reversed logic in disconnect testing of hiddev The logic for testing for disconnection is reversed in an ioctl leading to false reports of disconnection. Signed-off-by: Oliver Neukum Tested-by: Folkert van Heusden Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hiddev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index d73eea382ab..4940e4d70c2 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -656,7 +656,7 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case HIDIOCGSTRING: mutex_lock(&hiddev->existancelock); - if (!hiddev->exist) + if (hiddev->exist) r = hiddev_ioctl_string(hiddev, cmd, user_arg); else r = -ENODEV; -- cgit v1.2.3 From 656f1fb90aa2261daa316c0dd8f75e3420f81e9e Mon Sep 17 00:00:00 2001 From: Jarod Wilson Date: Wed, 28 Jan 2009 21:22:35 +0100 Subject: HID: add antec-branded soundgraph imon devices to blacklist hid_ignore_list additions for the Antec-branded SoundGraph iMon VFD and LCD devices (0x15c2:0x0044 and 0x0045). These devices are driven by lirc. Signed-off-by: Jarod Wilson Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 ++ drivers/hid/hid-ids.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 5d7640e49dc..7001c31c3f2 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1606,6 +1606,8 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2) }, { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) }, { HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index acc1abc834a..e899f510ebe 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -362,6 +362,8 @@ #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD 0x0038 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2 0x0036 #define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3 0x0034 +#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD4 0x0044 +#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5 0x0045 #define USB_VENDOR_ID_SUN 0x0430 #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab -- cgit v1.2.3 From bae7eb33b25387fdc7ccae08768bef1f9484a5b0 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Wed, 28 Jan 2009 23:06:37 +0100 Subject: HID: document difference between hid_blacklist and hid_ignore_list Many people get it wrong and add device IDs into hid_blacklist instead of hid_ignore_list. Let's put a little comment in place. Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7001c31c3f2..6cad69ed21c 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1218,6 +1218,7 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) } EXPORT_SYMBOL_GPL(hid_connect); +/* a list of devices for which there is a specialized driver on HID bus */ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, @@ -1476,6 +1477,7 @@ static struct bus_type hid_bus_type = { .uevent = hid_uevent, }; +/* a list of devices that shouldn't be handled by HID core at all */ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR) }, { HID_USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302) }, -- cgit v1.2.3 From 618b2c8db24522ae273d8299c6a936ea13793c4d Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Wed, 28 Jan 2009 16:50:20 -0800 Subject: xen: make sysfs files behave as their names suggest 1: make "target_kb" only accept and produce a memory size in kilobytes. 2: add a second "target" file which produces output in bytes, and will accept memparse input (scaled bytes) This fixes the rather irritating problem that writing the same value read back into target_kb would end up shrinking the domain by a factor of 1024, with generally bad results. Signed-off-by: Jeremy Fitzhardinge Cc: Stable Kernel Cc: "dan.magenheimer@oracle.com" Signed-off-by: Ingo Molnar --- drivers/xen/balloon.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 2ba8f95516a..efa4b363ce7 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -498,7 +498,7 @@ static ssize_t store_target_kb(struct sys_device *dev, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - target_bytes = memparse(buf, &endchar); + target_bytes = simple_strtoull(buf, &endchar, 0) * 1024; balloon_set_new_target(target_bytes >> PAGE_SHIFT); @@ -508,8 +508,39 @@ static ssize_t store_target_kb(struct sys_device *dev, static SYSDEV_ATTR(target_kb, S_IRUGO | S_IWUSR, show_target_kb, store_target_kb); + +static ssize_t show_target(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) +{ + return sprintf(buf, "%llu\n", + (u64)balloon_stats.target_pages << PAGE_SHIFT); +} + +static ssize_t store_target(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, + size_t count) +{ + char *endchar; + unsigned long long target_bytes; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + target_bytes = memparse(buf, &endchar); + + balloon_set_new_target(target_bytes >> PAGE_SHIFT); + + return count; +} + +static SYSDEV_ATTR(target, S_IRUGO | S_IWUSR, + show_target, store_target); + + static struct sysdev_attribute *balloon_attrs[] = { &attr_target_kb, + &attr_target, }; static struct attribute *balloon_info_attrs[] = { -- cgit v1.2.3 From c8c4707cf7ca8ff7dcc1653447e48cb3de0bf114 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 29 Jan 2009 00:11:59 +0100 Subject: firewire: sbp2: add workarounds for 2nd and 3rd generation iPods According to https://bugs.launchpad.net/bugs/294391 - 3rd generation iPods need the "fix capacity" workaround after all (apparently they crash after the last sector was accessed), - 2nd generation iPods need the "128 kB maximum request size" workaround. Alas both iPod generations feature the same model ID in the config ROM, hence we can only define a shared quirks list entry for them. Luckily the fix capacity workaround did not show a negative effect in Jarod's tests with 2nd gen. iPod. A side note: Apple computers in target mode (or at least an x86 Mac mini) don't have firmware_version and model_id, hence none of the iPod quirks list entries is active for them. Tested-by: Jarod Wilson Acked-by: Jarod Wilson Signed-off-by: Stefan Richter --- drivers/firewire/fw-sbp2.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 6635925a375..c71c4419d9e 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -360,15 +360,17 @@ static const struct { .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, }, - /* - * There are iPods (2nd gen, 3rd gen) with model_id == 0, but - * these iPods do not feature the read_capacity bug according - * to one report. Read_capacity behaviour as well as model_id - * could change due to Apple-supplied firmware updates though. + * iPod 2nd generation: needs 128k max transfer size workaround + * iPod 3rd generation: needs fix capacity workaround */ - - /* iPod 4th generation. */ { + { + .firmware_revision = 0x0a2700, + .model = 0x000000, + .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS | + SBP2_WORKAROUND_FIX_CAPACITY, + }, + /* iPod 4th generation */ { .firmware_revision = 0x0a2700, .model = 0x000021, .workarounds = SBP2_WORKAROUND_FIX_CAPACITY, -- cgit v1.2.3 From 1448d7c6a2ff96d3b52ecae49e2d0f046a097fe0 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Thu, 29 Jan 2009 00:13:20 +0100 Subject: ieee1394: sbp2: add workarounds for 2nd and 3rd generation iPods as per https://bugs.launchpad.net/bugs/294391. These got one sample of each iPod generation going. However there still occurred I/O stalls with the 3rd generation iPod which remain undiagnosed at the time of this writing. Acked-by: Jarod Wilson Signed-off-by: Stefan Richter --- drivers/ieee1394/sbp2.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index fb8f9f4430a..f3fd8657ce4 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -395,6 +395,16 @@ static const struct { .model = SBP2_ROM_VALUE_WILDCARD, .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS, }, + /* + * iPod 2nd generation: needs 128k max transfer size workaround + * iPod 3rd generation: needs fix capacity workaround + */ + { + .firmware_revision = 0x0a2700, + .model = 0x000000, + .workarounds = SBP2_WORKAROUND_128K_MAX_TRANS | + SBP2_WORKAROUND_FIX_CAPACITY, + }, /* iPod 4th generation */ { .firmware_revision = 0x0a2700, .model = 0x000021, -- cgit v1.2.3 From eb83bbf57429ab80f49b413e3e44d3b19c3fdc5a Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 27 Jan 2009 12:31:23 -0600 Subject: rtl8187: Fix error in setting OFDM power settings for RTL8187L MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After reports of poor performance, a review of the latest vendor driver (rtl8187_linux_26.1025.0328.2007) for RTL8187L devices was undertaken. A difference was found in the code used to index the OFDM power tables. When the Linux driver was changed, my unit works at a much greater range than before. I think this fixes Bugzilla #12380 and has been tested by at least two other users. Signed-off-by: Larry Finger Tested-by: Martín Ernesto Barreyro Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187_rtl8225.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c index 4e75e8e7fa9..78df281b297 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c +++ b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c @@ -285,7 +285,10 @@ static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) ofdm_power = priv->channels[channel - 1].hw_value >> 4; cck_power = min(cck_power, (u8)11); - ofdm_power = min(ofdm_power, (u8)35); + if (ofdm_power > (u8)15) + ofdm_power = 25; + else + ofdm_power += 10; rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1); @@ -536,7 +539,10 @@ static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) cck_power += priv->txpwr_base & 0xF; cck_power = min(cck_power, (u8)35); - ofdm_power = min(ofdm_power, (u8)15); + if (ofdm_power > (u8)15) + ofdm_power = 25; + else + ofdm_power += 10; ofdm_power += priv->txpwr_base >> 4; ofdm_power = min(ofdm_power, (u8)35); -- cgit v1.2.3 From 1f304e4e3bb161163d9f5bc3c6467a2a6fa9b3ae Mon Sep 17 00:00:00 2001 From: "Zhu, Yi" Date: Fri, 23 Jan 2009 13:45:22 -0800 Subject: iwlwifi: fix kernel oops when ucode DMA memory allocation failure The patch fixes memcpy to NULL address when the ucode DMA allocation failure. This is a fix to bug http://www.intellinuxwireless.org/bugzilla/show_bug.cgi?id=1861 Signed-off-by: Zhu Yi Signed-off-by: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 0dc8eed1640..b35c8813bef 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1719,6 +1719,10 @@ static int iwl_read_ucode(struct iwl_priv *priv) priv->ucode_data_backup.len = data_size; iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + if (!priv->ucode_code.v_addr || !priv->ucode_data.v_addr || + !priv->ucode_data_backup.v_addr) + goto err_pci_alloc; + /* Initialization instructions and data */ if (init_size && init_data_size) { priv->ucode_init.len = init_size; -- cgit v1.2.3 From be0093705c3ce98d73cac0ca8f93ea105cdf4e9c Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 22 Jan 2009 08:44:16 -0500 Subject: ath5k: fix locking in ath5k_config ath5k_config updates the software context without taking sc->lock. Changes-licensed-under: 3-Clause-BSD Signed-off-by: Bob Copeland Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 8ef87356e08..a533ed60bb4 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1028,6 +1028,8 @@ ath5k_setup_bands(struct ieee80211_hw *hw) * it's done by reseting the chip. To accomplish this we must * first cleanup any pending DMA, then restart stuff after a la * ath5k_init. + * + * Called with sc->lock. */ static int ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan) @@ -2814,11 +2816,17 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed) { struct ath5k_softc *sc = hw->priv; struct ieee80211_conf *conf = &hw->conf; + int ret; + + mutex_lock(&sc->lock); sc->bintval = conf->beacon_int; sc->power_level = conf->power_level; - return ath5k_chan_set(sc, conf->channel); + ret = ath5k_chan_set(sc, conf->channel); + + mutex_unlock(&sc->lock); + return ret; } static int -- cgit v1.2.3 From e125646ab56b490d0390b158e0afa9cccfc1f897 Mon Sep 17 00:00:00 2001 From: Dhananjay Phadke Date: Thu, 29 Jan 2009 16:05:19 -0800 Subject: netxen: revert jumbo ringsize Reducing jumbo ring size below 1024 reduces throughput for old firmwares (3.4.216 and older) running on older (NX2031) chip, so restore it back to 1024. This was reduced in commit 32ec803348b4d5f1353e1d7feae30880b8b3e342 ("netxen: reduce memory footprint"). Raising jumbo ring size from 512 to 1024, adds ~4MB per port, but there's still big saving because of original patch (~20MB per port). Signed-off-by: Dhananjay Phadke Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index a75a31005fd..9c78c963b72 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -210,7 +210,7 @@ #define MAX_CMD_DESCRIPTORS_HOST 1024 #define MAX_RCV_DESCRIPTORS_1G 2048 #define MAX_RCV_DESCRIPTORS_10G 4096 -#define MAX_JUMBO_RCV_DESCRIPTORS 512 +#define MAX_JUMBO_RCV_DESCRIPTORS 1024 #define MAX_LRO_RCV_DESCRIPTORS 8 #define MAX_RCVSTATUS_DESCRIPTORS MAX_RCV_DESCRIPTORS #define MAX_JUMBO_RCV_DESC MAX_JUMBO_RCV_DESCRIPTORS -- cgit v1.2.3 From 584dbe9475313e117abf9d2af88164edfd429c9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Thu, 29 Jan 2009 08:55:56 +0000 Subject: netxen: fix memory leak in drivers/net/netxen_nic_init.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For kernel bugzilla #12537: http://bugzilla.kernel.org/show_bug.cgi?id=12537 Free memory. Signed-off-by: Daniel Marjamäki Signed-off-by: David S. Miller --- drivers/net/netxen/netxen_nic_init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index ca7c8d8050c..ffd37bea162 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -947,8 +947,10 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) } for (i = 0; i < n; i++) { if (netxen_rom_fast_read(adapter, 8*i + 4*offset, &val) != 0 || - netxen_rom_fast_read(adapter, 8*i + 4*offset + 4, &addr) != 0) + netxen_rom_fast_read(adapter, 8*i + 4*offset + 4, &addr) != 0) { + kfree(buf); return -EIO; + } buf[i].addr = addr; buf[i].data = val; -- cgit v1.2.3 From 72410af921cbc9018da388ca1ddf75880a033ac1 Mon Sep 17 00:00:00 2001 From: Atsushi SAKAI Date: Fri, 16 Jan 2009 20:39:14 +0900 Subject: lguest: typos fix 3 points lguest_asm.S => i386_head.S LHCALL_BREAK => LHREQ_BREAK perferred => preferred Signed-off-by: Atsushi SAKAI Signed-off-by: Rusty Russell --- drivers/lguest/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/lguest/core.c b/drivers/lguest/core.c index 90663e01a56..60156dfdc60 100644 --- a/drivers/lguest/core.c +++ b/drivers/lguest/core.c @@ -224,7 +224,7 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) break; /* If the Guest asked to be stopped, we sleep. The Guest's - * clock timer or LHCALL_BREAK from the Waker will wake us. */ + * clock timer or LHREQ_BREAK from the Waker will wake us. */ if (cpu->halted) { set_current_state(TASK_INTERRUPTIBLE); schedule(); -- cgit v1.2.3 From 05dfdbbd678ea2b642db73f48b75667a23d15484 Mon Sep 17 00:00:00 2001 From: Mark Wallis Date: Mon, 26 Jan 2009 17:32:35 +1100 Subject: lguest: Fix a memory leak with the lg object during launcher close Fix a memory leak identified by Rusty Russell during LCA09 by kfree'ing the lg object instead of just clearing it when the launcher closes. Signed-off-by: Mark Wallis Signed-off-by: Rusty Russell --- drivers/lguest/lguest_user.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/lguest/lguest_user.c b/drivers/lguest/lguest_user.c index 34bc017b8b3..b8ee103eed5 100644 --- a/drivers/lguest/lguest_user.c +++ b/drivers/lguest/lguest_user.c @@ -307,9 +307,8 @@ static int close(struct inode *inode, struct file *file) * kmalloc()ed string, either of which is ok to hand to kfree(). */ if (!IS_ERR(lg->dead)) kfree(lg->dead); - /* We clear the entire structure, which also marks it as free for the - * next user. */ - memset(lg, 0, sizeof(*lg)); + /* Free the memory allocated to the lguest_struct */ + kfree(lg); /* Release lock and exit. */ mutex_unlock(&lguest_lock); -- cgit v1.2.3 From 1af7ad51049d6a310a19d497960597198290ddfa Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Thu, 29 Jan 2009 17:18:31 -0800 Subject: wimax: fix build issue when debugfs is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As reported by Toralf Förster and Randy Dunlap. - http://linuxwimax.org/pipermail/wimax/2009-January/000460.html - http://lkml.org/lkml/2009/1/29/279 The definitions needed for the wimax stack and i2400m driver debug infrastructure was, by mistake, compiled depending on CONFIG_DEBUG_FS (by them being placed in the debugfs.c files); thus the build broke in 2.6.29-rc3 when debugging was enabled (CONFIG_WIMAX_DEBUG) and DEBUG_FS was disabled. These definitions are always needed if debug is enabled at compile time (independently of DEBUG_FS being or not enabled), so moving them to a file that is always compiled fixes the issue. Signed-off-by: Inaky Perez-Gonzalez Signed-off-by: David S. Miller --- drivers/net/wimax/i2400m/debugfs.c | 14 -------------- drivers/net/wimax/i2400m/driver.c | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wimax/i2400m/debugfs.c b/drivers/net/wimax/i2400m/debugfs.c index 62663298597..9b81af3f80a 100644 --- a/drivers/net/wimax/i2400m/debugfs.c +++ b/drivers/net/wimax/i2400m/debugfs.c @@ -234,20 +234,6 @@ struct dentry *debugfs_create_i2400m_reset( &fops_i2400m_reset); } -/* - * Debug levels control; see debug.h - */ -struct d_level D_LEVEL[] = { - D_SUBMODULE_DEFINE(control), - D_SUBMODULE_DEFINE(driver), - D_SUBMODULE_DEFINE(debugfs), - D_SUBMODULE_DEFINE(fw), - D_SUBMODULE_DEFINE(netdev), - D_SUBMODULE_DEFINE(rfkill), - D_SUBMODULE_DEFINE(rx), - D_SUBMODULE_DEFINE(tx), -}; -size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); #define __debugfs_register(prefix, name, parent) \ do { \ diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 5f98047e18c..e80a0b65a75 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c @@ -707,6 +707,22 @@ void i2400m_release(struct i2400m *i2400m) EXPORT_SYMBOL_GPL(i2400m_release); +/* + * Debug levels control; see debug.h + */ +struct d_level D_LEVEL[] = { + D_SUBMODULE_DEFINE(control), + D_SUBMODULE_DEFINE(driver), + D_SUBMODULE_DEFINE(debugfs), + D_SUBMODULE_DEFINE(fw), + D_SUBMODULE_DEFINE(netdev), + D_SUBMODULE_DEFINE(rfkill), + D_SUBMODULE_DEFINE(rx), + D_SUBMODULE_DEFINE(tx), +}; +size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL); + + static int __init i2400m_driver_init(void) { -- cgit v1.2.3 From b1c4a9dddf09fe99b8f88252718ac5b357363dc4 Mon Sep 17 00:00:00 2001 From: Haiying Wang Date: Thu, 29 Jan 2009 17:28:04 -0800 Subject: ucc_geth: Change uec phy id to the same format as gianfar's The commit b31a1d8b41513b96e9c7ec2f68c5734cef0b26a4 ("gianfar: Convert gianfar to an of_platform_driver") changes the gianfar's phy id to the format like "mdio@xxxx:xx", but uec still uses the old format like "xxxxxxxx:xx". For the board whose UEC uses gianfar-mdio like MPC8568MDS, the phy can not be attached because of the incompatible phy id format. This patch changes uec's phy id to the same format as gianfar's. Signed-off-by: Haiying Wang Signed-off-by: David S. Miller --- drivers/net/ucc_geth.c | 20 ++++++++++++++++++-- drivers/net/ucc_geth.h | 2 ++ drivers/net/ucc_geth_mii.c | 12 +++++++++++- drivers/net/ucc_geth_mii.h | 1 + 4 files changed, 32 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 11441225bf4..e87986867ba 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -1536,6 +1536,11 @@ static void adjust_link(struct net_device *dev) static int init_phy(struct net_device *dev) { struct ucc_geth_private *priv = netdev_priv(dev); + struct device_node *np = priv->node; + struct device_node *phy, *mdio; + const phandle *ph; + char bus_name[MII_BUS_ID_SIZE]; + const unsigned int *id; struct phy_device *phydev; char phy_id[BUS_ID_SIZE]; @@ -1543,8 +1548,18 @@ static int init_phy(struct net_device *dev) priv->oldspeed = 0; priv->oldduplex = -1; - snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, priv->ug_info->mdio_bus, - priv->ug_info->phy_address); + ph = of_get_property(np, "phy-handle", NULL); + phy = of_find_node_by_phandle(*ph); + mdio = of_get_parent(phy); + + id = of_get_property(phy, "reg", NULL); + + of_node_put(phy); + of_node_put(mdio); + + uec_mdio_bus_name(bus_name, mdio); + snprintf(phy_id, sizeof(phy_id), "%s:%02x", + bus_name, *id); phydev = phy_connect(dev, phy_id, &adjust_link, 0, priv->phy_interface); @@ -3748,6 +3763,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma ugeth->ug_info = ug_info; ugeth->dev = dev; + ugeth->node = np; return 0; } diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h index 8f699cb773e..16cbe42ba43 100644 --- a/drivers/net/ucc_geth.h +++ b/drivers/net/ucc_geth.h @@ -1186,6 +1186,8 @@ struct ucc_geth_private { int oldspeed; int oldduplex; int oldlink; + + struct device_node *node; }; void uec_set_ethtool_ops(struct net_device *netdev); diff --git a/drivers/net/ucc_geth_mii.c b/drivers/net/ucc_geth_mii.c index c001d261366..54635911305 100644 --- a/drivers/net/ucc_geth_mii.c +++ b/drivers/net/ucc_geth_mii.c @@ -156,7 +156,7 @@ static int uec_mdio_probe(struct of_device *ofdev, const struct of_device_id *ma if (err) goto reg_map_fail; - snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", res.start); + uec_mdio_bus_name(new_bus->id, np); new_bus->irq = kmalloc(32 * sizeof(int), GFP_KERNEL); @@ -283,3 +283,13 @@ void uec_mdio_exit(void) { of_unregister_platform_driver(&uec_mdio_driver); } + +void uec_mdio_bus_name(char *name, struct device_node *np) +{ + const u32 *reg; + + reg = of_get_property(np, "reg", NULL); + + snprintf(name, MII_BUS_ID_SIZE, "%s@%x", np->name, reg ? *reg : 0); +} + diff --git a/drivers/net/ucc_geth_mii.h b/drivers/net/ucc_geth_mii.h index 1e45b2028a5..840cf80235b 100644 --- a/drivers/net/ucc_geth_mii.h +++ b/drivers/net/ucc_geth_mii.h @@ -97,4 +97,5 @@ int uec_mdio_read(struct mii_bus *bus, int mii_id, int regnum); int uec_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value); int __init uec_mdio_init(void); void uec_mdio_exit(void); +void uec_mdio_bus_name(char *name, struct device_node *np); #endif /* __UEC_MII_H */ -- cgit v1.2.3 From 1609559547ae0ddc2e4829c7f78ac2c4869875b9 Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Thu, 29 Jan 2009 17:29:15 -0800 Subject: smsc9420: fix interrupt signalling test failures smsc9420 performs an interrupt signalling test when the interface is brought up. The current code mistakenly sets its test flag to false AFTER enabling the software interrupt source, making failure quite likely. This patch changes the code to set the test flag BEFORE enabling interrupts. I've also removed an smp_wmb because the following spinlock provides an implicit memory barrier. Signed-off-by: Steve Glendinning Signed-off-by: David S. Miller --- drivers/net/smsc9420.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/smsc9420.c b/drivers/net/smsc9420.c index c14a4c6452c..d801900a503 100644 --- a/drivers/net/smsc9420.c +++ b/drivers/net/smsc9420.c @@ -1378,6 +1378,7 @@ static int smsc9420_open(struct net_device *dev) /* test the IRQ connection to the ISR */ smsc_dbg(IFUP, "Testing ISR using IRQ %d", dev->irq); + pd->software_irq_signal = false; spin_lock_irqsave(&pd->int_lock, flags); /* configure interrupt deassertion timer and enable interrupts */ @@ -1393,8 +1394,6 @@ static int smsc9420_open(struct net_device *dev) smsc9420_pci_flush_write(pd); timeout = 1000; - pd->software_irq_signal = false; - smp_wmb(); while (timeout--) { if (pd->software_irq_signal) break; -- cgit v1.2.3 From f307dbd88d82c4ccab7aec49613c366023b89cde Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 29 Jan 2009 17:30:00 -0800 Subject: smsc911x: timeout reaches -1 With a postfix decrement the timeout will reach -1 rather than 0, so the warning will not be issued. Signed-off-by: Roel Kluin Acked-by: Steve Glendinning Signed-off-by: David S. Miller --- drivers/net/smsc911x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/smsc911x.c b/drivers/net/smsc911x.c index f513bdf1c88..783c1a7b869 100644 --- a/drivers/net/smsc911x.c +++ b/drivers/net/smsc911x.c @@ -953,7 +953,7 @@ smsc911x_rx_fastforward(struct smsc911x_data *pdata, unsigned int pktbytes) do { udelay(1); val = smsc911x_reg_read(pdata, RX_DP_CTRL); - } while (timeout-- && (val & RX_DP_CTRL_RX_FFWD_)); + } while (--timeout && (val & RX_DP_CTRL_RX_FFWD_)); if (unlikely(timeout == 0)) SMSC_WARNING(HW, "Timed out waiting for " -- cgit v1.2.3 From e5664bb2a7fd8ae1bee1281c2e44653c471af9ca Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Thu, 29 Jan 2009 17:31:13 -0800 Subject: gianfar: Fix Wake-on-LAN support commit 0f0ca340e57bd7446855fefd07a64249acf81223 ("phy: power management support") caused a regression in the gianfar driver. Now phylib turns off PHY power during suspend, and thus WOL doesn't work anymore. This patch workarounds the issue by enabling wakeup in the MDIO device, i.e. just restores the old behaviour for the gianfar driver. Note that this way all PHYs on a given MDIO bus won't be turned off during suspend, which isn't good from the power saving point of view. A proper, per netdevice wakeup management support will need a bit reworked phylib suspend/resume logic. Signed-off-by: Anton Vorontsov Signed-off-by: David S. Miller --- drivers/net/gianfar_mii.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c index f3706e415b4..f49a426ad68 100644 --- a/drivers/net/gianfar_mii.c +++ b/drivers/net/gianfar_mii.c @@ -234,6 +234,8 @@ static int gfar_mdio_probe(struct of_device *ofdev, if (NULL == new_bus) return -ENOMEM; + device_init_wakeup(&ofdev->dev, 1); + new_bus->name = "Gianfar MII Bus", new_bus->read = &gfar_mdio_read, new_bus->write = &gfar_mdio_write, -- cgit v1.2.3 From c25b9abbc2c2c0da88e180c3933d6e773245815a Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 29 Jan 2009 17:32:20 -0800 Subject: drivers/net/skfp: if !capable(CAP_NET_ADMIN): inverted logic Fix inverted logic Signed-off-by: Roel Kluin Signed-off-by: David S. Miller --- drivers/net/skfp/skfddi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index 607efeaf0bc..9a00e5566af 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -1003,9 +1003,9 @@ static int skfp_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; case SKFP_CLR_STATS: /* Zero out the driver statistics */ if (!capable(CAP_NET_ADMIN)) { - memset(&lp->MacStat, 0, sizeof(lp->MacStat)); - } else { status = -EPERM; + } else { + memset(&lp->MacStat, 0, sizeof(lp->MacStat)); } break; default: -- cgit v1.2.3 From f99ec0649accb581cf3e8fcfeea796e82d05f4ea Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Thu, 29 Jan 2009 17:35:04 -0800 Subject: tulip: fix 21142 with 10Mbps without negotiation with current kernels, tulip 21142 ethernet controllers fail to connect to a 10Mbps only (i.e. without negotiation-partner) network. It used to work in 2.4 kernels. Fix that. Tested on a 21142 Rev 0x11. Signed-off-by: Philippe De Muyter Signed-off-by: David S. Miller --- drivers/net/tulip/21142.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tulip/21142.c b/drivers/net/tulip/21142.c index 1210fb3748a..db7d5e11855 100644 --- a/drivers/net/tulip/21142.c +++ b/drivers/net/tulip/21142.c @@ -9,6 +9,11 @@ Please refer to Documentation/DocBook/tulip-user.{pdf,ps,html} for more information on this driver. + + DC21143 manual "21143 PCI/CardBus 10/100Mb/s Ethernet LAN Controller + Hardware Reference Manual" is currently available at : + http://developer.intel.com/design/network/manuals/278074.htm + Please submit bugs to http://bugzilla.kernel.org/ . */ @@ -32,7 +37,11 @@ void t21142_media_task(struct work_struct *work) int csr12 = ioread32(ioaddr + CSR12); int next_tick = 60*HZ; int new_csr6 = 0; + int csr14 = ioread32(ioaddr + CSR14); + /* CSR12[LS10,LS100] are not reliable during autonegotiation */ + if ((csr14 & 0x80) && (csr12 & 0x7000) != 0x5000) + csr12 |= 6; if (tulip_debug > 2) printk(KERN_INFO"%s: 21143 negotiation status %8.8x, %s.\n", dev->name, csr12, medianame[dev->if_port]); @@ -76,7 +85,7 @@ void t21142_media_task(struct work_struct *work) new_csr6 = 0x83860000; dev->if_port = 3; iowrite32(0, ioaddr + CSR13); - iowrite32(0x0003FF7F, ioaddr + CSR14); + iowrite32(0x0003FFFF, ioaddr + CSR14); iowrite16(8, ioaddr + CSR15); iowrite32(1, ioaddr + CSR13); } @@ -132,10 +141,14 @@ void t21142_lnk_change(struct net_device *dev, int csr5) struct tulip_private *tp = netdev_priv(dev); void __iomem *ioaddr = tp->base_addr; int csr12 = ioread32(ioaddr + CSR12); + int csr14 = ioread32(ioaddr + CSR14); + /* CSR12[LS10,LS100] are not reliable during autonegotiation */ + if ((csr14 & 0x80) && (csr12 & 0x7000) != 0x5000) + csr12 |= 6; if (tulip_debug > 1) printk(KERN_INFO"%s: 21143 link status interrupt %8.8x, CSR5 %x, " - "%8.8x.\n", dev->name, csr12, csr5, ioread32(ioaddr + CSR14)); + "%8.8x.\n", dev->name, csr12, csr5, csr14); /* If NWay finished and we have a negotiated partner capability. */ if (tp->nway && !tp->nwayset && (csr12 & 0x7000) == 0x5000) { @@ -143,7 +156,9 @@ void t21142_lnk_change(struct net_device *dev, int csr5) int negotiated = tp->sym_advertise & (csr12 >> 16); tp->lpar = csr12 >> 16; tp->nwayset = 1; - if (negotiated & 0x0100) dev->if_port = 5; + /* If partner cannot negotiate, it is 10Mbps Half Duplex */ + if (!(csr12 & 0x8000)) dev->if_port = 0; + else if (negotiated & 0x0100) dev->if_port = 5; else if (negotiated & 0x0080) dev->if_port = 3; else if (negotiated & 0x0040) dev->if_port = 4; else if (negotiated & 0x0020) dev->if_port = 0; @@ -214,7 +229,7 @@ void t21142_lnk_change(struct net_device *dev, int csr5) tp->timer.expires = RUN_AT(3*HZ); add_timer(&tp->timer); } else if (dev->if_port == 5) - iowrite32(ioread32(ioaddr + CSR14) & ~0x080, ioaddr + CSR14); + iowrite32(csr14 & ~0x080, ioaddr + CSR14); } else if (dev->if_port == 0 || dev->if_port == 4) { if ((csr12 & 4) == 0) printk(KERN_INFO"%s: 21143 10baseT link beat good.\n", -- cgit v1.2.3 From 69b3bb65fa97a1e8563518dbbc35cd57beefb2d4 Mon Sep 17 00:00:00 2001 From: Robin Holt Date: Thu, 29 Jan 2009 14:25:06 -0800 Subject: sgi-xpc: ensure flags are updated before bte_copy The clearing of the msg->flags needs a barrier between it and the notify of the channel threads that the messages are cleaned and ready for use. Signed-off-by: Robin Holt Signed-off-by: Dean Nelson Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/misc/sgi-xp/xpc_sn2.c | 9 +++++---- drivers/misc/sgi-xp/xpc_uv.c | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sgi-xp/xpc_sn2.c b/drivers/misc/sgi-xp/xpc_sn2.c index 82fb9958f22..7aafb8a9194 100644 --- a/drivers/misc/sgi-xp/xpc_sn2.c +++ b/drivers/misc/sgi-xp/xpc_sn2.c @@ -1836,6 +1836,7 @@ xpc_process_msg_chctl_flags_sn2(struct xpc_partition *part, int ch_number) */ xpc_clear_remote_msgqueue_flags_sn2(ch); + smp_wmb(); /* ensure flags have been cleared before bte_copy */ ch_sn2->w_remote_GP.put = ch_sn2->remote_GP.put; dev_dbg(xpc_chan, "w_remote_GP.put changed to %ld, partid=%d, " @@ -1934,7 +1935,7 @@ xpc_get_deliverable_payload_sn2(struct xpc_channel *ch) break; get = ch_sn2->w_local_GP.get; - rmb(); /* guarantee that .get loads before .put */ + smp_rmb(); /* guarantee that .get loads before .put */ if (get == ch_sn2->w_remote_GP.put) break; @@ -2053,7 +2054,7 @@ xpc_allocate_msg_sn2(struct xpc_channel *ch, u32 flags, while (1) { put = ch_sn2->w_local_GP.put; - rmb(); /* guarantee that .put loads before .get */ + smp_rmb(); /* guarantee that .put loads before .get */ if (put - ch_sn2->w_remote_GP.get < ch->local_nentries) { /* There are available message entries. We need to try @@ -2186,7 +2187,7 @@ xpc_send_payload_sn2(struct xpc_channel *ch, u32 flags, void *payload, * The preceding store of msg->flags must occur before the following * load of local_GP->put. */ - mb(); + smp_mb(); /* see if the message is next in line to be sent, if so send it */ @@ -2287,7 +2288,7 @@ xpc_received_payload_sn2(struct xpc_channel *ch, void *payload) * The preceding store of msg->flags must occur before the following * load of local_GP->get. */ - mb(); + smp_mb(); /* * See if this message is next in line to be acknowledged as having diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index 91a55b1b103..f17f7d40ea2 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c @@ -1423,7 +1423,7 @@ xpc_send_payload_uv(struct xpc_channel *ch, u32 flags, void *payload, atomic_inc(&ch->n_to_notify); msg_slot->key = key; - wmb(); /* a non-NULL func must hit memory after the key */ + smp_wmb(); /* a non-NULL func must hit memory after the key */ msg_slot->func = func; if (ch->flags & XPC_C_DISCONNECTING) { -- cgit v1.2.3 From 17e2161654da4e6bdfd8d53d4f52e820ee93f423 Mon Sep 17 00:00:00 2001 From: Robin Holt Date: Thu, 29 Jan 2009 14:25:07 -0800 Subject: sgi-xpc: Remove NULL pointer dereference. If the bte copy fails, the attempt to retrieve payloads merely returns a null pointer deref and not NULL as was expected. Signed-off-by: Robin Holt Signed-off-by: Dean Nelson Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/misc/sgi-xp/xpc_sn2.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sgi-xp/xpc_sn2.c b/drivers/misc/sgi-xp/xpc_sn2.c index 7aafb8a9194..7d05fb5f4fe 100644 --- a/drivers/misc/sgi-xp/xpc_sn2.c +++ b/drivers/misc/sgi-xp/xpc_sn2.c @@ -1957,11 +1957,13 @@ xpc_get_deliverable_payload_sn2(struct xpc_channel *ch) msg = xpc_pull_remote_msg_sn2(ch, get); - DBUG_ON(msg != NULL && msg->number != get); - DBUG_ON(msg != NULL && (msg->flags & XPC_M_SN2_DONE)); - DBUG_ON(msg != NULL && !(msg->flags & XPC_M_SN2_READY)); + if (msg != NULL) { + DBUG_ON(msg->number != get); + DBUG_ON(msg->flags & XPC_M_SN2_DONE); + DBUG_ON(!(msg->flags & XPC_M_SN2_READY)); - payload = &msg->payload; + payload = &msg->payload; + } break; } -- cgit v1.2.3 From 252523ef2421b803de4810876223e4d695f23ec6 Mon Sep 17 00:00:00 2001 From: Robin Holt Date: Thu, 29 Jan 2009 14:25:07 -0800 Subject: sgi-xpc: fix up stale DBUG_ON statements Clean up the stale DBUG_ON checks and add a couple new ones. Signed-off-by: Robin Holt Signed-off-by: Dean Nelson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/misc/sgi-xp/xpc_channel.c | 3 --- drivers/misc/sgi-xp/xpc_sn2.c | 15 +++++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sgi-xp/xpc_channel.c b/drivers/misc/sgi-xp/xpc_channel.c index 9cd2ebe2a3b..45fd653dbe3 100644 --- a/drivers/misc/sgi-xp/xpc_channel.c +++ b/drivers/misc/sgi-xp/xpc_channel.c @@ -49,9 +49,6 @@ xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags) if (ch->flags & (XPC_C_CONNECTED | XPC_C_DISCONNECTING)) return; - - DBUG_ON(ch->local_msgqueue == NULL); - DBUG_ON(ch->remote_msgqueue == NULL); } if (!(ch->flags & XPC_C_OPENREPLY)) { diff --git a/drivers/misc/sgi-xp/xpc_sn2.c b/drivers/misc/sgi-xp/xpc_sn2.c index 7d05fb5f4fe..2e975762c32 100644 --- a/drivers/misc/sgi-xp/xpc_sn2.c +++ b/drivers/misc/sgi-xp/xpc_sn2.c @@ -1106,8 +1106,6 @@ xpc_process_activate_IRQ_rcvd_sn2(void) int n_IRQs_expected; int n_IRQs_detected; - DBUG_ON(xpc_activate_IRQ_rcvd == 0); - spin_lock_irqsave(&xpc_activate_IRQ_rcvd_lock, irq_flags); n_IRQs_expected = xpc_activate_IRQ_rcvd; xpc_activate_IRQ_rcvd = 0; @@ -1726,6 +1724,7 @@ xpc_clear_local_msgqueue_flags_sn2(struct xpc_channel *ch) msg = (struct xpc_msg_sn2 *)((u64)ch_sn2->local_msgqueue + (get % ch->local_nentries) * ch->entry_size); + DBUG_ON(!(msg->flags & XPC_M_SN2_READY)); msg->flags = 0; } while (++get < ch_sn2->remote_GP.get); } @@ -1740,11 +1739,18 @@ xpc_clear_remote_msgqueue_flags_sn2(struct xpc_channel *ch) struct xpc_msg_sn2 *msg; s64 put; - put = ch_sn2->w_remote_GP.put; + /* flags are zeroed when the buffer is allocated */ + if (ch_sn2->remote_GP.put < ch->remote_nentries) + return; + + put = max(ch_sn2->w_remote_GP.put, ch->remote_nentries); do { msg = (struct xpc_msg_sn2 *)((u64)ch_sn2->remote_msgqueue + (put % ch->remote_nentries) * ch->entry_size); + DBUG_ON(!(msg->flags & XPC_M_SN2_READY)); + DBUG_ON(!(msg->flags & XPC_M_SN2_DONE)); + DBUG_ON(msg->number != put - ch->remote_nentries); msg->flags = 0; } while (++put < ch_sn2->remote_GP.put); } @@ -2280,8 +2286,9 @@ xpc_received_payload_sn2(struct xpc_channel *ch, void *payload) dev_dbg(xpc_chan, "msg=0x%p, msg_number=%ld, partid=%d, channel=%d\n", (void *)msg, msg_number, ch->partid, ch->number); - DBUG_ON((((u64)msg - (u64)ch->remote_msgqueue) / ch->entry_size) != + DBUG_ON((((u64)msg - (u64)ch->sn.sn2.remote_msgqueue) / ch->entry_size) != msg_number % ch->remote_nentries); + DBUG_ON(!(msg->flags & XPC_M_SN2_READY)); DBUG_ON(msg->flags & XPC_M_SN2_DONE); msg->flags |= XPC_M_SN2_DONE; -- cgit v1.2.3 From 7460db567bbca76bf087d1694d792a1a96bdaa26 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Thu, 29 Jan 2009 14:25:12 -0800 Subject: gpiolib: fix request related issue Fix request-already-requested handling in gpio_request(). Signed-off-by: Magnus Damm Acked-by: David Brownell Cc: [2.6.28.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpio/gpiolib.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 35e7aea4222..42fb2fd24c0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -789,6 +789,7 @@ int gpio_request(unsigned gpio, const char *label) } else { status = -EBUSY; module_put(chip->owner); + goto done; } if (chip->request) { -- cgit v1.2.3 From 6989d5651a16b49908069b514329d5114217ea0e Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Thu, 29 Jan 2009 14:25:14 -0800 Subject: hp-wmi: fix regressions caused by missing if statement Error was introduced in commit fe8e4e039dc3 ("hp-wmi: handle rfkill_register() failure"). Signed-off-by: Frans Pop Acked-by: Larry Finger Acked-by: Matthew Garrett Cc: Matthew Garrett Cc: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/platform/x86/hp-wmi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 7c789f0a94d..626042066be 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -441,6 +441,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) bluetooth_rfkill->toggle_radio = hp_wmi_bluetooth_set; bluetooth_rfkill->user_claim_unsupported = 1; err = rfkill_register(bluetooth_rfkill); + if (err) goto register_bluetooth_error; } -- cgit v1.2.3 From 248ae0d43fe7f951352eedfff36572d4b75ce963 Mon Sep 17 00:00:00 2001 From: Alex Buell Date: Thu, 29 Jan 2009 14:25:16 -0800 Subject: fbdev: incorrect URL given in drivers/video/Kconfig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/video/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index c94f71980c1..f0267706cb4 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -41,7 +41,7 @@ menuconfig FB You need an utility program called fbset to make full use of frame buffer devices. Please read and the Framebuffer-HOWTO at - for more + for more information. Say Y here and to the driver for your graphics board below if you -- cgit v1.2.3 From c189f4ec955e1fe4a2a2742d028aeecc52a5848a Mon Sep 17 00:00:00 2001 From: David Altobelli Date: Thu, 29 Jan 2009 14:25:23 -0800 Subject: hpilo: increment version Bump hpilo module version to indicate that the open/close bug is fixed. Signed-off-by: David Altobelli Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/misc/hpilo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 05e29828923..10c421b73ea 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -758,7 +758,7 @@ static void __exit ilo_exit(void) class_destroy(ilo_class); } -MODULE_VERSION("0.05"); +MODULE_VERSION("0.06"); MODULE_ALIAS(ILO_NAME); MODULE_DESCRIPTION(ILO_NAME); MODULE_AUTHOR("David Altobelli "); -- cgit v1.2.3 From fb9f88e1dc76f9feb39d39c40a5d61aad6df4388 Mon Sep 17 00:00:00 2001 From: Bharath Ramesh Date: Thu, 29 Jan 2009 14:25:24 -0800 Subject: hwmon: applesmc: add support for MacPro 3 temperature sensors MacPro 3 have more temperature sensors than the previous MacPro's also the sensor THTG has been removed. This patch add supports for the newer temperature sensors in the MacPro3. Signed-off-by: Bharath Ramesh Signed-off-by: Henrik Rydberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index e3018623658..678e34b01e5 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -83,7 +83,7 @@ /* * Temperature sensors keys (sp78 - 2 bytes). */ -static const char* temperature_sensors_sets[][36] = { +static const char *temperature_sensors_sets[][41] = { /* Set 0: Macbook Pro */ { "TA0P", "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "Th0H", "Th1H", "Tm0P", "Ts0P", "Ts1P", NULL }, @@ -135,6 +135,13 @@ static const char* temperature_sensors_sets[][36] = { { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TN0D", "TTF0", "TV0P", "TVFP", "TW0P", "Th0P", "Tp0P", "Tp1P", "TpFP", "Ts0P", "Ts0S", NULL }, +/* Set 16: Mac Pro 3,1 (2 x Quad-Core) */ + { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", "TC0C", "TC0D", "TC0P", + "TC1C", "TC1D", "TC2C", "TC2D", "TC3C", "TC3D", "TH0P", "TH1P", + "TH2P", "TH3P", "TMAP", "TMAS", "TMBS", "TM0P", "TM0S", "TM1P", + "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", "TM8S", "TM9P", "TM9S", + "TN0C", "TN0D", "TN0H", "TS0C", "Tp0C", "Tp1C", "Tv0S", "Tv1S", + NULL }, }; /* List of keys used to read/write fan speeds */ @@ -1153,6 +1160,16 @@ static SENSOR_DEVICE_ATTR(temp34_input, S_IRUGO, applesmc_show_temperature, NULL, 33); static SENSOR_DEVICE_ATTR(temp35_input, S_IRUGO, applesmc_show_temperature, NULL, 34); +static SENSOR_DEVICE_ATTR(temp36_input, S_IRUGO, + applesmc_show_temperature, NULL, 35); +static SENSOR_DEVICE_ATTR(temp37_input, S_IRUGO, + applesmc_show_temperature, NULL, 36); +static SENSOR_DEVICE_ATTR(temp38_input, S_IRUGO, + applesmc_show_temperature, NULL, 37); +static SENSOR_DEVICE_ATTR(temp39_input, S_IRUGO, + applesmc_show_temperature, NULL, 38); +static SENSOR_DEVICE_ATTR(temp40_input, S_IRUGO, + applesmc_show_temperature, NULL, 39); static struct attribute *temperature_attributes[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, @@ -1190,6 +1207,11 @@ static struct attribute *temperature_attributes[] = { &sensor_dev_attr_temp33_input.dev_attr.attr, &sensor_dev_attr_temp34_input.dev_attr.attr, &sensor_dev_attr_temp35_input.dev_attr.attr, + &sensor_dev_attr_temp36_input.dev_attr.attr, + &sensor_dev_attr_temp37_input.dev_attr.attr, + &sensor_dev_attr_temp38_input.dev_attr.attr, + &sensor_dev_attr_temp39_input.dev_attr.attr, + &sensor_dev_attr_temp40_input.dev_attr.attr, NULL }; @@ -1312,6 +1334,8 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 0, .light = 0, .temperature_set = 14 }, /* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */ { .accelerometer = 1, .light = 1, .temperature_set = 15 }, +/* MacPro3,1: temperature set 16 */ + { .accelerometer = 0, .light = 0, .temperature_set = 16 }, }; /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". @@ -1369,6 +1393,10 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacPro2") }, &applesmc_dmi_data[4]}, + { applesmc_dmi_match, "Apple MacPro3", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacPro3") }, + &applesmc_dmi_data[16]}, { applesmc_dmi_match, "Apple MacPro", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacPro") }, -- cgit v1.2.3 From 3095eb87bb36ae880608fe3fc46cfd59ced1f319 Mon Sep 17 00:00:00 2001 From: Frans Pop Date: Thu, 29 Jan 2009 14:25:25 -0800 Subject: hp-wmi: set initial docking state If the initial state is not set when the input device is set up, the first docking event after the module is loaded will be lost. Signed-off-by: Frans Pop Acked-by: Matthew Garrett Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/platform/x86/hp-wmi.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 626042066be..de91ddab0a8 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -382,6 +382,11 @@ static int __init hp_wmi_input_setup(void) case KE_SW: set_bit(EV_SW, hp_wmi_input_dev->evbit); set_bit(key->keycode, hp_wmi_input_dev->swbit); + + /* Set initial dock state */ + input_report_switch(hp_wmi_input_dev, key->keycode, + hp_wmi_dock_state()); + input_sync(hp_wmi_input_dev); break; } } -- cgit v1.2.3 From 726a6699267e36c66043a55b13dfeec3d9925452 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 29 Jan 2009 14:25:27 -0800 Subject: drivers/gpu/drm/i915/intel_lvds.c: fix locking snafu s/unlock/lock/ Addresses http://bugzilla.kernel.org/show_bug.cgi?id=12575 Reported-by: Daniel Vetter Cc: Dave Airlie Acked-by: Jesse Barnes Cc: Eric Anholt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/gpu/drm/i915/intel_lvds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 6b1148fc2cb..b36a5214d8d 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -311,7 +311,7 @@ static int intel_lvds_get_modes(struct drm_connector *connector) if (dev_priv->panel_fixed_mode != NULL) { struct drm_display_mode *mode; - mutex_unlock(&dev->mode_config.mutex); + mutex_lock(&dev->mode_config.mutex); mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); drm_mode_probed_add(connector, mode); mutex_unlock(&dev->mode_config.mutex); -- cgit v1.2.3 From 5872fb94f85d2e4fdef94657bd14e1a492df9825 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 29 Jan 2009 16:28:02 -0800 Subject: Documentation: move DMA-mapping.txt to Doc/PCI/ Move DMA-mapping.txt to Documentation/PCI/. DMA-mapping.txt was supposed to be moved from Documentation/ to Documentation/PCI/. The 00-INDEX files in those two directories were updated, along with a few other text files, but the file itself somehow escaped being moved, so move it and update more text files and source files with its new location. Signed-off-by: Randy Dunlap Acked-by: Greg Kroah-Hartman cc: Jesse Barnes Signed-off-by: Linus Torvalds --- drivers/parisc/sba_iommu.c | 18 +++++++++--------- drivers/staging/altpciechdma/altpciechdma.c | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 3fac8f81d59..a70cf16ee1a 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -668,7 +668,7 @@ sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) * @dev: instance of PCI owned by the driver that's asking * @mask: number of address bits this PCI device can handle * - * See Documentation/DMA-mapping.txt + * See Documentation/PCI/PCI-DMA-mapping.txt */ static int sba_dma_supported( struct device *dev, u64 mask) { @@ -680,8 +680,8 @@ static int sba_dma_supported( struct device *dev, u64 mask) return(0); } - /* Documentation/DMA-mapping.txt tells drivers to try 64-bit first, - * then fall back to 32-bit if that fails. + /* Documentation/PCI/PCI-DMA-mapping.txt tells drivers to try 64-bit + * first, then fall back to 32-bit if that fails. * We are just "encouraging" 32-bit DMA masks here since we can * never allow IOMMU bypass unless we add special support for ZX1. */ @@ -706,7 +706,7 @@ static int sba_dma_supported( struct device *dev, u64 mask) * @size: number of bytes to map in driver buffer. * @direction: R/W or both. * - * See Documentation/DMA-mapping.txt + * See Documentation/PCI/PCI-DMA-mapping.txt */ static dma_addr_t sba_map_single(struct device *dev, void *addr, size_t size, @@ -785,7 +785,7 @@ sba_map_single(struct device *dev, void *addr, size_t size, * @size: number of bytes mapped in driver buffer. * @direction: R/W or both. * - * See Documentation/DMA-mapping.txt + * See Documentation/PCI/PCI-DMA-mapping.txt */ static void sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size, @@ -861,7 +861,7 @@ sba_unmap_single(struct device *dev, dma_addr_t iova, size_t size, * @size: number of bytes mapped in driver buffer. * @dma_handle: IOVA of new buffer. * - * See Documentation/DMA-mapping.txt + * See Documentation/PCI/PCI-DMA-mapping.txt */ static void *sba_alloc_consistent(struct device *hwdev, size_t size, dma_addr_t *dma_handle, gfp_t gfp) @@ -892,7 +892,7 @@ static void *sba_alloc_consistent(struct device *hwdev, size_t size, * @vaddr: virtual address IOVA of "consistent" buffer. * @dma_handler: IO virtual address of "consistent" buffer. * - * See Documentation/DMA-mapping.txt + * See Documentation/PCI/PCI-DMA-mapping.txt */ static void sba_free_consistent(struct device *hwdev, size_t size, void *vaddr, @@ -927,7 +927,7 @@ int dump_run_sg = 0; * @nents: number of entries in list * @direction: R/W or both. * - * See Documentation/DMA-mapping.txt + * See Documentation/PCI/PCI-DMA-mapping.txt */ static int sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents, @@ -1011,7 +1011,7 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents, * @nents: number of entries in list * @direction: R/W or both. * - * See Documentation/DMA-mapping.txt + * See Documentation/PCI/PCI-DMA-mapping.txt */ static void sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, diff --git a/drivers/staging/altpciechdma/altpciechdma.c b/drivers/staging/altpciechdma/altpciechdma.c index 8e2b4ca0651..f516140ca97 100644 --- a/drivers/staging/altpciechdma/altpciechdma.c +++ b/drivers/staging/altpciechdma/altpciechdma.c @@ -531,7 +531,7 @@ static int __devinit dma_test(struct ape_dev *ape, struct pci_dev *dev) goto fail; /* allocate and map coherently-cached memory for a DMA-able buffer */ - /* @see 2.6.26.2/Documentation/DMA-mapping.txt line 318 */ + /* @see Documentation/PCI/PCI-DMA-mapping.txt, near line 318 */ buffer_virt = (u8 *)pci_alloc_consistent(dev, PAGE_SIZE * 4, &buffer_bus); if (!buffer_virt) { printk(KERN_DEBUG "Could not allocate coherent DMA buffer.\n"); @@ -846,7 +846,7 @@ static int __devinit probe(struct pci_dev *dev, const struct pci_device_id *id) #if 1 // @todo For now, disable 64-bit, because I do not understand the implications (DAC!) /* query for DMA transfer */ - /* @see Documentation/DMA-mapping.txt */ + /* @see Documentation/PCI/PCI-DMA-mapping.txt */ if (!pci_set_dma_mask(dev, DMA_64BIT_MASK)) { pci_set_consistent_dma_mask(dev, DMA_64BIT_MASK); /* use 64-bit DMA */ -- cgit v1.2.3 From 1737ef7598d3515fdc11cb9ba7e054f334404e04 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Thu, 29 Jan 2009 02:30:56 +0300 Subject: sata_sil: Fix build breakage Commit e57db7b (SATA Sil: Blacklist system that spins off disks during ACPI power off) breaks build like the following, in both cases when CONFIG_DMI set or not. drivers/ata/sata_sil.c: In function 'sil_broken_system_poweroff': drivers/ata/sata_sil.c:713: error: implicit declaration of function 'dmi_first_match' drivers/ata/sata_sil.c:713: warning: initialization makes pointer from integer without a cast sata_sil.c should include dmi.h Signed-off-by: Alexander Beregalov Signed-off-by: Linus Torvalds --- drivers/ata/sata_sil.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index bfd55b085ae..9f029595f45 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -44,6 +44,7 @@ #include #include #include +#include #define DRV_NAME "sata_sil" #define DRV_VERSION "2.4" -- cgit v1.2.3 From 0461ec5bc7745b89a8ab67ba0ea497abd58a6301 Mon Sep 17 00:00:00 2001 From: Paul Larson Date: Fri, 30 Jan 2009 10:21:49 -0600 Subject: Add enable_ms to jsm driver This fixes a crash observed when non-existant enable_ms function is called for jsm driver. Signed-off-by: Scott Kilau Signed-off-by: Paul Larson Signed-off-by: Linus Torvalds --- drivers/serial/jsm/jsm_tty.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index 3547558d2ca..324c74d2f66 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -161,6 +161,11 @@ static void jsm_tty_stop_rx(struct uart_port *port) channel->ch_bd->bd_ops->disable_receiver(channel); } +static void jsm_tty_enable_ms(struct uart_port *port) +{ + /* Nothing needed */ +} + static void jsm_tty_break(struct uart_port *port, int break_state) { unsigned long lock_flags; @@ -345,6 +350,7 @@ static struct uart_ops jsm_ops = { .start_tx = jsm_tty_start_tx, .send_xchar = jsm_tty_send_xchar, .stop_rx = jsm_tty_stop_rx, + .enable_ms = jsm_tty_enable_ms, .break_ctl = jsm_tty_break, .startup = jsm_tty_open, .shutdown = jsm_tty_close, -- cgit v1.2.3 From 9bf503e6bec3f2d28298808454eebde031ab5b5b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Sun, 18 Jan 2009 14:32:27 +0100 Subject: regulator: move bq24022 init back to module_init instead of subsys_initcall This workaround was needed when regulator/ was not linked before both power/ and usb/otg/ in drivers/Makefile. Now that it is even linked before mfd/, this patch makes sure that bq24022 isn't probed before the GPIO expander is set up. Signed-off-by: Philipp Zabel Signed-off-by: Liam Girdwood --- drivers/regulator/bq24022.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c index 366565aba86..c175e38a4cd 100644 --- a/drivers/regulator/bq24022.c +++ b/drivers/regulator/bq24022.c @@ -152,11 +152,7 @@ static void __exit bq24022_exit(void) platform_driver_unregister(&bq24022_driver); } -/* - * make sure this is probed before gpio_vbus and pda_power, - * but after asic3 or other GPIO expander drivers. - */ -subsys_initcall(bq24022_init); +module_init(bq24022_init); module_exit(bq24022_exit); MODULE_AUTHOR("Philipp Zabel"); -- cgit v1.2.3 From 8dd2c9e3128a5784a01084b52d5bb7efd4371ac6 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sat, 17 Jan 2009 16:06:40 +0100 Subject: leds: Fix bounds checking of wm8350->pmic.led Fix bounds checking of wm8350->pmic.led Signed-off-by: Roel Kluin Signed-off-by: Liam Girdwood --- drivers/regulator/wm8350-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 7aa35248181..5056e23e441 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1435,7 +1435,7 @@ int wm8350_register_led(struct wm8350 *wm8350, int lednum, int dcdc, int isink, struct platform_device *pdev; int ret; - if (lednum > ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) { + if (lednum >= ARRAY_SIZE(wm8350->pmic.led) || lednum < 0) { dev_err(wm8350->dev, "Invalid LED index %d\n", lednum); return -ENODEV; } -- cgit v1.2.3 From a11da890e4c9850411303efcf6514f048ca880ee Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Fri, 30 Jan 2009 13:45:31 -0800 Subject: sky2: fix hard hang with netconsoling and iface going up Printing anything over netconsole before hw is up and running is, of course, not going to work. Signed-off-by: Alexey Dobriyan Acked-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/sky2.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 3668e81e474..994703cc0db 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1403,9 +1403,6 @@ static int sky2_up(struct net_device *dev) } - if (netif_msg_ifup(sky2)) - printk(KERN_INFO PFX "%s: enabling interface\n", dev->name); - netif_carrier_off(dev); /* must be power of 2 */ @@ -1484,6 +1481,9 @@ static int sky2_up(struct net_device *dev) sky2_write32(hw, B0_IMSK, imask); sky2_set_multicast(dev); + + if (netif_msg_ifup(sky2)) + printk(KERN_INFO PFX "%s: enabling interface\n", dev->name); return 0; err_out: -- cgit v1.2.3 From 869b5b3888fbd2024af632e3648c00860ba3cca6 Mon Sep 17 00:00:00 2001 From: Steve Hodgson Date: Thu, 29 Jan 2009 17:48:10 +0000 Subject: sfc: SFT9001: Enable robust link training Enable a firmware option that appears to be necessary for reliable operation. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/tenxpress.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index 9ecb77da954..19cfc318954 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -67,6 +67,8 @@ #define PMA_PMD_EXT_CLK312_WIDTH 1 #define PMA_PMD_EXT_LPOWER_LBN 12 #define PMA_PMD_EXT_LPOWER_WIDTH 1 +#define PMA_PMD_EXT_ROBUST_LBN 14 +#define PMA_PMD_EXT_ROBUST_WIDTH 1 #define PMA_PMD_EXT_SSR_LBN 15 #define PMA_PMD_EXT_SSR_WIDTH 1 @@ -284,7 +286,9 @@ static int tenxpress_init(struct efx_nic *efx) PMA_PMD_XCONTROL_REG); reg |= ((1 << PMA_PMD_EXT_GMII_EN_LBN) | (1 << PMA_PMD_EXT_CLK_OUT_LBN) | - (1 << PMA_PMD_EXT_CLK312_LBN)); + (1 << PMA_PMD_EXT_CLK312_LBN) | + (1 << PMA_PMD_EXT_ROBUST_LBN)); + mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, PMA_PMD_XCONTROL_REG, reg); mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT, -- cgit v1.2.3 From 2d18835d65b7433e7e6583f65395f8c01e7874af Mon Sep 17 00:00:00 2001 From: Steve Hodgson Date: Thu, 29 Jan 2009 17:48:43 +0000 Subject: sfc: SFX7101: Remove workaround for bad link training Early versions of the SFX7101 firmware could complete link training in a state where it would not adequately cancel noise (Solarflare bug 10750). We previously worked around this by resetting the PHY after seeing many Ethernet CRC errors. This workaround is unsafe since it takes no account of the interval between errors; it also appears to be unnecessary with production firmware. Therefore remove it. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/falcon.c | 4 ---- drivers/net/sfc/phy.h | 1 - drivers/net/sfc/tenxpress.c | 20 -------------------- drivers/net/sfc/workarounds.h | 3 --- 4 files changed, 28 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 5b9f2d9cc4e..ae7b0b48bfd 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -824,10 +824,6 @@ static void falcon_handle_rx_not_ok(struct efx_rx_queue *rx_queue, rx_ev_pause_frm ? " [PAUSE]" : ""); } #endif - - if (unlikely(rx_ev_eth_crc_err && EFX_WORKAROUND_10750(efx) && - efx->phy_type == PHY_TYPE_SFX7101)) - tenxpress_crc_err(efx); } /* Handle receive events that are not in-order. */ diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h index 58c493ef81b..07e855c148b 100644 --- a/drivers/net/sfc/phy.h +++ b/drivers/net/sfc/phy.h @@ -17,7 +17,6 @@ extern struct efx_phy_operations falcon_sfx7101_phy_ops; extern struct efx_phy_operations falcon_sft9001_phy_ops; extern void tenxpress_phy_blink(struct efx_nic *efx, bool blink); -extern void tenxpress_crc_err(struct efx_nic *efx); /**************************************************************************** * Exported functions from the driver for XFP optical PHYs diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index 19cfc318954..80c8d6e3131 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -189,25 +189,12 @@ * rails */ #define LNPGA_PDOWN_WAIT (HZ / 5) -static int crc_error_reset_threshold = 100; -module_param(crc_error_reset_threshold, int, 0644); -MODULE_PARM_DESC(crc_error_reset_threshold, - "Max number of CRC errors before XAUI reset"); - struct tenxpress_phy_data { enum efx_loopback_mode loopback_mode; - atomic_t bad_crc_count; enum efx_phy_mode phy_mode; int bad_lp_tries; }; -void tenxpress_crc_err(struct efx_nic *efx) -{ - struct tenxpress_phy_data *phy_data = efx->phy_data; - if (phy_data != NULL) - atomic_inc(&phy_data->bad_crc_count); -} - static ssize_t show_phy_short_reach(struct device *dev, struct device_attribute *attr, char *buf) { @@ -627,13 +614,6 @@ static void tenxpress_phy_poll(struct efx_nic *efx) if (phy_data->phy_mode != PHY_MODE_NORMAL) return; - - if (EFX_WORKAROUND_10750(efx) && - atomic_read(&phy_data->bad_crc_count) > crc_error_reset_threshold) { - EFX_ERR(efx, "Resetting XAUI due to too many CRC errors\n"); - falcon_reset_xaui(efx); - atomic_set(&phy_data->bad_crc_count, 0); - } } static void tenxpress_phy_fini(struct efx_nic *efx) diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index 82e03e1d737..797a0cf7cd6 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -18,7 +18,6 @@ #define EFX_WORKAROUND_ALWAYS(efx) 1 #define EFX_WORKAROUND_FALCON_A(efx) (falcon_rev(efx) <= FALCON_REV_A1) #define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx) -#define EFX_WORKAROUND_SFX7101(efx) ((efx)->phy_type == PHY_TYPE_SFX7101) #define EFX_WORKAROUND_SFT9001A(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A) /* XAUI resets if link not detected */ @@ -29,8 +28,6 @@ #define EFX_WORKAROUND_7884 EFX_WORKAROUND_10G /* TX pkt parser problem with <= 16 byte TXes */ #define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS -/* Low rate CRC errors require XAUI reset */ -#define EFX_WORKAROUND_10750 EFX_WORKAROUND_SFX7101 /* TX_EV_PKT_ERR can be caused by a dangling TX descriptor * or a PCIe error (bug 11028) */ #define EFX_WORKAROUND_10727 EFX_WORKAROUND_ALWAYS -- cgit v1.2.3 From 8b9dc8dd447cfe27c0214761ced22a8e4aa58f5e Mon Sep 17 00:00:00 2001 From: Steve Hodgson Date: Thu, 29 Jan 2009 17:49:09 +0000 Subject: sfc: SFT9001: Fix speed reporting in 1G PHY loopback Instead of disabling AN in loopback, just prevent restarting AN and override the speed in sft9001_get_settings(). Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/mdio_10g.c | 4 ++++ drivers/net/sfc/tenxpress.c | 27 +++++++++------------------ drivers/net/sfc/workarounds.h | 5 +++++ 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c index f6a16428113..7f09ab58194 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -15,6 +15,7 @@ #include "net_driver.h" #include "mdio_10g.h" #include "boards.h" +#include "workarounds.h" int mdio_clause45_reset_mmd(struct efx_nic *port, int mmd, int spins, int spintime) @@ -517,6 +518,9 @@ int mdio_clause45_set_settings(struct efx_nic *efx, reg |= BMCR_ANENABLE | BMCR_ANRESTART; else reg &= ~BMCR_ANENABLE; + if (EFX_WORKAROUND_15195(efx) + && LOOPBACK_MASK(efx) & efx->phy_op->loopbacks) + reg &= ~BMCR_ANRESTART; if (xnp) reg |= 1 << MDIO_AN_CTRL_XNP_LBN; else diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index 80c8d6e3131..bc2833f9cbe 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -511,7 +511,7 @@ static void tenxpress_phy_reconfigure(struct efx_nic *efx) { struct tenxpress_phy_data *phy_data = efx->phy_data; struct ethtool_cmd ecmd; - bool phy_mode_change, loop_reset, loop_toggle, loopback; + bool phy_mode_change, loop_reset; if (efx->phy_mode & (PHY_MODE_OFF | PHY_MODE_SPECIAL)) { phy_data->phy_mode = efx->phy_mode; @@ -522,12 +522,10 @@ static void tenxpress_phy_reconfigure(struct efx_nic *efx) phy_mode_change = (efx->phy_mode == PHY_MODE_NORMAL && phy_data->phy_mode != PHY_MODE_NORMAL); - loopback = LOOPBACK_MASK(efx) & efx->phy_op->loopbacks; - loop_toggle = LOOPBACK_CHANGED(phy_data, efx, efx->phy_op->loopbacks); loop_reset = (LOOPBACK_OUT_OF(phy_data, efx, efx->phy_op->loopbacks) || LOOPBACK_CHANGED(phy_data, efx, 1 << LOOPBACK_GPHY)); - if (loop_reset || loop_toggle || loopback || phy_mode_change) { + if (loop_reset || phy_mode_change) { int rc; efx->phy_op->get_settings(efx, &ecmd); @@ -542,20 +540,6 @@ static void tenxpress_phy_reconfigure(struct efx_nic *efx) falcon_reset_xaui(efx); } - if (efx->phy_type != PHY_TYPE_SFX7101) { - /* Only change autoneg once, on coming out or - * going into loopback */ - if (loop_toggle) - ecmd.autoneg = !loopback; - if (loopback) { - ecmd.duplex = DUPLEX_FULL; - if (efx->loopback_mode == LOOPBACK_GPHY) - ecmd.speed = SPEED_1000; - else - ecmd.speed = SPEED_10000; - } - } - rc = efx->phy_op->set_settings(efx, &ecmd); WARN_ON(rc); } @@ -813,6 +797,13 @@ static void sft9001_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) ecmd->duplex = (reg & (1 << GPHY_DUPLEX_LBN) ? DUPLEX_FULL : DUPLEX_HALF); } + + /* In loopback, the PHY automatically brings up the correct interface, + * but doesn't advertise the correct speed. So override it */ + if (efx->loopback_mode == LOOPBACK_GPHY) + ecmd->speed = SPEED_1000; + else if (LOOPBACK_MASK(efx) & SFT9001_LOOPBACKS) + ecmd->speed = SPEED_10000; } static int sft9001_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index 797a0cf7cd6..420fe153ea2 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -19,6 +19,8 @@ #define EFX_WORKAROUND_FALCON_A(efx) (falcon_rev(efx) <= FALCON_REV_A1) #define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx) #define EFX_WORKAROUND_SFT9001A(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A) +#define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \ + (efx)->phy_type == PHY_TYPE_SFT9001B) /* XAUI resets if link not detected */ #define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS @@ -56,4 +58,7 @@ /* Need to keep AN enabled */ #define EFX_WORKAROUND_13963 EFX_WORKAROUND_SFT9001A +/* Don't restart AN in near-side loopback */ +#define EFX_WORKAROUND_15195 EFX_WORKAROUND_SFT9001 + #endif /* EFX_WORKAROUNDS_H */ -- cgit v1.2.3 From 2f08575389ac37ece5922094777442d8fdd8c00a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 29 Jan 2009 17:49:29 +0000 Subject: sfc: SFN4111T: Fix GPIO sharing between I2C and FLASH_CFG_1 Change sfn4111t_reset() to change only GPIO output enables so that it doesn't break subsequent I2C operations. Update comments to explain exactly what we're doing. Add a short sleep to make sure the FLASH_CFG_1 value is latched before any subsequent I2C operations. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/sfe4001.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c index 16b80acb999..c7c95db2af6 100644 --- a/drivers/net/sfc/sfe4001.c +++ b/drivers/net/sfc/sfe4001.c @@ -186,19 +186,22 @@ static int sfn4111t_reset(struct efx_nic *efx) { efx_oword_t reg; - /* GPIO pins are also used for I2C, so block that temporarily */ + /* GPIO 3 and the GPIO register are shared with I2C, so block that */ mutex_lock(&efx->i2c_adap.bus_lock); + /* Pull RST_N (GPIO 2) low then let it up again, setting the + * FLASH_CFG_1 strap (GPIO 3) appropriately. Only change the + * output enables; the output levels should always be 0 (low) + * and we rely on external pull-ups. */ falcon_read(efx, ®, GPIO_CTL_REG_KER); EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, true); - EFX_SET_OWORD_FIELD(reg, GPIO2_OUT, false); falcon_write(efx, ®, GPIO_CTL_REG_KER); msleep(1000); - EFX_SET_OWORD_FIELD(reg, GPIO2_OUT, true); - EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, true); - EFX_SET_OWORD_FIELD(reg, GPIO3_OUT, - !(efx->phy_mode & PHY_MODE_SPECIAL)); + EFX_SET_OWORD_FIELD(reg, GPIO2_OEN, false); + EFX_SET_OWORD_FIELD(reg, GPIO3_OEN, + !!(efx->phy_mode & PHY_MODE_SPECIAL)); falcon_write(efx, ®, GPIO_CTL_REG_KER); + msleep(1); mutex_unlock(&efx->i2c_adap.bus_lock); -- cgit v1.2.3 From 0cc128387969753ae037401eb49e4bbb474186ea Mon Sep 17 00:00:00 2001 From: Steve Hodgson Date: Thu, 29 Jan 2009 17:49:59 +0000 Subject: sfc: Fix post-reset MAC selection Modify falcon_switch_mac() to always set NIC_STAT_REG, even if the the MAC is the same as it was before. This ensures that the value is correct after an online reset. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/falcon.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index ae7b0b48bfd..d9412f83a78 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -2283,16 +2283,12 @@ int falcon_switch_mac(struct efx_nic *efx) efx->link_fd = true; } + WARN_ON(!mutex_is_locked(&efx->mac_lock)); efx->mac_op = (EFX_IS10G(efx) ? &falcon_xmac_operations : &falcon_gmac_operations); - if (old_mac_op == efx->mac_op) - return 0; - - WARN_ON(!mutex_is_locked(&efx->mac_lock)); - - /* Not all macs support a mac-level link state */ - efx->mac_up = true; + /* Always push the NIC_STAT_REG setting even if the mac hasn't + * changed, because this function is run post online reset */ falcon_read(efx, &nic_stat, NIC_STAT_REG); strap_val = EFX_IS10G(efx) ? 5 : 3; if (falcon_rev(efx) >= FALCON_REV_B0) { @@ -2305,8 +2301,13 @@ int falcon_switch_mac(struct efx_nic *efx) BUG_ON(EFX_OWORD_FIELD(nic_stat, STRAP_PINS) != strap_val); } + if (old_mac_op == efx->mac_op) + return 0; EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G'); + /* Not all macs support a mac-level link state */ + efx->mac_up = true; + return falcon_reset_macs(efx); } -- cgit v1.2.3 From 4b988280be13a1b4c17f51cc66948aef467e7601 Mon Sep 17 00:00:00 2001 From: Steve Hodgson Date: Thu, 29 Jan 2009 17:50:51 +0000 Subject: sfc: Reinitialise the PHY completely in case of a PHY or NIC reset In particular, set pause advertising bits properly. A PHY reset is not necessary to recover from the register self-test, so use a "invisible" reset there instead. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/efx.c | 26 +++++++++++++++++++------- drivers/net/sfc/efx.h | 7 ++++--- drivers/net/sfc/selftest.c | 7 ++++--- drivers/net/sfc/tenxpress.c | 1 + 4 files changed, 28 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 7673fd92eaf..b0e53087bda 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -676,9 +676,8 @@ static int efx_init_port(struct efx_nic *efx) rc = efx->phy_op->init(efx); if (rc) return rc; - efx->phy_op->reconfigure(efx); - mutex_lock(&efx->mac_lock); + efx->phy_op->reconfigure(efx); rc = falcon_switch_mac(efx); mutex_unlock(&efx->mac_lock); if (rc) @@ -1622,7 +1621,8 @@ static void efx_unregister_netdev(struct efx_nic *efx) /* Tears down the entire software state and most of the hardware state * before reset. */ -void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) +void efx_reset_down(struct efx_nic *efx, enum reset_type method, + struct ethtool_cmd *ecmd) { EFX_ASSERT_RESET_SERIALISED(efx); @@ -1639,6 +1639,8 @@ void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) efx->phy_op->get_settings(efx, ecmd); efx_fini_channels(efx); + if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) + efx->phy_op->fini(efx); } /* This function will always ensure that the locks acquired in @@ -1646,7 +1648,8 @@ void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd) * that we were unable to reinitialise the hardware, and the * driver should be disabled. If ok is false, then the rx and tx * engines are not restarted, pending a RESET_DISABLE. */ -int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd, bool ok) +int efx_reset_up(struct efx_nic *efx, enum reset_type method, + struct ethtool_cmd *ecmd, bool ok) { int rc; @@ -1658,6 +1661,15 @@ int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd, bool ok) ok = false; } + if (efx->port_initialized && method != RESET_TYPE_INVISIBLE) { + if (ok) { + rc = efx->phy_op->init(efx); + if (rc) + ok = false; + } else + efx->port_initialized = false; + } + if (ok) { efx_init_channels(efx); @@ -1702,7 +1714,7 @@ static int efx_reset(struct efx_nic *efx) EFX_INFO(efx, "resetting (%d)\n", method); - efx_reset_down(efx, &ecmd); + efx_reset_down(efx, method, &ecmd); rc = falcon_reset_hw(efx, method); if (rc) { @@ -1721,10 +1733,10 @@ static int efx_reset(struct efx_nic *efx) /* Leave device stopped if necessary */ if (method == RESET_TYPE_DISABLE) { - efx_reset_up(efx, &ecmd, false); + efx_reset_up(efx, method, &ecmd, false); rc = -EIO; } else { - rc = efx_reset_up(efx, &ecmd, true); + rc = efx_reset_up(efx, method, &ecmd, true); } out_disable: diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index 0dd7a532c78..ac201587a16 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h @@ -40,9 +40,10 @@ extern void efx_reconfigure_port(struct efx_nic *efx); extern void __efx_reconfigure_port(struct efx_nic *efx); /* Reset handling */ -extern void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd); -extern int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd, - bool ok); +extern void efx_reset_down(struct efx_nic *efx, enum reset_type method, + struct ethtool_cmd *ecmd); +extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, + struct ethtool_cmd *ecmd, bool ok); /* Global */ extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c index dba0d64d50c..0a598084c51 100644 --- a/drivers/net/sfc/selftest.c +++ b/drivers/net/sfc/selftest.c @@ -665,6 +665,7 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, { enum efx_loopback_mode loopback_mode = efx->loopback_mode; int phy_mode = efx->phy_mode; + enum reset_type reset_method = RESET_TYPE_INVISIBLE; struct ethtool_cmd ecmd; struct efx_channel *channel; int rc_test = 0, rc_reset = 0, rc; @@ -718,21 +719,21 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, mutex_unlock(&efx->mac_lock); /* free up all consumers of SRAM (including all the queues) */ - efx_reset_down(efx, &ecmd); + efx_reset_down(efx, reset_method, &ecmd); rc = efx_test_chip(efx, tests); if (rc && !rc_test) rc_test = rc; /* reset the chip to recover from the register test */ - rc_reset = falcon_reset_hw(efx, RESET_TYPE_ALL); + rc_reset = falcon_reset_hw(efx, reset_method); /* Ensure that the phy is powered and out of loopback * for the bist and loopback tests */ efx->phy_mode &= ~PHY_MODE_LOW_POWER; efx->loopback_mode = LOOPBACK_NONE; - rc = efx_reset_up(efx, &ecmd, rc_reset == 0); + rc = efx_reset_up(efx, reset_method, &ecmd, rc_reset == 0); if (rc && !rc_reset) rc_reset = rc; diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index bc2833f9cbe..c9584619322 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -337,6 +337,7 @@ static int tenxpress_phy_init(struct efx_nic *efx) rc = tenxpress_init(efx); if (rc < 0) goto fail; + mdio_clause45_set_pause(efx); if (efx->phy_type == PHY_TYPE_SFT9001B) { rc = device_create_file(&efx->pci_dev->dev, -- cgit v1.2.3 From 67797763c60bfe3bbf99ef81ce1042e71678d109 Mon Sep 17 00:00:00 2001 From: Steve Hodgson Date: Thu, 29 Jan 2009 17:51:15 +0000 Subject: sfc: Test for PHYXS faults whenever we cannot test link state bits Depending on the loopback mode, there may be no pertinent link state bits. In this case we test the PHYXS RX fault bit instead. Make sure to do this in all cases where there are no link state bits. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/mdio_10g.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c index 7f09ab58194..16bc5853d0e 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -180,17 +180,12 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) return false; else if (efx_phy_mode_disabled(efx->phy_mode)) return false; - else if (efx->loopback_mode == LOOPBACK_PHYXS) { + else if (efx->loopback_mode == LOOPBACK_PHYXS) mmd_mask &= ~(MDIO_MMDREG_DEVS_PHYXS | MDIO_MMDREG_DEVS_PCS | MDIO_MMDREG_DEVS_PMAPMD | MDIO_MMDREG_DEVS_AN); - if (!mmd_mask) { - reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS, - MDIO_PHYXS_STATUS2); - return !(reg & (1 << MDIO_PHYXS_STATUS2_RX_FAULT_LBN)); - } - } else if (efx->loopback_mode == LOOPBACK_PCS) + else if (efx->loopback_mode == LOOPBACK_PCS) mmd_mask &= ~(MDIO_MMDREG_DEVS_PCS | MDIO_MMDREG_DEVS_PMAPMD | MDIO_MMDREG_DEVS_AN); @@ -198,6 +193,13 @@ bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) mmd_mask &= ~(MDIO_MMDREG_DEVS_PMAPMD | MDIO_MMDREG_DEVS_AN); + if (!mmd_mask) { + /* Use presence of XGMII faults in leui of link state */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS, + MDIO_PHYXS_STATUS2); + return !(reg & (1 << MDIO_PHYXS_STATUS2_RX_FAULT_LBN)); + } + while (mmd_mask) { if (mmd_mask & 1) { /* Double reads because link state is latched, and a -- cgit v1.2.3 From 44176b45d1aae04d99c505e6ee98d2d3c3fce173 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 29 Jan 2009 17:51:48 +0000 Subject: sfc: Update board info for hardware monitor on SFN4111T-R5 and later Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/sfe4001.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c index c7c95db2af6..853057e87fb 100644 --- a/drivers/net/sfc/sfe4001.c +++ b/drivers/net/sfc/sfe4001.c @@ -375,17 +375,25 @@ static void sfn4111t_fini(struct efx_nic *efx) i2c_unregister_device(efx->board_info.hwmon_client); } -static struct i2c_board_info sfn4111t_hwmon_info = { +static struct i2c_board_info sfn4111t_a0_hwmon_info = { I2C_BOARD_INFO("max6647", 0x4e), .irq = -1, }; +static struct i2c_board_info sfn4111t_r5_hwmon_info = { + I2C_BOARD_INFO("max6646", 0x4d), + .irq = -1, +}; + int sfn4111t_init(struct efx_nic *efx) { int rc; efx->board_info.hwmon_client = - i2c_new_device(&efx->i2c_adap, &sfn4111t_hwmon_info); + i2c_new_device(&efx->i2c_adap, + (efx->board_info.minor < 5) ? + &sfn4111t_a0_hwmon_info : + &sfn4111t_r5_hwmon_info); if (!efx->board_info.hwmon_client) return -EIO; -- cgit v1.2.3 From c9d5a53f060bb9ac6cd20d9768b4b75e22bc8689 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 29 Jan 2009 17:52:11 +0000 Subject: sfc: SFT9001: Always enable XNP exchange on SFT9001 rev B This workaround is not specific to rev A. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/workarounds.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index 420fe153ea2..b810dcdf468 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -54,7 +54,7 @@ #define EFX_WORKAROUND_8071 EFX_WORKAROUND_FALCON_A /* Need to send XNP pages for 100BaseT */ -#define EFX_WORKAROUND_13204 EFX_WORKAROUND_SFT9001A +#define EFX_WORKAROUND_13204 EFX_WORKAROUND_SFT9001 /* Need to keep AN enabled */ #define EFX_WORKAROUND_13963 EFX_WORKAROUND_SFT9001A -- cgit v1.2.3 From af4ad9bca0c4039355b20d760b4fd39afa48c59d Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 29 Jan 2009 17:59:37 +0000 Subject: sfc: SFX7101/SFT9001: Fix AN advertisements All 10Xpress PHYs require autonegotiation all the time; enforce this in the set_settings() method and do not treat it as a workaround. Remove claimed support for 100M HD mode since it is not supported by current firmware. Do not set speed override bits when AN is enabled, and do not use register 1.49192 for AN configuration as it can override what we set elsewhere. Always set the AN selector bits to 1 (802.3). Fix confusion between Next Page and Extended Next Page. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/ethtool.c | 3 - drivers/net/sfc/mdio_10g.c | 177 +++++++++++++++++++----------------------- drivers/net/sfc/mdio_10g.h | 3 +- drivers/net/sfc/net_driver.h | 4 +- drivers/net/sfc/tenxpress.c | 151 ++++++++++++++--------------------- drivers/net/sfc/workarounds.h | 4 - 6 files changed, 141 insertions(+), 201 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 53d259e9018..7b5924c039b 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -219,9 +219,6 @@ int efx_ethtool_set_settings(struct net_device *net_dev, struct efx_nic *efx = netdev_priv(net_dev); int rc; - if (EFX_WORKAROUND_13963(efx) && !ecmd->autoneg) - return -EINVAL; - /* Falcon GMAC does not support 1000Mbps HD */ if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) { EFX_LOG(efx, "rejecting unsupported 1000Mbps HD" diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c index 16bc5853d0e..f9e2f95c3b4 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -266,7 +266,7 @@ void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, } } -static u32 mdio_clause45_get_an(struct efx_nic *efx, u16 addr, u32 xnp) +static u32 mdio_clause45_get_an(struct efx_nic *efx, u16 addr) { int phy_id = efx->mii.phy_id; u32 result = 0; @@ -281,9 +281,6 @@ static u32 mdio_clause45_get_an(struct efx_nic *efx, u16 addr, u32 xnp) result |= ADVERTISED_100baseT_Half; if (reg & ADVERTISE_100FULL) result |= ADVERTISED_100baseT_Full; - if (reg & LPA_RESV) - result |= xnp; - return result; } @@ -313,7 +310,7 @@ void mdio_clause45_get_settings(struct efx_nic *efx, */ void mdio_clause45_get_settings_ext(struct efx_nic *efx, struct ethtool_cmd *ecmd, - u32 xnp, u32 xnp_lpa) + u32 npage_adv, u32 npage_lpa) { int phy_id = efx->mii.phy_id; int reg; @@ -364,8 +361,8 @@ void mdio_clause45_get_settings_ext(struct efx_nic *efx, ecmd->autoneg = AUTONEG_ENABLE; ecmd->advertising |= ADVERTISED_Autoneg | - mdio_clause45_get_an(efx, - MDIO_AN_ADVERTISE, xnp); + mdio_clause45_get_an(efx, MDIO_AN_ADVERTISE) | + npage_adv; } else ecmd->autoneg = AUTONEG_DISABLE; } else @@ -374,27 +371,30 @@ void mdio_clause45_get_settings_ext(struct efx_nic *efx, if (ecmd->autoneg) { /* If AN is complete, report best common mode, * otherwise report best advertised mode. */ - u32 common = ecmd->advertising; + u32 modes = 0; if (mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_MMDREG_STAT1) & - (1 << MDIO_AN_STATUS_AN_DONE_LBN)) { - common &= mdio_clause45_get_an(efx, MDIO_AN_LPA, - xnp_lpa); - } - if (common & ADVERTISED_10000baseT_Full) { + (1 << MDIO_AN_STATUS_AN_DONE_LBN)) + modes = (ecmd->advertising & + (mdio_clause45_get_an(efx, MDIO_AN_LPA) | + npage_lpa)); + if (modes == 0) + modes = ecmd->advertising; + + if (modes & ADVERTISED_10000baseT_Full) { ecmd->speed = SPEED_10000; ecmd->duplex = DUPLEX_FULL; - } else if (common & (ADVERTISED_1000baseT_Full | - ADVERTISED_1000baseT_Half)) { + } else if (modes & (ADVERTISED_1000baseT_Full | + ADVERTISED_1000baseT_Half)) { ecmd->speed = SPEED_1000; - ecmd->duplex = !!(common & ADVERTISED_1000baseT_Full); - } else if (common & (ADVERTISED_100baseT_Full | - ADVERTISED_100baseT_Half)) { + ecmd->duplex = !!(modes & ADVERTISED_1000baseT_Full); + } else if (modes & (ADVERTISED_100baseT_Full | + ADVERTISED_100baseT_Half)) { ecmd->speed = SPEED_100; - ecmd->duplex = !!(common & ADVERTISED_100baseT_Full); + ecmd->duplex = !!(modes & ADVERTISED_100baseT_Full); } else { ecmd->speed = SPEED_10; - ecmd->duplex = !!(common & ADVERTISED_10baseT_Full); + ecmd->duplex = !!(modes & ADVERTISED_10baseT_Full); } } else { /* Report forced settings */ @@ -418,7 +418,7 @@ int mdio_clause45_set_settings(struct efx_nic *efx, int phy_id = efx->mii.phy_id; struct ethtool_cmd prev; u32 required; - int ctrl1_bits, reg; + int reg; efx->phy_op->get_settings(efx, &prev); @@ -433,102 +433,83 @@ int mdio_clause45_set_settings(struct efx_nic *efx, if (prev.port != PORT_TP || ecmd->port != PORT_TP) return -EINVAL; - /* Check that PHY supports these settings and work out the - * basic control bits */ - if (ecmd->duplex) { + /* Check that PHY supports these settings */ + if (ecmd->autoneg) { + required = SUPPORTED_Autoneg; + } else if (ecmd->duplex) { switch (ecmd->speed) { - case SPEED_10: - ctrl1_bits = BMCR_FULLDPLX; - required = SUPPORTED_10baseT_Full; - break; - case SPEED_100: - ctrl1_bits = BMCR_SPEED100 | BMCR_FULLDPLX; - required = SUPPORTED_100baseT_Full; - break; - case SPEED_1000: - ctrl1_bits = BMCR_SPEED1000 | BMCR_FULLDPLX; - required = SUPPORTED_1000baseT_Full; - break; - case SPEED_10000: - ctrl1_bits = (BMCR_SPEED1000 | BMCR_SPEED100 | - BMCR_FULLDPLX); - required = SUPPORTED_10000baseT_Full; - break; - default: - return -EINVAL; + case SPEED_10: required = SUPPORTED_10baseT_Full; break; + case SPEED_100: required = SUPPORTED_100baseT_Full; break; + default: return -EINVAL; } } else { switch (ecmd->speed) { - case SPEED_10: - ctrl1_bits = 0; - required = SUPPORTED_10baseT_Half; - break; - case SPEED_100: - ctrl1_bits = BMCR_SPEED100; - required = SUPPORTED_100baseT_Half; - break; - case SPEED_1000: - ctrl1_bits = BMCR_SPEED1000; - required = SUPPORTED_1000baseT_Half; - break; - default: - return -EINVAL; + case SPEED_10: required = SUPPORTED_10baseT_Half; break; + case SPEED_100: required = SUPPORTED_100baseT_Half; break; + default: return -EINVAL; } } - if (ecmd->autoneg) - required |= SUPPORTED_Autoneg; required |= ecmd->advertising; if (required & ~prev.supported) return -EINVAL; - /* Set the basic control bits */ - reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, - MDIO_MMDREG_CTRL1); - reg &= ~(BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX | 0x003c); - reg |= ctrl1_bits; - mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, MDIO_MMDREG_CTRL1, - reg); - - /* Set the AN registers */ - if (ecmd->autoneg != prev.autoneg || - ecmd->advertising != prev.advertising) { - bool xnp = false; - - if (efx->phy_op->set_xnp_advertise) - xnp = efx->phy_op->set_xnp_advertise(efx, - ecmd->advertising); - - if (ecmd->autoneg) { - reg = 0; - if (ecmd->advertising & ADVERTISED_10baseT_Half) - reg |= ADVERTISE_10HALF; - if (ecmd->advertising & ADVERTISED_10baseT_Full) - reg |= ADVERTISE_10FULL; - if (ecmd->advertising & ADVERTISED_100baseT_Half) - reg |= ADVERTISE_100HALF; - if (ecmd->advertising & ADVERTISED_100baseT_Full) - reg |= ADVERTISE_100FULL; - if (xnp) - reg |= ADVERTISE_RESV; - mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, - MDIO_AN_ADVERTISE, reg); - } + if (ecmd->autoneg) { + bool xnp = (ecmd->advertising & ADVERTISED_10000baseT_Full + || EFX_WORKAROUND_13204(efx)); + + /* Set up the base page */ + reg = ADVERTISE_CSMA; + if (ecmd->advertising & ADVERTISED_10baseT_Half) + reg |= ADVERTISE_10HALF; + if (ecmd->advertising & ADVERTISED_10baseT_Full) + reg |= ADVERTISE_10FULL; + if (ecmd->advertising & ADVERTISED_100baseT_Half) + reg |= ADVERTISE_100HALF; + if (ecmd->advertising & ADVERTISED_100baseT_Full) + reg |= ADVERTISE_100FULL; + if (xnp) + reg |= ADVERTISE_RESV; + else if (ecmd->advertising & (ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full)) + reg |= ADVERTISE_NPAGE; + reg |= efx_fc_advertise(efx->wanted_fc); + mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_ADVERTISE, reg); + + /* Set up the (extended) next page if necessary */ + if (efx->phy_op->set_npage_adv) + efx->phy_op->set_npage_adv(efx, ecmd->advertising); + /* Enable and restart AN */ reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_MMDREG_CTRL1); - if (ecmd->autoneg) - reg |= BMCR_ANENABLE | BMCR_ANRESTART; - else - reg &= ~BMCR_ANENABLE; - if (EFX_WORKAROUND_15195(efx) - && LOOPBACK_MASK(efx) & efx->phy_op->loopbacks) - reg &= ~BMCR_ANRESTART; + reg |= BMCR_ANENABLE; + if (!(EFX_WORKAROUND_15195(efx) && + LOOPBACK_MASK(efx) & efx->phy_op->loopbacks)) + reg |= BMCR_ANRESTART; if (xnp) reg |= 1 << MDIO_AN_CTRL_XNP_LBN; else reg &= ~(1 << MDIO_AN_CTRL_XNP_LBN); mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, MDIO_MMDREG_CTRL1, reg); + } else { + /* Disable AN */ + mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_AN, + MDIO_MMDREG_CTRL1, + __ffs(BMCR_ANENABLE), false); + + /* Set the basic control bits */ + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_CTRL1); + reg &= ~(BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX | + 0x003c); + if (ecmd->speed == SPEED_100) + reg |= BMCR_SPEED100; + if (ecmd->duplex) + reg |= BMCR_FULLDPLX; + mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, + MDIO_MMDREG_CTRL1, reg); } return 0; diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h index 09bf801d056..8ba49773ce7 100644 --- a/drivers/net/sfc/mdio_10g.h +++ b/drivers/net/sfc/mdio_10g.h @@ -155,7 +155,8 @@ #define MDIO_AN_XNP 22 #define MDIO_AN_LPA_XNP 25 -#define MDIO_AN_10GBT_ADVERTISE 32 +#define MDIO_AN_10GBT_CTRL 32 +#define MDIO_AN_10GBT_CTRL_ADV_10G_LBN 12 #define MDIO_AN_10GBT_STATUS (33) #define MDIO_AN_10GBT_STATUS_MS_FLT_LBN (15) /* MASTER/SLAVE config fault */ #define MDIO_AN_10GBT_STATUS_MS_LBN (14) /* MASTER/SLAVE config */ diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 5f255f75754..8eb411a1c82 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -566,7 +566,7 @@ struct efx_mac_operations { * @poll: Poll for hardware state. Serialised by the mac_lock. * @get_settings: Get ethtool settings. Serialised by the mac_lock. * @set_settings: Set ethtool settings. Serialised by the mac_lock. - * @set_xnp_advertise: Set abilities advertised in Extended Next Page + * @set_npage_adv: Set abilities advertised in (Extended) Next Page * (only needed where AN bit is set in mmds) * @num_tests: Number of PHY-specific tests/results * @test_names: Names of the tests/results @@ -586,7 +586,7 @@ struct efx_phy_operations { struct ethtool_cmd *ecmd); int (*set_settings) (struct efx_nic *efx, struct ethtool_cmd *ecmd); - bool (*set_xnp_advertise) (struct efx_nic *efx, u32); + void (*set_npage_adv) (struct efx_nic *efx, u32); u32 num_tests; const char *const *test_names; int (*run_tests) (struct efx_nic *efx, int *results, unsigned flags); diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index c9584619322..412d209d831 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -179,11 +179,13 @@ #define C22EXT_STATUS_LINK_LBN 2 #define C22EXT_STATUS_LINK_WIDTH 1 -#define C22EXT_MSTSLV_REG 49162 -#define C22EXT_MSTSLV_1000_HD_LBN 10 -#define C22EXT_MSTSLV_1000_HD_WIDTH 1 -#define C22EXT_MSTSLV_1000_FD_LBN 11 -#define C22EXT_MSTSLV_1000_FD_WIDTH 1 +#define C22EXT_MSTSLV_CTRL 49161 +#define C22EXT_MSTSLV_CTRL_ADV_1000_HD_LBN 8 +#define C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN 9 + +#define C22EXT_MSTSLV_STATUS 49162 +#define C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN 10 +#define C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN 11 /* Time to wait between powering down the LNPGA and turning off the power * rails */ @@ -741,114 +743,76 @@ reset: return rc; } -static u32 tenxpress_get_xnp_lpa(struct efx_nic *efx) +static void +tenxpress_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) { - int phy = efx->mii.phy_id; - u32 lpa = 0; + int phy_id = efx->mii.phy_id; + u32 adv = 0, lpa = 0; int reg; if (efx->phy_type != PHY_TYPE_SFX7101) { - reg = mdio_clause45_read(efx, phy, MDIO_MMD_C22EXT, - C22EXT_MSTSLV_REG); - if (reg & (1 << C22EXT_MSTSLV_1000_HD_LBN)) + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_C22EXT, + C22EXT_MSTSLV_CTRL); + if (reg & (1 << C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN)) + adv |= ADVERTISED_1000baseT_Full; + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_C22EXT, + C22EXT_MSTSLV_STATUS); + if (reg & (1 << C22EXT_MSTSLV_STATUS_LP_1000_HD_LBN)) lpa |= ADVERTISED_1000baseT_Half; - if (reg & (1 << C22EXT_MSTSLV_1000_FD_LBN)) + if (reg & (1 << C22EXT_MSTSLV_STATUS_LP_1000_FD_LBN)) lpa |= ADVERTISED_1000baseT_Full; } - reg = mdio_clause45_read(efx, phy, MDIO_MMD_AN, MDIO_AN_10GBT_STATUS); + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_10GBT_CTRL); + if (reg & (1 << MDIO_AN_10GBT_CTRL_ADV_10G_LBN)) + adv |= ADVERTISED_10000baseT_Full; + reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_10GBT_STATUS); if (reg & (1 << MDIO_AN_10GBT_STATUS_LP_10G_LBN)) lpa |= ADVERTISED_10000baseT_Full; - return lpa; -} - -static void sfx7101_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - mdio_clause45_get_settings_ext(efx, ecmd, ADVERTISED_10000baseT_Full, - tenxpress_get_xnp_lpa(efx)); - ecmd->supported |= SUPPORTED_10000baseT_Full; - ecmd->advertising |= ADVERTISED_10000baseT_Full; -} - -static void sft9001_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) -{ - int phy_id = efx->mii.phy_id; - u32 xnp_adv = 0; - int reg; - - reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, - PMA_PMD_SPEED_ENABLE_REG); - if (EFX_WORKAROUND_13204(efx) && (reg & (1 << PMA_PMD_100TX_ADV_LBN))) - xnp_adv |= ADVERTISED_100baseT_Full; - if (reg & (1 << PMA_PMD_1000T_ADV_LBN)) - xnp_adv |= ADVERTISED_1000baseT_Full; - if (reg & (1 << PMA_PMD_10000T_ADV_LBN)) - xnp_adv |= ADVERTISED_10000baseT_Full; - - mdio_clause45_get_settings_ext(efx, ecmd, xnp_adv, - tenxpress_get_xnp_lpa(efx)); - ecmd->supported |= (SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | - SUPPORTED_1000baseT_Full); + mdio_clause45_get_settings_ext(efx, ecmd, adv, lpa); - /* Use the vendor defined C22ext register for duplex settings */ - if (ecmd->speed != SPEED_10000 && !ecmd->autoneg) { - reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_C22EXT, - GPHY_XCONTROL_REG); - ecmd->duplex = (reg & (1 << GPHY_DUPLEX_LBN) ? - DUPLEX_FULL : DUPLEX_HALF); - } + if (efx->phy_type != PHY_TYPE_SFX7101) + ecmd->supported |= (SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full); /* In loopback, the PHY automatically brings up the correct interface, * but doesn't advertise the correct speed. So override it */ if (efx->loopback_mode == LOOPBACK_GPHY) ecmd->speed = SPEED_1000; - else if (LOOPBACK_MASK(efx) & SFT9001_LOOPBACKS) + else if (LOOPBACK_MASK(efx) & efx->phy_op->loopbacks) ecmd->speed = SPEED_10000; } -static int sft9001_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) +static int tenxpress_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) { - int phy_id = efx->mii.phy_id; - int rc; - - rc = mdio_clause45_set_settings(efx, ecmd); - if (rc) - return rc; + if (!ecmd->autoneg) + return -EINVAL; - if (ecmd->speed != SPEED_10000 && !ecmd->autoneg) - mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT, - GPHY_XCONTROL_REG, GPHY_DUPLEX_LBN, - ecmd->duplex == DUPLEX_FULL); + return mdio_clause45_set_settings(efx, ecmd); +} - return rc; +static void sfx7101_set_npage_adv(struct efx_nic *efx, u32 advertising) +{ + mdio_clause45_set_flag(efx, efx->mii.phy_id, MDIO_MMD_AN, + MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV_10G_LBN, + advertising & ADVERTISED_10000baseT_Full); } -static bool sft9001_set_xnp_advertise(struct efx_nic *efx, u32 advertising) +static void sft9001_set_npage_adv(struct efx_nic *efx, u32 advertising) { - int phy = efx->mii.phy_id; - int reg = mdio_clause45_read(efx, phy, MDIO_MMD_PMAPMD, - PMA_PMD_SPEED_ENABLE_REG); - bool enabled; - - reg &= ~((1 << 2) | (1 << 3)); - if (EFX_WORKAROUND_13204(efx) && - (advertising & ADVERTISED_100baseT_Full)) - reg |= 1 << PMA_PMD_100TX_ADV_LBN; - if (advertising & ADVERTISED_1000baseT_Full) - reg |= 1 << PMA_PMD_1000T_ADV_LBN; - if (advertising & ADVERTISED_10000baseT_Full) - reg |= 1 << PMA_PMD_10000T_ADV_LBN; - mdio_clause45_write(efx, phy, MDIO_MMD_PMAPMD, - PMA_PMD_SPEED_ENABLE_REG, reg); - - enabled = (advertising & - (ADVERTISED_1000baseT_Half | - ADVERTISED_1000baseT_Full | - ADVERTISED_10000baseT_Full)); - if (EFX_WORKAROUND_13204(efx)) - enabled |= (advertising & ADVERTISED_100baseT_Full); - return enabled; + int phy_id = efx->mii.phy_id; + + mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_C22EXT, + C22EXT_MSTSLV_CTRL, + C22EXT_MSTSLV_CTRL_ADV_1000_FD_LBN, + advertising & ADVERTISED_1000baseT_Full); + mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_AN, + MDIO_AN_10GBT_CTRL, + MDIO_AN_10GBT_CTRL_ADV_10G_LBN, + advertising & ADVERTISED_10000baseT_Full); } struct efx_phy_operations falcon_sfx7101_phy_ops = { @@ -858,8 +822,9 @@ struct efx_phy_operations falcon_sfx7101_phy_ops = { .poll = tenxpress_phy_poll, .fini = tenxpress_phy_fini, .clear_interrupt = efx_port_dummy_op_void, - .get_settings = sfx7101_get_settings, - .set_settings = mdio_clause45_set_settings, + .get_settings = tenxpress_get_settings, + .set_settings = tenxpress_set_settings, + .set_npage_adv = sfx7101_set_npage_adv, .num_tests = ARRAY_SIZE(sfx7101_test_names), .test_names = sfx7101_test_names, .run_tests = sfx7101_run_tests, @@ -874,9 +839,9 @@ struct efx_phy_operations falcon_sft9001_phy_ops = { .poll = tenxpress_phy_poll, .fini = tenxpress_phy_fini, .clear_interrupt = efx_port_dummy_op_void, - .get_settings = sft9001_get_settings, - .set_settings = sft9001_set_settings, - .set_xnp_advertise = sft9001_set_xnp_advertise, + .get_settings = tenxpress_get_settings, + .set_settings = tenxpress_set_settings, + .set_npage_adv = sft9001_set_npage_adv, .num_tests = ARRAY_SIZE(sft9001_test_names), .test_names = sft9001_test_names, .run_tests = sft9001_run_tests, diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index b810dcdf468..78de68f4a95 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -18,7 +18,6 @@ #define EFX_WORKAROUND_ALWAYS(efx) 1 #define EFX_WORKAROUND_FALCON_A(efx) (falcon_rev(efx) <= FALCON_REV_A1) #define EFX_WORKAROUND_10G(efx) EFX_IS10G(efx) -#define EFX_WORKAROUND_SFT9001A(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A) #define EFX_WORKAROUND_SFT9001(efx) ((efx)->phy_type == PHY_TYPE_SFT9001A || \ (efx)->phy_type == PHY_TYPE_SFT9001B) @@ -55,9 +54,6 @@ /* Need to send XNP pages for 100BaseT */ #define EFX_WORKAROUND_13204 EFX_WORKAROUND_SFT9001 -/* Need to keep AN enabled */ -#define EFX_WORKAROUND_13963 EFX_WORKAROUND_SFT9001A - /* Don't restart AN in near-side loopback */ #define EFX_WORKAROUND_15195 EFX_WORKAROUND_SFT9001 -- cgit v1.2.3 From 1974cc205e63cec4a17a6b3fca31fa4240ded77e Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Thu, 29 Jan 2009 18:00:07 +0000 Subject: sfc: Replace stats_enabled flag with a disable count Currently we use a spin-lock to serialise statistics fetches and also to inhibit them for short periods of time, plus a flag to enable/disable statistics fetches for longer periods of time, during online reset. This was apparently insufficient to deal with the several reasons for stats being disabled. Signed-off-by: Ben Hutchings Signed-off-by: David S. Miller --- drivers/net/sfc/efx.c | 33 ++++++++++++++++++++++----------- drivers/net/sfc/efx.h | 2 ++ drivers/net/sfc/falcon.c | 15 +++++++++++---- drivers/net/sfc/net_driver.h | 5 ++--- drivers/net/sfc/sfe4001.c | 15 ++++++++++++++- drivers/net/sfc/tenxpress.c | 12 ++++++------ 6 files changed, 57 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index b0e53087bda..ab0e09bf154 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -685,7 +685,7 @@ static int efx_init_port(struct efx_nic *efx) efx->mac_op->reconfigure(efx); efx->port_initialized = true; - efx->stats_enabled = true; + efx_stats_enable(efx); return 0; fail: @@ -734,6 +734,7 @@ static void efx_fini_port(struct efx_nic *efx) if (!efx->port_initialized) return; + efx_stats_disable(efx); efx->phy_op->fini(efx); efx->port_initialized = false; @@ -1360,6 +1361,20 @@ static int efx_net_stop(struct net_device *net_dev) return 0; } +void efx_stats_disable(struct efx_nic *efx) +{ + spin_lock(&efx->stats_lock); + ++efx->stats_disable_count; + spin_unlock(&efx->stats_lock); +} + +void efx_stats_enable(struct efx_nic *efx) +{ + spin_lock(&efx->stats_lock); + --efx->stats_disable_count; + spin_unlock(&efx->stats_lock); +} + /* Context: process, dev_base_lock or RTNL held, non-blocking. */ static struct net_device_stats *efx_net_stats(struct net_device *net_dev) { @@ -1368,12 +1383,12 @@ static struct net_device_stats *efx_net_stats(struct net_device *net_dev) struct net_device_stats *stats = &net_dev->stats; /* Update stats if possible, but do not wait if another thread - * is updating them (or resetting the NIC); slightly stale - * stats are acceptable. + * is updating them or if MAC stats fetches are temporarily + * disabled; slightly stale stats are acceptable. */ if (!spin_trylock(&efx->stats_lock)) return stats; - if (efx->stats_enabled) { + if (!efx->stats_disable_count) { efx->mac_op->update_stats(efx); falcon_update_nic_stats(efx); } @@ -1626,12 +1641,7 @@ void efx_reset_down(struct efx_nic *efx, enum reset_type method, { EFX_ASSERT_RESET_SERIALISED(efx); - /* The net_dev->get_stats handler is quite slow, and will fail - * if a fetch is pending over reset. Serialise against it. */ - spin_lock(&efx->stats_lock); - efx->stats_enabled = false; - spin_unlock(&efx->stats_lock); - + efx_stats_disable(efx); efx_stop_all(efx); mutex_lock(&efx->mac_lock); mutex_lock(&efx->spi_lock); @@ -1682,7 +1692,7 @@ int efx_reset_up(struct efx_nic *efx, enum reset_type method, if (ok) { efx_start_all(efx); - efx->stats_enabled = true; + efx_stats_enable(efx); } return rc; } @@ -1888,6 +1898,7 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type, efx->rx_checksum_enabled = true; spin_lock_init(&efx->netif_stop_lock); spin_lock_init(&efx->stats_lock); + efx->stats_disable_count = 1; mutex_init(&efx->mac_lock); efx->mac_op = &efx_dummy_mac_operations; efx->phy_op = &efx_dummy_phy_operations; diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index ac201587a16..55d0f131b0e 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h @@ -36,6 +36,8 @@ extern void efx_process_channel_now(struct efx_channel *channel); extern void efx_flush_queues(struct efx_nic *efx); /* Ports */ +extern void efx_stats_disable(struct efx_nic *efx); +extern void efx_stats_enable(struct efx_nic *efx); extern void efx_reconfigure_port(struct efx_nic *efx); extern void __efx_reconfigure_port(struct efx_nic *efx); diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index d9412f83a78..d5378e60fcd 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -1883,7 +1883,7 @@ static int falcon_reset_macs(struct efx_nic *efx) /* MAC stats will fail whilst the TX fifo is draining. Serialise * the drain sequence with the statistics fetch */ - spin_lock(&efx->stats_lock); + efx_stats_disable(efx); falcon_read(efx, ®, MAC0_CTRL_REG_KER); EFX_SET_OWORD_FIELD(reg, TXFIFO_DRAIN_EN_B0, 1); @@ -1913,7 +1913,7 @@ static int falcon_reset_macs(struct efx_nic *efx) udelay(10); } - spin_unlock(&efx->stats_lock); + efx_stats_enable(efx); /* If we've reset the EM block and the link is up, then * we'll have to kick the XAUI link so the PHY can recover */ @@ -2273,6 +2273,10 @@ int falcon_switch_mac(struct efx_nic *efx) struct efx_mac_operations *old_mac_op = efx->mac_op; efx_oword_t nic_stat; unsigned strap_val; + int rc = 0; + + /* Don't try to fetch MAC stats while we're switching MACs */ + efx_stats_disable(efx); /* Internal loopbacks override the phy speed setting */ if (efx->loopback_mode == LOOPBACK_GMAC) { @@ -2302,13 +2306,16 @@ int falcon_switch_mac(struct efx_nic *efx) } if (old_mac_op == efx->mac_op) - return 0; + goto out; EFX_LOG(efx, "selected %cMAC\n", EFX_IS10G(efx) ? 'X' : 'G'); /* Not all macs support a mac-level link state */ efx->mac_up = true; - return falcon_reset_macs(efx); + rc = falcon_reset_macs(efx); +out: + efx_stats_enable(efx); + return rc; } /* This call is responsible for hooking in the MAC and PHY operations */ diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 8eb411a1c82..e019ad1fb9a 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -754,8 +754,7 @@ union efx_multicast_hash { * &struct net_device_stats. * @stats_buffer: DMA buffer for statistics * @stats_lock: Statistics update lock. Serialises statistics fetches - * @stats_enabled: Temporarily disable statistics fetches. - * Serialised by @stats_lock + * @stats_disable_count: Nest count for disabling statistics fetches * @mac_op: MAC interface * @mac_address: Permanent MAC address * @phy_type: PHY type @@ -837,7 +836,7 @@ struct efx_nic { struct efx_mac_stats mac_stats; struct efx_buffer stats_buffer; spinlock_t stats_lock; - bool stats_enabled; + unsigned int stats_disable_count; struct efx_mac_operations *mac_op; unsigned char mac_address[ETH_ALEN]; diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c index 853057e87fb..cb25ae5b257 100644 --- a/drivers/net/sfc/sfe4001.c +++ b/drivers/net/sfc/sfe4001.c @@ -235,12 +235,18 @@ static ssize_t set_phy_flash_cfg(struct device *dev, } else if (efx->state != STATE_RUNNING || netif_running(efx->net_dev)) { err = -EBUSY; } else { + /* Reset the PHY, reconfigure the MAC and enable/disable + * MAC stats accordingly. */ efx->phy_mode = new_mode; + if (new_mode & PHY_MODE_SPECIAL) + efx_stats_disable(efx); if (efx->board_info.type == EFX_BOARD_SFE4001) err = sfe4001_poweron(efx); else err = sfn4111t_reset(efx); efx_reconfigure_port(efx); + if (!(new_mode & PHY_MODE_SPECIAL)) + efx_stats_enable(efx); } rtnl_unlock(); @@ -329,6 +335,11 @@ int sfe4001_init(struct efx_nic *efx) efx->board_info.monitor = sfe4001_check_hw; efx->board_info.fini = sfe4001_fini; + if (efx->phy_mode & PHY_MODE_SPECIAL) { + /* PHY won't generate a 156.25 MHz clock and MAC stats fetch + * will fail. */ + efx_stats_disable(efx); + } rc = sfe4001_poweron(efx); if (rc) goto fail_ioexp; @@ -405,8 +416,10 @@ int sfn4111t_init(struct efx_nic *efx) if (rc) goto fail_hwmon; - if (efx->phy_mode & PHY_MODE_SPECIAL) + if (efx->phy_mode & PHY_MODE_SPECIAL) { + efx_stats_disable(efx); sfn4111t_reset(efx); + } return 0; diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index 412d209d831..f0efd246962 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -370,8 +370,8 @@ static int tenxpress_special_reset(struct efx_nic *efx) /* The XGMAC clock is driven from the SFC7101/SFT9001 312MHz clock, so * a special software reset can glitch the XGMAC sufficiently for stats - * requests to fail. Since we don't often special_reset, just lock. */ - spin_lock(&efx->stats_lock); + * requests to fail. */ + efx_stats_disable(efx); /* Initiate reset */ reg = mdio_clause45_read(efx, efx->mii.phy_id, @@ -386,17 +386,17 @@ static int tenxpress_special_reset(struct efx_nic *efx) rc = mdio_clause45_wait_reset_mmds(efx, TENXPRESS_REQUIRED_DEVS); if (rc < 0) - goto unlock; + goto out; /* Try and reconfigure the device */ rc = tenxpress_init(efx); if (rc < 0) - goto unlock; + goto out; /* Wait for the XGXS state machine to churn */ mdelay(10); -unlock: - spin_unlock(&efx->stats_lock); +out: + efx_stats_enable(efx); return rc; } -- cgit v1.2.3 From bbd98fe48a43464b4a044bc4cbeefad284d6aa80 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Sat, 31 Jan 2009 00:52:30 -0800 Subject: igb: Fix DCA errors and do not use context index for 82576 82576 was being incorrectly flagged as needing a context index. It does not as each ring has it's own table of 2 contexts. Driver was registering after registering the driver instead of the other way around. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/igb/igb.h | 9 ++++----- drivers/net/igb/igb_main.c | 16 ++++++---------- 2 files changed, 10 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/net/igb/igb.h b/drivers/net/igb/igb.h index 5a27825cc48..aebef8e48e7 100644 --- a/drivers/net/igb/igb.h +++ b/drivers/net/igb/igb.h @@ -300,11 +300,10 @@ struct igb_adapter { #define IGB_FLAG_HAS_MSI (1 << 0) #define IGB_FLAG_MSI_ENABLE (1 << 1) -#define IGB_FLAG_HAS_DCA (1 << 2) -#define IGB_FLAG_DCA_ENABLED (1 << 3) -#define IGB_FLAG_IN_NETPOLL (1 << 5) -#define IGB_FLAG_QUAD_PORT_A (1 << 6) -#define IGB_FLAG_NEED_CTX_IDX (1 << 7) +#define IGB_FLAG_DCA_ENABLED (1 << 2) +#define IGB_FLAG_IN_NETPOLL (1 << 3) +#define IGB_FLAG_QUAD_PORT_A (1 << 4) +#define IGB_FLAG_NEED_CTX_IDX (1 << 5) enum e1000_state_t { __IGB_TESTING, diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index b82b0fb2056..cc94412ddf8 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -206,10 +206,11 @@ static int __init igb_init_module(void) global_quad_port_a = 0; - ret = pci_register_driver(&igb_driver); #ifdef CONFIG_IGB_DCA dca_register_notify(&dca_notifier); #endif + + ret = pci_register_driver(&igb_driver); return ret; } @@ -1156,11 +1157,10 @@ static int __devinit igb_probe(struct pci_dev *pdev, /* set flags */ switch (hw->mac.type) { - case e1000_82576: case e1000_82575: - adapter->flags |= IGB_FLAG_HAS_DCA; adapter->flags |= IGB_FLAG_NEED_CTX_IDX; break; + case e1000_82576: default: break; } @@ -1310,8 +1310,7 @@ static int __devinit igb_probe(struct pci_dev *pdev, goto err_register; #ifdef CONFIG_IGB_DCA - if ((adapter->flags & IGB_FLAG_HAS_DCA) && - (dca_add_requester(&pdev->dev) == 0)) { + if (dca_add_requester(&pdev->dev) == 0) { adapter->flags |= IGB_FLAG_DCA_ENABLED; dev_info(&pdev->dev, "DCA enabled\n"); /* Always use CB2 mode, difference is masked @@ -3473,19 +3472,16 @@ static int __igb_notify_dca(struct device *dev, void *data) struct e1000_hw *hw = &adapter->hw; unsigned long event = *(unsigned long *)data; - if (!(adapter->flags & IGB_FLAG_HAS_DCA)) - goto out; - switch (event) { case DCA_PROVIDER_ADD: /* if already enabled, don't do it again */ if (adapter->flags & IGB_FLAG_DCA_ENABLED) break; - adapter->flags |= IGB_FLAG_DCA_ENABLED; /* Always use CB2 mode, difference is masked * in the CB driver. */ wr32(E1000_DCA_CTRL, 2); if (dca_add_requester(dev) == 0) { + adapter->flags |= IGB_FLAG_DCA_ENABLED; dev_info(&adapter->pdev->dev, "DCA enabled\n"); igb_setup_dca(adapter); break; @@ -3502,7 +3498,7 @@ static int __igb_notify_dca(struct device *dev, void *data) } break; } -out: + return 0; } -- cgit v1.2.3 From ec54d7d6e40b04c16dfce0e41e506198a20c8645 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Sat, 31 Jan 2009 00:52:57 -0800 Subject: igb: prevent skb_over panic w/ mtu smaller than 1K A panic has been observed with frame sizes smaller than 1K. This has been root caused to the hardware spanning larger frames across multiple buffers and then reporting the original frame size in the first descriptor. To prevent this we can enable set the LPE bit which in turn will restrict packet sizes to those set in the RLPML register. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/igb/igb_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index cc94412ddf8..a50db5398fa 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -1834,11 +1834,11 @@ static void igb_setup_rctl(struct igb_adapter *adapter) rctl |= E1000_RCTL_SECRC; /* - * disable store bad packets, long packet enable, and clear size bits. + * disable store bad packets and clear size bits. */ - rctl &= ~(E1000_RCTL_SBP | E1000_RCTL_LPE | E1000_RCTL_SZ_256); + rctl &= ~(E1000_RCTL_SBP | E1000_RCTL_SZ_256); - if (adapter->netdev->mtu > ETH_DATA_LEN) + /* enable LPE when to prevent packets larger than max_frame_size */ rctl |= E1000_RCTL_LPE; /* Setup buffer sizes */ @@ -1864,7 +1864,7 @@ static void igb_setup_rctl(struct igb_adapter *adapter) */ /* allocations using alloc_page take too long for regular MTU * so only enable packet split for jumbo frames */ - if (rctl & E1000_RCTL_LPE) { + if (adapter->netdev->mtu > ETH_DATA_LEN) { adapter->rx_ps_hdr_size = IGB_RXBUFFER_128; srrctl |= adapter->rx_ps_hdr_size << E1000_SRRCTL_BSIZEHDRSIZE_SHIFT; -- cgit v1.2.3 From 5d0932a5dd00d83df5d1e15eeffb6edf015a8579 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Sat, 31 Jan 2009 00:53:18 -0800 Subject: igb: fix link reporting when using sgmii When using sgmii the link was not being properly passed up to the driver from the underlying link management functions. This change corrects it so that get_link_status is cleared when a link has been found. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: David S. Miller --- drivers/net/igb/e1000_82575.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/igb/e1000_82575.c b/drivers/net/igb/e1000_82575.c index f5e2e7235fc..13ca73f96ec 100644 --- a/drivers/net/igb/e1000_82575.c +++ b/drivers/net/igb/e1000_82575.c @@ -699,11 +699,18 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw) /* SGMII link check is done through the PCS register. */ if ((hw->phy.media_type != e1000_media_type_copper) || - (igb_sgmii_active_82575(hw))) + (igb_sgmii_active_82575(hw))) { ret_val = igb_get_pcs_speed_and_duplex_82575(hw, &speed, &duplex); - else + /* + * Use this flag to determine if link needs to be checked or + * not. If we have link clear the flag so that we do not + * continue to check for link. + */ + hw->mac.get_link_status = !hw->mac.serdes_has_link; + } else { ret_val = igb_check_for_copper_link(hw); + } return ret_val; } -- cgit v1.2.3 From fc8744adc870a8d4366908221508bb113d8b72ee Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 31 Jan 2009 15:08:56 -0800 Subject: Stop playing silly games with the VM_ACCOUNT flag The mmap_region() code would temporarily set the VM_ACCOUNT flag for anonymous shared mappings just to inform shmem_zero_setup() that it should enable accounting for the resulting shm object. It would then clear the flag after calling ->mmap (for the /dev/zero case) or doing shmem_zero_setup() (for the MAP_ANON case). This just resulted in vma merge issues, but also made for just unnecessary confusion. Use the already-existing VM_NORESERVE flag for this instead, and let shmem_{zero|file}_setup() just figure it out from that. This also happens to make it obvious that the new DRI2 GEM layer uses a non-reserving backing store for its object allocation - which is quite possibly not intentional. But since I didn't want to change semantics in this patch, I left it alone, and just updated the caller to use the new flag semantics. Signed-off-by: Linus Torvalds --- drivers/gpu/drm/drm_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 9da58145287..6915fb82d0b 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -136,7 +136,7 @@ drm_gem_object_alloc(struct drm_device *dev, size_t size) obj = kcalloc(1, sizeof(*obj), GFP_KERNEL); obj->dev = dev; - obj->filp = shmem_file_setup("drm mm object", size, 0); + obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); if (IS_ERR(obj->filp)) { kfree(obj); return NULL; -- cgit v1.2.3 From 878b8619f711280fd05845e21956434b5e588cc4 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Fri, 30 Jan 2009 15:27:14 -0500 Subject: Fix memory corruption in console selection Fix an off-by-two memory error in console selection. The loop below goes from sel_start to sel_end (inclusive), so it writes one more character. This one more character was added to the allocated size (+1), but it was not multiplied by an UTF-8 multiplier. This patch fixes a memory corruption when UTF-8 console is used and the user selects a few characters, all of them 3-byte in UTF-8 (for example a frame line). When memory redzones are enabled, a redzone corruption is reported. When they are not enabled, trashing of random memory occurs. Signed-off-by: Mikulas Patocka Signed-off-by: Linus Torvalds --- drivers/char/selection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/selection.c b/drivers/char/selection.c index f29fbe9b8ed..cb8ca569896 100644 --- a/drivers/char/selection.c +++ b/drivers/char/selection.c @@ -268,7 +268,7 @@ int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *t /* Allocate a new buffer before freeing the old one ... */ multiplier = use_unicode ? 3 : 1; /* chars can take up to 3 bytes */ - bp = kmalloc((sel_end-sel_start)/2*multiplier+1, GFP_KERNEL); + bp = kmalloc(((sel_end-sel_start)/2+1)*multiplier, GFP_KERNEL); if (!bp) { printk(KERN_WARNING "selection: kmalloc() failed\n"); clear_selection(); -- cgit v1.2.3