From 48ce8b056c88920c8ac187781048f5dae33c81b9 Mon Sep 17 00:00:00 2001 From: Evgeniy Dushistov Date: Mon, 5 Jun 2006 08:21:03 -0500 Subject: JFS: commit_mutex cleanups I look at code, and see that 1)locks wasn't release in the opposite order in which they were taken 2)in jfs_rename we lock new_ip, and in "error path" we didn't unlock it 3)I see strange expression: "! !" May be this worth to fix? Signed-off-by: Evgeniy Dushistov Signed-off-by: Dave Kleikamp --- fs/jfs/jfs_txnmgr.c | 2 +- fs/jfs/namei.c | 33 ++++++++++++++++----------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c index ac3d66948e8c..49618dd94f9a 100644 --- a/fs/jfs/jfs_txnmgr.c +++ b/fs/jfs/jfs_txnmgr.c @@ -2944,7 +2944,7 @@ int jfs_sync(void *arg) * Inode is being freed */ list_del_init(&jfs_ip->anon_inode_list); - } else if (! !mutex_trylock(&jfs_ip->commit_mutex)) { + } else if (mutex_trylock(&jfs_ip->commit_mutex)) { /* * inode will be removed from anonymous list * when it is committed diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c index 09ea03f62277..295268ad231b 100644 --- a/fs/jfs/namei.c +++ b/fs/jfs/namei.c @@ -165,8 +165,8 @@ static int jfs_create(struct inode *dip, struct dentry *dentry, int mode, out3: txEnd(tid); - mutex_unlock(&JFS_IP(dip)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); + mutex_unlock(&JFS_IP(dip)->commit_mutex); if (rc) { free_ea_wmap(ip); ip->i_nlink = 0; @@ -300,8 +300,8 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, int mode) out3: txEnd(tid); - mutex_unlock(&JFS_IP(dip)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); + mutex_unlock(&JFS_IP(dip)->commit_mutex); if (rc) { free_ea_wmap(ip); ip->i_nlink = 0; @@ -384,8 +384,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry) if (rc == -EIO) txAbort(tid, 1); txEnd(tid); - mutex_unlock(&JFS_IP(dip)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); + mutex_unlock(&JFS_IP(dip)->commit_mutex); goto out2; } @@ -422,8 +422,8 @@ static int jfs_rmdir(struct inode *dip, struct dentry *dentry) txEnd(tid); - mutex_unlock(&JFS_IP(dip)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); + mutex_unlock(&JFS_IP(dip)->commit_mutex); /* * Truncating the directory index table is not guaranteed. It @@ -503,8 +503,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry) if (rc == -EIO) txAbort(tid, 1); /* Marks FS Dirty */ txEnd(tid); - mutex_unlock(&JFS_IP(dip)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); + mutex_unlock(&JFS_IP(dip)->commit_mutex); IWRITE_UNLOCK(ip); goto out1; } @@ -527,8 +527,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry) if ((new_size = commitZeroLink(tid, ip)) < 0) { txAbort(tid, 1); /* Marks FS Dirty */ txEnd(tid); - mutex_unlock(&JFS_IP(dip)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); + mutex_unlock(&JFS_IP(dip)->commit_mutex); IWRITE_UNLOCK(ip); rc = new_size; goto out1; @@ -556,9 +556,8 @@ static int jfs_unlink(struct inode *dip, struct dentry *dentry) txEnd(tid); - mutex_unlock(&JFS_IP(dip)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); - + mutex_unlock(&JFS_IP(dip)->commit_mutex); while (new_size && (rc == 0)) { tid = txBegin(dip->i_sb, 0); @@ -847,8 +846,8 @@ static int jfs_link(struct dentry *old_dentry, out: txEnd(tid); - mutex_unlock(&JFS_IP(dir)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); + mutex_unlock(&JFS_IP(dir)->commit_mutex); jfs_info("jfs_link: rc:%d", rc); return rc; @@ -1037,8 +1036,8 @@ static int jfs_symlink(struct inode *dip, struct dentry *dentry, out3: txEnd(tid); - mutex_unlock(&JFS_IP(dip)->commit_mutex); mutex_unlock(&JFS_IP(ip)->commit_mutex); + mutex_unlock(&JFS_IP(dip)->commit_mutex); if (rc) { free_ea_wmap(ip); ip->i_nlink = 0; @@ -1160,10 +1159,11 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (S_ISDIR(new_ip->i_mode)) { new_ip->i_nlink--; if (new_ip->i_nlink) { - mutex_unlock(&JFS_IP(new_dir)->commit_mutex); - mutex_unlock(&JFS_IP(old_ip)->commit_mutex); + mutex_unlock(&JFS_IP(new_ip)->commit_mutex); if (old_dir != new_dir) mutex_unlock(&JFS_IP(old_dir)->commit_mutex); + mutex_unlock(&JFS_IP(old_ip)->commit_mutex); + mutex_unlock(&JFS_IP(new_dir)->commit_mutex); if (!S_ISDIR(old_ip->i_mode) && new_ip) IWRITE_UNLOCK(new_ip); jfs_error(new_ip->i_sb, @@ -1281,13 +1281,12 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry, out4: txEnd(tid); - - mutex_unlock(&JFS_IP(new_dir)->commit_mutex); - mutex_unlock(&JFS_IP(old_ip)->commit_mutex); - if (old_dir != new_dir) - mutex_unlock(&JFS_IP(old_dir)->commit_mutex); if (new_ip) mutex_unlock(&JFS_IP(new_ip)->commit_mutex); + if (old_dir != new_dir) + mutex_unlock(&JFS_IP(old_dir)->commit_mutex); + mutex_unlock(&JFS_IP(old_ip)->commit_mutex); + mutex_unlock(&JFS_IP(new_dir)->commit_mutex); while (new_size && (rc == 0)) { tid = txBegin(new_ip->i_sb, 0); -- cgit v1.2.3 From a487a8f7b6947f6445e5a1aa876522916785d5ec Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Fri, 9 Jun 2006 11:29:03 -0700 Subject: e1000: prevent statistics from garbling during bus resets If a PCI bus error/fault triggers a PCI bus reset, attempts to get the ethernet packet count statistics from the hardware will fail, returning garbage data upstream. This patch skips statistics data collection if the PCI device is not on the bus. Signed-off-by: Linas Vepstas Cc: Jesse Brandeburg Signed-off-by: Andrew Morton Acked-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 56c7492e3e91..a373ccb308d8 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -3045,14 +3045,20 @@ void e1000_update_stats(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; + struct pci_dev *pdev = adapter->pdev; unsigned long flags; uint16_t phy_tmp; #define PHY_IDLE_ERROR_COUNT_MASK 0x00FF - /* Prevent stats update while adapter is being reset */ + /* + * Prevent stats update while adapter is being reset, or if the pci + * connection is down. + */ if (adapter->link_speed == 0) return; + if (pdev->error_state && pdev->error_state != pci_channel_io_normal) + return; spin_lock_irqsave(&adapter->stats_lock, flags); -- cgit v1.2.3 From acfbc9fde2ec7f304398f6ad7644002e07bf84bc Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:24 -0700 Subject: e1000: fix loopback ethtool test Ethtool was reporting that loopback failed randomly on esb2 systems. Upon study it was found that the phy manual was changed with respect to the loopback mode bits. The new value fixes it. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 6ed7f599eba3..845d293b2f6a 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -1246,7 +1246,7 @@ e1000_integrated_phy_loopback(struct e1000_adapter *adapter) } else if (adapter->hw.phy_type == e1000_phy_gg82563) { e1000_write_phy_reg(&adapter->hw, GG82563_PHY_KMRN_MODE_CTRL, - 0x1CE); + 0x1CC); } /* force 1000, set loopback */ e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x4140); -- cgit v1.2.3 From 2db10a081c5c1082d58809a1bcf1a6073f4db160 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:28 -0700 Subject: e1000: rework driver hardware reset locking After studying the driver mac reset code it was found that there were multiple race conditions possible to reset the unit twice or bring it e1000_up() double. This fixes all occurences where the driver needs to reset the mac. We also remove irq requesting/releasing into _open and _close so that while the device is _up we will never touch the irq's. This fixes the double free irq bug that people saw. To make sure that the watchdog task doesn't cause another race we let it run as a non-scheduled task. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000.h | 8 ++- drivers/net/e1000/e1000_ethtool.c | 46 ++++++++------ drivers/net/e1000/e1000_main.c | 126 ++++++++++++++++++++++---------------- 3 files changed, 105 insertions(+), 75 deletions(-) diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 2bc34fbfa69c..2b96ad0482ef 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -69,7 +69,6 @@ #ifdef NETIF_F_TSO #include #endif -#include #include #include #include @@ -255,7 +254,6 @@ struct e1000_adapter { spinlock_t tx_queue_lock; #endif atomic_t irq_sem; - struct work_struct watchdog_task; struct work_struct reset_task; uint8_t fc_autoneg; @@ -340,8 +338,13 @@ struct e1000_adapter { #ifdef NETIF_F_TSO boolean_t tso_force; #endif + unsigned long flags; }; +enum e1000_state_t { + __E1000_DRIVER_TESTING, + __E1000_RESETTING, +}; /* e1000_main.c */ extern char e1000_driver_name[]; @@ -349,6 +352,7 @@ extern char e1000_driver_version[]; int e1000_up(struct e1000_adapter *adapter); void e1000_down(struct e1000_adapter *adapter); void e1000_reset(struct e1000_adapter *adapter); +void e1000_reinit_locked(struct e1000_adapter *adapter); int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); void e1000_free_all_tx_resources(struct e1000_adapter *adapter); int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 845d293b2f6a..cf5c5f46341f 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -203,11 +203,9 @@ e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) /* reset the link */ - if (netif_running(adapter->netdev)) { - e1000_down(adapter); - e1000_reset(adapter); - e1000_up(adapter); - } else + if (netif_running(adapter->netdev)) + e1000_reinit_locked(adapter); + else e1000_reset(adapter); return 0; @@ -254,10 +252,9 @@ e1000_set_pauseparam(struct net_device *netdev, hw->original_fc = hw->fc; if (adapter->fc_autoneg == AUTONEG_ENABLE) { - if (netif_running(adapter->netdev)) { - e1000_down(adapter); - e1000_up(adapter); - } else + if (netif_running(adapter->netdev)) + e1000_reinit_locked(adapter); + else e1000_reset(adapter); } else return ((hw->media_type == e1000_media_type_fiber) ? @@ -279,10 +276,9 @@ e1000_set_rx_csum(struct net_device *netdev, uint32_t data) struct e1000_adapter *adapter = netdev_priv(netdev); adapter->rx_csum = data; - if (netif_running(netdev)) { - e1000_down(adapter); - e1000_up(adapter); - } else + if (netif_running(netdev)) + e1000_reinit_locked(adapter); + else e1000_reset(adapter); return 0; } @@ -631,6 +627,9 @@ e1000_set_ringparam(struct net_device *netdev, tx_ring_size = sizeof(struct e1000_tx_ring) * adapter->num_tx_queues; rx_ring_size = sizeof(struct e1000_rx_ring) * adapter->num_rx_queues; + while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) + msleep(1); + if (netif_running(adapter->netdev)) e1000_down(adapter); @@ -691,9 +690,11 @@ e1000_set_ringparam(struct net_device *netdev, adapter->rx_ring = rx_new; adapter->tx_ring = tx_new; if ((err = e1000_up(adapter))) - return err; + goto err_setup; } + clear_bit(__E1000_RESETTING, &adapter->flags); + return 0; err_setup_tx: e1000_free_all_rx_resources(adapter); @@ -701,6 +702,8 @@ err_setup_rx: adapter->rx_ring = rx_old; adapter->tx_ring = tx_old; e1000_up(adapter); +err_setup: + clear_bit(__E1000_RESETTING, &adapter->flags); return err; } @@ -1568,6 +1571,7 @@ e1000_diag_test(struct net_device *netdev, struct e1000_adapter *adapter = netdev_priv(netdev); boolean_t if_running = netif_running(netdev); + set_bit(__E1000_DRIVER_TESTING, &adapter->flags); if (eth_test->flags == ETH_TEST_FL_OFFLINE) { /* Offline tests */ @@ -1582,7 +1586,8 @@ e1000_diag_test(struct net_device *netdev, eth_test->flags |= ETH_TEST_FL_FAILED; if (if_running) - e1000_down(adapter); + /* indicate we're in test mode */ + dev_close(netdev); else e1000_reset(adapter); @@ -1607,8 +1612,9 @@ e1000_diag_test(struct net_device *netdev, adapter->hw.autoneg = autoneg; e1000_reset(adapter); + clear_bit(__E1000_DRIVER_TESTING, &adapter->flags); if (if_running) - e1000_up(adapter); + dev_open(netdev); } else { /* Online tests */ if (e1000_link_test(adapter, &data[4])) @@ -1619,6 +1625,8 @@ e1000_diag_test(struct net_device *netdev, data[1] = 0; data[2] = 0; data[3] = 0; + + clear_bit(__E1000_DRIVER_TESTING, &adapter->flags); } msleep_interruptible(4 * 1000); } @@ -1807,10 +1815,8 @@ static int e1000_nway_reset(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); - if (netif_running(netdev)) { - e1000_down(adapter); - e1000_up(adapter); - } + if (netif_running(netdev)) + e1000_reinit_locked(adapter); return 0; } diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index a373ccb308d8..52d698bb2595 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -133,7 +133,6 @@ static void e1000_clean_rx_ring(struct e1000_adapter *adapter, static void e1000_set_multi(struct net_device *netdev); static void e1000_update_phy_info(unsigned long data); static void e1000_watchdog(unsigned long data); -static void e1000_watchdog_task(struct e1000_adapter *adapter); static void e1000_82547_tx_fifo_stall(unsigned long data); static int e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev); static struct net_device_stats * e1000_get_stats(struct net_device *netdev); @@ -261,6 +260,44 @@ e1000_exit_module(void) module_exit(e1000_exit_module); +static int e1000_request_irq(struct e1000_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + int flags, err = 0; + + flags = SA_SHIRQ | SA_SAMPLE_RANDOM; +#ifdef CONFIG_PCI_MSI + if (adapter->hw.mac_type > e1000_82547_rev_2) { + adapter->have_msi = TRUE; + if ((err = pci_enable_msi(adapter->pdev))) { + DPRINTK(PROBE, ERR, + "Unable to allocate MSI interrupt Error: %d\n", err); + adapter->have_msi = FALSE; + } + } + if (adapter->have_msi) + flags &= ~SA_SHIRQ; +#endif + if ((err = request_irq(adapter->pdev->irq, &e1000_intr, flags, + netdev->name, netdev))) + DPRINTK(PROBE, ERR, + "Unable to allocate interrupt Error: %d\n", err); + + return err; +} + +static void e1000_free_irq(struct e1000_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + free_irq(adapter->pdev->irq, netdev); + +#ifdef CONFIG_PCI_MSI + if (adapter->have_msi) + pci_disable_msi(adapter->pdev); +#endif +} + /** * e1000_irq_disable - Mask off interrupt generation on the NIC * @adapter: board private structure @@ -387,7 +424,7 @@ int e1000_up(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; - int i, err; + int i; /* hardware has been reset, we need to reload some things */ @@ -415,24 +452,6 @@ e1000_up(struct e1000_adapter *adapter) E1000_DESC_UNUSED(ring)); } -#ifdef CONFIG_PCI_MSI - if (adapter->hw.mac_type > e1000_82547_rev_2) { - adapter->have_msi = TRUE; - if ((err = pci_enable_msi(adapter->pdev))) { - DPRINTK(PROBE, ERR, - "Unable to allocate MSI interrupt Error: %d\n", err); - adapter->have_msi = FALSE; - } - } -#endif - if ((err = request_irq(adapter->pdev->irq, &e1000_intr, - SA_SHIRQ | SA_SAMPLE_RANDOM, - netdev->name, netdev))) { - DPRINTK(PROBE, ERR, - "Unable to allocate interrupt Error: %d\n", err); - return err; - } - adapter->tx_queue_len = netdev->tx_queue_len; mod_timer(&adapter->watchdog_timer, jiffies); @@ -450,16 +469,10 @@ e1000_down(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; boolean_t mng_mode_enabled = (adapter->hw.mac_type >= e1000_82571) && - e1000_check_mng_mode(&adapter->hw); + e1000_check_mng_mode(&adapter->hw); e1000_irq_disable(adapter); - free_irq(adapter->pdev->irq, netdev); -#ifdef CONFIG_PCI_MSI - if (adapter->hw.mac_type > e1000_82547_rev_2 && - adapter->have_msi == TRUE) - pci_disable_msi(adapter->pdev); -#endif del_timer_sync(&adapter->tx_fifo_stall_timer); del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_info_timer); @@ -495,6 +508,17 @@ e1000_down(struct e1000_adapter *adapter) } } +void +e1000_reinit_locked(struct e1000_adapter *adapter) +{ + WARN_ON(in_interrupt()); + while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) + msleep(1); + e1000_down(adapter); + e1000_up(adapter); + clear_bit(__E1000_RESETTING, &adapter->flags); +} + void e1000_reset(struct e1000_adapter *adapter) { @@ -758,9 +782,6 @@ e1000_probe(struct pci_dev *pdev, adapter->watchdog_timer.function = &e1000_watchdog; adapter->watchdog_timer.data = (unsigned long) adapter; - INIT_WORK(&adapter->watchdog_task, - (void (*)(void *))e1000_watchdog_task, adapter); - init_timer(&adapter->phy_info_timer); adapter->phy_info_timer.function = &e1000_update_phy_info; adapter->phy_info_timer.data = (unsigned long) adapter; @@ -1078,6 +1099,10 @@ e1000_open(struct net_device *netdev) struct e1000_adapter *adapter = netdev_priv(netdev); int err; + /* disallow open during test */ + if (test_bit(__E1000_DRIVER_TESTING, &adapter->flags)) + return -EBUSY; + /* allocate transmit descriptors */ if ((err = e1000_setup_all_tx_resources(adapter))) @@ -1088,6 +1113,10 @@ e1000_open(struct net_device *netdev) if ((err = e1000_setup_all_rx_resources(adapter))) goto err_setup_rx; + err = e1000_request_irq(adapter); + if (err) + goto err_up; + if ((err = e1000_up(adapter))) goto err_up; adapter->mng_vlan_id = E1000_MNG_VLAN_NONE; @@ -1131,7 +1160,9 @@ e1000_close(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); + WARN_ON(test_bit(__E1000_RESETTING, &adapter->flags)); e1000_down(adapter); + e1000_free_irq(adapter); e1000_free_all_tx_resources(adapter); e1000_free_all_rx_resources(adapter); @@ -2201,14 +2232,6 @@ static void e1000_watchdog(unsigned long data) { struct e1000_adapter *adapter = (struct e1000_adapter *) data; - - /* Do the rest outside of interrupt context */ - schedule_work(&adapter->watchdog_task); -} - -static void -e1000_watchdog_task(struct e1000_adapter *adapter) -{ struct net_device *netdev = adapter->netdev; struct e1000_tx_ring *txdr = adapter->tx_ring; uint32_t link, tctl; @@ -2919,8 +2942,7 @@ e1000_reset_task(struct net_device *netdev) { struct e1000_adapter *adapter = netdev_priv(netdev); - e1000_down(adapter); - e1000_up(adapter); + e1000_reinit_locked(adapter); } /** @@ -3026,10 +3048,8 @@ e1000_change_mtu(struct net_device *netdev, int new_mtu) netdev->mtu = new_mtu; - if (netif_running(netdev)) { - e1000_down(adapter); - e1000_up(adapter); - } + if (netif_running(netdev)) + e1000_reinit_locked(adapter); adapter->hw.max_frame_size = max_frame; @@ -4180,10 +4200,9 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) return retval; } } - if (netif_running(adapter->netdev)) { - e1000_down(adapter); - e1000_up(adapter); - } else + if (netif_running(adapter->netdev)) + e1000_reinit_locked(adapter); + else e1000_reset(adapter); break; case M88E1000_PHY_SPEC_CTRL: @@ -4200,10 +4219,9 @@ e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) case PHY_CTRL: if (mii_reg & MII_CR_POWER_DOWN) break; - if (netif_running(adapter->netdev)) { - e1000_down(adapter); - e1000_up(adapter); - } else + if (netif_running(adapter->netdev)) + e1000_reinit_locked(adapter); + else e1000_reset(adapter); break; } @@ -4462,8 +4480,10 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) netif_device_detach(netdev); - if (netif_running(netdev)) + if (netif_running(netdev)) { + WARN_ON(test_bit(__E1000_RESETTING, &adapter->flags)); e1000_down(adapter); + } #ifdef CONFIG_PM /* Implement our own version of pci_save_state(pdev) because pci- -- cgit v1.2.3 From 79f05bf0ba9b41561600f3cdfdf86b5184fc6675 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:32 -0700 Subject: e1000: Make PHY powerup/down a function In relation to the irq work done earlier we also move the PHY powerup and powerdown functions into separate functions and move the calls to _close and _open, making the PHY stay in it's power state as long as the device is _up. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 76 +++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 52d698bb2595..813d5e0e458d 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -428,14 +428,6 @@ e1000_up(struct e1000_adapter *adapter) /* hardware has been reset, we need to reload some things */ - /* Reset the PHY if it was previously powered down */ - if (adapter->hw.media_type == e1000_media_type_copper) { - uint16_t mii_reg; - e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg); - if (mii_reg & MII_CR_POWER_DOWN) - e1000_phy_hw_reset(&adapter->hw); - } - e1000_set_multi(netdev); e1000_restore_vlan(adapter); @@ -464,12 +456,56 @@ e1000_up(struct e1000_adapter *adapter) return 0; } +/** + * e1000_power_up_phy - restore link in case the phy was powered down + * @adapter: address of board private structure + * + * The phy may be powered down to save power and turn off link when the + * driver is unloaded and wake on lan is not enabled (among others) + * *** this routine MUST be followed by a call to e1000_reset *** + * + **/ + +static void e1000_power_up_phy(struct e1000_adapter *adapter) +{ + uint16_t mii_reg = 0; + + /* Just clear the power down bit to wake the phy back up */ + if (adapter->hw.media_type == e1000_media_type_copper) { + /* according to the manual, the phy will retain its + * settings across a power-down/up cycle */ + e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg); + mii_reg &= ~MII_CR_POWER_DOWN; + e1000_write_phy_reg(&adapter->hw, PHY_CTRL, mii_reg); + } +} + +static void e1000_power_down_phy(struct e1000_adapter *adapter) +{ + boolean_t mng_mode_enabled = (adapter->hw.mac_type >= e1000_82571) && + e1000_check_mng_mode(&adapter->hw); + /* Power down the PHY so no link is implied when interface is down + * The PHY cannot be powered down if any of the following is TRUE + * (a) WoL is enabled + * (b) AMT is active + * (c) SoL/IDER session is active */ + if (!adapter->wol && adapter->hw.mac_type >= e1000_82540 && + adapter->hw.media_type == e1000_media_type_copper && + !(E1000_READ_REG(&adapter->hw, MANC) & E1000_MANC_SMBUS_EN) && + !mng_mode_enabled && + !e1000_check_phy_reset_block(&adapter->hw)) { + uint16_t mii_reg = 0; + e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg); + mii_reg |= MII_CR_POWER_DOWN; + e1000_write_phy_reg(&adapter->hw, PHY_CTRL, mii_reg); + mdelay(1); + } +} + void e1000_down(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; - boolean_t mng_mode_enabled = (adapter->hw.mac_type >= e1000_82571) && - e1000_check_mng_mode(&adapter->hw); e1000_irq_disable(adapter); @@ -489,23 +525,6 @@ e1000_down(struct e1000_adapter *adapter) e1000_reset(adapter); e1000_clean_all_tx_rings(adapter); e1000_clean_all_rx_rings(adapter); - - /* Power down the PHY so no link is implied when interface is down * - * The PHY cannot be powered down if any of the following is TRUE * - * (a) WoL is enabled - * (b) AMT is active - * (c) SoL/IDER session is active */ - if (!adapter->wol && adapter->hw.mac_type >= e1000_82540 && - adapter->hw.media_type == e1000_media_type_copper && - !(E1000_READ_REG(&adapter->hw, MANC) & E1000_MANC_SMBUS_EN) && - !mng_mode_enabled && - !e1000_check_phy_reset_block(&adapter->hw)) { - uint16_t mii_reg; - e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg); - mii_reg |= MII_CR_POWER_DOWN; - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, mii_reg); - mdelay(1); - } } void @@ -1117,6 +1136,8 @@ e1000_open(struct net_device *netdev) if (err) goto err_up; + e1000_power_up_phy(adapter); + if ((err = e1000_up(adapter))) goto err_up; adapter->mng_vlan_id = E1000_MNG_VLAN_NONE; @@ -1162,6 +1183,7 @@ e1000_close(struct net_device *netdev) WARN_ON(test_bit(__E1000_RESETTING, &adapter->flags)); e1000_down(adapter); + e1000_power_down_phy(adapter); e1000_free_irq(adapter); e1000_free_all_tx_resources(adapter); -- cgit v1.2.3 From 6fdfef162426766611b1f640138e4720f56e45f8 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:36 -0700 Subject: e1000: fix CONFIG_PM blocks e1000_suspend is called even when !CONFIG_PM. The non-PM code inside of it is properly #ifdef'd. This fixes the compiler warnings when !CONFIG_PM. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 813d5e0e458d..7cf5e5dce0da 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -177,8 +177,8 @@ static void e1000_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid); static void e1000_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid); static void e1000_restore_vlan(struct e1000_adapter *adapter); -#ifdef CONFIG_PM static int e1000_suspend(struct pci_dev *pdev, pm_message_t state); +#ifdef CONFIG_PM static int e1000_resume(struct pci_dev *pdev); #endif static void e1000_shutdown(struct pci_dev *pdev); @@ -205,8 +205,8 @@ static struct pci_driver e1000_driver = { .probe = e1000_probe, .remove = __devexit_p(e1000_remove), /* Power Managment Hooks */ -#ifdef CONFIG_PM .suspend = e1000_suspend, +#ifdef CONFIG_PM .resume = e1000_resume, #endif .shutdown = e1000_shutdown, @@ -4498,7 +4498,9 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) struct e1000_adapter *adapter = netdev_priv(netdev); uint32_t ctrl, ctrl_ext, rctl, manc, status; uint32_t wufc = adapter->wol; +#ifdef CONFIG_PM int retval = 0; +#endif netif_device_detach(netdev); -- cgit v1.2.3 From 0cbabbb0725fa34a37d6b7870b2d2fe17d1a0a94 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:41 -0700 Subject: e1000: small performance tweak by removing double code buffer_info is already filled at the end of this while() loop. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 7cf5e5dce0da..76e36a4fd985 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -3717,7 +3717,6 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, buffer_info = &rx_ring->buffer_info[i]; while (staterr & E1000_RXD_STAT_DD) { - buffer_info = &rx_ring->buffer_info[i]; ps_page = &rx_ring->ps_page[i]; ps_page_dma = &rx_ring->ps_page_dma[i]; #ifdef CONFIG_E1000_NAPI -- cgit v1.2.3 From 9a53a2029885e0088e9149679215b95d04deb57b Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:45 -0700 Subject: e1000: add smart power down code Smart Power Down is a power saving feature in newer e1000 hardware. We disable it because it causes time to link to be long, but make it a user choice. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000.h | 1 + drivers/net/e1000/e1000_main.c | 15 +++++++++++++++ drivers/net/e1000/e1000_param.c | 25 +++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 2b96ad0482ef..dbdaa332cac6 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -338,6 +338,7 @@ struct e1000_adapter { #ifdef NETIF_F_TSO boolean_t tso_force; #endif + boolean_t smart_power_down; /* phy smart power down */ unsigned long flags; }; diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 76e36a4fd985..ea18f3085f69 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -607,6 +607,21 @@ e1000_reset(struct e1000_adapter *adapter) e1000_reset_adaptive(&adapter->hw); e1000_phy_get_info(&adapter->hw, &adapter->phy_info); + + if (!adapter->smart_power_down && + (adapter->hw.mac_type == e1000_82571 || + adapter->hw.mac_type == e1000_82572)) { + uint16_t phy_data = 0; + /* speed up time to link by disabling smart power down, ignore + * the return value of this function because there is nothing + * different we would do if it failed */ + e1000_read_phy_reg(&adapter->hw, IGP02E1000_PHY_POWER_MGMT, + &phy_data); + phy_data &= ~IGP02E1000_PM_SPD; + e1000_write_phy_reg(&adapter->hw, IGP02E1000_PHY_POWER_MGMT, + phy_data); + } + if (adapter->en_mng_pt) { manc = E1000_READ_REG(&adapter->hw, MANC); manc |= (E1000_MANC_ARP_EN | E1000_MANC_EN_MNG2HOST); diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c index e55f8969a0fb..a8d9295b0779 100644 --- a/drivers/net/e1000/e1000_param.c +++ b/drivers/net/e1000/e1000_param.c @@ -183,6 +183,15 @@ E1000_PARAM(RxAbsIntDelay, "Receive Absolute Interrupt Delay"); E1000_PARAM(InterruptThrottleRate, "Interrupt Throttling Rate"); +/* Enable Smart Power Down of the PHY + * + * Valid Range: 0, 1 + * + * Default Value: 0 (disabled) + */ + +E1000_PARAM(SmartPowerDownEnable, "Enable PHY smart power down"); + #define AUTONEG_ADV_DEFAULT 0x2F #define AUTONEG_ADV_MASK 0x2F #define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL @@ -494,6 +503,22 @@ e1000_check_options(struct e1000_adapter *adapter) adapter->itr = opt.def; } } + { /* Smart Power Down */ + struct e1000_option opt = { + .type = enable_option, + .name = "PHY Smart Power Down", + .err = "defaulting to Disabled", + .def = OPTION_DISABLED + }; + + if (num_SmartPowerDownEnable > bd) { + int spd = SmartPowerDownEnable[bd]; + e1000_validate_option(&spd, &opt, adapter); + adapter->smart_power_down = spd; + } else { + adapter->smart_power_down = opt.def; + } + } switch (adapter->hw.media_type) { case e1000_media_type_fiber: -- cgit v1.2.3 From a5eafce2ce2fd3a1956657d85908ca691b870e5d Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:49 -0700 Subject: e1000: change printk into DPRINTK Changing a printk message to make clear that this message is originating from e1000. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index ea18f3085f69..fc39cbcee2bc 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -2841,7 +2841,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) case e1000_82573: pull_size = min((unsigned int)4, skb->data_len); if (!__pskb_pull_tail(skb, pull_size)) { - printk(KERN_ERR + DPRINTK(DRV, ERR, "__pskb_pull_tail failed.\n"); dev_kfree_skb_any(skb); return NETDEV_TX_OK; -- cgit v1.2.3 From 864c4e45ec6d1bfe16a14d887d5baad201419aba Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:53 -0700 Subject: e1000: recycle skb Recycle an skb to improve performance a bit. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index fc39cbcee2bc..c58fafd1c4be 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -3604,7 +3604,8 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter, /* All receives must fit into a single buffer */ E1000_DBG("%s: Receive packet consumed multiple" " buffers\n", netdev->name); - dev_kfree_skb_irq(skb); + /* recycle */ + buffer_info-> skb = skb; goto next_desc; } -- cgit v1.2.3 From 17231712ab89fa085bc235ef19c1e6e0c163e4fc Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:06:59 -0700 Subject: e1000: rework module param code with uninitialized values We can take uninitialized values into account which minimizes code and allows us to simplify the parameter checking code greatly. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_param.c | 167 +++++++++++++++------------------------- 1 file changed, 61 insertions(+), 106 deletions(-) diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c index a8d9295b0779..bd6c040b3cfb 100644 --- a/drivers/net/e1000/e1000_param.c +++ b/drivers/net/e1000/e1000_param.c @@ -45,6 +45,16 @@ */ #define E1000_PARAM_INIT { [0 ... E1000_MAX_NIC] = OPTION_UNSET } +/* Module Parameters are always initialized to -1, so that the driver + * can tell the difference between no user specified value or the + * user asking for the default value. + * The true default values are loaded in when e1000_check_options is called. + * + * This is a GCC extension to ANSI C. + * See the item "Labeled Elements in Initializers" in the section + * "Extensions to the C Language Family" of the GCC documentation. + */ + #define E1000_PARAM(X, desc) \ static int __devinitdata X[E1000_MAX_NIC+1] = E1000_PARAM_INIT; \ static int num_##X = 0; \ @@ -305,6 +315,7 @@ e1000_check_options(struct e1000_adapter *adapter) DPRINTK(PROBE, NOTICE, "Warning: no configuration for board #%i\n", bd); DPRINTK(PROBE, NOTICE, "Using defaults for all values\n"); + bd = E1000_MAX_NIC; } { /* Transmit Descriptor Count */ @@ -322,14 +333,9 @@ e1000_check_options(struct e1000_adapter *adapter) opt.arg.r.max = mac_type < e1000_82544 ? E1000_MAX_TXD : E1000_MAX_82544_TXD; - if (num_TxDescriptors > bd) { - tx_ring->count = TxDescriptors[bd]; - e1000_validate_option(&tx_ring->count, &opt, adapter); - E1000_ROUNDUP(tx_ring->count, - REQ_TX_DESCRIPTOR_MULTIPLE); - } else { - tx_ring->count = opt.def; - } + tx_ring->count = TxDescriptors[bd]; + e1000_validate_option(&tx_ring->count, &opt, adapter); + E1000_ROUNDUP(tx_ring->count, REQ_TX_DESCRIPTOR_MULTIPLE); for (i = 0; i < adapter->num_tx_queues; i++) tx_ring[i].count = tx_ring->count; } @@ -348,14 +354,9 @@ e1000_check_options(struct e1000_adapter *adapter) opt.arg.r.max = mac_type < e1000_82544 ? E1000_MAX_RXD : E1000_MAX_82544_RXD; - if (num_RxDescriptors > bd) { - rx_ring->count = RxDescriptors[bd]; - e1000_validate_option(&rx_ring->count, &opt, adapter); - E1000_ROUNDUP(rx_ring->count, - REQ_RX_DESCRIPTOR_MULTIPLE); - } else { - rx_ring->count = opt.def; - } + rx_ring->count = RxDescriptors[bd]; + e1000_validate_option(&rx_ring->count, &opt, adapter); + E1000_ROUNDUP(rx_ring->count, REQ_RX_DESCRIPTOR_MULTIPLE); for (i = 0; i < adapter->num_rx_queues; i++) rx_ring[i].count = rx_ring->count; } @@ -367,13 +368,9 @@ e1000_check_options(struct e1000_adapter *adapter) .def = OPTION_ENABLED }; - if (num_XsumRX > bd) { - int rx_csum = XsumRX[bd]; - e1000_validate_option(&rx_csum, &opt, adapter); - adapter->rx_csum = rx_csum; - } else { - adapter->rx_csum = opt.def; - } + int rx_csum = XsumRX[bd]; + e1000_validate_option(&rx_csum, &opt, adapter); + adapter->rx_csum = rx_csum; } { /* Flow Control */ @@ -393,13 +390,9 @@ e1000_check_options(struct e1000_adapter *adapter) .p = fc_list }} }; - if (num_FlowControl > bd) { - int fc = FlowControl[bd]; - e1000_validate_option(&fc, &opt, adapter); - adapter->hw.fc = adapter->hw.original_fc = fc; - } else { - adapter->hw.fc = adapter->hw.original_fc = opt.def; - } + int fc = FlowControl[bd]; + e1000_validate_option(&fc, &opt, adapter); + adapter->hw.fc = adapter->hw.original_fc = fc; } { /* Transmit Interrupt Delay */ struct e1000_option opt = { @@ -411,13 +404,8 @@ e1000_check_options(struct e1000_adapter *adapter) .max = MAX_TXDELAY }} }; - if (num_TxIntDelay > bd) { - adapter->tx_int_delay = TxIntDelay[bd]; - e1000_validate_option(&adapter->tx_int_delay, &opt, - adapter); - } else { - adapter->tx_int_delay = opt.def; - } + adapter->tx_int_delay = TxIntDelay[bd]; + e1000_validate_option(&adapter->tx_int_delay, &opt, adapter); } { /* Transmit Absolute Interrupt Delay */ struct e1000_option opt = { @@ -429,13 +417,9 @@ e1000_check_options(struct e1000_adapter *adapter) .max = MAX_TXABSDELAY }} }; - if (num_TxAbsIntDelay > bd) { - adapter->tx_abs_int_delay = TxAbsIntDelay[bd]; - e1000_validate_option(&adapter->tx_abs_int_delay, &opt, - adapter); - } else { - adapter->tx_abs_int_delay = opt.def; - } + adapter->tx_abs_int_delay = TxAbsIntDelay[bd]; + e1000_validate_option(&adapter->tx_abs_int_delay, &opt, + adapter); } { /* Receive Interrupt Delay */ struct e1000_option opt = { @@ -447,13 +431,8 @@ e1000_check_options(struct e1000_adapter *adapter) .max = MAX_RXDELAY }} }; - if (num_RxIntDelay > bd) { - adapter->rx_int_delay = RxIntDelay[bd]; - e1000_validate_option(&adapter->rx_int_delay, &opt, - adapter); - } else { - adapter->rx_int_delay = opt.def; - } + adapter->rx_int_delay = RxIntDelay[bd]; + e1000_validate_option(&adapter->rx_int_delay, &opt, adapter); } { /* Receive Absolute Interrupt Delay */ struct e1000_option opt = { @@ -465,13 +444,9 @@ e1000_check_options(struct e1000_adapter *adapter) .max = MAX_RXABSDELAY }} }; - if (num_RxAbsIntDelay > bd) { - adapter->rx_abs_int_delay = RxAbsIntDelay[bd]; - e1000_validate_option(&adapter->rx_abs_int_delay, &opt, - adapter); - } else { - adapter->rx_abs_int_delay = opt.def; - } + adapter->rx_abs_int_delay = RxAbsIntDelay[bd]; + e1000_validate_option(&adapter->rx_abs_int_delay, &opt, + adapter); } { /* Interrupt Throttling Rate */ struct e1000_option opt = { @@ -483,24 +458,18 @@ e1000_check_options(struct e1000_adapter *adapter) .max = MAX_ITR }} }; - if (num_InterruptThrottleRate > bd) { - adapter->itr = InterruptThrottleRate[bd]; - switch (adapter->itr) { - case 0: - DPRINTK(PROBE, INFO, "%s turned off\n", - opt.name); - break; - case 1: - DPRINTK(PROBE, INFO, "%s set to dynamic mode\n", - opt.name); - break; - default: - e1000_validate_option(&adapter->itr, &opt, - adapter); - break; - } - } else { - adapter->itr = opt.def; + adapter->itr = InterruptThrottleRate[bd]; + switch (adapter->itr) { + case 0: + DPRINTK(PROBE, INFO, "%s turned off\n", opt.name); + break; + case 1: + DPRINTK(PROBE, INFO, "%s set to dynamic mode\n", + opt.name); + break; + default: + e1000_validate_option(&adapter->itr, &opt, adapter); + break; } } { /* Smart Power Down */ @@ -511,13 +480,9 @@ e1000_check_options(struct e1000_adapter *adapter) .def = OPTION_DISABLED }; - if (num_SmartPowerDownEnable > bd) { - int spd = SmartPowerDownEnable[bd]; - e1000_validate_option(&spd, &opt, adapter); - adapter->smart_power_down = spd; - } else { - adapter->smart_power_down = opt.def; - } + int spd = SmartPowerDownEnable[bd]; + e1000_validate_option(&spd, &opt, adapter); + adapter->smart_power_down = spd; } switch (adapter->hw.media_type) { @@ -544,17 +509,18 @@ static void __devinit e1000_check_fiber_options(struct e1000_adapter *adapter) { int bd = adapter->bd_number; - if (num_Speed > bd) { + bd = bd > E1000_MAX_NIC ? E1000_MAX_NIC : bd; + if ((Speed[bd] != OPTION_UNSET)) { DPRINTK(PROBE, INFO, "Speed not valid for fiber adapters, " "parameter ignored\n"); } - if (num_Duplex > bd) { + if ((Duplex[bd] != OPTION_UNSET)) { DPRINTK(PROBE, INFO, "Duplex not valid for fiber adapters, " "parameter ignored\n"); } - if ((num_AutoNeg > bd) && (AutoNeg[bd] != 0x20)) { + if ((AutoNeg[bd] != OPTION_UNSET) && (AutoNeg[bd] != 0x20)) { DPRINTK(PROBE, INFO, "AutoNeg other than 1000/Full is " "not valid for fiber adapters, " "parameter ignored\n"); @@ -573,6 +539,7 @@ e1000_check_copper_options(struct e1000_adapter *adapter) { int speed, dplx, an; int bd = adapter->bd_number; + bd = bd > E1000_MAX_NIC ? E1000_MAX_NIC : bd; { /* Speed */ struct e1000_opt_list speed_list[] = {{ 0, "" }, @@ -589,12 +556,8 @@ e1000_check_copper_options(struct e1000_adapter *adapter) .p = speed_list }} }; - if (num_Speed > bd) { - speed = Speed[bd]; - e1000_validate_option(&speed, &opt, adapter); - } else { - speed = opt.def; - } + speed = Speed[bd]; + e1000_validate_option(&speed, &opt, adapter); } { /* Duplex */ struct e1000_opt_list dplx_list[] = {{ 0, "" }, @@ -616,15 +579,11 @@ e1000_check_copper_options(struct e1000_adapter *adapter) "Speed/Duplex/AutoNeg parameter ignored.\n"); return; } - if (num_Duplex > bd) { - dplx = Duplex[bd]; - e1000_validate_option(&dplx, &opt, adapter); - } else { - dplx = opt.def; - } + dplx = Duplex[bd]; + e1000_validate_option(&dplx, &opt, adapter); } - if ((num_AutoNeg > bd) && (speed != 0 || dplx != 0)) { + if (AutoNeg[bd] != OPTION_UNSET && (speed != 0 || dplx != 0)) { DPRINTK(PROBE, INFO, "AutoNeg specified along with Speed or Duplex, " "parameter ignored\n"); @@ -673,19 +632,15 @@ e1000_check_copper_options(struct e1000_adapter *adapter) .p = an_list }} }; - if (num_AutoNeg > bd) { - an = AutoNeg[bd]; - e1000_validate_option(&an, &opt, adapter); - } else { - an = opt.def; - } + an = AutoNeg[bd]; + e1000_validate_option(&an, &opt, adapter); adapter->hw.autoneg_advertised = an; } switch (speed + dplx) { case 0: adapter->hw.autoneg = adapter->fc_autoneg = 1; - if ((num_Speed > bd) && (speed != 0 || dplx != 0)) + if (Speed[bd] != OPTION_UNSET || Duplex[bd] != OPTION_UNSET) DPRINTK(PROBE, INFO, "Speed and duplex autonegotiation enabled\n"); break; -- cgit v1.2.3 From 4ca213a6957318219822c024c8e4c61b3e42a7dc Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:07:08 -0700 Subject: e1000: force register write flushes to circumvent broken platforms A certain AMD64 bridge (8132) has an option to turn on write combining which breaks our adapter. To circumvent this we need to flush every write. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_hw.c | 24 ++++++++++++++++++++++-- drivers/net/e1000/e1000_main.c | 18 +++++++++++------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 3959039b16ec..749d621a07da 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -705,8 +705,12 @@ e1000_init_hw(struct e1000_hw *hw) /* Zero out the Multicast HASH table */ DEBUGOUT("Zeroing the MTA\n"); mta_size = E1000_MC_TBL_SIZE; - for(i = 0; i < mta_size; i++) + for(i = 0; i < mta_size; i++) { E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + /* use write flush to prevent Memory Write Block (MWB) from + * occuring when accessing our register space */ + E1000_WRITE_FLUSH(hw); + } /* Set the PCI priority bit correctly in the CTRL register. This * determines if the adapter gives priority to receives, or if it @@ -5106,7 +5110,9 @@ e1000_init_rx_addrs(struct e1000_hw *hw) DEBUGOUT("Clearing RAR[1-15]\n"); for(i = 1; i < rar_num; i++) { E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_FLUSH(hw); E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + E1000_WRITE_FLUSH(hw); } } @@ -5153,7 +5159,9 @@ e1000_mc_addr_list_update(struct e1000_hw *hw, for(i = rar_used_count; i < num_rar_entry; i++) { E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0); + E1000_WRITE_FLUSH(hw); E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0); + E1000_WRITE_FLUSH(hw); } /* Clear the MTA */ @@ -5161,6 +5169,7 @@ e1000_mc_addr_list_update(struct e1000_hw *hw, num_mta_entry = E1000_NUM_MTA_REGISTERS; for(i = 0; i < num_mta_entry; i++) { E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + E1000_WRITE_FLUSH(hw); } /* Add the new addresses */ @@ -5275,9 +5284,12 @@ e1000_mta_set(struct e1000_hw *hw, if((hw->mac_type == e1000_82544) && ((hash_reg & 0x1) == 1)) { temp = E1000_READ_REG_ARRAY(hw, MTA, (hash_reg - 1)); E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta); + E1000_WRITE_FLUSH(hw); E1000_WRITE_REG_ARRAY(hw, MTA, (hash_reg - 1), temp); + E1000_WRITE_FLUSH(hw); } else { E1000_WRITE_REG_ARRAY(hw, MTA, hash_reg, mta); + E1000_WRITE_FLUSH(hw); } } @@ -5334,7 +5346,9 @@ e1000_rar_set(struct e1000_hw *hw, } E1000_WRITE_REG_ARRAY(hw, RA, (index << 1), rar_low); + E1000_WRITE_FLUSH(hw); E1000_WRITE_REG_ARRAY(hw, RA, ((index << 1) + 1), rar_high); + E1000_WRITE_FLUSH(hw); } /****************************************************************************** @@ -5354,9 +5368,12 @@ e1000_write_vfta(struct e1000_hw *hw, if((hw->mac_type == e1000_82544) && ((offset & 0x1) == 1)) { temp = E1000_READ_REG_ARRAY(hw, VFTA, (offset - 1)); E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); + E1000_WRITE_FLUSH(hw); E1000_WRITE_REG_ARRAY(hw, VFTA, (offset - 1), temp); + E1000_WRITE_FLUSH(hw); } else { E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); + E1000_WRITE_FLUSH(hw); } } @@ -5392,6 +5409,7 @@ e1000_clear_vfta(struct e1000_hw *hw) * manageability unit */ vfta_value = (offset == vfta_offset) ? vfta_bit_in_reg : 0; E1000_WRITE_REG_ARRAY(hw, VFTA, offset, vfta_value); + E1000_WRITE_FLUSH(hw); } } @@ -6928,8 +6946,10 @@ e1000_mng_write_cmd_header(struct e1000_hw * hw, length >>= 2; /* The device driver writes the relevant command block into the ram area. */ - for (i = 0; i < length; i++) + for (i = 0; i < length; i++) { E1000_WRITE_REG_ARRAY_DWORD(hw, HOST_IF, i, *((uint32_t *) hdr + i)); + E1000_WRITE_FLUSH(hw); + } return E1000_SUCCESS; } diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index c58fafd1c4be..c44ed6f4ba78 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -1370,11 +1370,11 @@ e1000_configure_tx(struct e1000_adapter *adapter) tdba = adapter->tx_ring[0].dma; tdlen = adapter->tx_ring[0].count * sizeof(struct e1000_tx_desc); - E1000_WRITE_REG(hw, TDBAL, (tdba & 0x00000000ffffffffULL)); - E1000_WRITE_REG(hw, TDBAH, (tdba >> 32)); E1000_WRITE_REG(hw, TDLEN, tdlen); - E1000_WRITE_REG(hw, TDH, 0); + E1000_WRITE_REG(hw, TDBAH, (tdba >> 32)); + E1000_WRITE_REG(hw, TDBAL, (tdba & 0x00000000ffffffffULL)); E1000_WRITE_REG(hw, TDT, 0); + E1000_WRITE_REG(hw, TDH, 0); adapter->tx_ring[0].tdh = E1000_TDH; adapter->tx_ring[0].tdt = E1000_TDT; break; @@ -1780,11 +1780,11 @@ e1000_configure_rx(struct e1000_adapter *adapter) case 1: default: rdba = adapter->rx_ring[0].dma; - E1000_WRITE_REG(hw, RDBAL, (rdba & 0x00000000ffffffffULL)); - E1000_WRITE_REG(hw, RDBAH, (rdba >> 32)); E1000_WRITE_REG(hw, RDLEN, rdlen); - E1000_WRITE_REG(hw, RDH, 0); + E1000_WRITE_REG(hw, RDBAH, (rdba >> 32)); + E1000_WRITE_REG(hw, RDBAL, (rdba & 0x00000000ffffffffULL)); E1000_WRITE_REG(hw, RDT, 0); + E1000_WRITE_REG(hw, RDH, 0); adapter->rx_ring[0].rdh = E1000_RDH; adapter->rx_ring[0].rdt = E1000_RDT; break; @@ -2189,14 +2189,18 @@ e1000_set_multi(struct net_device *netdev) mc_ptr = mc_ptr->next; } else { E1000_WRITE_REG_ARRAY(hw, RA, i << 1, 0); + E1000_WRITE_FLUSH(hw); E1000_WRITE_REG_ARRAY(hw, RA, (i << 1) + 1, 0); + E1000_WRITE_FLUSH(hw); } } /* clear the old settings from the multicast hash table */ - for (i = 0; i < E1000_NUM_MTA_REGISTERS; i++) + for (i = 0; i < E1000_NUM_MTA_REGISTERS; i++) { E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); + E1000_WRITE_FLUSH(hw); + } /* load any remaining addresses into the hash table */ -- cgit v1.2.3 From 7dfee0cb1d506897805b663dd95e9651619a4d4d Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:07:50 -0700 Subject: e1000: disable CRC stripping workaround CRC stripping is breaking SMBUS-connected BMC's. We disable this feature to make it work. This fixes related bugs regarding SOL. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index c44ed6f4ba78..a9e55dc10c55 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -1628,9 +1628,6 @@ e1000_setup_rctl(struct e1000_adapter *adapter) E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF | (adapter->hw.mc_filter_type << E1000_RCTL_MO_SHIFT); - if (adapter->hw.mac_type > e1000_82543) - rctl |= E1000_RCTL_SECRC; - if (adapter->hw.tbi_compatibility_on == 1) rctl |= E1000_RCTL_SBP; else @@ -1696,7 +1693,7 @@ e1000_setup_rctl(struct e1000_adapter *adapter) rfctl |= E1000_RFCTL_IPV6_DIS; E1000_WRITE_REG(&adapter->hw, RFCTL, rfctl); - rctl |= E1000_RCTL_DTYP_PS | E1000_RCTL_SECRC; + rctl |= E1000_RCTL_DTYP_PS; psrctl |= adapter->rx_ps_bsize0 >> E1000_PSRCTL_BSIZE0_SHIFT; -- cgit v1.2.3 From f1b3a85354d3877fae45ef448e7e49c2efd692d5 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:07:56 -0700 Subject: e1000: fix adapter led blinking inconsistency Several e1000 adapters were not blinking correctly or inconsistently. This patch cleans this up and makes them all behave the same as far as possible. Signed-off-by: Jeff Kirsher Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_ethtool.c | 15 +-------------- drivers/net/e1000/e1000_hw.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/net/e1000/e1000_hw.h | 1 + 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index cf5c5f46341f..060915532923 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -1786,21 +1786,8 @@ e1000_phys_id(struct net_device *netdev, uint32_t data) mod_timer(&adapter->blink_timer, jiffies); msleep_interruptible(data * 1000); del_timer_sync(&adapter->blink_timer); - } else if (adapter->hw.mac_type < e1000_82573) { - E1000_WRITE_REG(&adapter->hw, LEDCTL, - (E1000_LEDCTL_LED2_BLINK_RATE | - E1000_LEDCTL_LED0_BLINK | E1000_LEDCTL_LED2_BLINK | - (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED2_MODE_SHIFT) | - (E1000_LEDCTL_MODE_LINK_ACTIVITY << E1000_LEDCTL_LED0_MODE_SHIFT) | - (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED1_MODE_SHIFT))); - msleep_interruptible(data * 1000); } else { - E1000_WRITE_REG(&adapter->hw, LEDCTL, - (E1000_LEDCTL_LED2_BLINK_RATE | - E1000_LEDCTL_LED1_BLINK | E1000_LEDCTL_LED2_BLINK | - (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED2_MODE_SHIFT) | - (E1000_LEDCTL_MODE_LINK_ACTIVITY << E1000_LEDCTL_LED1_MODE_SHIFT) | - (E1000_LEDCTL_MODE_LED_OFF << E1000_LEDCTL_LED0_MODE_SHIFT))); + e1000_blink_led_start(&adapter->hw); msleep_interruptible(data * 1000); } diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 749d621a07da..1c5b18478fb2 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -5536,6 +5536,44 @@ e1000_setup_led(struct e1000_hw *hw) return E1000_SUCCESS; } +/****************************************************************************** + * Used on 82571 and later Si that has LED blink bits. + * Callers must use their own timer and should have already called + * e1000_id_led_init() + * Call e1000_cleanup led() to stop blinking + * + * hw - Struct containing variables accessed by shared code + *****************************************************************************/ +int32_t +e1000_blink_led_start(struct e1000_hw *hw) +{ + int16_t i; + uint32_t ledctl_blink = 0; + + DEBUGFUNC("e1000_id_led_blink_on"); + + if (hw->mac_type < e1000_82571) { + /* Nothing to do */ + return E1000_SUCCESS; + } + if (hw->media_type == e1000_media_type_fiber) { + /* always blink LED0 for PCI-E fiber */ + ledctl_blink = E1000_LEDCTL_LED0_BLINK | + (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); + } else { + /* set the blink bit for each LED that's "on" (0x0E) in ledctl_mode2 */ + ledctl_blink = hw->ledctl_mode2; + for (i=0; i < 4; i++) + if (((hw->ledctl_mode2 >> (i * 8)) & 0xFF) == + E1000_LEDCTL_MODE_LED_ON) + ledctl_blink |= (E1000_LEDCTL_LED0_BLINK << (i * 8)); + } + + E1000_WRITE_REG(hw, LEDCTL, ledctl_blink); + + return E1000_SUCCESS; +} + /****************************************************************************** * Restores the saved state of the SW controlable LED. * diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h index 467c9ed944f8..941b47d61674 100644 --- a/drivers/net/e1000/e1000_hw.h +++ b/drivers/net/e1000/e1000_hw.h @@ -401,6 +401,7 @@ int32_t e1000_setup_led(struct e1000_hw *hw); int32_t e1000_cleanup_led(struct e1000_hw *hw); int32_t e1000_led_on(struct e1000_hw *hw); int32_t e1000_led_off(struct e1000_hw *hw); +int32_t e1000_blink_led_start(struct e1000_hw *hw); /* Adaptive IFS Functions */ -- cgit v1.2.3 From ee04022a21764a12e29eee144b72344ebfe0a55c Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:08:03 -0700 Subject: e1000: M88 PHY workaround M88 rev 2 PHY needs a longer downshift to function properly. This adds a much longer downshift counter for this specific device. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_hw.c | 46 ++++++++++++++++++++++++++++---------------- drivers/net/e1000/e1000_hw.h | 11 +++++++++++ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 1c5b18478fb2..37eb351b4c9b 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -1565,28 +1565,40 @@ e1000_copper_link_mgp_setup(struct e1000_hw *hw) phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; if(hw->disable_polarity_correction == 1) phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; - ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); - if(ret_val) - return ret_val; - - /* Force TX_CLK in the Extended PHY Specific Control Register - * to 25MHz clock. - */ - ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); - if(ret_val) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if (ret_val) return ret_val; - phy_data |= M88E1000_EPSCR_TX_CLK_25; - if (hw->phy_revision < M88E1011_I_REV_4) { - /* Configure Master and Slave downshift values */ - phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | + /* Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + + if ((hw->phy_revision == E1000_REVISION_2) && + (hw->phy_id == M88E1111_I_PHY_ID)) { + /* Vidalia Phy, set the downshift counter to 5x */ + phy_data &= ~(M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK); + phy_data |= M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X; + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } else { + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); - phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); - ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); - if(ret_val) - return ret_val; + ret_val = e1000_write_phy_reg(hw, + M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if (ret_val) + return ret_val; + } } /* SW Reset the PHY so all changes take effect */ diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h index 941b47d61674..1908e0d3110c 100644 --- a/drivers/net/e1000/e1000_hw.h +++ b/drivers/net/e1000/e1000_hw.h @@ -2765,6 +2765,17 @@ struct e1000_host_command_info { #define M88E1000_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */ #define M88E1000_EPSCR_TX_CLK_0 0x0000 /* NO TX_CLK */ +/* M88EC018 Rev 2 specific DownShift settings */ +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_1X 0x0000 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_2X 0x0200 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_3X 0x0400 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_4X 0x0600 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_6X 0x0A00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_7X 0x0C00 +#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_8X 0x0E00 + /* IGP01E1000 Specific Port Config Register - R/W */ #define IGP01E1000_PSCFR_AUTO_MDIX_PAR_DETECT 0x0010 #define IGP01E1000_PSCFR_PRE_EN 0x0020 -- cgit v1.2.3 From 592600a0536f0d5aab4dc95f19515a37d03430f8 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:08:09 -0700 Subject: e1000: check return value of _get_speed_and_duplex We were not checking the return value of get_speed_and_duplex properly, whih may contain an error value. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_hw.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 37eb351b4c9b..784f9505864a 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -2737,8 +2737,12 @@ e1000_check_for_link(struct e1000_hw *hw) */ if(hw->tbi_compatibility_en) { uint16_t speed, duplex; - e1000_get_speed_and_duplex(hw, &speed, &duplex); - if(speed != SPEED_1000) { + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if (ret_val) { + DEBUGOUT("Error getting link speed and duplex\n"); + return ret_val; + } + if (speed != SPEED_1000) { /* If link speed is not set to gigabit speed, we do not need * to enable TBI compatibility. */ -- cgit v1.2.3 From ab7bc0ad72a12ef8eacc1560c9342aa567f3531d Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:08:13 -0700 Subject: e1000: disable ERT Hardware is reported to have problems with ERT. We disable it for all hardware to make sure we are not seeing unexplainable user problems. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index a9e55dc10c55..cc0c77c4c170 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -1806,9 +1806,6 @@ e1000_configure_rx(struct e1000_adapter *adapter) E1000_WRITE_REG(hw, RXCSUM, rxcsum); } - if (hw->mac_type == e1000_82573) - E1000_WRITE_REG(hw, ERT, 0x0100); - /* Enable Receives */ E1000_WRITE_REG(hw, RCTL, rctl); } -- cgit v1.2.3 From d37ea5d56293b7a883d2a993df5d8b9fb660ed3b Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:08:17 -0700 Subject: e1000: add ich8lan core functions This implements the core new functions needed for ich8's internal NIC. This includes: * ich8 specific read/write code * flash/nvm access code * software semaphore flag functions * 10/100 PHY (fe - no gigabit speed) support for low-end versions * A workaround for a powerdown sequence problem discovered that affects a small number of motherboard. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_hw.c | 1000 ++++++++++++++++++++++++++++++++++++++- drivers/net/e1000/e1000_hw.h | 386 ++++++++++++++- drivers/net/e1000/e1000_osdep.h | 13 + 3 files changed, 1392 insertions(+), 7 deletions(-) diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index 784f9505864a..a3f5ccdfafc6 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -3616,12 +3616,121 @@ e1000_phy_reset(struct e1000_hw *hw) return E1000_SUCCESS; } +/****************************************************************************** +* Work-around for 82566 power-down: on D3 entry- +* 1) disable gigabit link +* 2) write VR power-down enable +* 3) read it back +* if successful continue, else issue LCD reset and repeat +* +* hw - struct containing variables accessed by shared code +******************************************************************************/ +void +e1000_phy_powerdown_workaround(struct e1000_hw *hw) +{ + int32_t reg; + uint16_t phy_data; + int32_t retry = 0; + + DEBUGFUNC("e1000_phy_powerdown_workaround"); + + if (hw->phy_type != e1000_phy_igp_3) + return; + + do { + /* Disable link */ + reg = E1000_READ_REG(hw, PHY_CTRL); + E1000_WRITE_REG(hw, PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | + E1000_PHY_CTRL_NOND0A_GBE_DISABLE); + + /* Write VR power-down enable */ + e1000_read_phy_reg(hw, IGP3_VR_CTRL, &phy_data); + e1000_write_phy_reg(hw, IGP3_VR_CTRL, phy_data | + IGP3_VR_CTRL_MODE_SHUT); + + /* Read it back and test */ + e1000_read_phy_reg(hw, IGP3_VR_CTRL, &phy_data); + if ((phy_data & IGP3_VR_CTRL_MODE_SHUT) || retry) + break; + + /* Issue PHY reset and repeat at most one more time */ + reg = E1000_READ_REG(hw, CTRL); + E1000_WRITE_REG(hw, CTRL, reg | E1000_CTRL_PHY_RST); + retry++; + } while (retry); + + return; + +} + +/****************************************************************************** +* Work-around for 82566 Kumeran PCS lock loss: +* On link status change (i.e. PCI reset, speed change) and link is up and +* speed is gigabit- +* 0) if workaround is optionally disabled do nothing +* 1) wait 1ms for Kumeran link to come up +* 2) check Kumeran Diagnostic register PCS lock loss bit +* 3) if not set the link is locked (all is good), otherwise... +* 4) reset the PHY +* 5) repeat up to 10 times +* Note: this is only called for IGP3 copper when speed is 1gb. +* +* hw - struct containing variables accessed by shared code +******************************************************************************/ +int32_t +e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw) +{ + int32_t ret_val; + int32_t reg; + int32_t cnt; + uint16_t phy_data; + + if (hw->kmrn_lock_loss_workaround_disabled) + return E1000_SUCCESS; + + /* Make sure link is up before proceeding. If not just return. + * Attempting this while link is negotiating fouls up link + * stability */ + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + + if (phy_data & MII_SR_LINK_STATUS) { + for (cnt = 0; cnt < 10; cnt++) { + /* read once to clear */ + ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &phy_data); + if (ret_val) + return ret_val; + /* and again to get new status */ + ret_val = e1000_read_phy_reg(hw, IGP3_KMRN_DIAG, &phy_data); + if (ret_val) + return ret_val; + + /* check for PCS lock */ + if (!(phy_data & IGP3_KMRN_DIAG_PCS_LOCK_LOSS)) + return E1000_SUCCESS; + + /* Issue PHY reset */ + e1000_phy_hw_reset(hw); + msec_delay_irq(5); + } + /* Disable GigE link negotiation */ + reg = E1000_READ_REG(hw, PHY_CTRL); + E1000_WRITE_REG(hw, PHY_CTRL, reg | E1000_PHY_CTRL_GBE_DISABLE | + E1000_PHY_CTRL_NOND0A_GBE_DISABLE); + + /* unable to acquire PCS lock */ + return E1000_ERR_PHY; + } + + return E1000_SUCCESS; +} + /****************************************************************************** * Probes the expected PHY address for known PHY IDs * * hw - Struct containing variables accessed by shared code ******************************************************************************/ -static int32_t +int32_t e1000_detect_gig_phy(struct e1000_hw *hw) { int32_t phy_init_status, ret_val; @@ -3803,6 +3912,53 @@ e1000_phy_igp_get_info(struct e1000_hw *hw, return E1000_SUCCESS; } +/****************************************************************************** +* Get PHY information from various PHY registers for ife PHY only. +* +* hw - Struct containing variables accessed by shared code +* phy_info - PHY information structure +******************************************************************************/ +int32_t +e1000_phy_ife_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) +{ + int32_t ret_val; + uint16_t phy_data, polarity; + + DEBUGFUNC("e1000_phy_ife_get_info"); + + phy_info->downshift = (e1000_downshift)hw->speed_downgraded; + phy_info->extended_10bt_distance = e1000_10bt_ext_dist_enable_normal; + + ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &phy_data); + if (ret_val) + return ret_val; + phy_info->polarity_correction = + (phy_data & IFE_PSC_AUTO_POLARITY_DISABLE) >> + IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT; + + if (phy_info->polarity_correction == e1000_polarity_reversal_enabled) { + ret_val = e1000_check_polarity(hw, &polarity); + if (ret_val) + return ret_val; + } else { + /* Polarity is forced. */ + polarity = (phy_data & IFE_PSC_FORCE_POLARITY) >> + IFE_PSC_FORCE_POLARITY_SHIFT; + } + phy_info->cable_polarity = polarity; + + ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + phy_info->mdix_mode = + (phy_data & (IFE_PMC_AUTO_MDIX | IFE_PMC_FORCE_MDIX)) >> + IFE_PMC_MDIX_MODE_SHIFT; + + return E1000_SUCCESS; +} + /****************************************************************************** * Get PHY information from various PHY registers fot m88 PHY only. * @@ -7630,4 +7786,846 @@ e1000_arc_subsystem_valid(struct e1000_hw *hw) } +/****************************************************************************** + * Configure PCI-Ex no-snoop + * + * hw - Struct containing variables accessed by shared code. + * no_snoop - Bitmap of no-snoop events. + * + * returns: E1000_SUCCESS + * + *****************************************************************************/ +int32_t +e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, uint32_t no_snoop) +{ + uint32_t gcr_reg = 0; + + DEBUGFUNC("e1000_set_pci_ex_no_snoop"); + + if (hw->bus_type == e1000_bus_type_unknown) + e1000_get_bus_info(hw); + + if (hw->bus_type != e1000_bus_type_pci_express) + return E1000_SUCCESS; + + if (no_snoop) { + gcr_reg = E1000_READ_REG(hw, GCR); + gcr_reg &= ~(PCI_EX_NO_SNOOP_ALL); + gcr_reg |= no_snoop; + E1000_WRITE_REG(hw, GCR, gcr_reg); + } + if (hw->mac_type == e1000_ich8lan) { + uint32_t ctrl_ext; + + E1000_WRITE_REG(hw, GCR, PCI_EX_82566_SNOOP_ALL); + + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_RO_DIS; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Get software semaphore FLAG bit (SWFLAG). + * SWFLAG is used to synchronize the access to all shared resource between + * SW, FW and HW. + * + * hw: Struct containing variables accessed by shared code + * + ***************************************************************************/ +int32_t +e1000_get_software_flag(struct e1000_hw *hw) +{ + int32_t timeout = PHY_CFG_TIMEOUT; + uint32_t extcnf_ctrl; + + DEBUGFUNC("e1000_get_software_flag"); + + if (hw->mac_type == e1000_ich8lan) { + while (timeout) { + extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG; + E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); + + extcnf_ctrl = E1000_READ_REG(hw, EXTCNF_CTRL); + if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) + break; + msec_delay_irq(1); + timeout--; + } + + if (!timeout) { + DEBUGOUT("FW or HW locks the resource too long.\n"); + return -E1000_ERR_CONFIG; + } + } + + return E1000_SUCCESS; +} + +/*************************************************************************** + * + * Release software semaphore FLAG bit (SWFLAG). + * SWFLAG is used to synchronize the access to all shared resource between + * SW, FW and HW. + * + * hw: Struct containing variables accessed by shared code + * + ***************************************************************************/ +void +e1000_release_software_flag(struct e1000_hw *hw) +{ + uint32_t extcnf_ctrl; + + DEBUGFUNC("e1000_release_software_flag"); + + if (hw->mac_type == e1000_ich8lan) { + extcnf_ctrl= E1000_READ_REG(hw, EXTCNF_CTRL); + extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG; + E1000_WRITE_REG(hw, EXTCNF_CTRL, extcnf_ctrl); + } + + return; +} + +/*************************************************************************** + * + * Disable dynamic power down mode in ife PHY. + * It can be used to workaround band-gap problem. + * + * hw: Struct containing variables accessed by shared code + * + ***************************************************************************/ +int32_t +e1000_ife_disable_dynamic_power_down(struct e1000_hw *hw) +{ + uint16_t phy_data; + int32_t ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_ife_disable_dynamic_power_down"); + + if (hw->phy_type == e1000_phy_ife) { + ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN; + ret_val = e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, phy_data); + } + + return ret_val; +} + +/*************************************************************************** + * + * Enable dynamic power down mode in ife PHY. + * It can be used to workaround band-gap problem. + * + * hw: Struct containing variables accessed by shared code + * + ***************************************************************************/ +int32_t +e1000_ife_enable_dynamic_power_down(struct e1000_hw *hw) +{ + uint16_t phy_data; + int32_t ret_val = E1000_SUCCESS; + + DEBUGFUNC("e1000_ife_enable_dynamic_power_down"); + + if (hw->phy_type == e1000_phy_ife) { + ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN; + ret_val = e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, phy_data); + } + + return ret_val; +} + +/****************************************************************************** + * Reads a 16 bit word or words from the EEPROM using the ICH8's flash access + * register. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to read + * data - word read from the EEPROM + * words - number of words to read + *****************************************************************************/ +int32_t +e1000_read_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, + uint16_t *data) +{ + int32_t error = E1000_SUCCESS; + uint32_t flash_bank = 0; + uint32_t act_offset = 0; + uint32_t bank_offset = 0; + uint16_t word = 0; + uint16_t i = 0; + + /* We need to know which is the valid flash bank. In the event + * that we didn't allocate eeprom_shadow_ram, we may not be + * managing flash_bank. So it cannot be trusted and needs + * to be updated with each read. + */ + /* Value of bit 22 corresponds to the flash bank we're on. */ + flash_bank = (E1000_READ_REG(hw, EECD) & E1000_EECD_SEC1VAL) ? 1 : 0; + + /* Adjust offset appropriately if we're on bank 1 - adjust for word size */ + bank_offset = flash_bank * (hw->flash_bank_size * 2); + + error = e1000_get_software_flag(hw); + if (error != E1000_SUCCESS) + return error; + + for (i = 0; i < words; i++) { + if (hw->eeprom_shadow_ram != NULL && + hw->eeprom_shadow_ram[offset+i].modified == TRUE) { + data[i] = hw->eeprom_shadow_ram[offset+i].eeprom_word; + } else { + /* The NVM part needs a byte offset, hence * 2 */ + act_offset = bank_offset + ((offset + i) * 2); + error = e1000_read_ich8_word(hw, act_offset, &word); + if (error != E1000_SUCCESS) + break; + data[i] = word; + } + } + + e1000_release_software_flag(hw); + + return error; +} + +/****************************************************************************** + * Writes a 16 bit word or words to the EEPROM using the ICH8's flash access + * register. Actually, writes are written to the shadow ram cache in the hw + * structure hw->e1000_shadow_ram. e1000_commit_shadow_ram flushes this to + * the NVM, which occurs when the NVM checksum is updated. + * + * hw - Struct containing variables accessed by shared code + * offset - offset of word in the EEPROM to write + * words - number of words to write + * data - words to write to the EEPROM + *****************************************************************************/ +int32_t +e1000_write_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, + uint16_t *data) +{ + uint32_t i = 0; + int32_t error = E1000_SUCCESS; + + error = e1000_get_software_flag(hw); + if (error != E1000_SUCCESS) + return error; + + /* A driver can write to the NVM only if it has eeprom_shadow_ram + * allocated. Subsequent reads to the modified words are read from + * this cached structure as well. Writes will only go into this + * cached structure unless it's followed by a call to + * e1000_update_eeprom_checksum() where it will commit the changes + * and clear the "modified" field. + */ + if (hw->eeprom_shadow_ram != NULL) { + for (i = 0; i < words; i++) { + if ((offset + i) < E1000_SHADOW_RAM_WORDS) { + hw->eeprom_shadow_ram[offset+i].modified = TRUE; + hw->eeprom_shadow_ram[offset+i].eeprom_word = data[i]; + } else { + error = -E1000_ERR_EEPROM; + break; + } + } + } else { + /* Drivers have the option to not allocate eeprom_shadow_ram as long + * as they don't perform any NVM writes. An attempt in doing so + * will result in this error. + */ + error = -E1000_ERR_EEPROM; + } + + e1000_release_software_flag(hw); + + return error; +} + +/****************************************************************************** + * This function does initial flash setup so that a new read/write/erase cycle + * can be started. + * + * hw - The pointer to the hw structure + ****************************************************************************/ +int32_t +e1000_ich8_cycle_init(struct e1000_hw *hw) +{ + union ich8_hws_flash_status hsfsts; + int32_t error = E1000_ERR_EEPROM; + int32_t i = 0; + + DEBUGFUNC("e1000_ich8_cycle_init"); + + hsfsts.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFSTS); + + /* May be check the Flash Des Valid bit in Hw status */ + if (hsfsts.hsf_status.fldesvalid == 0) { + DEBUGOUT("Flash descriptor invalid. SW Sequencing must be used."); + return error; + } + + /* Clear FCERR in Hw status by writing 1 */ + /* Clear DAEL in Hw status by writing a 1 */ + hsfsts.hsf_status.flcerr = 1; + hsfsts.hsf_status.dael = 1; + + E1000_WRITE_ICH8_REG16(hw, ICH8_FLASH_HSFSTS, hsfsts.regval); + + /* Either we should have a hardware SPI cycle in progress bit to check + * against, in order to start a new cycle or FDONE bit should be changed + * in the hardware so that it is 1 after harware reset, which can then be + * used as an indication whether a cycle is in progress or has been + * completed .. we should also have some software semaphore mechanism to + * guard FDONE or the cycle in progress bit so that two threads access to + * those bits can be sequentiallized or a way so that 2 threads dont + * start the cycle at the same time */ + + if (hsfsts.hsf_status.flcinprog == 0) { + /* There is no cycle running at present, so we can start a cycle */ + /* Begin by setting Flash Cycle Done. */ + hsfsts.hsf_status.flcdone = 1; + E1000_WRITE_ICH8_REG16(hw, ICH8_FLASH_HSFSTS, hsfsts.regval); + error = E1000_SUCCESS; + } else { + /* otherwise poll for sometime so the current cycle has a chance + * to end before giving up. */ + for (i = 0; i < ICH8_FLASH_COMMAND_TIMEOUT; i++) { + hsfsts.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcinprog == 0) { + error = E1000_SUCCESS; + break; + } + udelay(1); + } + if (error == E1000_SUCCESS) { + /* Successful in waiting for previous cycle to timeout, + * now set the Flash Cycle Done. */ + hsfsts.hsf_status.flcdone = 1; + E1000_WRITE_ICH8_REG16(hw, ICH8_FLASH_HSFSTS, hsfsts.regval); + } else { + DEBUGOUT("Flash controller busy, cannot get access"); + } + } + return error; +} + +/****************************************************************************** + * This function starts a flash cycle and waits for its completion + * + * hw - The pointer to the hw structure + ****************************************************************************/ +int32_t +e1000_ich8_flash_cycle(struct e1000_hw *hw, uint32_t timeout) +{ + union ich8_hws_flash_ctrl hsflctl; + union ich8_hws_flash_status hsfsts; + int32_t error = E1000_ERR_EEPROM; + uint32_t i = 0; + + /* Start a cycle by writing 1 in Flash Cycle Go in Hw Flash Control */ + hsflctl.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFCTL); + hsflctl.hsf_ctrl.flcgo = 1; + E1000_WRITE_ICH8_REG16(hw, ICH8_FLASH_HSFCTL, hsflctl.regval); + + /* wait till FDONE bit is set to 1 */ + do { + hsfsts.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcdone == 1) + break; + udelay(1); + i++; + } while (i < timeout); + if (hsfsts.hsf_status.flcdone == 1 && hsfsts.hsf_status.flcerr == 0) { + error = E1000_SUCCESS; + } + return error; +} + +/****************************************************************************** + * Reads a byte or word from the NVM using the ICH8 flash access registers. + * + * hw - The pointer to the hw structure + * index - The index of the byte or word to read. + * size - Size of data to read, 1=byte 2=word + * data - Pointer to the word to store the value read. + *****************************************************************************/ +int32_t +e1000_read_ich8_data(struct e1000_hw *hw, uint32_t index, + uint32_t size, uint16_t* data) +{ + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + uint32_t flash_linear_address; + uint32_t flash_data = 0; + int32_t error = -E1000_ERR_EEPROM; + int32_t count = 0; + + DEBUGFUNC("e1000_read_ich8_data"); + + if (size < 1 || size > 2 || data == 0x0 || + index > ICH8_FLASH_LINEAR_ADDR_MASK) + return error; + + flash_linear_address = (ICH8_FLASH_LINEAR_ADDR_MASK & index) + + hw->flash_base_addr; + + do { + udelay(1); + /* Steps */ + error = e1000_ich8_cycle_init(hw); + if (error != E1000_SUCCESS) + break; + + hsflctl.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFCTL); + /* 0b/1b corresponds to 1 or 2 byte size, respectively. */ + hsflctl.hsf_ctrl.fldbcount = size - 1; + hsflctl.hsf_ctrl.flcycle = ICH8_CYCLE_READ; + E1000_WRITE_ICH8_REG16(hw, ICH8_FLASH_HSFCTL, hsflctl.regval); + + /* Write the last 24 bits of index into Flash Linear address field in + * Flash Address */ + /* TODO: TBD maybe check the index against the size of flash */ + + E1000_WRITE_ICH8_REG(hw, ICH8_FLASH_FADDR, flash_linear_address); + + error = e1000_ich8_flash_cycle(hw, ICH8_FLASH_COMMAND_TIMEOUT); + + /* Check if FCERR is set to 1, if set to 1, clear it and try the whole + * sequence a few more times, else read in (shift in) the Flash Data0, + * the order is least significant byte first msb to lsb */ + if (error == E1000_SUCCESS) { + flash_data = E1000_READ_ICH8_REG(hw, ICH8_FLASH_FDATA0); + if (size == 1) { + *data = (uint8_t)(flash_data & 0x000000FF); + } else if (size == 2) { + *data = (uint16_t)(flash_data & 0x0000FFFF); + } + break; + } else { + /* If we've gotten here, then things are probably completely hosed, + * but if the error condition is detected, it won't hurt to give + * it another try...ICH8_FLASH_CYCLE_REPEAT_COUNT times. + */ + hsfsts.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* Repeat for some time before giving up. */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) { + DEBUGOUT("Timeout error - flash cycle did not complete."); + break; + } + } + } while (count++ < ICH8_FLASH_CYCLE_REPEAT_COUNT); + + return error; +} + +/****************************************************************************** + * Writes One /two bytes to the NVM using the ICH8 flash access registers. + * + * hw - The pointer to the hw structure + * index - The index of the byte/word to read. + * size - Size of data to read, 1=byte 2=word + * data - The byte(s) to write to the NVM. + *****************************************************************************/ +int32_t +e1000_write_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size, + uint16_t data) +{ + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + uint32_t flash_linear_address; + uint32_t flash_data = 0; + int32_t error = -E1000_ERR_EEPROM; + int32_t count = 0; + + DEBUGFUNC("e1000_write_ich8_data"); + + if (size < 1 || size > 2 || data > size * 0xff || + index > ICH8_FLASH_LINEAR_ADDR_MASK) + return error; + + flash_linear_address = (ICH8_FLASH_LINEAR_ADDR_MASK & index) + + hw->flash_base_addr; + + do { + udelay(1); + /* Steps */ + error = e1000_ich8_cycle_init(hw); + if (error != E1000_SUCCESS) + break; + + hsflctl.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFCTL); + /* 0b/1b corresponds to 1 or 2 byte size, respectively. */ + hsflctl.hsf_ctrl.fldbcount = size -1; + hsflctl.hsf_ctrl.flcycle = ICH8_CYCLE_WRITE; + E1000_WRITE_ICH8_REG16(hw, ICH8_FLASH_HSFCTL, hsflctl.regval); + + /* Write the last 24 bits of index into Flash Linear address field in + * Flash Address */ + E1000_WRITE_ICH8_REG(hw, ICH8_FLASH_FADDR, flash_linear_address); + + if (size == 1) + flash_data = (uint32_t)data & 0x00FF; + else + flash_data = (uint32_t)data; + + E1000_WRITE_ICH8_REG(hw, ICH8_FLASH_FDATA0, flash_data); + + /* check if FCERR is set to 1 , if set to 1, clear it and try the whole + * sequence a few more times else done */ + error = e1000_ich8_flash_cycle(hw, ICH8_FLASH_COMMAND_TIMEOUT); + if (error == E1000_SUCCESS) { + break; + } else { + /* If we're here, then things are most likely completely hosed, + * but if the error condition is detected, it won't hurt to give + * it another try...ICH8_FLASH_CYCLE_REPEAT_COUNT times. + */ + hsfsts.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* Repeat for some time before giving up. */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) { + DEBUGOUT("Timeout error - flash cycle did not complete."); + break; + } + } + } while (count++ < ICH8_FLASH_CYCLE_REPEAT_COUNT); + + return error; +} + +/****************************************************************************** + * Reads a single byte from the NVM using the ICH8 flash access registers. + * + * hw - pointer to e1000_hw structure + * index - The index of the byte to read. + * data - Pointer to a byte to store the value read. + *****************************************************************************/ +int32_t +e1000_read_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t* data) +{ + int32_t status = E1000_SUCCESS; + uint16_t word = 0; + + status = e1000_read_ich8_data(hw, index, 1, &word); + if (status == E1000_SUCCESS) { + *data = (uint8_t)word; + } + + return status; +} + +/****************************************************************************** + * Writes a single byte to the NVM using the ICH8 flash access registers. + * Performs verification by reading back the value and then going through + * a retry algorithm before giving up. + * + * hw - pointer to e1000_hw structure + * index - The index of the byte to write. + * byte - The byte to write to the NVM. + *****************************************************************************/ +int32_t +e1000_verify_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte) +{ + int32_t error = E1000_SUCCESS; + int32_t program_retries; + uint8_t temp_byte; + + e1000_write_ich8_byte(hw, index, byte); + udelay(100); + + for (program_retries = 0; program_retries < 100; program_retries++) { + e1000_read_ich8_byte(hw, index, &temp_byte); + if (temp_byte == byte) + break; + udelay(10); + e1000_write_ich8_byte(hw, index, byte); + udelay(100); + } + if (program_retries == 100) + error = E1000_ERR_EEPROM; + + return error; +} + +/****************************************************************************** + * Writes a single byte to the NVM using the ICH8 flash access registers. + * + * hw - pointer to e1000_hw structure + * index - The index of the byte to read. + * data - The byte to write to the NVM. + *****************************************************************************/ +int32_t +e1000_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t data) +{ + int32_t status = E1000_SUCCESS; + uint16_t word = (uint16_t)data; + + status = e1000_write_ich8_data(hw, index, 1, word); + + return status; +} + +/****************************************************************************** + * Reads a word from the NVM using the ICH8 flash access registers. + * + * hw - pointer to e1000_hw structure + * index - The starting byte index of the word to read. + * data - Pointer to a word to store the value read. + *****************************************************************************/ +int32_t +e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t *data) +{ + int32_t status = E1000_SUCCESS; + status = e1000_read_ich8_data(hw, index, 2, data); + return status; +} + +/****************************************************************************** + * Writes a word to the NVM using the ICH8 flash access registers. + * + * hw - pointer to e1000_hw structure + * index - The starting byte index of the word to read. + * data - The word to write to the NVM. + *****************************************************************************/ +int32_t +e1000_write_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t data) +{ + int32_t status = E1000_SUCCESS; + status = e1000_write_ich8_data(hw, index, 2, data); + return status; +} + +/****************************************************************************** + * Erases the bank specified. Each bank is a 4k block. Segments are 0 based. + * segment N is 4096 * N + flash_reg_addr. + * + * hw - pointer to e1000_hw structure + * segment - 0 for first segment, 1 for second segment, etc. + *****************************************************************************/ +int32_t +e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t segment) +{ + union ich8_hws_flash_status hsfsts; + union ich8_hws_flash_ctrl hsflctl; + uint32_t flash_linear_address; + int32_t count = 0; + int32_t error = E1000_ERR_EEPROM; + int32_t iteration, seg_size; + int32_t sector_size; + int32_t j = 0; + int32_t error_flag = 0; + + hsfsts.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFSTS); + + /* Determine HW Sector size: Read BERASE bits of Hw flash Status register */ + /* 00: The Hw sector is 256 bytes, hence we need to erase 16 + * consecutive sectors. The start index for the nth Hw sector can be + * calculated as = segment * 4096 + n * 256 + * 01: The Hw sector is 4K bytes, hence we need to erase 1 sector. + * The start index for the nth Hw sector can be calculated + * as = segment * 4096 + * 10: Error condition + * 11: The Hw sector size is much bigger than the size asked to + * erase...error condition */ + if (hsfsts.hsf_status.berasesz == 0x0) { + /* Hw sector size 256 */ + sector_size = seg_size = ICH8_FLASH_SEG_SIZE_256; + iteration = ICH8_FLASH_SECTOR_SIZE / ICH8_FLASH_SEG_SIZE_256; + } else if (hsfsts.hsf_status.berasesz == 0x1) { + sector_size = seg_size = ICH8_FLASH_SEG_SIZE_4K; + iteration = 1; + } else if (hsfsts.hsf_status.berasesz == 0x3) { + sector_size = seg_size = ICH8_FLASH_SEG_SIZE_64K; + iteration = 1; + } else { + return error; + } + + for (j = 0; j < iteration ; j++) { + do { + count++; + /* Steps */ + error = e1000_ich8_cycle_init(hw); + if (error != E1000_SUCCESS) { + error_flag = 1; + break; + } + + /* Write a value 11 (block Erase) in Flash Cycle field in Hw flash + * Control */ + hsflctl.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFCTL); + hsflctl.hsf_ctrl.flcycle = ICH8_CYCLE_ERASE; + E1000_WRITE_ICH8_REG16(hw, ICH8_FLASH_HSFCTL, hsflctl.regval); + + /* Write the last 24 bits of an index within the block into Flash + * Linear address field in Flash Address. This probably needs to + * be calculated here based off the on-chip segment size and the + * software segment size assumed (4K) */ + /* TBD */ + flash_linear_address = segment * sector_size + j * seg_size; + flash_linear_address &= ICH8_FLASH_LINEAR_ADDR_MASK; + flash_linear_address += hw->flash_base_addr; + + E1000_WRITE_ICH8_REG(hw, ICH8_FLASH_FADDR, flash_linear_address); + + error = e1000_ich8_flash_cycle(hw, 1000000); + /* Check if FCERR is set to 1. If 1, clear it and try the whole + * sequence a few more times else Done */ + if (error == E1000_SUCCESS) { + break; + } else { + hsfsts.regval = E1000_READ_ICH8_REG16(hw, ICH8_FLASH_HSFSTS); + if (hsfsts.hsf_status.flcerr == 1) { + /* repeat for some time before giving up */ + continue; + } else if (hsfsts.hsf_status.flcdone == 0) { + error_flag = 1; + break; + } + } + } while ((count < ICH8_FLASH_CYCLE_REPEAT_COUNT) && !error_flag); + if (error_flag == 1) + break; + } + if (error_flag != 1) + error = E1000_SUCCESS; + return error; +} + +/****************************************************************************** + * + * Reverse duplex setting without breaking the link. + * + * hw: Struct containing variables accessed by shared code + * + *****************************************************************************/ +int32_t +e1000_duplex_reversal(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + if (hw->phy_type != e1000_phy_igp_3) + return E1000_SUCCESS; + + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data ^= MII_CR_FULL_DUPLEX; + + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if (ret_val) + return ret_val; + + ret_val = e1000_read_phy_reg(hw, IGP3E1000_PHY_MISC_CTRL, &phy_data); + if (ret_val) + return ret_val; + + phy_data |= IGP3_PHY_MISC_DUPLEX_MANUAL_SET; + ret_val = e1000_write_phy_reg(hw, IGP3E1000_PHY_MISC_CTRL, phy_data); + + return ret_val; +} + +int32_t +e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, + uint32_t cnf_base_addr, uint32_t cnf_size) +{ + uint32_t ret_val = E1000_SUCCESS; + uint16_t word_addr, reg_data, reg_addr; + uint16_t i; + + /* cnf_base_addr is in DWORD */ + word_addr = (uint16_t)(cnf_base_addr << 1); + + /* cnf_size is returned in size of dwords */ + for (i = 0; i < cnf_size; i++) { + ret_val = e1000_read_eeprom(hw, (word_addr + i*2), 1, ®_data); + if (ret_val) + return ret_val; + + ret_val = e1000_read_eeprom(hw, (word_addr + i*2 + 1), 1, ®_addr); + if (ret_val) + return ret_val; + + ret_val = e1000_get_software_flag(hw); + if (ret_val != E1000_SUCCESS) + return ret_val; + + ret_val = e1000_write_phy_reg_ex(hw, (uint32_t)reg_addr, reg_data); + + e1000_release_software_flag(hw); + } + + return ret_val; +} + + +int32_t +e1000_init_lcd_from_nvm(struct e1000_hw *hw) +{ + uint32_t reg_data, cnf_base_addr, cnf_size, ret_val, loop; + + if (hw->phy_type != e1000_phy_igp_3) + return E1000_SUCCESS; + + /* Check if SW needs configure the PHY */ + reg_data = E1000_READ_REG(hw, FEXTNVM); + if (!(reg_data & FEXTNVM_SW_CONFIG)) + return E1000_SUCCESS; + + /* Wait for basic configuration completes before proceeding*/ + loop = 0; + do { + reg_data = E1000_READ_REG(hw, STATUS) & E1000_STATUS_LAN_INIT_DONE; + udelay(100); + loop++; + } while ((!reg_data) && (loop < 50)); + + /* Clear the Init Done bit for the next init event */ + reg_data = E1000_READ_REG(hw, STATUS); + reg_data &= ~E1000_STATUS_LAN_INIT_DONE; + E1000_WRITE_REG(hw, STATUS, reg_data); + + /* Make sure HW does not configure LCD from PHY extended configuration + before SW configuration */ + reg_data = E1000_READ_REG(hw, EXTCNF_CTRL); + if ((reg_data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE) == 0x0000) { + reg_data = E1000_READ_REG(hw, EXTCNF_SIZE); + cnf_size = reg_data & E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH; + cnf_size >>= 16; + if (cnf_size) { + reg_data = E1000_READ_REG(hw, EXTCNF_CTRL); + cnf_base_addr = reg_data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER; + /* cnf_base_addr is in DWORD */ + cnf_base_addr >>= 16; + + /* Configure LCD from extended configuration region. */ + ret_val = e1000_init_lcd_from_nvm_config_region(hw, cnf_base_addr, + cnf_size); + if (ret_val) + return ret_val; + } + } + + return E1000_SUCCESS; +} + + diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h index 1908e0d3110c..f9341e3276b3 100644 --- a/drivers/net/e1000/e1000_hw.h +++ b/drivers/net/e1000/e1000_hw.h @@ -62,6 +62,7 @@ typedef enum { e1000_82572, e1000_82573, e1000_80003es2lan, + e1000_ich8lan, e1000_num_macs } e1000_mac_type; @@ -70,6 +71,7 @@ typedef enum { e1000_eeprom_spi, e1000_eeprom_microwire, e1000_eeprom_flash, + e1000_eeprom_ich8, e1000_eeprom_none, /* No NVM support */ e1000_num_eeprom_types } e1000_eeprom_type; @@ -98,6 +100,11 @@ typedef enum { e1000_fc_default = 0xFF } e1000_fc_type; +struct e1000_shadow_ram { + uint16_t eeprom_word; + boolean_t modified; +}; + /* PCI bus types */ typedef enum { e1000_bus_type_unknown = 0, @@ -218,6 +225,8 @@ typedef enum { e1000_phy_igp, e1000_phy_igp_2, e1000_phy_gg82563, + e1000_phy_igp_3, + e1000_phy_ife, e1000_phy_undefined = 0xFF } e1000_phy_type; @@ -313,6 +322,10 @@ int32_t e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy int32_t e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data); int32_t e1000_phy_hw_reset(struct e1000_hw *hw); int32_t e1000_phy_reset(struct e1000_hw *hw); +void e1000_phy_powerdown_workaround(struct e1000_hw *hw); +int32_t e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw); +int32_t e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, uint32_t cnf_base_addr, uint32_t cnf_size); +int32_t e1000_init_lcd_from_nvm(struct e1000_hw *hw); int32_t e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); int32_t e1000_validate_mdi_setting(struct e1000_hw *hw); int32_t e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *data); @@ -331,6 +344,7 @@ uint32_t e1000_enable_mng_pass_thru(struct e1000_hw *hw); #define E1000_MNG_DHCP_COOKIE_OFFSET 0x6F0 /* Cookie offset */ #define E1000_MNG_DHCP_COOKIE_LENGTH 0x10 /* Cookie length */ #define E1000_MNG_IAMT_MODE 0x3 +#define E1000_MNG_ICH_IAMT_MODE 0x2 #define E1000_IAMT_SIGNATURE 0x544D4149 /* Intel(R) Active Management Technology signature */ #define E1000_MNG_DHCP_COOKIE_STATUS_PARSING_SUPPORT 0x1 /* DHCP parsing enabled */ @@ -388,6 +402,8 @@ int32_t e1000_read_part_num(struct e1000_hw *hw, uint32_t * part_num); int32_t e1000_read_mac_addr(struct e1000_hw * hw); int32_t e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask); void e1000_swfw_sync_release(struct e1000_hw *hw, uint16_t mask); +void e1000_release_software_flag(struct e1000_hw *hw); +int32_t e1000_get_software_flag(struct e1000_hw *hw); /* Filters (multicast, vlan, receive) */ void e1000_mc_addr_list_update(struct e1000_hw *hw, uint8_t * mc_addr_list, uint32_t mc_addr_count, uint32_t pad, uint32_t rar_used_count); @@ -423,6 +439,29 @@ int32_t e1000_disable_pciex_master(struct e1000_hw *hw); int32_t e1000_get_software_semaphore(struct e1000_hw *hw); void e1000_release_software_semaphore(struct e1000_hw *hw); int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); +int32_t e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, uint32_t no_snoop); + +int32_t e1000_read_ich8_byte(struct e1000_hw *hw, uint32_t index, + uint8_t *data); +int32_t e1000_verify_write_ich8_byte(struct e1000_hw *hw, uint32_t index, + uint8_t byte); +int32_t e1000_write_ich8_byte(struct e1000_hw *hw, uint32_t index, + uint8_t byte); +int32_t e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index, + uint16_t *data); +int32_t e1000_read_ich8_data(struct e1000_hw *hw, uint32_t index, + uint32_t size, uint16_t *data); +int32_t e1000_read_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, + uint16_t words, uint16_t *data); +int32_t e1000_write_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, + uint16_t words, uint16_t *data); +int32_t e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t segment); + + +#define E1000_READ_REG_IO(a, reg) \ + e1000_read_reg_io((a), E1000_##reg) +#define E1000_WRITE_REG_IO(a, reg, val) \ + e1000_write_reg_io((a), E1000_##reg, val) /* PCI Device IDs */ #define E1000_DEV_ID_82542 0x1000 @@ -447,6 +486,7 @@ int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); #define E1000_DEV_ID_82546EB_QUAD_COPPER 0x101D #define E1000_DEV_ID_82541EI 0x1013 #define E1000_DEV_ID_82541EI_MOBILE 0x1018 +#define E1000_DEV_ID_82541ER_LOM 0x1014 #define E1000_DEV_ID_82541ER 0x1078 #define E1000_DEV_ID_82547GI 0x1075 #define E1000_DEV_ID_82541GI 0x1076 @@ -458,18 +498,28 @@ int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); #define E1000_DEV_ID_82546GB_PCIE 0x108A #define E1000_DEV_ID_82546GB_QUAD_COPPER 0x1099 #define E1000_DEV_ID_82547EI 0x1019 +#define E1000_DEV_ID_82547EI_MOBILE 0x101A #define E1000_DEV_ID_82571EB_COPPER 0x105E #define E1000_DEV_ID_82571EB_FIBER 0x105F #define E1000_DEV_ID_82571EB_SERDES 0x1060 #define E1000_DEV_ID_82572EI_COPPER 0x107D #define E1000_DEV_ID_82572EI_FIBER 0x107E #define E1000_DEV_ID_82572EI_SERDES 0x107F +#define E1000_DEV_ID_82572EI 0x10B9 #define E1000_DEV_ID_82573E 0x108B #define E1000_DEV_ID_82573E_IAMT 0x108C #define E1000_DEV_ID_82573L 0x109A #define E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3 0x10B5 #define E1000_DEV_ID_80003ES2LAN_COPPER_DPT 0x1096 #define E1000_DEV_ID_80003ES2LAN_SERDES_DPT 0x1098 +#define E1000_DEV_ID_80003ES2LAN_COPPER_SPT 0x10BA +#define E1000_DEV_ID_80003ES2LAN_SERDES_SPT 0x10BB + +#define E1000_DEV_ID_ICH8_IGP_M_AMT 0x1049 +#define E1000_DEV_ID_ICH8_IGP_AMT 0x104A +#define E1000_DEV_ID_ICH8_IGP_C 0x104B +#define E1000_DEV_ID_ICH8_IFE 0x104C +#define E1000_DEV_ID_ICH8_IGP_M 0x104D #define NODE_ADDRESS_SIZE 6 @@ -540,6 +590,14 @@ int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); E1000_IMS_RXSEQ | \ E1000_IMS_LSC) +/* Additional interrupts need to be handled for e1000_ich8lan: + DSW = The FW changed the status of the DISSW bit in FWSM + PHYINT = The LAN connected device generates an interrupt + EPRST = Manageability reset event */ +#define IMS_ICH8LAN_ENABLE_MASK (\ + E1000_IMS_DSW | \ + E1000_IMS_PHYINT | \ + E1000_IMS_EPRST) /* Number of high/low register pairs in the RAR. The RAR (Receive Address * Registers) holds the directed and multicast addresses that we monitor. We @@ -547,6 +605,7 @@ int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); * E1000_RAR_ENTRIES - 1 multicast addresses. */ #define E1000_RAR_ENTRIES 15 +#define E1000_RAR_ENTRIES_ICH8LAN 7 #define MIN_NUMBER_OF_DESCRIPTORS 8 #define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8 @@ -768,6 +827,9 @@ struct e1000_data_desc { #define E1000_MC_TBL_SIZE 128 /* Multicast Filter Table (4096 bits) */ #define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ +#define E1000_NUM_UNICAST_ICH8LAN 7 +#define E1000_MC_TBL_SIZE_ICH8LAN 32 + /* Receive Address Register */ struct e1000_rar { @@ -777,6 +839,7 @@ struct e1000_rar { /* Number of entries in the Multicast Table Array (MTA). */ #define E1000_NUM_MTA_REGISTERS 128 +#define E1000_NUM_MTA_REGISTERS_ICH8LAN 32 /* IPv4 Address Table Entry */ struct e1000_ipv4_at_entry { @@ -787,6 +850,7 @@ struct e1000_ipv4_at_entry { /* Four wakeup IP addresses are supported */ #define E1000_WAKEUP_IP_ADDRESS_COUNT_MAX 4 #define E1000_IP4AT_SIZE E1000_WAKEUP_IP_ADDRESS_COUNT_MAX +#define E1000_IP4AT_SIZE_ICH8LAN 3 #define E1000_IP6AT_SIZE 1 /* IPv6 Address Table Entry */ @@ -845,6 +909,7 @@ struct e1000_ffvt_entry { #define E1000_FLA 0x0001C /* Flash Access - RW */ #define E1000_MDIC 0x00020 /* MDI Control - RW */ #define E1000_SCTL 0x00024 /* SerDes Control - RW */ +#define E1000_FEXTNVM 0x00028 /* Future Extended NVM register */ #define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ #define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ #define E1000_FCT 0x00030 /* Flow Control Type - RW */ @@ -873,6 +938,8 @@ struct e1000_ffvt_entry { #define E1000_LEDCTL 0x00E00 /* LED Control - RW */ #define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ #define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ +#define E1000_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */ +#define FEXTNVM_SW_CONFIG 0x0001 #define E1000_PBA 0x01000 /* Packet Buffer Allocation - RW */ #define E1000_PBS 0x01008 /* Packet Buffer Size */ #define E1000_EEMNGCTL 0x01010 /* MNG EEprom Control */ @@ -900,11 +967,13 @@ struct e1000_ffvt_entry { #define E1000_RDH0 E1000_RDH /* RX Desc Head (0) - RW */ #define E1000_RDT0 E1000_RDT /* RX Desc Tail (0) - RW */ #define E1000_RDTR0 E1000_RDTR /* RX Delay Timer (0) - RW */ -#define E1000_RXDCTL 0x02828 /* RX Descriptor Control - RW */ +#define E1000_RXDCTL 0x02828 /* RX Descriptor Control queue 0 - RW */ +#define E1000_RXDCTL1 0x02928 /* RX Descriptor Control queue 1 - RW */ #define E1000_RADV 0x0282C /* RX Interrupt Absolute Delay Timer - RW */ #define E1000_RSRPD 0x02C00 /* RX Small Packet Detect - RW */ #define E1000_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */ #define E1000_TXDMAC 0x03000 /* TX DMA Control - RW */ +#define E1000_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */ #define E1000_TDFH 0x03410 /* TX Data FIFO Head - RW */ #define E1000_TDFT 0x03418 /* TX Data FIFO Tail - RW */ #define E1000_TDFHS 0x03420 /* TX Data FIFO Head Saved - RW */ @@ -1051,6 +1120,7 @@ struct e1000_ffvt_entry { #define E1000_82542_FLA E1000_FLA #define E1000_82542_MDIC E1000_MDIC #define E1000_82542_SCTL E1000_SCTL +#define E1000_82542_FEXTNVM E1000_FEXTNVM #define E1000_82542_FCAL E1000_FCAL #define E1000_82542_FCAH E1000_FCAH #define E1000_82542_FCT E1000_FCT @@ -1074,6 +1144,19 @@ struct e1000_ffvt_entry { #define E1000_82542_RDLEN0 E1000_82542_RDLEN #define E1000_82542_RDH0 E1000_82542_RDH #define E1000_82542_RDT0 E1000_82542_RDT +#define E1000_82542_SRRCTL(_n) (0x280C + ((_n) << 8)) /* Split and Replication + * RX Control - RW */ +#define E1000_82542_DCA_RXCTRL(_n) (0x02814 + ((_n) << 8)) +#define E1000_82542_RDBAH3 0x02B04 /* RX Desc Base High Queue 3 - RW */ +#define E1000_82542_RDBAL3 0x02B00 /* RX Desc Low Queue 3 - RW */ +#define E1000_82542_RDLEN3 0x02B08 /* RX Desc Length Queue 3 - RW */ +#define E1000_82542_RDH3 0x02B10 /* RX Desc Head Queue 3 - RW */ +#define E1000_82542_RDT3 0x02B18 /* RX Desc Tail Queue 3 - RW */ +#define E1000_82542_RDBAL2 0x02A00 /* RX Desc Base Low Queue 2 - RW */ +#define E1000_82542_RDBAH2 0x02A04 /* RX Desc Base High Queue 2 - RW */ +#define E1000_82542_RDLEN2 0x02A08 /* RX Desc Length Queue 2 - RW */ +#define E1000_82542_RDH2 0x02A10 /* RX Desc Head Queue 2 - RW */ +#define E1000_82542_RDT2 0x02A18 /* RX Desc Tail Queue 2 - RW */ #define E1000_82542_RDTR1 0x00130 #define E1000_82542_RDBAL1 0x00138 #define E1000_82542_RDBAH1 0x0013C @@ -1111,11 +1194,14 @@ struct e1000_ffvt_entry { #define E1000_82542_FLOP E1000_FLOP #define E1000_82542_EXTCNF_CTRL E1000_EXTCNF_CTRL #define E1000_82542_EXTCNF_SIZE E1000_EXTCNF_SIZE +#define E1000_82542_PHY_CTRL E1000_PHY_CTRL #define E1000_82542_ERT E1000_ERT #define E1000_82542_RXDCTL E1000_RXDCTL +#define E1000_82542_RXDCTL1 E1000_RXDCTL1 #define E1000_82542_RADV E1000_RADV #define E1000_82542_RSRPD E1000_RSRPD #define E1000_82542_TXDMAC E1000_TXDMAC +#define E1000_82542_KABGTXD E1000_KABGTXD #define E1000_82542_TDFHS E1000_TDFHS #define E1000_82542_TDFTS E1000_TDFTS #define E1000_82542_TDFPC E1000_TDFPC @@ -1311,13 +1397,16 @@ struct e1000_hw_stats { /* Structure containing variables used by the shared code (e1000_hw.c) */ struct e1000_hw { - uint8_t __iomem *hw_addr; + uint8_t *hw_addr; uint8_t *flash_address; e1000_mac_type mac_type; e1000_phy_type phy_type; uint32_t phy_init_script; e1000_media_type media_type; void *back; + struct e1000_shadow_ram *eeprom_shadow_ram; + uint32_t flash_bank_size; + uint32_t flash_base_addr; e1000_fc_type fc; e1000_bus_speed bus_speed; e1000_bus_width bus_width; @@ -1329,6 +1418,7 @@ struct e1000_hw { uint32_t asf_firmware_present; uint32_t eeprom_semaphore_present; uint32_t swfw_sync_present; + uint32_t swfwhw_semaphore_present; unsigned long io_base; uint32_t phy_id; uint32_t phy_revision; @@ -1388,6 +1478,7 @@ struct e1000_hw { boolean_t in_ifs_mode; boolean_t mng_reg_access_disabled; boolean_t leave_av_bit_off; + boolean_t kmrn_lock_loss_workaround_disabled; }; @@ -1436,6 +1527,7 @@ struct e1000_hw { #define E1000_CTRL_RTE 0x20000000 /* Routing tag enable */ #define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ #define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ +#define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ /* Device Status */ #define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ @@ -1450,6 +1542,8 @@ struct e1000_hw { #define E1000_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */ #define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */ #define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */ +#define E1000_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Completion + by EEPROM/Flash */ #define E1000_STATUS_ASDV 0x00000300 /* Auto speed detect value */ #define E1000_STATUS_DOCK_CI 0x00000800 /* Change in Dock/Undock state. Clear on write '0'. */ #define E1000_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Status of Master requests. */ @@ -1507,6 +1601,10 @@ struct e1000_hw { #define E1000_STM_OPCODE 0xDB00 #define E1000_HICR_FW_RESET 0xC0 +#define E1000_SHADOW_RAM_WORDS 2048 +#define E1000_ICH8_NVM_SIG_WORD 0x13 +#define E1000_ICH8_NVM_SIG_MASK 0xC0 + /* EEPROM Read */ #define E1000_EERD_START 0x00000001 /* Start Read */ #define E1000_EERD_DONE 0x00000010 /* Read Done */ @@ -1552,7 +1650,6 @@ struct e1000_hw { #define E1000_CTRL_EXT_WR_WMARK_320 0x01000000 #define E1000_CTRL_EXT_WR_WMARK_384 0x02000000 #define E1000_CTRL_EXT_WR_WMARK_448 0x03000000 -#define E1000_CTRL_EXT_CANC 0x04000000 /* Interrupt delay cancellation */ #define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */ #define E1000_CTRL_EXT_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */ #define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */ @@ -1592,12 +1689,31 @@ struct e1000_hw { #define E1000_KUMCTRLSTA_FIFO_CTRL_TX_BYPASS 0x00000800 /* In-Band Control */ +#define E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT 0x00000500 #define E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING 0x00000010 /* Half-Duplex Control */ #define E1000_KUMCTRLSTA_HD_CTRL_10_100_DEFAULT 0x00000004 #define E1000_KUMCTRLSTA_HD_CTRL_1000_DEFAULT 0x00000000 +#define E1000_KUMCTRLSTA_OFFSET_K0S_CTRL 0x0000001E + +#define E1000_KUMCTRLSTA_DIAG_FELPBK 0x2000 +#define E1000_KUMCTRLSTA_DIAG_NELPBK 0x1000 + +#define E1000_KUMCTRLSTA_K0S_100_EN 0x2000 +#define E1000_KUMCTRLSTA_K0S_GBE_EN 0x1000 +#define E1000_KUMCTRLSTA_K0S_ENTRY_LATENCY_MASK 0x0003 + +#define E1000_KABGTXD_BGSQLBIAS 0x00050000 + +#define E1000_PHY_CTRL_SPD_EN 0x00000001 +#define E1000_PHY_CTRL_D0A_LPLU 0x00000002 +#define E1000_PHY_CTRL_NOND0A_LPLU 0x00000004 +#define E1000_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008 +#define E1000_PHY_CTRL_GBE_DISABLE 0x00000040 +#define E1000_PHY_CTRL_B2B_EN 0x00000080 + /* LED Control */ #define E1000_LEDCTL_LED0_MODE_MASK 0x0000000F #define E1000_LEDCTL_LED0_MODE_SHIFT 0 @@ -1667,6 +1783,9 @@ struct e1000_hw { #define E1000_ICR_RXD_FIFO_PAR1 0x01000000 /* queue 1 Rx descriptor FIFO parity error */ #define E1000_ICR_TXD_FIFO_PAR1 0x02000000 /* queue 1 Tx descriptor FIFO parity error */ #define E1000_ICR_ALL_PARITY 0x03F00000 /* all parity error bits */ +#define E1000_ICR_DSW 0x00000020 /* FW changed the status of DISSW bit in the FWSM */ +#define E1000_ICR_PHYINT 0x00001000 /* LAN connected device generates an interrupt */ +#define E1000_ICR_EPRST 0x00100000 /* ME handware reset occurs */ /* Interrupt Cause Set */ #define E1000_ICS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ @@ -1693,6 +1812,9 @@ struct e1000_hw { #define E1000_ICS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ #define E1000_ICS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ #define E1000_ICS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_ICS_DSW E1000_ICR_DSW +#define E1000_ICS_PHYINT E1000_ICR_PHYINT +#define E1000_ICS_EPRST E1000_ICR_EPRST /* Interrupt Mask Set */ #define E1000_IMS_TXDW E1000_ICR_TXDW /* Transmit desc written back */ @@ -1719,6 +1841,9 @@ struct e1000_hw { #define E1000_IMS_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ #define E1000_IMS_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ #define E1000_IMS_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMS_DSW E1000_ICR_DSW +#define E1000_IMS_PHYINT E1000_ICR_PHYINT +#define E1000_IMS_EPRST E1000_ICR_EPRST /* Interrupt Mask Clear */ #define E1000_IMC_TXDW E1000_ICR_TXDW /* Transmit desc written back */ @@ -1745,6 +1870,9 @@ struct e1000_hw { #define E1000_IMC_PB_PAR E1000_ICR_PB_PAR /* packet buffer parity error */ #define E1000_IMC_RXD_FIFO_PAR1 E1000_ICR_RXD_FIFO_PAR1 /* queue 1 Rx descriptor FIFO parity error */ #define E1000_IMC_TXD_FIFO_PAR1 E1000_ICR_TXD_FIFO_PAR1 /* queue 1 Tx descriptor FIFO parity error */ +#define E1000_IMC_DSW E1000_ICR_DSW +#define E1000_IMC_PHYINT E1000_ICR_PHYINT +#define E1000_IMC_EPRST E1000_ICR_EPRST /* Receive Control */ #define E1000_RCTL_RST 0x00000001 /* Software reset */ @@ -1919,9 +2047,10 @@ struct e1000_hw { #define E1000_MRQC_RSS_FIELD_MASK 0xFFFF0000 #define E1000_MRQC_RSS_FIELD_IPV4_TCP 0x00010000 #define E1000_MRQC_RSS_FIELD_IPV4 0x00020000 -#define E1000_MRQC_RSS_FIELD_IPV6_TCP 0x00040000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP_EX 0x00040000 #define E1000_MRQC_RSS_FIELD_IPV6_EX 0x00080000 #define E1000_MRQC_RSS_FIELD_IPV6 0x00100000 +#define E1000_MRQC_RSS_FIELD_IPV6_TCP 0x00200000 /* Definitions for power management and wakeup registers */ /* Wake Up Control */ @@ -2011,6 +2140,15 @@ struct e1000_hw { #define E1000_FWSM_MODE_SHIFT 1 #define E1000_FWSM_FW_VALID 0x00008000 /* FW established a valid mode */ +#define E1000_FWSM_RSPCIPHY 0x00000040 /* Reset PHY on PCI reset */ +#define E1000_FWSM_DISSW 0x10000000 /* FW disable SW Write Access */ +#define E1000_FWSM_SKUSEL_MASK 0x60000000 /* LAN SKU select */ +#define E1000_FWSM_SKUEL_SHIFT 29 +#define E1000_FWSM_SKUSEL_EMB 0x0 /* Embedded SKU */ +#define E1000_FWSM_SKUSEL_CONS 0x1 /* Consumer SKU */ +#define E1000_FWSM_SKUSEL_PERF_100 0x2 /* Perf & Corp 10/100 SKU */ +#define E1000_FWSM_SKUSEL_PERF_GBE 0x3 /* Perf & Copr GbE SKU */ + /* FFLT Debug Register */ #define E1000_FFLT_DBG_INVC 0x00100000 /* Invalid /C/ code handling */ @@ -2083,6 +2221,8 @@ struct e1000_host_command_info { E1000_GCR_TXDSCW_NO_SNOOP | \ E1000_GCR_TXDSCR_NO_SNOOP) +#define PCI_EX_82566_SNOOP_ALL PCI_EX_NO_SNOOP_ALL + #define E1000_GCR_L1_ACT_WITHOUT_L0S_RX 0x08000000 /* Function Active and Power State to MNG */ #define E1000_FACTPS_FUNC0_POWER_STATE_MASK 0x00000003 @@ -2141,8 +2281,10 @@ struct e1000_host_command_info { #define EEPROM_PHY_CLASS_WORD 0x0007 #define EEPROM_INIT_CONTROL1_REG 0x000A #define EEPROM_INIT_CONTROL2_REG 0x000F +#define EEPROM_SWDEF_PINS_CTRL_PORT_1 0x0010 #define EEPROM_INIT_CONTROL3_PORT_B 0x0014 #define EEPROM_INIT_3GIO_3 0x001A +#define EEPROM_SWDEF_PINS_CTRL_PORT_0 0x0020 #define EEPROM_INIT_CONTROL3_PORT_A 0x0024 #define EEPROM_CFG 0x0012 #define EEPROM_FLASH_VERSION 0x0032 @@ -2154,10 +2296,16 @@ struct e1000_host_command_info { /* Word definitions for ID LED Settings */ #define ID_LED_RESERVED_0000 0x0000 #define ID_LED_RESERVED_FFFF 0xFFFF +#define ID_LED_RESERVED_82573 0xF746 +#define ID_LED_DEFAULT_82573 0x1811 #define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \ (ID_LED_OFF1_OFF2 << 8) | \ (ID_LED_DEF1_DEF2 << 4) | \ (ID_LED_DEF1_DEF2)) +#define ID_LED_DEFAULT_ICH8LAN ((ID_LED_DEF1_DEF2 << 12) | \ + (ID_LED_DEF1_OFF2 << 8) | \ + (ID_LED_DEF1_ON2 << 4) | \ + (ID_LED_DEF1_DEF2)) #define ID_LED_DEF1_DEF2 0x1 #define ID_LED_DEF1_ON2 0x2 #define ID_LED_DEF1_OFF2 0x3 @@ -2192,6 +2340,11 @@ struct e1000_host_command_info { #define EEPROM_WORD0F_ASM_DIR 0x2000 #define EEPROM_WORD0F_ANE 0x0800 #define EEPROM_WORD0F_SWPDIO_EXT 0x00F0 +#define EEPROM_WORD0F_LPLU 0x0001 + +/* Mask bits for fields in Word 0x10/0x20 of the EEPROM */ +#define EEPROM_WORD1020_GIGA_DISABLE 0x0010 +#define EEPROM_WORD1020_GIGA_DISABLE_NON_D0A 0x0008 /* Mask bits for fields in Word 0x1a of the EEPROM */ #define EEPROM_WORD1A_ASPM_MASK 0x000C @@ -2266,23 +2419,29 @@ struct e1000_host_command_info { #define E1000_EXTCNF_CTRL_D_UD_OWNER 0x00000010 #define E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020 #define E1000_EXTCNF_CTRL_MDIO_HW_OWNERSHIP 0x00000040 -#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER 0x1FFF0000 +#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER 0x0FFF0000 #define E1000_EXTCNF_SIZE_EXT_PHY_LENGTH 0x000000FF #define E1000_EXTCNF_SIZE_EXT_DOCK_LENGTH 0x0000FF00 #define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH 0x00FF0000 +#define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001 +#define E1000_EXTCNF_CTRL_SWFLAG 0x00000020 /* PBA constants */ +#define E1000_PBA_8K 0x0008 /* 8KB, default Rx allocation */ #define E1000_PBA_12K 0x000C /* 12KB, default Rx allocation */ #define E1000_PBA_16K 0x0010 /* 16KB, default TX allocation */ #define E1000_PBA_22K 0x0016 #define E1000_PBA_24K 0x0018 #define E1000_PBA_30K 0x001E #define E1000_PBA_32K 0x0020 +#define E1000_PBA_34K 0x0022 #define E1000_PBA_38K 0x0026 #define E1000_PBA_40K 0x0028 #define E1000_PBA_48K 0x0030 /* 48KB, default RX allocation */ +#define E1000_PBS_16K E1000_PBA_16K + /* Flow Control Constants */ #define FLOW_CONTROL_ADDRESS_LOW 0x00C28001 #define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 @@ -2337,7 +2496,7 @@ struct e1000_host_command_info { /* Number of milliseconds we wait for Eeprom auto read bit done after MAC reset */ #define AUTO_READ_DONE_TIMEOUT 10 /* Number of milliseconds we wait for PHY configuration done after MAC reset */ -#define PHY_CFG_TIMEOUT 40 +#define PHY_CFG_TIMEOUT 100 #define E1000_TX_BUFFER_SIZE ((uint32_t)1514) @@ -3002,6 +3161,221 @@ struct e1000_host_command_info { #define L1LXT971A_PHY_ID 0x001378E0 #define GG82563_E_PHY_ID 0x01410CA0 + +/* Bits... + * 15-5: page + * 4-0: register offset + */ +#define PHY_PAGE_SHIFT 5 +#define PHY_REG(page, reg) \ + (((page) << PHY_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS)) + +#define IGP3_PHY_PORT_CTRL \ + PHY_REG(769, 17) /* Port General Configuration */ +#define IGP3_PHY_RATE_ADAPT_CTRL \ + PHY_REG(769, 25) /* Rate Adapter Control Register */ + +#define IGP3_KMRN_FIFO_CTRL_STATS \ + PHY_REG(770, 16) /* KMRN FIFO's control/status register */ +#define IGP3_KMRN_POWER_MNG_CTRL \ + PHY_REG(770, 17) /* KMRN Power Management Control Register */ +#define IGP3_KMRN_INBAND_CTRL \ + PHY_REG(770, 18) /* KMRN Inband Control Register */ +#define IGP3_KMRN_DIAG \ + PHY_REG(770, 19) /* KMRN Diagnostic register */ +#define IGP3_KMRN_DIAG_PCS_LOCK_LOSS 0x0002 /* RX PCS is not synced */ +#define IGP3_KMRN_ACK_TIMEOUT \ + PHY_REG(770, 20) /* KMRN Acknowledge Timeouts register */ + +#define IGP3_VR_CTRL \ + PHY_REG(776, 18) /* Voltage regulator control register */ +#define IGP3_VR_CTRL_MODE_SHUT 0x0200 /* Enter powerdown, shutdown VRs */ + +#define IGP3_CAPABILITY \ + PHY_REG(776, 19) /* IGP3 Capability Register */ + +/* Capabilities for SKU Control */ +#define IGP3_CAP_INITIATE_TEAM 0x0001 /* Able to initiate a team */ +#define IGP3_CAP_WFM 0x0002 /* Support WoL and PXE */ +#define IGP3_CAP_ASF 0x0004 /* Support ASF */ +#define IGP3_CAP_LPLU 0x0008 /* Support Low Power Link Up */ +#define IGP3_CAP_DC_AUTO_SPEED 0x0010 /* Support AC/DC Auto Link Speed */ +#define IGP3_CAP_SPD 0x0020 /* Support Smart Power Down */ +#define IGP3_CAP_MULT_QUEUE 0x0040 /* Support 2 tx & 2 rx queues */ +#define IGP3_CAP_RSS 0x0080 /* Support RSS */ +#define IGP3_CAP_8021PQ 0x0100 /* Support 802.1Q & 802.1p */ +#define IGP3_CAP_AMT_CB 0x0200 /* Support active manageability and circuit breaker */ + +#define IGP3_PPC_JORDAN_EN 0x0001 +#define IGP3_PPC_JORDAN_GIGA_SPEED 0x0002 + +#define IGP3_KMRN_PMC_EE_IDLE_LINK_DIS 0x0001 +#define IGP3_KMRN_PMC_K0S_ENTRY_LATENCY_MASK 0x001E +#define IGP3_KMRN_PMC_K0S_MODE1_EN_GIGA 0x0020 +#define IGP3_KMRN_PMC_K0S_MODE1_EN_100 0x0040 + +#define IGP3E1000_PHY_MISC_CTRL 0x1B /* Misc. Ctrl register */ +#define IGP3_PHY_MISC_DUPLEX_MANUAL_SET 0x1000 /* Duplex Manual Set */ + +#define IGP3_KMRN_EXT_CTRL PHY_REG(770, 18) +#define IGP3_KMRN_EC_DIS_INBAND 0x0080 + +#define IGP03E1000_E_PHY_ID 0x02A80390 +#define IFE_E_PHY_ID 0x02A80330 /* 10/100 PHY */ +#define IFE_PLUS_E_PHY_ID 0x02A80320 +#define IFE_C_E_PHY_ID 0x02A80310 + +#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10 /* 100BaseTx Extended Status, Control and Address */ +#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY special control register */ +#define IFE_PHY_RCV_FALSE_CARRIER 0x13 /* 100BaseTx Receive False Carrier Counter */ +#define IFE_PHY_RCV_DISCONNECT 0x14 /* 100BaseTx Receive Disconnet Counter */ +#define IFE_PHY_RCV_ERROT_FRAME 0x15 /* 100BaseTx Receive Error Frame Counter */ +#define IFE_PHY_RCV_SYMBOL_ERR 0x16 /* Receive Symbol Error Counter */ +#define IFE_PHY_PREM_EOF_ERR 0x17 /* 100BaseTx Receive Premature End Of Frame Error Counter */ +#define IFE_PHY_RCV_EOF_ERR 0x18 /* 10BaseT Receive End Of Frame Error Counter */ +#define IFE_PHY_TX_JABBER_DETECT 0x19 /* 10BaseT Transmit Jabber Detect Counter */ +#define IFE_PHY_EQUALIZER 0x1A /* PHY Equalizer Control and Status */ +#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY special control and LED configuration */ +#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control register */ +#define IFE_PHY_HWI_CONTROL 0x1D /* Hardware Integrity Control (HWI) */ + +#define IFE_PESC_REDUCED_POWER_DOWN_DISABLE 0x2000 /* Defaut 1 = Disable auto reduced power down */ +#define IFE_PESC_100BTX_POWER_DOWN 0x0400 /* Indicates the power state of 100BASE-TX */ +#define IFE_PESC_10BTX_POWER_DOWN 0x0200 /* Indicates the power state of 10BASE-T */ +#define IFE_PESC_POLARITY_REVERSED 0x0100 /* Indicates 10BASE-T polarity */ +#define IFE_PESC_PHY_ADDR_MASK 0x007C /* Bit 6:2 for sampled PHY address */ +#define IFE_PESC_SPEED 0x0002 /* Auto-negotiation speed result 1=100Mbs, 0=10Mbs */ +#define IFE_PESC_DUPLEX 0x0001 /* Auto-negotiation duplex result 1=Full, 0=Half */ +#define IFE_PESC_POLARITY_REVERSED_SHIFT 8 + +#define IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN 0x0100 /* 1 = Dyanmic Power Down disabled */ +#define IFE_PSC_FORCE_POLARITY 0x0020 /* 1=Reversed Polarity, 0=Normal */ +#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010 /* 1=Auto Polarity Disabled, 0=Enabled */ +#define IFE_PSC_JABBER_FUNC_DISABLE 0x0001 /* 1=Jabber Disabled, 0=Normal Jabber Operation */ +#define IFE_PSC_FORCE_POLARITY_SHIFT 5 +#define IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT 4 + +#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X feature, default 0=disabled */ +#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, 0=force MDI */ +#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ +#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorthm is completed */ +#define IFE_PMC_MDIX_MODE_SHIFT 6 +#define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */ + +#define IFE_PHC_HWI_ENABLE 0x8000 /* Enable the HWI feature */ +#define IFE_PHC_ABILITY_CHECK 0x4000 /* 1= Test Passed, 0=failed */ +#define IFE_PHC_TEST_EXEC 0x2000 /* PHY launch test pulses on the wire */ +#define IFE_PHC_HIGHZ 0x0200 /* 1 = Open Circuit */ +#define IFE_PHC_LOWZ 0x0400 /* 1 = Short Circuit */ +#define IFE_PHC_LOW_HIGH_Z_MASK 0x0600 /* Mask for indication type of problem on the line */ +#define IFE_PHC_DISTANCE_MASK 0x01FF /* Mask for distance to the cable problem, in 80cm granularity */ +#define IFE_PHC_RESET_ALL_MASK 0x0000 /* Disable HWI */ +#define IFE_PSCL_PROBE_MODE 0x0020 /* LED Probe mode */ +#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */ +#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */ + +#define ICH8_FLASH_COMMAND_TIMEOUT 500 /* 500 ms , should be adjusted */ +#define ICH8_FLASH_CYCLE_REPEAT_COUNT 10 /* 10 cycles , should be adjusted */ +#define ICH8_FLASH_SEG_SIZE_256 256 +#define ICH8_FLASH_SEG_SIZE_4K 4096 +#define ICH8_FLASH_SEG_SIZE_64K 65536 + +#define ICH8_CYCLE_READ 0x0 +#define ICH8_CYCLE_RESERVED 0x1 +#define ICH8_CYCLE_WRITE 0x2 +#define ICH8_CYCLE_ERASE 0x3 + +#define ICH8_FLASH_GFPREG 0x0000 +#define ICH8_FLASH_HSFSTS 0x0004 +#define ICH8_FLASH_HSFCTL 0x0006 +#define ICH8_FLASH_FADDR 0x0008 +#define ICH8_FLASH_FDATA0 0x0010 +#define ICH8_FLASH_FRACC 0x0050 +#define ICH8_FLASH_FREG0 0x0054 +#define ICH8_FLASH_FREG1 0x0058 +#define ICH8_FLASH_FREG2 0x005C +#define ICH8_FLASH_FREG3 0x0060 +#define ICH8_FLASH_FPR0 0x0074 +#define ICH8_FLASH_FPR1 0x0078 +#define ICH8_FLASH_SSFSTS 0x0090 +#define ICH8_FLASH_SSFCTL 0x0092 +#define ICH8_FLASH_PREOP 0x0094 +#define ICH8_FLASH_OPTYPE 0x0096 +#define ICH8_FLASH_OPMENU 0x0098 + +#define ICH8_FLASH_REG_MAPSIZE 0x00A0 +#define ICH8_FLASH_SECTOR_SIZE 4096 +#define ICH8_GFPREG_BASE_MASK 0x1FFF +#define ICH8_FLASH_LINEAR_ADDR_MASK 0x00FFFFFF + +/* ICH8 GbE Flash Hardware Sequencing Flash Status Register bit breakdown */ +/* Offset 04h HSFSTS */ +union ich8_hws_flash_status { + struct ich8_hsfsts { +#ifdef E1000_BIG_ENDIAN + uint16_t reserved2 :6; + uint16_t fldesvalid :1; + uint16_t flockdn :1; + uint16_t flcdone :1; + uint16_t flcerr :1; + uint16_t dael :1; + uint16_t berasesz :2; + uint16_t flcinprog :1; + uint16_t reserved1 :2; +#else + uint16_t flcdone :1; /* bit 0 Flash Cycle Done */ + uint16_t flcerr :1; /* bit 1 Flash Cycle Error */ + uint16_t dael :1; /* bit 2 Direct Access error Log */ + uint16_t berasesz :2; /* bit 4:3 Block/Sector Erase Size */ + uint16_t flcinprog :1; /* bit 5 flash SPI cycle in Progress */ + uint16_t reserved1 :2; /* bit 13:6 Reserved */ + uint16_t reserved2 :6; /* bit 13:6 Reserved */ + uint16_t fldesvalid :1; /* bit 14 Flash Descriptor Valid */ + uint16_t flockdn :1; /* bit 15 Flash Configuration Lock-Down */ +#endif + } hsf_status; + uint16_t regval; +}; + +/* ICH8 GbE Flash Hardware Sequencing Flash control Register bit breakdown */ +/* Offset 06h FLCTL */ +union ich8_hws_flash_ctrl { + struct ich8_hsflctl { +#ifdef E1000_BIG_ENDIAN + uint16_t fldbcount :2; + uint16_t flockdn :6; + uint16_t flcgo :1; + uint16_t flcycle :2; + uint16_t reserved :5; +#else + uint16_t flcgo :1; /* 0 Flash Cycle Go */ + uint16_t flcycle :2; /* 2:1 Flash Cycle */ + uint16_t reserved :5; /* 7:3 Reserved */ + uint16_t fldbcount :2; /* 9:8 Flash Data Byte Count */ + uint16_t flockdn :6; /* 15:10 Reserved */ +#endif + } hsf_ctrl; + uint16_t regval; +}; + +/* ICH8 Flash Region Access Permissions */ +union ich8_hws_flash_regacc { + struct ich8_flracc { +#ifdef E1000_BIG_ENDIAN + uint32_t gmwag :8; + uint32_t gmrag :8; + uint32_t grwa :8; + uint32_t grra :8; +#else + uint32_t grra :8; /* 0:7 GbE region Read Access */ + uint32_t grwa :8; /* 8:15 GbE region Write Access */ + uint32_t gmrag :8; /* 23:16 GbE Master Read Access Grant */ + uint32_t gmwag :8; /* 31:24 GbE Master Write Access Grant */ +#endif + } hsf_flregacc; + uint16_t regval; +}; + /* Miscellaneous PHY bit definitions. */ #define PHY_PREAMBLE 0xFFFFFFFF #define PHY_SOF 0x01 diff --git a/drivers/net/e1000/e1000_osdep.h b/drivers/net/e1000/e1000_osdep.h index 048d052be29d..2d3e8b06cab0 100644 --- a/drivers/net/e1000/e1000_osdep.h +++ b/drivers/net/e1000/e1000_osdep.h @@ -127,4 +127,17 @@ typedef enum { #define E1000_WRITE_FLUSH(a) E1000_READ_REG(a, STATUS) +#define E1000_WRITE_ICH8_REG(a, reg, value) ( \ + writel((value), ((a)->flash_address + reg))) + +#define E1000_READ_ICH8_REG(a, reg) ( \ + readl((a)->flash_address + reg)) + +#define E1000_WRITE_ICH8_REG16(a, reg, value) ( \ + writew((value), ((a)->flash_address + reg))) + +#define E1000_READ_ICH8_REG16(a, reg) ( \ + readw((a)->flash_address + reg)) + + #endif /* _E1000_OSDEP_H_ */ -- cgit v1.2.3 From cd94dd0b648ceb64ca5e41d9ccfa99c1e30e92ef Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:08:22 -0700 Subject: e1000: integrate ich8 support into driver This hooks up the ich8 structure into the driver itself. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000.h | 1 + drivers/net/e1000/e1000_ethtool.c | 80 +++-- drivers/net/e1000/e1000_hw.c | 654 ++++++++++++++++++++++++++++++++------ drivers/net/e1000/e1000_main.c | 120 ++++++- 4 files changed, 725 insertions(+), 130 deletions(-) diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index dbdaa332cac6..323a2683417f 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -143,6 +143,7 @@ struct e1000_adapter; #define AUTO_ALL_MODES 0 #define E1000_EEPROM_82544_APM 0x0004 +#define E1000_EEPROM_ICH8_APME 0x0004 #define E1000_EEPROM_APME 0x0400 #ifndef E1000_MASTER_SLAVE diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 060915532923..3a0b847fac31 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -109,7 +109,8 @@ e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) SUPPORTED_1000baseT_Full| SUPPORTED_Autoneg | SUPPORTED_TP); - + if (hw->phy_type == e1000_phy_ife) + ecmd->supported &= ~SUPPORTED_1000baseT_Full; ecmd->advertising = ADVERTISED_TP; if (hw->autoneg == 1) { @@ -573,6 +574,7 @@ e1000_get_drvinfo(struct net_device *netdev, case e1000_82572: case e1000_82573: case e1000_80003es2lan: + case e1000_ich8lan: sprintf(firmware_version, "%d.%d-%d", (eeprom_data & 0xF000) >> 12, (eeprom_data & 0x0FF0) >> 4, @@ -757,6 +759,7 @@ e1000_reg_test(struct e1000_adapter *adapter, uint64_t *data) toggle = 0x7FFFF3FF; break; case e1000_82573: + case e1000_ich8lan: toggle = 0x7FFFF033; break; default: @@ -776,11 +779,12 @@ e1000_reg_test(struct e1000_adapter *adapter, uint64_t *data) } /* restore previous status */ E1000_WRITE_REG(&adapter->hw, STATUS, before); - - REG_PATTERN_TEST(FCAL, 0xFFFFFFFF, 0xFFFFFFFF); - REG_PATTERN_TEST(FCAH, 0x0000FFFF, 0xFFFFFFFF); - REG_PATTERN_TEST(FCT, 0x0000FFFF, 0xFFFFFFFF); - REG_PATTERN_TEST(VET, 0x0000FFFF, 0xFFFFFFFF); + if (adapter->hw.mac_type != e1000_ich8lan) { + REG_PATTERN_TEST(FCAL, 0xFFFFFFFF, 0xFFFFFFFF); + REG_PATTERN_TEST(FCAH, 0x0000FFFF, 0xFFFFFFFF); + REG_PATTERN_TEST(FCT, 0x0000FFFF, 0xFFFFFFFF); + REG_PATTERN_TEST(VET, 0x0000FFFF, 0xFFFFFFFF); + } REG_PATTERN_TEST(RDTR, 0x0000FFFF, 0xFFFFFFFF); REG_PATTERN_TEST(RDBAH, 0xFFFFFFFF, 0xFFFFFFFF); REG_PATTERN_TEST(RDLEN, 0x000FFF80, 0x000FFFFF); @@ -793,20 +797,22 @@ e1000_reg_test(struct e1000_adapter *adapter, uint64_t *data) REG_PATTERN_TEST(TDLEN, 0x000FFF80, 0x000FFFFF); REG_SET_AND_CHECK(RCTL, 0xFFFFFFFF, 0x00000000); - REG_SET_AND_CHECK(RCTL, 0x06DFB3FE, 0x003FFFFB); + before = (adapter->hw.mac_type == e1000_ich8lan ? + 0x06C3B33E : 0x06DFB3FE); + REG_SET_AND_CHECK(RCTL, before, 0x003FFFFB); REG_SET_AND_CHECK(TCTL, 0xFFFFFFFF, 0x00000000); if (adapter->hw.mac_type >= e1000_82543) { - REG_SET_AND_CHECK(RCTL, 0x06DFB3FE, 0xFFFFFFFF); + REG_SET_AND_CHECK(RCTL, before, 0xFFFFFFFF); REG_PATTERN_TEST(RDBAL, 0xFFFFFFF0, 0xFFFFFFFF); - REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF); + if (adapter->hw.mac_type != e1000_ich8lan) + REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF); REG_PATTERN_TEST(TDBAL, 0xFFFFFFF0, 0xFFFFFFFF); REG_PATTERN_TEST(TIDV, 0x0000FFFF, 0x0000FFFF); - - for (i = 0; i < E1000_RAR_ENTRIES; i++) { - REG_PATTERN_TEST(RA + ((i << 1) << 2), 0xFFFFFFFF, - 0xFFFFFFFF); + value = (adapter->hw.mac_type == e1000_ich8lan ? + E1000_RAR_ENTRIES_ICH8LAN : E1000_RAR_ENTRIES); + for (i = 0; i < value; i++) { REG_PATTERN_TEST(RA + (((i << 1) + 1) << 2), 0x8003FFFF, 0xFFFFFFFF); } @@ -820,7 +826,9 @@ e1000_reg_test(struct e1000_adapter *adapter, uint64_t *data) } - for (i = 0; i < E1000_MC_TBL_SIZE; i++) + value = (adapter->hw.mac_type == e1000_ich8lan ? + E1000_MC_TBL_SIZE_ICH8LAN : E1000_MC_TBL_SIZE); + for (i = 0; i < value; i++) REG_PATTERN_TEST(MTA + (i << 2), 0xFFFFFFFF, 0xFFFFFFFF); *data = 0; @@ -892,6 +900,8 @@ e1000_intr_test(struct e1000_adapter *adapter, uint64_t *data) /* Test each interrupt */ for (; i < 10; i++) { + if (adapter->hw.mac_type == e1000_ich8lan && i == 8) + continue; /* Interrupt to test */ mask = 1 << i; @@ -1251,16 +1261,31 @@ e1000_integrated_phy_loopback(struct e1000_adapter *adapter) GG82563_PHY_KMRN_MODE_CTRL, 0x1CC); } - /* force 1000, set loopback */ - e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x4140); - /* Now set up the MAC to the same speed/duplex as the PHY. */ ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL); - ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ - ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */ - E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */ - E1000_CTRL_SPD_1000 |/* Force Speed to 1000 */ - E1000_CTRL_FD); /* Force Duplex to FULL */ + + if (adapter->hw.phy_type == e1000_phy_ife) { + /* force 100, set loopback */ + e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x6100); + + /* Now set up the MAC to the same speed/duplex as the PHY. */ + ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ + ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */ + E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */ + E1000_CTRL_SPD_100 |/* Force Speed to 100 */ + E1000_CTRL_FD); /* Force Duplex to FULL */ + } else { + /* force 1000, set loopback */ + e1000_write_phy_reg(&adapter->hw, PHY_CTRL, 0x4140); + + /* Now set up the MAC to the same speed/duplex as the PHY. */ + ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL); + ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ + ctrl_reg |= (E1000_CTRL_FRCSPD | /* Set the Force Speed Bit */ + E1000_CTRL_FRCDPX | /* Set the Force Duplex Bit */ + E1000_CTRL_SPD_1000 |/* Force Speed to 1000 */ + E1000_CTRL_FD); /* Force Duplex to FULL */ + } if (adapter->hw.media_type == e1000_media_type_copper && adapter->hw.phy_type == e1000_phy_m88) { @@ -1320,6 +1345,7 @@ e1000_set_phy_loopback(struct e1000_adapter *adapter) case e1000_82572: case e1000_82573: case e1000_80003es2lan: + case e1000_ich8lan: return e1000_integrated_phy_loopback(adapter); break; @@ -1786,6 +1812,16 @@ e1000_phys_id(struct net_device *netdev, uint32_t data) mod_timer(&adapter->blink_timer, jiffies); msleep_interruptible(data * 1000); del_timer_sync(&adapter->blink_timer); + } else if (adapter->hw.phy_type == e1000_phy_ife) { + if (!adapter->blink_timer.function) { + init_timer(&adapter->blink_timer); + adapter->blink_timer.function = e1000_led_blink_callback; + adapter->blink_timer.data = (unsigned long) adapter; + } + mod_timer(&adapter->blink_timer, jiffies); + msleep_interruptible(data * 1000); + del_timer_sync(&adapter->blink_timer); + e1000_write_phy_reg(&(adapter->hw), IFE_PHY_SPECIAL_CONTROL_LED, 0); } else { e1000_blink_led_start(&adapter->hw); msleep_interruptible(data * 1000); diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index a3f5ccdfafc6..583518ae49ce 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -101,7 +101,8 @@ static void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, #define E1000_WRITE_REG_IO(a, reg, val) \ e1000_write_reg_io((a), E1000_##reg, val) -static int32_t e1000_configure_kmrn_for_10_100(struct e1000_hw *hw); +static int32_t e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, + uint16_t duplex); static int32_t e1000_configure_kmrn_for_1000(struct e1000_hw *hw); /* IGP cable length table */ @@ -156,6 +157,14 @@ e1000_set_phy_type(struct e1000_hw *hw) hw->phy_type = e1000_phy_igp; break; } + case IGP03E1000_E_PHY_ID: + hw->phy_type = e1000_phy_igp_3; + break; + case IFE_E_PHY_ID: + case IFE_PLUS_E_PHY_ID: + case IFE_C_E_PHY_ID: + hw->phy_type = e1000_phy_ife; + break; case GG82563_E_PHY_ID: if (hw->mac_type == e1000_80003es2lan) { hw->phy_type = e1000_phy_gg82563; @@ -332,6 +341,7 @@ e1000_set_mac_type(struct e1000_hw *hw) break; case E1000_DEV_ID_82541EI: case E1000_DEV_ID_82541EI_MOBILE: + case E1000_DEV_ID_82541ER_LOM: hw->mac_type = e1000_82541; break; case E1000_DEV_ID_82541ER: @@ -341,6 +351,7 @@ e1000_set_mac_type(struct e1000_hw *hw) hw->mac_type = e1000_82541_rev_2; break; case E1000_DEV_ID_82547EI: + case E1000_DEV_ID_82547EI_MOBILE: hw->mac_type = e1000_82547; break; case E1000_DEV_ID_82547GI: @@ -354,6 +365,7 @@ e1000_set_mac_type(struct e1000_hw *hw) case E1000_DEV_ID_82572EI_COPPER: case E1000_DEV_ID_82572EI_FIBER: case E1000_DEV_ID_82572EI_SERDES: + case E1000_DEV_ID_82572EI: hw->mac_type = e1000_82572; break; case E1000_DEV_ID_82573E: @@ -361,16 +373,29 @@ e1000_set_mac_type(struct e1000_hw *hw) case E1000_DEV_ID_82573L: hw->mac_type = e1000_82573; break; + case E1000_DEV_ID_80003ES2LAN_COPPER_SPT: + case E1000_DEV_ID_80003ES2LAN_SERDES_SPT: case E1000_DEV_ID_80003ES2LAN_COPPER_DPT: case E1000_DEV_ID_80003ES2LAN_SERDES_DPT: hw->mac_type = e1000_80003es2lan; break; + case E1000_DEV_ID_ICH8_IGP_M_AMT: + case E1000_DEV_ID_ICH8_IGP_AMT: + case E1000_DEV_ID_ICH8_IGP_C: + case E1000_DEV_ID_ICH8_IFE: + case E1000_DEV_ID_ICH8_IGP_M: + hw->mac_type = e1000_ich8lan; + break; default: /* Should never have loaded on this device */ return -E1000_ERR_MAC_TYPE; } switch(hw->mac_type) { + case e1000_ich8lan: + hw->swfwhw_semaphore_present = TRUE; + hw->asf_firmware_present = TRUE; + break; case e1000_80003es2lan: hw->swfw_sync_present = TRUE; /* fall through */ @@ -423,6 +448,7 @@ e1000_set_media_type(struct e1000_hw *hw) case e1000_82542_rev2_1: hw->media_type = e1000_media_type_fiber; break; + case e1000_ich8lan: case e1000_82573: /* The STATUS_TBIMODE bit is reserved or reused for the this * device. @@ -527,6 +553,14 @@ e1000_reset_hw(struct e1000_hw *hw) } while(timeout); } + /* Workaround for ICH8 bit corruption issue in FIFO memory */ + if (hw->mac_type == e1000_ich8lan) { + /* Set Tx and Rx buffer allocation to 8k apiece. */ + E1000_WRITE_REG(hw, PBA, E1000_PBA_8K); + /* Set Packet Buffer Size to 16k. */ + E1000_WRITE_REG(hw, PBS, E1000_PBS_16K); + } + /* Issue a global reset to the MAC. This will reset the chip's * transmit, receive, DMA, and link units. It will not effect * the current PCI configuration. The global reset bit is self- @@ -550,6 +584,20 @@ e1000_reset_hw(struct e1000_hw *hw) /* Reset is performed on a shadow of the control register */ E1000_WRITE_REG(hw, CTRL_DUP, (ctrl | E1000_CTRL_RST)); break; + case e1000_ich8lan: + if (!hw->phy_reset_disable && + e1000_check_phy_reset_block(hw) == E1000_SUCCESS) { + /* e1000_ich8lan PHY HW reset requires MAC CORE reset + * at the same time to make sure the interface between + * MAC and the external PHY is reset. + */ + ctrl |= E1000_CTRL_PHY_RST; + } + + e1000_get_software_flag(hw); + E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); + msec_delay(5); + break; default: E1000_WRITE_REG(hw, CTRL, (ctrl | E1000_CTRL_RST)); break; @@ -591,6 +639,7 @@ e1000_reset_hw(struct e1000_hw *hw) /* fall through */ case e1000_82571: case e1000_82572: + case e1000_ich8lan: case e1000_80003es2lan: ret_val = e1000_get_auto_rd_done(hw); if(ret_val) @@ -633,6 +682,12 @@ e1000_reset_hw(struct e1000_hw *hw) e1000_pci_set_mwi(hw); } + if (hw->mac_type == e1000_ich8lan) { + uint32_t kab = E1000_READ_REG(hw, KABGTXD); + kab |= E1000_KABGTXD_BGSQLBIAS; + E1000_WRITE_REG(hw, KABGTXD, kab); + } + return E1000_SUCCESS; } @@ -675,9 +730,12 @@ e1000_init_hw(struct e1000_hw *hw) /* Disabling VLAN filtering. */ DEBUGOUT("Initializing the IEEE VLAN\n"); - if (hw->mac_type < e1000_82545_rev_3) - E1000_WRITE_REG(hw, VET, 0); - e1000_clear_vfta(hw); + /* VET hardcoded to standard value and VFTA removed in ICH8 LAN */ + if (hw->mac_type != e1000_ich8lan) { + if (hw->mac_type < e1000_82545_rev_3) + E1000_WRITE_REG(hw, VET, 0); + e1000_clear_vfta(hw); + } /* For 82542 (rev 2.0), disable MWI and put the receiver into reset */ if(hw->mac_type == e1000_82542_rev2_0) { @@ -705,6 +763,8 @@ e1000_init_hw(struct e1000_hw *hw) /* Zero out the Multicast HASH table */ DEBUGOUT("Zeroing the MTA\n"); mta_size = E1000_MC_TBL_SIZE; + if (hw->mac_type == e1000_ich8lan) + mta_size = E1000_MC_TBL_SIZE_ICH8LAN; for(i = 0; i < mta_size; i++) { E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); /* use write flush to prevent Memory Write Block (MWB) from @@ -748,6 +808,10 @@ e1000_init_hw(struct e1000_hw *hw) break; } + /* More time needed for PHY to initialize */ + if (hw->mac_type == e1000_ich8lan) + msec_delay(15); + /* Call a subroutine to configure the link and setup flow control. */ ret_val = e1000_setup_link(hw); @@ -761,6 +825,7 @@ e1000_init_hw(struct e1000_hw *hw) case e1000_82571: case e1000_82572: case e1000_82573: + case e1000_ich8lan: case e1000_80003es2lan: ctrl |= E1000_TXDCTL_COUNT_DESC; break; @@ -799,6 +864,7 @@ e1000_init_hw(struct e1000_hw *hw) /* Fall through */ case e1000_82571: case e1000_82572: + case e1000_ich8lan: ctrl = E1000_READ_REG(hw, TXDCTL1); ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB; if(hw->mac_type >= e1000_82571) @@ -822,6 +888,11 @@ e1000_init_hw(struct e1000_hw *hw) */ e1000_clear_hw_cntrs(hw); + /* ICH8 No-snoop bits are opposite polarity. + * Set to snoop by default after reset. */ + if (hw->mac_type == e1000_ich8lan) + e1000_set_pci_ex_no_snoop(hw, PCI_EX_82566_SNOOP_ALL); + if (hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER || hw->device_id == E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3) { ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); @@ -909,6 +980,7 @@ e1000_setup_link(struct e1000_hw *hw) */ if (hw->fc == e1000_fc_default) { switch (hw->mac_type) { + case e1000_ich8lan: case e1000_82573: hw->fc = e1000_fc_full; break; @@ -975,9 +1047,12 @@ e1000_setup_link(struct e1000_hw *hw) */ DEBUGOUT("Initializing the Flow Control address, type and timer regs\n"); - E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW); - E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH); - E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE); + /* FCAL/H and FCT are hardcoded to standard values in e1000_ich8lan. */ + if (hw->mac_type != e1000_ich8lan) { + E1000_WRITE_REG(hw, FCT, FLOW_CONTROL_TYPE); + E1000_WRITE_REG(hw, FCAH, FLOW_CONTROL_ADDRESS_HIGH); + E1000_WRITE_REG(hw, FCAL, FLOW_CONTROL_ADDRESS_LOW); + } E1000_WRITE_REG(hw, FCTTV, hw->fc_pause_time); @@ -1241,12 +1316,13 @@ e1000_copper_link_igp_setup(struct e1000_hw *hw) /* Wait 10ms for MAC to configure PHY from eeprom settings */ msec_delay(15); - + if (hw->mac_type != e1000_ich8lan) { /* Configure activity LED after PHY reset */ led_ctrl = E1000_READ_REG(hw, LEDCTL); led_ctrl &= IGP_ACTIVITY_LED_MASK; led_ctrl |= (IGP_ACTIVITY_LED_ENABLE | IGP_LED3_MODE); E1000_WRITE_REG(hw, LEDCTL, led_ctrl); + } /* disable lplu d3 during driver init */ ret_val = e1000_set_d3_lplu_state(hw, FALSE); @@ -1482,8 +1558,7 @@ e1000_copper_link_ggp_setup(struct e1000_hw *hw) if (ret_val) return ret_val; - /* Enable Pass False Carrier on the PHY */ - phy_data |= GG82563_KMCR_PASS_FALSE_CARRIER; + phy_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, phy_data); @@ -1636,6 +1711,10 @@ e1000_copper_link_autoneg(struct e1000_hw *hw) if(hw->autoneg_advertised == 0) hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; + /* IFE phy only supports 10/100 */ + if (hw->phy_type == e1000_phy_ife) + hw->autoneg_advertised &= AUTONEG_ADVERTISE_10_100_ALL; + DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); ret_val = e1000_phy_setup_autoneg(hw); if(ret_val) { @@ -1733,6 +1812,26 @@ e1000_setup_copper_link(struct e1000_hw *hw) DEBUGFUNC("e1000_setup_copper_link"); + switch (hw->mac_type) { + case e1000_80003es2lan: + case e1000_ich8lan: + /* Set the mac to wait the maximum time between each + * iteration and increase the max iterations when + * polling the phy; this fixes erroneous timeouts at 10Mbps. */ + ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 4), 0xFFFF); + if (ret_val) + return ret_val; + ret_val = e1000_read_kmrn_reg(hw, GG82563_REG(0x34, 9), ®_data); + if (ret_val) + return ret_val; + reg_data |= 0x3F; + ret_val = e1000_write_kmrn_reg(hw, GG82563_REG(0x34, 9), reg_data); + if (ret_val) + return ret_val; + default: + break; + } + /* Check if it is a valid PHY and set PHY mode if necessary. */ ret_val = e1000_copper_link_preconfig(hw); if(ret_val) @@ -1740,10 +1839,8 @@ e1000_setup_copper_link(struct e1000_hw *hw) switch (hw->mac_type) { case e1000_80003es2lan: - ret_val = e1000_read_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_INB_CTRL, - ®_data); - if (ret_val) - return ret_val; + /* Kumeran registers are written-only */ + reg_data = E1000_KUMCTRLSTA_INB_CTRL_LINK_STATUS_TX_TIMEOUT_DEFAULT; reg_data |= E1000_KUMCTRLSTA_INB_CTRL_DIS_PADDING; ret_val = e1000_write_kmrn_reg(hw, E1000_KUMCTRLSTA_OFFSET_INB_CTRL, reg_data); @@ -1755,6 +1852,7 @@ e1000_setup_copper_link(struct e1000_hw *hw) } if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || hw->phy_type == e1000_phy_igp_2) { ret_val = e1000_copper_link_igp_setup(hw); if(ret_val) @@ -1819,7 +1917,7 @@ e1000_setup_copper_link(struct e1000_hw *hw) * hw - Struct containing variables accessed by shared code ******************************************************************************/ static int32_t -e1000_configure_kmrn_for_10_100(struct e1000_hw *hw) +e1000_configure_kmrn_for_10_100(struct e1000_hw *hw, uint16_t duplex) { int32_t ret_val = E1000_SUCCESS; uint32_t tipg; @@ -1839,6 +1937,18 @@ e1000_configure_kmrn_for_10_100(struct e1000_hw *hw) tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_10_100; E1000_WRITE_REG(hw, TIPG, tipg); + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + + if (ret_val) + return ret_val; + + if (duplex == HALF_DUPLEX) + reg_data |= GG82563_KMCR_PASS_FALSE_CARRIER; + else + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + return ret_val; } @@ -1863,6 +1973,14 @@ e1000_configure_kmrn_for_1000(struct e1000_hw *hw) tipg |= DEFAULT_80003ES2LAN_TIPG_IPGT_1000; E1000_WRITE_REG(hw, TIPG, tipg); + ret_val = e1000_read_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, ®_data); + + if (ret_val) + return ret_val; + + reg_data &= ~GG82563_KMCR_PASS_FALSE_CARRIER; + ret_val = e1000_write_phy_reg(hw, GG82563_PHY_KMRN_MODE_CTRL, reg_data); + return ret_val; } @@ -1885,10 +2003,13 @@ e1000_phy_setup_autoneg(struct e1000_hw *hw) if(ret_val) return ret_val; - /* Read the MII 1000Base-T Control Register (Address 9). */ - ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg); - if(ret_val) - return ret_val; + if (hw->phy_type != e1000_phy_ife) { + /* Read the MII 1000Base-T Control Register (Address 9). */ + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } else + mii_1000t_ctrl_reg=0; /* Need to parse both autoneg_advertised and fc and set up * the appropriate PHY registers. First we will parse for @@ -1939,6 +2060,9 @@ e1000_phy_setup_autoneg(struct e1000_hw *hw) if(hw->autoneg_advertised & ADVERTISE_1000_FULL) { DEBUGOUT("Advertise 1000mb Full duplex\n"); mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; + if (hw->phy_type == e1000_phy_ife) { + DEBUGOUT("e1000_phy_ife is a 10/100 PHY. Gigabit speed is not supported.\n"); + } } /* Check for a software override of the flow control settings, and @@ -2000,9 +2124,11 @@ e1000_phy_setup_autoneg(struct e1000_hw *hw) DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); - ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); - if(ret_val) - return ret_val; + if (hw->phy_type != e1000_phy_ife) { + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); + if (ret_val) + return ret_val; + } return E1000_SUCCESS; } @@ -2105,6 +2231,18 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw) /* Need to reset the PHY or these changes will be ignored */ mii_ctrl_reg |= MII_CR_RESET; + /* Disable MDI-X support for 10/100 */ + } else if (hw->phy_type == e1000_phy_ife) { + ret_val = e1000_read_phy_reg(hw, IFE_PHY_MDIX_CONTROL, &phy_data); + if (ret_val) + return ret_val; + + phy_data &= ~IFE_PMC_AUTO_MDIX; + phy_data &= ~IFE_PMC_FORCE_MDIX; + + ret_val = e1000_write_phy_reg(hw, IFE_PHY_MDIX_CONTROL, phy_data); + if (ret_val) + return ret_val; } else { /* Clear Auto-Crossover to force MDI manually. IGP requires MDI * forced whenever speed or duplex are forced. @@ -2909,7 +3047,13 @@ e1000_get_speed_and_duplex(struct e1000_hw *hw, if (*speed == SPEED_1000) ret_val = e1000_configure_kmrn_for_1000(hw); else - ret_val = e1000_configure_kmrn_for_10_100(hw); + ret_val = e1000_configure_kmrn_for_10_100(hw, *duplex); + if (ret_val) + return ret_val; + } + + if ((hw->phy_type == e1000_phy_igp_3) && (*speed == SPEED_1000)) { + ret_val = e1000_kumeran_lock_loss_workaround(hw); if (ret_val) return ret_val; } @@ -3099,6 +3243,9 @@ e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask) DEBUGFUNC("e1000_swfw_sync_acquire"); + if (hw->swfwhw_semaphore_present) + return e1000_get_software_flag(hw); + if (!hw->swfw_sync_present) return e1000_get_hw_eeprom_semaphore(hw); @@ -3138,6 +3285,11 @@ e1000_swfw_sync_release(struct e1000_hw *hw, uint16_t mask) DEBUGFUNC("e1000_swfw_sync_release"); + if (hw->swfwhw_semaphore_present) { + e1000_release_software_flag(hw); + return; + } + if (!hw->swfw_sync_present) { e1000_put_hw_eeprom_semaphore(hw); return; @@ -3180,7 +3332,8 @@ e1000_read_phy_reg(struct e1000_hw *hw, if (e1000_swfw_sync_acquire(hw, swfw)) return -E1000_ERR_SWFW_SYNC; - if((hw->phy_type == e1000_phy_igp || + if ((hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || hw->phy_type == e1000_phy_igp_2) && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, @@ -3319,7 +3472,8 @@ e1000_write_phy_reg(struct e1000_hw *hw, if (e1000_swfw_sync_acquire(hw, swfw)) return -E1000_ERR_SWFW_SYNC; - if((hw->phy_type == e1000_phy_igp || + if ((hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || hw->phy_type == e1000_phy_igp_2) && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, @@ -3534,7 +3688,7 @@ e1000_phy_hw_reset(struct e1000_hw *hw) E1000_WRITE_FLUSH(hw); if (hw->mac_type >= e1000_82571) - msec_delay(10); + msec_delay_irq(10); e1000_swfw_sync_release(hw, swfw); } else { /* Read the Extended Device Control Register, assert the PHY_RESET_DIR @@ -3564,6 +3718,12 @@ e1000_phy_hw_reset(struct e1000_hw *hw) ret_val = e1000_get_phy_cfg_done(hw); e1000_release_software_semaphore(hw); + if ((hw->mac_type == e1000_ich8lan) && + (hw->phy_type == e1000_phy_igp_3)) { + ret_val = e1000_init_lcd_from_nvm(hw); + if (ret_val) + return ret_val; + } return ret_val; } @@ -3592,9 +3752,11 @@ e1000_phy_reset(struct e1000_hw *hw) case e1000_82541_rev_2: case e1000_82571: case e1000_82572: + case e1000_ich8lan: ret_val = e1000_phy_hw_reset(hw); if(ret_val) return ret_val; + break; default: ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); @@ -3742,8 +3904,8 @@ e1000_detect_gig_phy(struct e1000_hw *hw) /* The 82571 firmware may still be configuring the PHY. In this * case, we cannot access the PHY until the configuration is done. So * we explicitly set the PHY values. */ - if(hw->mac_type == e1000_82571 || - hw->mac_type == e1000_82572) { + if (hw->mac_type == e1000_82571 || + hw->mac_type == e1000_82572) { hw->phy_id = IGP01E1000_I_PHY_ID; hw->phy_type = e1000_phy_igp_2; return E1000_SUCCESS; @@ -3760,7 +3922,7 @@ e1000_detect_gig_phy(struct e1000_hw *hw) /* Read the PHY ID Registers to identify which PHY is onboard. */ ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high); - if(ret_val) + if (ret_val) return ret_val; hw->phy_id = (uint32_t) (phy_id_high << 16); @@ -3798,6 +3960,12 @@ e1000_detect_gig_phy(struct e1000_hw *hw) case e1000_80003es2lan: if (hw->phy_id == GG82563_E_PHY_ID) match = TRUE; break; + case e1000_ich8lan: + if (hw->phy_id == IGP03E1000_E_PHY_ID) match = TRUE; + if (hw->phy_id == IFE_E_PHY_ID) match = TRUE; + if (hw->phy_id == IFE_PLUS_E_PHY_ID) match = TRUE; + if (hw->phy_id == IFE_C_E_PHY_ID) match = TRUE; + break; default: DEBUGOUT1("Invalid MAC type %d\n", hw->mac_type); return -E1000_ERR_CONFIG; @@ -4074,9 +4242,12 @@ e1000_phy_get_info(struct e1000_hw *hw, return -E1000_ERR_CONFIG; } - if(hw->phy_type == e1000_phy_igp || + if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || hw->phy_type == e1000_phy_igp_2) return e1000_phy_igp_get_info(hw, phy_info); + else if (hw->phy_type == e1000_phy_ife) + return e1000_phy_ife_get_info(hw, phy_info); else return e1000_phy_m88_get_info(hw, phy_info); } @@ -4225,6 +4396,35 @@ e1000_init_eeprom_params(struct e1000_hw *hw) eeprom->use_eerd = TRUE; eeprom->use_eewr = FALSE; break; + case e1000_ich8lan: + { + int32_t i = 0; + uint32_t flash_size = E1000_READ_ICH8_REG(hw, ICH8_FLASH_GFPREG); + + eeprom->type = e1000_eeprom_ich8; + eeprom->use_eerd = FALSE; + eeprom->use_eewr = FALSE; + eeprom->word_size = E1000_SHADOW_RAM_WORDS; + + /* Zero the shadow RAM structure. But don't load it from NVM + * so as to save time for driver init */ + if (hw->eeprom_shadow_ram != NULL) { + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + hw->eeprom_shadow_ram[i].modified = FALSE; + hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF; + } + } + + hw->flash_base_addr = (flash_size & ICH8_GFPREG_BASE_MASK) * + ICH8_FLASH_SECTOR_SIZE; + + hw->flash_bank_size = ((flash_size >> 16) & ICH8_GFPREG_BASE_MASK) + 1; + hw->flash_bank_size -= (flash_size & ICH8_GFPREG_BASE_MASK); + hw->flash_bank_size *= ICH8_FLASH_SECTOR_SIZE; + hw->flash_bank_size /= 2 * sizeof(uint16_t); + + break; + } default: break; } @@ -4645,7 +4845,10 @@ e1000_read_eeprom(struct e1000_hw *hw, return ret_val; } - if(eeprom->type == e1000_eeprom_spi) { + if (eeprom->type == e1000_eeprom_ich8) + return e1000_read_eeprom_ich8(hw, offset, words, data); + + if (eeprom->type == e1000_eeprom_spi) { uint16_t word_in; uint8_t read_opcode = EEPROM_READ_OPCODE_SPI; @@ -4812,7 +5015,10 @@ e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw) DEBUGFUNC("e1000_is_onboard_nvm_eeprom"); - if(hw->mac_type == e1000_82573) { + if (hw->mac_type == e1000_ich8lan) + return FALSE; + + if (hw->mac_type == e1000_82573) { eecd = E1000_READ_REG(hw, EECD); /* Isolate bits 15 & 16 */ @@ -4862,8 +5068,22 @@ e1000_validate_eeprom_checksum(struct e1000_hw *hw) } } - for(i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { - if(e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { + if (hw->mac_type == e1000_ich8lan) { + /* Drivers must allocate the shadow ram structure for the + * EEPROM checksum to be updated. Otherwise, this bit as well + * as the checksum must both be set correctly for this + * validation to pass. + */ + e1000_read_eeprom(hw, 0x19, 1, &eeprom_data); + if ((eeprom_data & 0x40) == 0) { + eeprom_data |= 0x40; + e1000_write_eeprom(hw, 0x19, 1, &eeprom_data); + e1000_update_eeprom_checksum(hw); + } + } + + for (i = 0; i < (EEPROM_CHECKSUM_REG + 1); i++) { + if (e1000_read_eeprom(hw, i, 1, &eeprom_data) < 0) { DEBUGOUT("EEPROM Read Error\n"); return -E1000_ERR_EEPROM; } @@ -4889,6 +5109,7 @@ e1000_validate_eeprom_checksum(struct e1000_hw *hw) int32_t e1000_update_eeprom_checksum(struct e1000_hw *hw) { + uint32_t ctrl_ext; uint16_t checksum = 0; uint16_t i, eeprom_data; @@ -4907,6 +5128,14 @@ e1000_update_eeprom_checksum(struct e1000_hw *hw) return -E1000_ERR_EEPROM; } else if (hw->eeprom.type == e1000_eeprom_flash) { e1000_commit_shadow_ram(hw); + } else if (hw->eeprom.type == e1000_eeprom_ich8) { + e1000_commit_shadow_ram(hw); + /* Reload the EEPROM, or else modifications will not appear + * until after next adapter reset. */ + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + ctrl_ext |= E1000_CTRL_EXT_EE_RST; + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + msec_delay(10); } return E1000_SUCCESS; } @@ -4946,6 +5175,9 @@ e1000_write_eeprom(struct e1000_hw *hw, if(eeprom->use_eewr == TRUE) return e1000_write_eeprom_eewr(hw, offset, words, data); + if (eeprom->type == e1000_eeprom_ich8) + return e1000_write_eeprom_ich8(hw, offset, words, data); + /* Prepare the EEPROM for writing */ if (e1000_acquire_eeprom(hw) != E1000_SUCCESS) return -E1000_ERR_EEPROM; @@ -5133,11 +5365,17 @@ e1000_commit_shadow_ram(struct e1000_hw *hw) uint32_t flop = 0; uint32_t i = 0; int32_t error = E1000_SUCCESS; - - /* The flop register will be used to determine if flash type is STM */ - flop = E1000_READ_REG(hw, FLOP); + uint32_t old_bank_offset = 0; + uint32_t new_bank_offset = 0; + uint32_t sector_retries = 0; + uint8_t low_byte = 0; + uint8_t high_byte = 0; + uint8_t temp_byte = 0; + boolean_t sector_write_failed = FALSE; if (hw->mac_type == e1000_82573) { + /* The flop register will be used to determine if flash type is STM */ + flop = E1000_READ_REG(hw, FLOP); for (i=0; i < attempts; i++) { eecd = E1000_READ_REG(hw, EECD); if ((eecd & E1000_EECD_FLUPD) == 0) { @@ -5171,6 +5409,106 @@ e1000_commit_shadow_ram(struct e1000_hw *hw) } } + if (hw->mac_type == e1000_ich8lan && hw->eeprom_shadow_ram != NULL) { + /* We're writing to the opposite bank so if we're on bank 1, + * write to bank 0 etc. We also need to erase the segment that + * is going to be written */ + if (!(E1000_READ_REG(hw, EECD) & E1000_EECD_SEC1VAL)) { + new_bank_offset = hw->flash_bank_size * 2; + old_bank_offset = 0; + e1000_erase_ich8_4k_segment(hw, 1); + } else { + old_bank_offset = hw->flash_bank_size * 2; + new_bank_offset = 0; + e1000_erase_ich8_4k_segment(hw, 0); + } + + do { + sector_write_failed = FALSE; + /* Loop for every byte in the shadow RAM, + * which is in units of words. */ + for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) { + /* Determine whether to write the value stored + * in the other NVM bank or a modified value stored + * in the shadow RAM */ + if (hw->eeprom_shadow_ram[i].modified == TRUE) { + low_byte = (uint8_t)hw->eeprom_shadow_ram[i].eeprom_word; + e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset, + &temp_byte); + udelay(100); + error = e1000_verify_write_ich8_byte(hw, + (i << 1) + new_bank_offset, + low_byte); + if (error != E1000_SUCCESS) + sector_write_failed = TRUE; + high_byte = + (uint8_t)(hw->eeprom_shadow_ram[i].eeprom_word >> 8); + e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset + 1, + &temp_byte); + udelay(100); + } else { + e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset, + &low_byte); + udelay(100); + error = e1000_verify_write_ich8_byte(hw, + (i << 1) + new_bank_offset, low_byte); + if (error != E1000_SUCCESS) + sector_write_failed = TRUE; + e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset + 1, + &high_byte); + } + + /* If the word is 0x13, then make sure the signature bits + * (15:14) are 11b until the commit has completed. + * This will allow us to write 10b which indicates the + * signature is valid. We want to do this after the write + * has completed so that we don't mark the segment valid + * while the write is still in progress */ + if (i == E1000_ICH8_NVM_SIG_WORD) + high_byte = E1000_ICH8_NVM_SIG_MASK | high_byte; + + error = e1000_verify_write_ich8_byte(hw, + (i << 1) + new_bank_offset + 1, high_byte); + if (error != E1000_SUCCESS) + sector_write_failed = TRUE; + + if (sector_write_failed == FALSE) { + /* Clear the now not used entry in the cache */ + hw->eeprom_shadow_ram[i].modified = FALSE; + hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF; + } + } + + /* Don't bother writing the segment valid bits if sector + * programming failed. */ + if (sector_write_failed == FALSE) { + /* Finally validate the new segment by setting bit 15:14 + * to 10b in word 0x13 , this can be done without an + * erase as well since these bits are 11 to start with + * and we need to change bit 14 to 0b */ + e1000_read_ich8_byte(hw, + E1000_ICH8_NVM_SIG_WORD * 2 + 1 + new_bank_offset, + &high_byte); + high_byte &= 0xBF; + error = e1000_verify_write_ich8_byte(hw, + E1000_ICH8_NVM_SIG_WORD * 2 + 1 + new_bank_offset, + high_byte); + if (error != E1000_SUCCESS) + sector_write_failed = TRUE; + + /* And invalidate the previously valid segment by setting + * its signature word (0x13) high_byte to 0b. This can be + * done without an erase because flash erase sets all bits + * to 1's. We can write 1's to 0's without an erase */ + error = e1000_verify_write_ich8_byte(hw, + E1000_ICH8_NVM_SIG_WORD * 2 + 1 + old_bank_offset, + 0); + if (error != E1000_SUCCESS) + sector_write_failed = TRUE; + } + } while (++sector_retries < 10 && sector_write_failed == TRUE); + } + return error; } @@ -5278,6 +5616,9 @@ e1000_init_rx_addrs(struct e1000_hw *hw) * the other port. */ if ((hw->mac_type == e1000_82571) && (hw->laa_is_present == TRUE)) rar_num -= 1; + if (hw->mac_type == e1000_ich8lan) + rar_num = E1000_RAR_ENTRIES_ICH8LAN; + /* Zero out the other 15 receive addresses. */ DEBUGOUT("Clearing RAR[1-15]\n"); for(i = 1; i < rar_num; i++) { @@ -5288,7 +5629,6 @@ e1000_init_rx_addrs(struct e1000_hw *hw) } } -#if 0 /****************************************************************************** * Updates the MAC's list of multicast addresses. * @@ -5323,6 +5663,8 @@ e1000_mc_addr_list_update(struct e1000_hw *hw, /* Clear RAR[1-15] */ DEBUGOUT(" Clearing RAR[1-15]\n"); num_rar_entry = E1000_RAR_ENTRIES; + if (hw->mac_type == e1000_ich8lan) + num_rar_entry = E1000_RAR_ENTRIES_ICH8LAN; /* Reserve a spot for the Locally Administered Address to work around * an 82571 issue in which a reset on one port will reload the MAC on * the other port. */ @@ -5339,6 +5681,8 @@ e1000_mc_addr_list_update(struct e1000_hw *hw, /* Clear the MTA */ DEBUGOUT(" Clearing MTA\n"); num_mta_entry = E1000_NUM_MTA_REGISTERS; + if (hw->mac_type == e1000_ich8lan) + num_mta_entry = E1000_NUM_MTA_REGISTERS_ICH8LAN; for(i = 0; i < num_mta_entry; i++) { E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); E1000_WRITE_FLUSH(hw); @@ -5375,7 +5719,6 @@ e1000_mc_addr_list_update(struct e1000_hw *hw, } DEBUGOUT("MC Update Complete\n"); } -#endif /* 0 */ /****************************************************************************** * Hashes an address to determine its location in the multicast table @@ -5398,24 +5741,46 @@ e1000_hash_mc_addr(struct e1000_hw *hw, * LSB MSB */ case 0: - /* [47:36] i.e. 0x563 for above example address */ - hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4)); + if (hw->mac_type == e1000_ich8lan) { + /* [47:38] i.e. 0x158 for above example address */ + hash_value = ((mc_addr[4] >> 6) | (((uint16_t) mc_addr[5]) << 2)); + } else { + /* [47:36] i.e. 0x563 for above example address */ + hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4)); + } break; case 1: - /* [46:35] i.e. 0xAC6 for above example address */ - hash_value = ((mc_addr[4] >> 3) | (((uint16_t) mc_addr[5]) << 5)); + if (hw->mac_type == e1000_ich8lan) { + /* [46:37] i.e. 0x2B1 for above example address */ + hash_value = ((mc_addr[4] >> 5) | (((uint16_t) mc_addr[5]) << 3)); + } else { + /* [46:35] i.e. 0xAC6 for above example address */ + hash_value = ((mc_addr[4] >> 3) | (((uint16_t) mc_addr[5]) << 5)); + } break; case 2: - /* [45:34] i.e. 0x5D8 for above example address */ - hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6)); + if (hw->mac_type == e1000_ich8lan) { + /*[45:36] i.e. 0x163 for above example address */ + hash_value = ((mc_addr[4] >> 4) | (((uint16_t) mc_addr[5]) << 4)); + } else { + /* [45:34] i.e. 0x5D8 for above example address */ + hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6)); + } break; case 3: - /* [43:32] i.e. 0x634 for above example address */ - hash_value = ((mc_addr[4]) | (((uint16_t) mc_addr[5]) << 8)); + if (hw->mac_type == e1000_ich8lan) { + /* [43:34] i.e. 0x18D for above example address */ + hash_value = ((mc_addr[4] >> 2) | (((uint16_t) mc_addr[5]) << 6)); + } else { + /* [43:32] i.e. 0x634 for above example address */ + hash_value = ((mc_addr[4]) | (((uint16_t) mc_addr[5]) << 8)); + } break; } hash_value &= 0xFFF; + if (hw->mac_type == e1000_ich8lan) + hash_value &= 0x3FF; return hash_value; } @@ -5443,6 +5808,8 @@ e1000_mta_set(struct e1000_hw *hw, * register are determined by the lower 5 bits of the value. */ hash_reg = (hash_value >> 5) & 0x7F; + if (hw->mac_type == e1000_ich8lan) + hash_reg &= 0x1F; hash_bit = hash_value & 0x1F; mta = E1000_READ_REG_ARRAY(hw, MTA, hash_reg); @@ -5537,7 +5904,10 @@ e1000_write_vfta(struct e1000_hw *hw, { uint32_t temp; - if((hw->mac_type == e1000_82544) && ((offset & 0x1) == 1)) { + if (hw->mac_type == e1000_ich8lan) + return; + + if ((hw->mac_type == e1000_82544) && ((offset & 0x1) == 1)) { temp = E1000_READ_REG_ARRAY(hw, VFTA, (offset - 1)); E1000_WRITE_REG_ARRAY(hw, VFTA, offset, value); E1000_WRITE_FLUSH(hw); @@ -5562,6 +5932,9 @@ e1000_clear_vfta(struct e1000_hw *hw) uint32_t vfta_offset = 0; uint32_t vfta_bit_in_reg = 0; + if (hw->mac_type == e1000_ich8lan) + return; + if (hw->mac_type == e1000_82573) { if (hw->mng_cookie.vlan_id != 0) { /* The VFTA is a 4096b bit-field, each identifying a single VLAN @@ -5611,9 +5984,18 @@ e1000_id_led_init(struct e1000_hw * hw) DEBUGOUT("EEPROM Read Error\n"); return -E1000_ERR_EEPROM; } - if((eeprom_data== ID_LED_RESERVED_0000) || - (eeprom_data == ID_LED_RESERVED_FFFF)) eeprom_data = ID_LED_DEFAULT; - for(i = 0; i < 4; i++) { + + if ((hw->mac_type == e1000_82573) && + (eeprom_data == ID_LED_RESERVED_82573)) + eeprom_data = ID_LED_DEFAULT_82573; + else if ((eeprom_data == ID_LED_RESERVED_0000) || + (eeprom_data == ID_LED_RESERVED_FFFF)) { + if (hw->mac_type == e1000_ich8lan) + eeprom_data = ID_LED_DEFAULT_ICH8LAN; + else + eeprom_data = ID_LED_DEFAULT; + } + for (i = 0; i < 4; i++) { temp = (eeprom_data >> (i << 2)) & led_mask; switch(temp) { case ID_LED_ON1_DEF2: @@ -5776,6 +6158,10 @@ e1000_cleanup_led(struct e1000_hw *hw) return ret_val; /* Fall Through */ default: + if (hw->phy_type == e1000_phy_ife) { + e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, 0); + break; + } /* Restore LEDCTL settings */ E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_default); break; @@ -5820,7 +6206,10 @@ e1000_led_on(struct e1000_hw *hw) /* Clear SW Defineable Pin 0 to turn on the LED */ ctrl &= ~E1000_CTRL_SWDPIN0; ctrl |= E1000_CTRL_SWDPIO0; - } else if(hw->media_type == e1000_media_type_copper) { + } else if (hw->phy_type == e1000_phy_ife) { + e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, + (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_ON)); + } else if (hw->media_type == e1000_media_type_copper) { E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode2); return E1000_SUCCESS; } @@ -5868,7 +6257,10 @@ e1000_led_off(struct e1000_hw *hw) /* Set SW Defineable Pin 0 to turn off the LED */ ctrl |= E1000_CTRL_SWDPIN0; ctrl |= E1000_CTRL_SWDPIO0; - } else if(hw->media_type == e1000_media_type_copper) { + } else if (hw->phy_type == e1000_phy_ife) { + e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL_LED, + (IFE_PSCL_PROBE_MODE | IFE_PSCL_PROBE_LEDS_OFF)); + } else if (hw->media_type == e1000_media_type_copper) { E1000_WRITE_REG(hw, LEDCTL, hw->ledctl_mode1); return E1000_SUCCESS; } @@ -5906,12 +6298,16 @@ e1000_clear_hw_cntrs(struct e1000_hw *hw) temp = E1000_READ_REG(hw, XOFFRXC); temp = E1000_READ_REG(hw, XOFFTXC); temp = E1000_READ_REG(hw, FCRUC); + + if (hw->mac_type != e1000_ich8lan) { temp = E1000_READ_REG(hw, PRC64); temp = E1000_READ_REG(hw, PRC127); temp = E1000_READ_REG(hw, PRC255); temp = E1000_READ_REG(hw, PRC511); temp = E1000_READ_REG(hw, PRC1023); temp = E1000_READ_REG(hw, PRC1522); + } + temp = E1000_READ_REG(hw, GPRC); temp = E1000_READ_REG(hw, BPRC); temp = E1000_READ_REG(hw, MPRC); @@ -5931,12 +6327,16 @@ e1000_clear_hw_cntrs(struct e1000_hw *hw) temp = E1000_READ_REG(hw, TOTH); temp = E1000_READ_REG(hw, TPR); temp = E1000_READ_REG(hw, TPT); + + if (hw->mac_type != e1000_ich8lan) { temp = E1000_READ_REG(hw, PTC64); temp = E1000_READ_REG(hw, PTC127); temp = E1000_READ_REG(hw, PTC255); temp = E1000_READ_REG(hw, PTC511); temp = E1000_READ_REG(hw, PTC1023); temp = E1000_READ_REG(hw, PTC1522); + } + temp = E1000_READ_REG(hw, MPTC); temp = E1000_READ_REG(hw, BPTC); @@ -5959,6 +6359,9 @@ e1000_clear_hw_cntrs(struct e1000_hw *hw) temp = E1000_READ_REG(hw, IAC); temp = E1000_READ_REG(hw, ICRXOC); + + if (hw->mac_type == e1000_ich8lan) return; + temp = E1000_READ_REG(hw, ICRXPTC); temp = E1000_READ_REG(hw, ICRXATC); temp = E1000_READ_REG(hw, ICTXPTC); @@ -6139,6 +6542,7 @@ e1000_get_bus_info(struct e1000_hw *hw) hw->bus_width = e1000_bus_width_pciex_1; break; case e1000_82571: + case e1000_ich8lan: case e1000_80003es2lan: hw->bus_type = e1000_bus_type_pci_express; hw->bus_speed = e1000_bus_speed_2500; @@ -6176,8 +6580,6 @@ e1000_get_bus_info(struct e1000_hw *hw) break; } } - -#if 0 /****************************************************************************** * Reads a value from one of the devices registers using port I/O (as opposed * memory mapped I/O). Only 82544 and newer devices support port I/O. @@ -6195,7 +6597,6 @@ e1000_read_reg_io(struct e1000_hw *hw, e1000_io_write(hw, io_addr, offset); return e1000_io_read(hw, io_data); } -#endif /* 0 */ /****************************************************************************** * Writes a value to one of the devices registers using port I/O (as opposed to @@ -6240,8 +6641,6 @@ e1000_get_cable_length(struct e1000_hw *hw, { int32_t ret_val; uint16_t agc_value = 0; - uint16_t cur_agc, min_agc = IGP01E1000_AGC_LENGTH_TABLE_SIZE; - uint16_t max_agc = 0; uint16_t i, phy_data; uint16_t cable_length; @@ -6314,6 +6713,8 @@ e1000_get_cable_length(struct e1000_hw *hw, break; } } else if(hw->phy_type == e1000_phy_igp) { /* For IGP PHY */ + uint16_t cur_agc_value; + uint16_t min_agc_value = IGP01E1000_AGC_LENGTH_TABLE_SIZE; uint16_t agc_reg_array[IGP01E1000_PHY_CHANNEL_NUM] = {IGP01E1000_PHY_AGC_A, IGP01E1000_PHY_AGC_B, @@ -6326,23 +6727,23 @@ e1000_get_cable_length(struct e1000_hw *hw, if(ret_val) return ret_val; - cur_agc = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT; + cur_agc_value = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT; - /* Array bound check. */ - if((cur_agc >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) || - (cur_agc == 0)) + /* Value bound check. */ + if ((cur_agc_value >= IGP01E1000_AGC_LENGTH_TABLE_SIZE - 1) || + (cur_agc_value == 0)) return -E1000_ERR_PHY; - agc_value += cur_agc; + agc_value += cur_agc_value; /* Update minimal AGC value. */ - if(min_agc > cur_agc) - min_agc = cur_agc; + if (min_agc_value > cur_agc_value) + min_agc_value = cur_agc_value; } /* Remove the minimal AGC result for length < 50m */ - if(agc_value < IGP01E1000_PHY_CHANNEL_NUM * e1000_igp_cable_length_50) { - agc_value -= min_agc; + if (agc_value < IGP01E1000_PHY_CHANNEL_NUM * e1000_igp_cable_length_50) { + agc_value -= min_agc_value; /* Get the average length of the remaining 3 channels */ agc_value /= (IGP01E1000_PHY_CHANNEL_NUM - 1); @@ -6358,7 +6759,10 @@ e1000_get_cable_length(struct e1000_hw *hw, IGP01E1000_AGC_RANGE) : 0; *max_length = e1000_igp_cable_length_table[agc_value] + IGP01E1000_AGC_RANGE; - } else if (hw->phy_type == e1000_phy_igp_2) { + } else if (hw->phy_type == e1000_phy_igp_2 || + hw->phy_type == e1000_phy_igp_3) { + uint16_t cur_agc_index, max_agc_index = 0; + uint16_t min_agc_index = IGP02E1000_AGC_LENGTH_TABLE_SIZE - 1; uint16_t agc_reg_array[IGP02E1000_PHY_CHANNEL_NUM] = {IGP02E1000_PHY_AGC_A, IGP02E1000_PHY_AGC_B, @@ -6373,19 +6777,27 @@ e1000_get_cable_length(struct e1000_hw *hw, /* Getting bits 15:9, which represent the combination of course and * fine gain values. The result is a number that can be put into * the lookup table to obtain the approximate cable length. */ - cur_agc = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & - IGP02E1000_AGC_LENGTH_MASK; + cur_agc_index = (phy_data >> IGP02E1000_AGC_LENGTH_SHIFT) & + IGP02E1000_AGC_LENGTH_MASK; + + /* Array index bound check. */ + if ((cur_agc_index >= IGP02E1000_AGC_LENGTH_TABLE_SIZE) || + (cur_agc_index == 0)) + return -E1000_ERR_PHY; /* Remove min & max AGC values from calculation. */ - if (e1000_igp_2_cable_length_table[min_agc] > e1000_igp_2_cable_length_table[cur_agc]) - min_agc = cur_agc; - if (e1000_igp_2_cable_length_table[max_agc] < e1000_igp_2_cable_length_table[cur_agc]) - max_agc = cur_agc; + if (e1000_igp_2_cable_length_table[min_agc_index] > + e1000_igp_2_cable_length_table[cur_agc_index]) + min_agc_index = cur_agc_index; + if (e1000_igp_2_cable_length_table[max_agc_index] < + e1000_igp_2_cable_length_table[cur_agc_index]) + max_agc_index = cur_agc_index; - agc_value += e1000_igp_2_cable_length_table[cur_agc]; + agc_value += e1000_igp_2_cable_length_table[cur_agc_index]; } - agc_value -= (e1000_igp_2_cable_length_table[min_agc] + e1000_igp_2_cable_length_table[max_agc]); + agc_value -= (e1000_igp_2_cable_length_table[min_agc_index] + + e1000_igp_2_cable_length_table[max_agc_index]); agc_value /= (IGP02E1000_PHY_CHANNEL_NUM - 2); /* Calculate cable length with the error range of +/- 10 meters. */ @@ -6431,7 +6843,8 @@ e1000_check_polarity(struct e1000_hw *hw, return ret_val; *polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) >> M88E1000_PSSR_REV_POLARITY_SHIFT; - } else if(hw->phy_type == e1000_phy_igp || + } else if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || hw->phy_type == e1000_phy_igp_2) { /* Read the Status register to check the speed */ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, @@ -6457,6 +6870,13 @@ e1000_check_polarity(struct e1000_hw *hw, * 100 Mbps this bit is always 0) */ *polarity = phy_data & IGP01E1000_PSSR_POLARITY_REVERSED; } + } else if (hw->phy_type == e1000_phy_ife) { + ret_val = e1000_read_phy_reg(hw, IFE_PHY_EXTENDED_STATUS_CONTROL, + &phy_data); + if (ret_val) + return ret_val; + *polarity = (phy_data & IFE_PESC_POLARITY_REVERSED) >> + IFE_PESC_POLARITY_REVERSED_SHIFT; } return E1000_SUCCESS; } @@ -6484,7 +6904,8 @@ e1000_check_downshift(struct e1000_hw *hw) DEBUGFUNC("e1000_check_downshift"); - if(hw->phy_type == e1000_phy_igp || + if (hw->phy_type == e1000_phy_igp || + hw->phy_type == e1000_phy_igp_3 || hw->phy_type == e1000_phy_igp_2) { ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH, &phy_data); @@ -6501,6 +6922,9 @@ e1000_check_downshift(struct e1000_hw *hw) hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >> M88E1000_PSSR_DOWNSHIFT_SHIFT; + } else if (hw->phy_type == e1000_phy_ife) { + /* e1000_phy_ife supports 10/100 speed only */ + hw->speed_downgraded = FALSE; } return E1000_SUCCESS; @@ -6545,7 +6969,9 @@ e1000_config_dsp_after_link_change(struct e1000_hw *hw, if(speed == SPEED_1000) { - e1000_get_cable_length(hw, &min_length, &max_length); + ret_val = e1000_get_cable_length(hw, &min_length, &max_length); + if (ret_val) + return ret_val; if((hw->dsp_config_state == e1000_dsp_config_enabled) && min_length >= e1000_igp_cable_length_50) { @@ -6753,20 +7179,27 @@ static int32_t e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active) { + uint32_t phy_ctrl = 0; int32_t ret_val; uint16_t phy_data; DEBUGFUNC("e1000_set_d3_lplu_state"); - if(hw->phy_type != e1000_phy_igp && hw->phy_type != e1000_phy_igp_2) + if (hw->phy_type != e1000_phy_igp && hw->phy_type != e1000_phy_igp_2 + && hw->phy_type != e1000_phy_igp_3) return E1000_SUCCESS; /* During driver activity LPLU should not be used or it will attain link * from the lowest speeds starting from 10Mbps. The capability is used for * Dx transitions and states */ - if(hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) { + if (hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) { ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data); - if(ret_val) + if (ret_val) return ret_val; + } else if (hw->mac_type == e1000_ich8lan) { + /* MAC writes into PHY register based on the state transition + * and start auto-negotiation. SW driver can overwrite the settings + * in CSR PHY power control E1000_PHY_CTRL register. */ + phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); } else { ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); if(ret_val) @@ -6781,11 +7214,16 @@ e1000_set_d3_lplu_state(struct e1000_hw *hw, if(ret_val) return ret_val; } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl &= ~E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { phy_data &= ~IGP02E1000_PM_D3_LPLU; ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); if (ret_val) return ret_val; + } } /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during @@ -6821,17 +7259,22 @@ e1000_set_d3_lplu_state(struct e1000_hw *hw, (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) { if(hw->mac_type == e1000_82541_rev_2 || - hw->mac_type == e1000_82547_rev_2) { + hw->mac_type == e1000_82547_rev_2) { phy_data |= IGP01E1000_GMII_FLEX_SPD; ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); if(ret_val) return ret_val; } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl |= E1000_PHY_CTRL_NOND0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { phy_data |= IGP02E1000_PM_D3_LPLU; ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); if (ret_val) return ret_val; + } } /* When LPLU is enabled we should disable SmartSpeed */ @@ -6866,6 +7309,7 @@ static int32_t e1000_set_d0_lplu_state(struct e1000_hw *hw, boolean_t active) { + uint32_t phy_ctrl = 0; int32_t ret_val; uint16_t phy_data; DEBUGFUNC("e1000_set_d0_lplu_state"); @@ -6873,15 +7317,24 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, if(hw->mac_type <= e1000_82547_rev_2) return E1000_SUCCESS; + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl = E1000_READ_REG(hw, PHY_CTRL); + } else { ret_val = e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_data); if(ret_val) return ret_val; + } if (!active) { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl &= ~E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { phy_data &= ~IGP02E1000_PM_D0_LPLU; ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); if (ret_val) return ret_val; + } /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during * Dx states where the power conservation is most important. During @@ -6914,10 +7367,15 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw, } else { + if (hw->mac_type == e1000_ich8lan) { + phy_ctrl |= E1000_PHY_CTRL_D0A_LPLU; + E1000_WRITE_REG(hw, PHY_CTRL, phy_ctrl); + } else { phy_data |= IGP02E1000_PM_D0_LPLU; ret_val = e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_data); if (ret_val) return ret_val; + } /* When LPLU is enabled we should disable SmartSpeed */ ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); @@ -7191,15 +7649,18 @@ e1000_mng_write_commit( * returns - TRUE when the mode is IAMT or FALSE. ****************************************************************************/ boolean_t -e1000_check_mng_mode( - struct e1000_hw *hw) +e1000_check_mng_mode(struct e1000_hw *hw) { uint32_t fwsm; fwsm = E1000_READ_REG(hw, FWSM); - if((fwsm & E1000_FWSM_MODE_MASK) == - (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + if (hw->mac_type == e1000_ich8lan) { + if ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_ICH_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) + return TRUE; + } else if ((fwsm & E1000_FWSM_MODE_MASK) == + (E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)) return TRUE; return FALSE; @@ -7439,7 +7900,6 @@ e1000_set_pci_express_master_disable(struct e1000_hw *hw) E1000_WRITE_REG(hw, CTRL, ctrl); } -#if 0 /*************************************************************************** * * Enables PCI-Express master access. @@ -7463,7 +7923,6 @@ e1000_enable_pciex_master(struct e1000_hw *hw) ctrl &= ~E1000_CTRL_GIO_MASTER_DISABLE; E1000_WRITE_REG(hw, CTRL, ctrl); } -#endif /* 0 */ /******************************************************************************* * @@ -7529,8 +7988,10 @@ e1000_get_auto_rd_done(struct e1000_hw *hw) case e1000_82572: case e1000_82573: case e1000_80003es2lan: - while(timeout) { - if (E1000_READ_REG(hw, EECD) & E1000_EECD_AUTO_RD) break; + case e1000_ich8lan: + while (timeout) { + if (E1000_READ_REG(hw, EECD) & E1000_EECD_AUTO_RD) + break; else msec_delay(1); timeout--; } @@ -7570,7 +8031,7 @@ e1000_get_phy_cfg_done(struct e1000_hw *hw) switch (hw->mac_type) { default: - msec_delay(10); + msec_delay_irq(10); break; case e1000_80003es2lan: /* Separate *_CFG_DONE_* bit for each port */ @@ -7753,6 +8214,13 @@ int32_t e1000_check_phy_reset_block(struct e1000_hw *hw) { uint32_t manc = 0; + uint32_t fwsm = 0; + + if (hw->mac_type == e1000_ich8lan) { + fwsm = E1000_READ_REG(hw, FWSM); + return (fwsm & E1000_FWSM_RSPCIPHY) ? E1000_SUCCESS + : E1000_BLK_PHY_RESET; + } if (hw->mac_type > e1000_82547_rev_2) manc = E1000_READ_REG(hw, MANC); @@ -7779,6 +8247,8 @@ e1000_arc_subsystem_valid(struct e1000_hw *hw) if((fwsm & E1000_FWSM_MODE_MASK) != 0) return TRUE; break; + case e1000_ich8lan: + return TRUE; default: break; } diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index cc0c77c4c170..f5c6f08966c7 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -366,6 +366,7 @@ e1000_release_hw_control(struct e1000_adapter *adapter) { uint32_t ctrl_ext; uint32_t swsm; + uint32_t extcnf; /* Let firmware taken over control of h/w */ switch (adapter->hw.mac_type) { @@ -380,6 +381,11 @@ e1000_release_hw_control(struct e1000_adapter *adapter) swsm = E1000_READ_REG(&adapter->hw, SWSM); E1000_WRITE_REG(&adapter->hw, SWSM, swsm & ~E1000_SWSM_DRV_LOAD); + case e1000_ich8lan: + extcnf = E1000_READ_REG(&adapter->hw, CTRL_EXT); + E1000_WRITE_REG(&adapter->hw, CTRL_EXT, + extcnf & ~E1000_CTRL_EXT_DRV_LOAD); + break; default: break; } @@ -401,6 +407,7 @@ e1000_get_hw_control(struct e1000_adapter *adapter) { uint32_t ctrl_ext; uint32_t swsm; + uint32_t extcnf; /* Let firmware know the driver has taken over */ switch (adapter->hw.mac_type) { case e1000_82571: @@ -415,6 +422,11 @@ e1000_get_hw_control(struct e1000_adapter *adapter) E1000_WRITE_REG(&adapter->hw, SWSM, swsm | E1000_SWSM_DRV_LOAD); break; + case e1000_ich8lan: + extcnf = E1000_READ_REG(&adapter->hw, EXTCNF_CTRL); + E1000_WRITE_REG(&adapter->hw, EXTCNF_CTRL, + extcnf | E1000_EXTCNF_CTRL_SWFLAG); + break; default: break; } @@ -490,6 +502,7 @@ static void e1000_power_down_phy(struct e1000_adapter *adapter) * (b) AMT is active * (c) SoL/IDER session is active */ if (!adapter->wol && adapter->hw.mac_type >= e1000_82540 && + adapter->hw.mac_type != e1000_ich8lan && adapter->hw.media_type == e1000_media_type_copper && !(E1000_READ_REG(&adapter->hw, MANC) & E1000_MANC_SMBUS_EN) && !mng_mode_enabled && @@ -561,6 +574,9 @@ e1000_reset(struct e1000_adapter *adapter) case e1000_82573: pba = E1000_PBA_12K; break; + case e1000_ich8lan: + pba = E1000_PBA_8K; + break; default: pba = E1000_PBA_48K; break; @@ -585,6 +601,12 @@ e1000_reset(struct e1000_adapter *adapter) /* Set the FC high water mark to 90% of the FIFO size. * Required to clear last 3 LSB */ fc_high_water_mark = ((pba * 9216)/10) & 0xFFF8; + /* We can't use 90% on small FIFOs because the remainder + * would be less than 1 full frame. In this case, we size + * it to allow at least a full frame above the high water + * mark. */ + if (pba < E1000_PBA_16K) + fc_high_water_mark = (pba * 1024) - 1600; adapter->hw.fc_high_water = fc_high_water_mark; adapter->hw.fc_low_water = fc_high_water_mark - 8; @@ -622,6 +644,8 @@ e1000_reset(struct e1000_adapter *adapter) phy_data); } + if (adapter->hw.mac_type < e1000_ich8lan) + /* FIXME: this code is duplicate and wrong for PCI Express */ if (adapter->en_mng_pt) { manc = E1000_READ_REG(&adapter->hw, MANC); manc |= (E1000_MANC_ARP_EN | E1000_MANC_EN_MNG2HOST); @@ -648,6 +672,7 @@ e1000_probe(struct pci_dev *pdev, struct net_device *netdev; struct e1000_adapter *adapter; unsigned long mmio_start, mmio_len; + unsigned long flash_start, flash_len; static int cards_found = 0; static int e1000_ksp3_port_a = 0; /* global ksp3 port a indication */ @@ -657,10 +682,12 @@ e1000_probe(struct pci_dev *pdev, if ((err = pci_enable_device(pdev))) return err; - if (!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK))) { + if (!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK)) && + !(err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK))) { pci_using_dac = 1; } else { - if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) { + if ((err = pci_set_dma_mask(pdev, DMA_32BIT_MASK)) && + (err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK))) { E1000_ERR("No usable DMA configuration, aborting\n"); return err; } @@ -740,6 +767,19 @@ e1000_probe(struct pci_dev *pdev, if ((err = e1000_sw_init(adapter))) goto err_sw_init; + /* Flash BAR mapping must happen after e1000_sw_init + * because it depends on mac_type */ + if ((adapter->hw.mac_type == e1000_ich8lan) && + (pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) { + flash_start = pci_resource_start(pdev, 1); + flash_len = pci_resource_len(pdev, 1); + adapter->hw.flash_address = ioremap(flash_start, flash_len); + if (!adapter->hw.flash_address) { + err = -EIO; + goto err_flashmap; + } + } + if ((err = e1000_check_phy_reset_block(&adapter->hw))) DPRINTK(PROBE, INFO, "PHY reset is blocked due to SOL/IDER session.\n"); @@ -758,6 +798,8 @@ e1000_probe(struct pci_dev *pdev, NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; + if (adapter->hw.mac_type == e1000_ich8lan) + netdev->features &= ~NETIF_F_HW_VLAN_FILTER; } #ifdef NETIF_F_TSO @@ -773,11 +815,17 @@ e1000_probe(struct pci_dev *pdev, if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; - /* hard_start_xmit is safe against parallel locking */ netdev->features |= NETIF_F_LLTX; adapter->en_mng_pt = e1000_enable_mng_pass_thru(&adapter->hw); + /* initialize eeprom parameters */ + + if (e1000_init_eeprom_params(&adapter->hw)) { + E1000_ERR("EEPROM initialization failed\n"); + return -EIO; + } + /* before reading the EEPROM, reset the controller to * put the device in a known good starting state */ @@ -845,6 +893,11 @@ e1000_probe(struct pci_dev *pdev, EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data); eeprom_apme_mask = E1000_EEPROM_82544_APM; break; + case e1000_ich8lan: + e1000_read_eeprom(&adapter->hw, + EEPROM_INIT_CONTROL1_REG, 1, &eeprom_data); + eeprom_apme_mask = E1000_EEPROM_ICH8_APME; + break; case e1000_82546: case e1000_82546_rev_3: case e1000_82571: @@ -904,6 +957,9 @@ e1000_probe(struct pci_dev *pdev, return 0; err_register: + if (adapter->hw.flash_address) + iounmap(adapter->hw.flash_address); +err_flashmap: err_sw_init: err_eeprom: iounmap(adapter->hw.hw_addr); @@ -937,6 +993,7 @@ e1000_remove(struct pci_dev *pdev) flush_scheduled_work(); if (adapter->hw.mac_type >= e1000_82540 && + adapter->hw.mac_type != e1000_ich8lan && adapter->hw.media_type == e1000_media_type_copper) { manc = E1000_READ_REG(&adapter->hw, MANC); if (manc & E1000_MANC_SMBUS_EN) { @@ -965,6 +1022,8 @@ e1000_remove(struct pci_dev *pdev) #endif iounmap(adapter->hw.hw_addr); + if (adapter->hw.flash_address) + iounmap(adapter->hw.flash_address); pci_release_regions(pdev); free_netdev(netdev); @@ -1015,13 +1074,6 @@ e1000_sw_init(struct e1000_adapter *adapter) return -EIO; } - /* initialize eeprom parameters */ - - if (e1000_init_eeprom_params(hw)) { - E1000_ERR("EEPROM initialization failed\n"); - return -EIO; - } - switch (hw->mac_type) { default: break; @@ -1257,8 +1309,7 @@ e1000_setup_tx_resources(struct e1000_adapter *adapter, int size; size = sizeof(struct e1000_buffer) * txdr->count; - - txdr->buffer_info = vmalloc_node(size, pcibus_to_node(pdev->bus)); + txdr->buffer_info = vmalloc(size); if (!txdr->buffer_info) { DPRINTK(PROBE, ERR, "Unable to allocate memory for the transmit descriptor ring\n"); @@ -1486,7 +1537,7 @@ e1000_setup_rx_resources(struct e1000_adapter *adapter, int size, desc_len; size = sizeof(struct e1000_buffer) * rxdr->count; - rxdr->buffer_info = vmalloc_node(size, pcibus_to_node(pdev->bus)); + rxdr->buffer_info = vmalloc(size); if (!rxdr->buffer_info) { DPRINTK(PROBE, ERR, "Unable to allocate memory for the receive descriptor ring\n"); @@ -2145,6 +2196,12 @@ e1000_set_multi(struct net_device *netdev) uint32_t rctl; uint32_t hash_value; int i, rar_entries = E1000_RAR_ENTRIES; + int mta_reg_count = (hw->mac_type == e1000_ich8lan) ? + E1000_NUM_MTA_REGISTERS_ICH8LAN : + E1000_NUM_MTA_REGISTERS; + + if (adapter->hw.mac_type == e1000_ich8lan) + rar_entries = E1000_RAR_ENTRIES_ICH8LAN; /* reserve RAR[14] for LAA over-write work-around */ if (adapter->hw.mac_type == e1000_82571) @@ -2191,7 +2248,7 @@ e1000_set_multi(struct net_device *netdev) /* clear the old settings from the multicast hash table */ - for (i = 0; i < E1000_NUM_MTA_REGISTERS; i++) { + for (i = 0; i < mta_reg_count; i++) { E1000_WRITE_REG_ARRAY(hw, MTA, i, 0); E1000_WRITE_FLUSH(hw); } @@ -2270,8 +2327,16 @@ e1000_watchdog(unsigned long data) struct net_device *netdev = adapter->netdev; struct e1000_tx_ring *txdr = adapter->tx_ring; uint32_t link, tctl; - - e1000_check_for_link(&adapter->hw); + int32_t ret_val; + + ret_val = e1000_check_for_link(&adapter->hw); + if ((ret_val == E1000_ERR_PHY) && + (adapter->hw.phy_type == e1000_phy_igp_3) && + (E1000_READ_REG(&adapter->hw, CTRL) & E1000_PHY_CTRL_GBE_DISABLE)) { + /* See e1000_kumeran_lock_loss_workaround() */ + DPRINTK(LINK, INFO, + "Gigabit has been disabled, downgrading speed\n"); + } if (adapter->hw.mac_type == e1000_82573) { e1000_enable_tx_pkt_filtering(&adapter->hw); if (adapter->mng_vlan_id != adapter->hw.mng_cookie.vlan_id) @@ -2837,6 +2902,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) case e1000_82571: case e1000_82572: case e1000_82573: + case e1000_ich8lan: pull_size = min((unsigned int)4, skb->data_len); if (!__pskb_pull_tail(skb, pull_size)) { DPRINTK(DRV, ERR, @@ -3021,6 +3087,7 @@ e1000_change_mtu(struct net_device *netdev, int new_mtu) /* Adapter-specific max frame size limits. */ switch (adapter->hw.mac_type) { case e1000_undefined ... e1000_82542_rev2_1: + case e1000_ich8lan: if (max_frame > MAXIMUM_ETHERNET_FRAME_SIZE) { DPRINTK(PROBE, ERR, "Jumbo Frames not supported.\n"); return -EINVAL; @@ -3129,12 +3196,15 @@ e1000_update_stats(struct e1000_adapter *adapter) adapter->stats.bprc += E1000_READ_REG(hw, BPRC); adapter->stats.mprc += E1000_READ_REG(hw, MPRC); adapter->stats.roc += E1000_READ_REG(hw, ROC); + + if (adapter->hw.mac_type != e1000_ich8lan) { adapter->stats.prc64 += E1000_READ_REG(hw, PRC64); adapter->stats.prc127 += E1000_READ_REG(hw, PRC127); adapter->stats.prc255 += E1000_READ_REG(hw, PRC255); adapter->stats.prc511 += E1000_READ_REG(hw, PRC511); adapter->stats.prc1023 += E1000_READ_REG(hw, PRC1023); adapter->stats.prc1522 += E1000_READ_REG(hw, PRC1522); + } adapter->stats.symerrs += E1000_READ_REG(hw, SYMERRS); adapter->stats.mpc += E1000_READ_REG(hw, MPC); @@ -3162,12 +3232,16 @@ e1000_update_stats(struct e1000_adapter *adapter) adapter->stats.totl += E1000_READ_REG(hw, TOTL); adapter->stats.toth += E1000_READ_REG(hw, TOTH); adapter->stats.tpr += E1000_READ_REG(hw, TPR); + + if (adapter->hw.mac_type != e1000_ich8lan) { adapter->stats.ptc64 += E1000_READ_REG(hw, PTC64); adapter->stats.ptc127 += E1000_READ_REG(hw, PTC127); adapter->stats.ptc255 += E1000_READ_REG(hw, PTC255); adapter->stats.ptc511 += E1000_READ_REG(hw, PTC511); adapter->stats.ptc1023 += E1000_READ_REG(hw, PTC1023); adapter->stats.ptc1522 += E1000_READ_REG(hw, PTC1522); + } + adapter->stats.mptc += E1000_READ_REG(hw, MPTC); adapter->stats.bptc += E1000_READ_REG(hw, BPTC); @@ -3189,6 +3263,8 @@ e1000_update_stats(struct e1000_adapter *adapter) if (hw->mac_type > e1000_82547_rev_2) { adapter->stats.iac += E1000_READ_REG(hw, IAC); adapter->stats.icrxoc += E1000_READ_REG(hw, ICRXOC); + + if (adapter->hw.mac_type != e1000_ich8lan) { adapter->stats.icrxptc += E1000_READ_REG(hw, ICRXPTC); adapter->stats.icrxatc += E1000_READ_REG(hw, ICRXATC); adapter->stats.ictxptc += E1000_READ_REG(hw, ICTXPTC); @@ -3196,6 +3272,7 @@ e1000_update_stats(struct e1000_adapter *adapter) adapter->stats.ictxqec += E1000_READ_REG(hw, ICTXQEC); adapter->stats.ictxqmtc += E1000_READ_REG(hw, ICTXQMTC); adapter->stats.icrxdmtc += E1000_READ_REG(hw, ICRXDMTC); + } } /* Fill out the OS statistics structure */ @@ -4330,18 +4407,21 @@ e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) ctrl |= E1000_CTRL_VME; E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + if (adapter->hw.mac_type != e1000_ich8lan) { /* enable VLAN receive filtering */ rctl = E1000_READ_REG(&adapter->hw, RCTL); rctl |= E1000_RCTL_VFE; rctl &= ~E1000_RCTL_CFIEN; E1000_WRITE_REG(&adapter->hw, RCTL, rctl); e1000_update_mng_vlan(adapter); + } } else { /* disable VLAN tag insert/strip */ ctrl = E1000_READ_REG(&adapter->hw, CTRL); ctrl &= ~E1000_CTRL_VME; E1000_WRITE_REG(&adapter->hw, CTRL, ctrl); + if (adapter->hw.mac_type != e1000_ich8lan) { /* disable VLAN filtering */ rctl = E1000_READ_REG(&adapter->hw, RCTL); rctl &= ~E1000_RCTL_VFE; @@ -4350,6 +4430,7 @@ e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp) e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id); adapter->mng_vlan_id = E1000_MNG_VLAN_NONE; } + } } e1000_irq_enable(adapter); @@ -4578,7 +4659,9 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) pci_enable_wake(pdev, PCI_D3cold, 0); } + /* FIXME: this code is incorrect for PCI Express */ if (adapter->hw.mac_type >= e1000_82540 && + adapter->hw.mac_type != e1000_ich8lan && adapter->hw.media_type == e1000_media_type_copper) { manc = E1000_READ_REG(&adapter->hw, MANC); if (manc & E1000_MANC_SMBUS_EN) { @@ -4589,6 +4672,9 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) } } + if (adapter->hw.phy_type == e1000_phy_igp_3) + e1000_phy_powerdown_workaround(&adapter->hw); + /* Release control of h/w to f/w. If f/w is AMT enabled, this * would have already happened in close and is redundant. */ e1000_release_hw_control(adapter); @@ -4624,7 +4710,9 @@ e1000_resume(struct pci_dev *pdev) netif_device_attach(netdev); + /* FIXME: this code is incorrect for PCI Express */ if (adapter->hw.mac_type >= e1000_82540 && + adapter->hw.mac_type != e1000_ich8lan && adapter->hw.media_type == e1000_media_type_copper) { manc = E1000_READ_REG(&adapter->hw, MANC); manc &= ~(E1000_MANC_ARP_EN); -- cgit v1.2.3 From 1f9e7e3d32f7ff3fd3a936fc9ad59770b3d29774 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:08:26 -0700 Subject: e1000: allow user to disable ich8 lock loss workaround The workaround for the ich8 lock loss problem is only needed for a very small amount of systems. This adds an option for the user to disable the workaround. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_param.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c index bd6c040b3cfb..0ef413172c68 100644 --- a/drivers/net/e1000/e1000_param.c +++ b/drivers/net/e1000/e1000_param.c @@ -202,6 +202,15 @@ E1000_PARAM(InterruptThrottleRate, "Interrupt Throttling Rate"); E1000_PARAM(SmartPowerDownEnable, "Enable PHY smart power down"); +/* Enable Kumeran Lock Loss workaround + * + * Valid Range: 0, 1 + * + * Default Value: 1 (enabled) + */ + +E1000_PARAM(KumeranLockLoss, "Enable Kumeran lock loss workaround"); + #define AUTONEG_ADV_DEFAULT 0x2F #define AUTONEG_ADV_MASK 0x2F #define FLOW_CONTROL_DEFAULT FLOW_CONTROL_FULL @@ -484,6 +493,18 @@ e1000_check_options(struct e1000_adapter *adapter) e1000_validate_option(&spd, &opt, adapter); adapter->smart_power_down = spd; } + { /* Kumeran Lock Loss Workaround */ + struct e1000_option opt = { + .type = enable_option, + .name = "Kumeran Lock Loss Workaround", + .err = "defaulting to Enabled", + .def = OPTION_ENABLED + }; + + int kmrn_lock_loss = KumeranLockLoss[bd]; + e1000_validate_option(&kmrn_lock_loss, &opt, adapter); + adapter->hw.kmrn_lock_loss_workaround_disabled = !kmrn_lock_loss; + } switch (adapter->hw.media_type) { case e1000_media_type_fiber: -- cgit v1.2.3 From ae2c3860eb18712b71861bb6fc8d7e11e0f79e6d Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:08:30 -0700 Subject: e1000: add ich8lan device ID's Add the device ID's of the supported ICH8 LAN devices. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index f5c6f08966c7..70d2dfe20226 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -73,6 +73,11 @@ static struct pci_device_id e1000_pci_tbl[] = { INTEL_E1000_ETHERNET_DEVICE(0x1026), INTEL_E1000_ETHERNET_DEVICE(0x1027), INTEL_E1000_ETHERNET_DEVICE(0x1028), + INTEL_E1000_ETHERNET_DEVICE(0x1049), + INTEL_E1000_ETHERNET_DEVICE(0x104A), + INTEL_E1000_ETHERNET_DEVICE(0x104B), + INTEL_E1000_ETHERNET_DEVICE(0x104C), + INTEL_E1000_ETHERNET_DEVICE(0x104D), INTEL_E1000_ETHERNET_DEVICE(0x105E), INTEL_E1000_ETHERNET_DEVICE(0x105F), INTEL_E1000_ETHERNET_DEVICE(0x1060), @@ -96,6 +101,8 @@ static struct pci_device_id e1000_pci_tbl[] = { INTEL_E1000_ETHERNET_DEVICE(0x109A), INTEL_E1000_ETHERNET_DEVICE(0x10B5), INTEL_E1000_ETHERNET_DEVICE(0x10B9), + INTEL_E1000_ETHERNET_DEVICE(0x10BA), + INTEL_E1000_ETHERNET_DEVICE(0x10BB), /* required last entry */ {0,} }; -- cgit v1.2.3 From e4ac9773be2ea01b707da0496ab1527ec6cdda4e Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 27 Jun 2006 09:08:34 -0700 Subject: e1000: increase version to 7.1.9-k2 Increment the version to 7.1.9-k2 Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok --- drivers/net/e1000/e1000_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 70d2dfe20226..f9a02c1ab523 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -36,7 +36,7 @@ static char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; #else #define DRIVERNAPI "-NAPI" #endif -#define DRV_VERSION "7.0.38-k4"DRIVERNAPI +#define DRV_VERSION "7.1.9-k2"DRIVERNAPI char e1000_driver_version[] = DRV_VERSION; static char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation."; -- cgit v1.2.3 From 607f31e80b6f982d7c0dd7a5045377fc368fe507 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 28 Jun 2006 16:52:45 -0400 Subject: Revert "Merge branch 'odirect'" This reverts ccf01ef7aa9c6c293a1c64c27331a2ce227916ec commit. No idea how git managed this one: when I asked it to merge the odirect topic branch it actually generated a patch which reverted the change. Reverting the 'merge' will once again reveal Chuck's recent NFS/O_DIRECT work to the world. Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 435 ++++++++++++++++++++++-------------------------- include/linux/nfs_xdr.h | 2 + 2 files changed, 203 insertions(+), 234 deletions(-) diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index 8ca9707be6c9..9ae7b6f6bf30 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -68,25 +68,19 @@ struct nfs_direct_req { struct kref kref; /* release manager */ /* I/O parameters */ - struct list_head list, /* nfs_read/write_data structs */ - rewrite_list; /* saved nfs_write_data structs */ struct nfs_open_context *ctx; /* file open context info */ struct kiocb * iocb; /* controlling i/o request */ struct inode * inode; /* target file of i/o */ - unsigned long user_addr; /* location of user's buffer */ - size_t user_count; /* total bytes to move */ - loff_t pos; /* starting offset in file */ - struct page ** pages; /* pages in our buffer */ - unsigned int npages; /* count of pages */ /* completion state */ + atomic_t io_count; /* i/os we're waiting for */ spinlock_t lock; /* protect completion state */ - int outstanding; /* i/os we're waiting for */ ssize_t count, /* bytes actually processed */ error; /* any reported error */ struct completion completion; /* wait for i/o completion */ /* commit state */ + struct list_head rewrite_list; /* saved nfs_write_data structs */ struct nfs_write_data * commit_data; /* special write_data for commits */ int flags; #define NFS_ODIRECT_DO_COMMIT (1) /* an unstable reply was received */ @@ -94,8 +88,37 @@ struct nfs_direct_req { struct nfs_writeverf verf; /* unstable write verifier */ }; -static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync); static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode); +static const struct rpc_call_ops nfs_write_direct_ops; + +static inline void get_dreq(struct nfs_direct_req *dreq) +{ + atomic_inc(&dreq->io_count); +} + +static inline int put_dreq(struct nfs_direct_req *dreq) +{ + return atomic_dec_and_test(&dreq->io_count); +} + +/* + * "size" is never larger than rsize or wsize. + */ +static inline int nfs_direct_count_pages(unsigned long user_addr, size_t size) +{ + int page_count; + + page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT; + page_count -= user_addr >> PAGE_SHIFT; + BUG_ON(page_count < 0); + + return page_count; +} + +static inline unsigned int nfs_max_pages(unsigned int size) +{ + return (size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; +} /** * nfs_direct_IO - NFS address space operation for direct I/O @@ -119,50 +142,21 @@ ssize_t nfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_ return -EINVAL; } -static void nfs_free_user_pages(struct page **pages, int npages, int do_dirty) +static void nfs_direct_dirty_pages(struct page **pages, int npages) { int i; for (i = 0; i < npages; i++) { struct page *page = pages[i]; - if (do_dirty && !PageCompound(page)) + if (!PageCompound(page)) set_page_dirty_lock(page); - page_cache_release(page); } - kfree(pages); } -static inline int nfs_get_user_pages(int rw, unsigned long user_addr, size_t size, struct page ***pages) +static void nfs_direct_release_pages(struct page **pages, int npages) { - int result = -ENOMEM; - unsigned long page_count; - size_t array_size; - - page_count = (user_addr + size + PAGE_SIZE - 1) >> PAGE_SHIFT; - page_count -= user_addr >> PAGE_SHIFT; - - array_size = (page_count * sizeof(struct page *)); - *pages = kmalloc(array_size, GFP_KERNEL); - if (*pages) { - down_read(¤t->mm->mmap_sem); - result = get_user_pages(current, current->mm, user_addr, - page_count, (rw == READ), 0, - *pages, NULL); - up_read(¤t->mm->mmap_sem); - if (result != page_count) { - /* - * If we got fewer pages than expected from - * get_user_pages(), the user buffer runs off the - * end of a mapping; return EFAULT. - */ - if (result >= 0) { - nfs_free_user_pages(*pages, result, 0); - result = -EFAULT; - } else - kfree(*pages); - *pages = NULL; - } - } - return result; + int i; + for (i = 0; i < npages; i++) + page_cache_release(pages[i]); } static inline struct nfs_direct_req *nfs_direct_req_alloc(void) @@ -174,13 +168,13 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void) return NULL; kref_init(&dreq->kref); + kref_get(&dreq->kref); init_completion(&dreq->completion); - INIT_LIST_HEAD(&dreq->list); INIT_LIST_HEAD(&dreq->rewrite_list); dreq->iocb = NULL; dreq->ctx = NULL; spin_lock_init(&dreq->lock); - dreq->outstanding = 0; + atomic_set(&dreq->io_count, 0); dreq->count = 0; dreq->error = 0; dreq->flags = 0; @@ -221,18 +215,11 @@ out: } /* - * We must hold a reference to all the pages in this direct read request - * until the RPCs complete. This could be long *after* we are woken up in - * nfs_direct_wait (for instance, if someone hits ^C on a slow server). - * - * In addition, synchronous I/O uses a stack-allocated iocb. Thus we - * can't trust the iocb is still valid here if this is a synchronous - * request. If the waiter is woken prematurely, the iocb is long gone. + * Synchronous I/O uses a stack-allocated iocb. Thus we can't trust + * the iocb is still valid here if this is a synchronous request. */ static void nfs_direct_complete(struct nfs_direct_req *dreq) { - nfs_free_user_pages(dreq->pages, dreq->npages, 1); - if (dreq->iocb) { long res = (long) dreq->error; if (!res) @@ -245,48 +232,10 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq) } /* - * Note we also set the number of requests we have in the dreq when we are - * done. This prevents races with I/O completion so we will always wait - * until all requests have been dispatched and completed. + * We must hold a reference to all the pages in this direct read request + * until the RPCs complete. This could be long *after* we are woken up in + * nfs_direct_wait (for instance, if someone hits ^C on a slow server). */ -static struct nfs_direct_req *nfs_direct_read_alloc(size_t nbytes, size_t rsize) -{ - struct list_head *list; - struct nfs_direct_req *dreq; - unsigned int rpages = (rsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - - dreq = nfs_direct_req_alloc(); - if (!dreq) - return NULL; - - list = &dreq->list; - for(;;) { - struct nfs_read_data *data = nfs_readdata_alloc(rpages); - - if (unlikely(!data)) { - while (!list_empty(list)) { - data = list_entry(list->next, - struct nfs_read_data, pages); - list_del(&data->pages); - nfs_readdata_free(data); - } - kref_put(&dreq->kref, nfs_direct_req_release); - return NULL; - } - - INIT_LIST_HEAD(&data->pages); - list_add(&data->pages, list); - - data->req = (struct nfs_page *) dreq; - dreq->outstanding++; - if (nbytes <= rsize) - break; - nbytes -= rsize; - } - kref_get(&dreq->kref); - return dreq; -} - static void nfs_direct_read_result(struct rpc_task *task, void *calldata) { struct nfs_read_data *data = calldata; @@ -295,6 +244,9 @@ static void nfs_direct_read_result(struct rpc_task *task, void *calldata) if (nfs_readpage_result(task, data) != 0) return; + nfs_direct_dirty_pages(data->pagevec, data->npages); + nfs_direct_release_pages(data->pagevec, data->npages); + spin_lock(&dreq->lock); if (likely(task->tk_status >= 0)) @@ -302,13 +254,10 @@ static void nfs_direct_read_result(struct rpc_task *task, void *calldata) else dreq->error = task->tk_status; - if (--dreq->outstanding) { - spin_unlock(&dreq->lock); - return; - } - spin_unlock(&dreq->lock); - nfs_direct_complete(dreq); + + if (put_dreq(dreq)) + nfs_direct_complete(dreq); } static const struct rpc_call_ops nfs_read_direct_ops = { @@ -317,41 +266,60 @@ static const struct rpc_call_ops nfs_read_direct_ops = { }; /* - * For each nfs_read_data struct that was allocated on the list, dispatch - * an NFS READ operation + * For each rsize'd chunk of the user's buffer, dispatch an NFS READ + * operation. If nfs_readdata_alloc() or get_user_pages() fails, + * bail and stop sending more reads. Read length accounting is + * handled automatically by nfs_direct_read_result(). Otherwise, if + * no requests have been sent, just return an error. */ -static void nfs_direct_read_schedule(struct nfs_direct_req *dreq) +static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos) { struct nfs_open_context *ctx = dreq->ctx; struct inode *inode = ctx->dentry->d_inode; - struct list_head *list = &dreq->list; - struct page **pages = dreq->pages; - size_t count = dreq->user_count; - loff_t pos = dreq->pos; size_t rsize = NFS_SERVER(inode)->rsize; - unsigned int curpage, pgbase; + unsigned int rpages = nfs_max_pages(rsize); + unsigned int pgbase; + int result; + ssize_t started = 0; + + get_dreq(dreq); - curpage = 0; - pgbase = dreq->user_addr & ~PAGE_MASK; + pgbase = user_addr & ~PAGE_MASK; do { struct nfs_read_data *data; size_t bytes; + result = -ENOMEM; + data = nfs_readdata_alloc(rpages); + if (unlikely(!data)) + break; + bytes = rsize; if (count < rsize) bytes = count; - BUG_ON(list_empty(list)); - data = list_entry(list->next, struct nfs_read_data, pages); - list_del_init(&data->pages); + data->npages = nfs_direct_count_pages(user_addr, bytes); + down_read(¤t->mm->mmap_sem); + result = get_user_pages(current, current->mm, user_addr, + data->npages, 1, 0, data->pagevec, NULL); + up_read(¤t->mm->mmap_sem); + if (unlikely(result < data->npages)) { + if (result > 0) + nfs_direct_release_pages(data->pagevec, result); + nfs_readdata_release(data); + break; + } + + get_dreq(dreq); + data->req = (struct nfs_page *) dreq; data->inode = inode; data->cred = ctx->cred; data->args.fh = NFS_FH(inode); data->args.context = ctx; data->args.offset = pos; data->args.pgbase = pgbase; - data->args.pages = &pages[curpage]; + data->args.pages = data->pagevec; data->args.count = bytes; data->res.fattr = &data->fattr; data->res.eof = 0; @@ -374,33 +342,35 @@ static void nfs_direct_read_schedule(struct nfs_direct_req *dreq) bytes, (unsigned long long)data->args.offset); + started += bytes; + user_addr += bytes; pos += bytes; pgbase += bytes; - curpage += pgbase >> PAGE_SHIFT; pgbase &= ~PAGE_MASK; count -= bytes; } while (count != 0); - BUG_ON(!list_empty(list)); + + if (put_dreq(dreq)) + nfs_direct_complete(dreq); + + if (started) + return 0; + return result < 0 ? (ssize_t) result : -EFAULT; } -static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos, struct page **pages, unsigned int nr_pages) +static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos) { - ssize_t result; + ssize_t result = 0; sigset_t oldset; struct inode *inode = iocb->ki_filp->f_mapping->host; struct rpc_clnt *clnt = NFS_CLIENT(inode); struct nfs_direct_req *dreq; - dreq = nfs_direct_read_alloc(count, NFS_SERVER(inode)->rsize); + dreq = nfs_direct_req_alloc(); if (!dreq) return -ENOMEM; - dreq->user_addr = user_addr; - dreq->user_count = count; - dreq->pos = pos; - dreq->pages = pages; - dreq->npages = nr_pages; dreq->inode = inode; dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data); if (!is_sync_kiocb(iocb)) @@ -408,8 +378,9 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size nfs_add_stats(inode, NFSIOS_DIRECTREADBYTES, count); rpc_clnt_sigmask(clnt, &oldset); - nfs_direct_read_schedule(dreq); - result = nfs_direct_wait(dreq); + result = nfs_direct_read_schedule(dreq, user_addr, count, pos); + if (!result) + result = nfs_direct_wait(dreq); rpc_clnt_sigunmask(clnt, &oldset); return result; @@ -417,10 +388,10 @@ static ssize_t nfs_direct_read(struct kiocb *iocb, unsigned long user_addr, size static void nfs_direct_free_writedata(struct nfs_direct_req *dreq) { - list_splice_init(&dreq->rewrite_list, &dreq->list); - while (!list_empty(&dreq->list)) { - struct nfs_write_data *data = list_entry(dreq->list.next, struct nfs_write_data, pages); + while (!list_empty(&dreq->rewrite_list)) { + struct nfs_write_data *data = list_entry(dreq->rewrite_list.next, struct nfs_write_data, pages); list_del(&data->pages); + nfs_direct_release_pages(data->pagevec, data->npages); nfs_writedata_release(data); } } @@ -428,14 +399,51 @@ static void nfs_direct_free_writedata(struct nfs_direct_req *dreq) #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq) { - struct list_head *pos; + struct inode *inode = dreq->inode; + struct list_head *p; + struct nfs_write_data *data; - list_splice_init(&dreq->rewrite_list, &dreq->list); - list_for_each(pos, &dreq->list) - dreq->outstanding++; dreq->count = 0; + get_dreq(dreq); + + list_for_each(p, &dreq->rewrite_list) { + data = list_entry(p, struct nfs_write_data, pages); + + get_dreq(dreq); + + /* + * Reset data->res. + */ + nfs_fattr_init(&data->fattr); + data->res.count = data->args.count; + memset(&data->verf, 0, sizeof(data->verf)); + + /* + * Reuse data->task; data->args should not have changed + * since the original request was sent. + */ + rpc_init_task(&data->task, NFS_CLIENT(inode), RPC_TASK_ASYNC, + &nfs_write_direct_ops, data); + NFS_PROTO(inode)->write_setup(data, FLUSH_STABLE); + + data->task.tk_priority = RPC_PRIORITY_NORMAL; + data->task.tk_cookie = (unsigned long) inode; + + /* + * We're called via an RPC callback, so BKL is already held. + */ + rpc_execute(&data->task); + + dprintk("NFS: %5u rescheduled direct write call (req %s/%Ld, %u bytes @ offset %Lu)\n", + data->task.tk_pid, + inode->i_sb->s_id, + (long long)NFS_FILEID(inode), + data->args.count, + (unsigned long long)data->args.offset); + } - nfs_direct_write_schedule(dreq, FLUSH_STABLE); + if (put_dreq(dreq)) + nfs_direct_write_complete(dreq, inode); } static void nfs_direct_commit_result(struct rpc_task *task, void *calldata) @@ -472,8 +480,8 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) data->cred = dreq->ctx->cred; data->args.fh = NFS_FH(data->inode); - data->args.offset = dreq->pos; - data->args.count = dreq->user_count; + data->args.offset = 0; + data->args.count = 0; data->res.count = 0; data->res.fattr = &data->fattr; data->res.verf = &data->verf; @@ -535,47 +543,6 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode } #endif -static struct nfs_direct_req *nfs_direct_write_alloc(size_t nbytes, size_t wsize) -{ - struct list_head *list; - struct nfs_direct_req *dreq; - unsigned int wpages = (wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; - - dreq = nfs_direct_req_alloc(); - if (!dreq) - return NULL; - - list = &dreq->list; - for(;;) { - struct nfs_write_data *data = nfs_writedata_alloc(wpages); - - if (unlikely(!data)) { - while (!list_empty(list)) { - data = list_entry(list->next, - struct nfs_write_data, pages); - list_del(&data->pages); - nfs_writedata_free(data); - } - kref_put(&dreq->kref, nfs_direct_req_release); - return NULL; - } - - INIT_LIST_HEAD(&data->pages); - list_add(&data->pages, list); - - data->req = (struct nfs_page *) dreq; - dreq->outstanding++; - if (nbytes <= wsize) - break; - nbytes -= wsize; - } - - nfs_alloc_commit_data(dreq); - - kref_get(&dreq->kref); - return dreq; -} - static void nfs_direct_write_result(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; @@ -605,8 +572,6 @@ static void nfs_direct_write_result(struct rpc_task *task, void *calldata) } } } - /* In case we have to resend */ - data->args.stable = NFS_FILE_SYNC; spin_unlock(&dreq->lock); } @@ -620,14 +585,8 @@ static void nfs_direct_write_release(void *calldata) struct nfs_write_data *data = calldata; struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req; - spin_lock(&dreq->lock); - if (--dreq->outstanding) { - spin_unlock(&dreq->lock); - return; - } - spin_unlock(&dreq->lock); - - nfs_direct_write_complete(dreq, data->inode); + if (put_dreq(dreq)) + nfs_direct_write_complete(dreq, data->inode); } static const struct rpc_call_ops nfs_write_direct_ops = { @@ -636,41 +595,62 @@ static const struct rpc_call_ops nfs_write_direct_ops = { }; /* - * For each nfs_write_data struct that was allocated on the list, dispatch - * an NFS WRITE operation + * For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE + * operation. If nfs_writedata_alloc() or get_user_pages() fails, + * bail and stop sending more writes. Write length accounting is + * handled automatically by nfs_direct_write_result(). Otherwise, if + * no requests have been sent, just return an error. */ -static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync) +static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned long user_addr, size_t count, loff_t pos, int sync) { struct nfs_open_context *ctx = dreq->ctx; struct inode *inode = ctx->dentry->d_inode; - struct list_head *list = &dreq->list; - struct page **pages = dreq->pages; - size_t count = dreq->user_count; - loff_t pos = dreq->pos; size_t wsize = NFS_SERVER(inode)->wsize; - unsigned int curpage, pgbase; + unsigned int wpages = nfs_max_pages(wsize); + unsigned int pgbase; + int result; + ssize_t started = 0; - curpage = 0; - pgbase = dreq->user_addr & ~PAGE_MASK; + get_dreq(dreq); + + pgbase = user_addr & ~PAGE_MASK; do { struct nfs_write_data *data; size_t bytes; + result = -ENOMEM; + data = nfs_writedata_alloc(wpages); + if (unlikely(!data)) + break; + bytes = wsize; if (count < wsize) bytes = count; - BUG_ON(list_empty(list)); - data = list_entry(list->next, struct nfs_write_data, pages); + data->npages = nfs_direct_count_pages(user_addr, bytes); + down_read(¤t->mm->mmap_sem); + result = get_user_pages(current, current->mm, user_addr, + data->npages, 0, 0, data->pagevec, NULL); + up_read(¤t->mm->mmap_sem); + if (unlikely(result < data->npages)) { + if (result > 0) + nfs_direct_release_pages(data->pagevec, result); + nfs_writedata_release(data); + break; + } + + get_dreq(dreq); + list_move_tail(&data->pages, &dreq->rewrite_list); + data->req = (struct nfs_page *) dreq; data->inode = inode; data->cred = ctx->cred; data->args.fh = NFS_FH(inode); data->args.context = ctx; data->args.offset = pos; data->args.pgbase = pgbase; - data->args.pages = &pages[curpage]; + data->args.pages = data->pagevec; data->args.count = bytes; data->res.fattr = &data->fattr; data->res.count = bytes; @@ -694,19 +674,26 @@ static void nfs_direct_write_schedule(struct nfs_direct_req *dreq, int sync) bytes, (unsigned long long)data->args.offset); + started += bytes; + user_addr += bytes; pos += bytes; pgbase += bytes; - curpage += pgbase >> PAGE_SHIFT; pgbase &= ~PAGE_MASK; count -= bytes; } while (count != 0); - BUG_ON(!list_empty(list)); + + if (put_dreq(dreq)) + nfs_direct_write_complete(dreq, inode); + + if (started) + return 0; + return result < 0 ? (ssize_t) result : -EFAULT; } -static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos, struct page **pages, int nr_pages) +static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, size_t count, loff_t pos) { - ssize_t result; + ssize_t result = 0; sigset_t oldset; struct inode *inode = iocb->ki_filp->f_mapping->host; struct rpc_clnt *clnt = NFS_CLIENT(inode); @@ -714,17 +701,14 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz size_t wsize = NFS_SERVER(inode)->wsize; int sync = 0; - dreq = nfs_direct_write_alloc(count, wsize); + dreq = nfs_direct_req_alloc(); if (!dreq) return -ENOMEM; + nfs_alloc_commit_data(dreq); + if (dreq->commit_data == NULL || count < wsize) sync = FLUSH_STABLE; - dreq->user_addr = user_addr; - dreq->user_count = count; - dreq->pos = pos; - dreq->pages = pages; - dreq->npages = nr_pages; dreq->inode = inode; dreq->ctx = get_nfs_open_context((struct nfs_open_context *)iocb->ki_filp->private_data); if (!is_sync_kiocb(iocb)) @@ -735,8 +719,9 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz nfs_begin_data_update(inode); rpc_clnt_sigmask(clnt, &oldset); - nfs_direct_write_schedule(dreq, sync); - result = nfs_direct_wait(dreq); + result = nfs_direct_write_schedule(dreq, user_addr, count, pos, sync); + if (!result) + result = nfs_direct_wait(dreq); rpc_clnt_sigunmask(clnt, &oldset); return result; @@ -766,8 +751,6 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, unsigned long user_addr, siz ssize_t nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, loff_t pos) { ssize_t retval = -EINVAL; - int page_count; - struct page **pages; struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -789,14 +772,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, char __user *buf, size_t count, if (retval) goto out; - retval = nfs_get_user_pages(READ, (unsigned long) buf, - count, &pages); - if (retval < 0) - goto out; - page_count = retval; - - retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos, - pages, page_count); + retval = nfs_direct_read(iocb, (unsigned long) buf, count, pos); if (retval > 0) iocb->ki_pos = pos + retval; @@ -832,8 +808,6 @@ out: ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t count, loff_t pos) { ssize_t retval; - int page_count; - struct page **pages; struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; @@ -861,14 +835,7 @@ ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, size_t if (retval) goto out; - retval = nfs_get_user_pages(WRITE, (unsigned long) buf, - count, &pages); - if (retval < 0) - goto out; - page_count = retval; - - retval = nfs_direct_write(iocb, (unsigned long) buf, count, - pos, pages, page_count); + retval = nfs_direct_write(iocb, (unsigned long) buf, count, pos); /* * XXX: nfs_end_data_update() already ensures this file's diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 7c7320fa51aa..2d3fb6416d91 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -729,6 +729,7 @@ struct nfs_read_data { struct list_head pages; /* Coalesced read requests */ struct nfs_page *req; /* multi ops per nfs_page */ struct page **pagevec; + unsigned int npages; /* active pages in pagevec */ struct nfs_readargs args; struct nfs_readres res; #ifdef CONFIG_NFS_V4 @@ -747,6 +748,7 @@ struct nfs_write_data { struct list_head pages; /* Coalesced requests we wish to flush */ struct nfs_page *req; /* multi ops per nfs_page */ struct page **pagevec; + unsigned int npages; /* active pages in pagevec */ struct nfs_writeargs args; /* argument struct */ struct nfs_writeres res; /* result struct */ #ifdef CONFIG_NFS_V4 -- cgit v1.2.3 From 20e652761cbf6983fd067aef2f0242c262057737 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Wed, 5 Jul 2006 14:31:13 +0100 Subject: [ARM] 3710/1: AT91 Serial: Use GPIO API Patch from Andrew Victor The AT91RM9200 errata work-around should be using the GPIO API and not accessing the PIO registers directly. Signed-off-by: Andrew Victor Signed-off-by: Russell King --- drivers/serial/at91_serial.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/serial/at91_serial.c b/drivers/serial/at91_serial.c index a7d664383dae..54c6b2adf7b7 100644 --- a/drivers/serial/at91_serial.c +++ b/drivers/serial/at91_serial.c @@ -41,6 +41,7 @@ #include #include #include +#include #if defined(CONFIG_SERIAL_AT91_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ @@ -140,9 +141,9 @@ static void at91_set_mctrl(struct uart_port *port, u_int mctrl) */ if (port->mapbase == AT91_BASE_US0) { if (mctrl & TIOCM_RTS) - at91_sys_write(AT91_PIOA + PIO_CODR, AT91_PA21_RTS0); + at91_set_gpio_value(AT91_PIN_PA21, 0); else - at91_sys_write(AT91_PIOA + PIO_SODR, AT91_PA21_RTS0); + at91_set_gpio_value(AT91_PIN_PA21, 1); } } -- cgit v1.2.3 From 5904a7f9167cdeb95569799e0be652c2ce6d3298 Mon Sep 17 00:00:00 2001 From: Vitaly Wool Date: Wed, 5 Jul 2006 14:47:20 +0100 Subject: [ARM] 3709/1: pnx4008: convert to generic irq subsystem Patch from Vitaly Wool Convert pnx4008 chip support to use generic irq subsystem Signed-off-by: Vitaly Wool Signed-off-by: Russell King --- arch/arm/mach-pnx4008/core.c | 2 -- arch/arm/mach-pnx4008/dma.c | 1 - arch/arm/mach-pnx4008/irq.c | 22 ++++++++++------------ arch/arm/mach-pnx4008/time.c | 8 +++----- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-pnx4008/core.c b/arch/arm/mach-pnx4008/core.c index ba91daad64fb..3d73c1e93752 100644 --- a/arch/arm/mach-pnx4008/core.c +++ b/arch/arm/mach-pnx4008/core.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -36,7 +35,6 @@ #include #include -#include #include #include diff --git a/arch/arm/mach-pnx4008/dma.c b/arch/arm/mach-pnx4008/dma.c index 981aa9dcdede..ec01574f88ac 100644 --- a/arch/arm/mach-pnx4008/dma.c +++ b/arch/arm/mach-pnx4008/dma.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include diff --git a/arch/arm/mach-pnx4008/irq.c b/arch/arm/mach-pnx4008/irq.c index 9b0a8e084e99..3a4bcf3d91fa 100644 --- a/arch/arm/mach-pnx4008/irq.c +++ b/arch/arm/mach-pnx4008/irq.c @@ -22,8 +22,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -96,26 +96,24 @@ void __init pnx4008_init_irq(void) { unsigned int i; - /* configure and enable IRQ 0,1,30,31 (cascade interrupts) mask all others */ + /* configure IRQ's */ + for (i = 0; i < NR_IRQS; i++) { + set_irq_flags(i, IRQF_VALID); + set_irq_chip(i, &pnx4008_irq_chip); + pnx4008_set_irq_type(i, pnx4008_irq_type[i]); + } + + /* configure and enable IRQ 0,1,30,31 (cascade interrupts) */ pnx4008_set_irq_type(SUB1_IRQ_N, pnx4008_irq_type[SUB1_IRQ_N]); pnx4008_set_irq_type(SUB2_IRQ_N, pnx4008_irq_type[SUB2_IRQ_N]); pnx4008_set_irq_type(SUB1_FIQ_N, pnx4008_irq_type[SUB1_FIQ_N]); pnx4008_set_irq_type(SUB2_FIQ_N, pnx4008_irq_type[SUB2_FIQ_N]); + /* mask all others */ __raw_writel((1 << SUB2_FIQ_N) | (1 << SUB1_FIQ_N) | (1 << SUB2_IRQ_N) | (1 << SUB1_IRQ_N), INTC_ER(MAIN_BASE_INT)); __raw_writel(0, INTC_ER(SIC1_BASE_INT)); __raw_writel(0, INTC_ER(SIC2_BASE_INT)); - - /* configure all other IRQ's */ - for (i = 0; i < NR_IRQS; i++) { - if (i == SUB2_FIQ_N || i == SUB1_FIQ_N || - i == SUB2_IRQ_N || i == SUB1_IRQ_N) - continue; - set_irq_flags(i, IRQF_VALID); - set_irq_chip(i, &pnx4008_irq_chip); - pnx4008_set_irq_type(i, pnx4008_irq_type[i]); - } } diff --git a/arch/arm/mach-pnx4008/time.c b/arch/arm/mach-pnx4008/time.c index 888bf6cfba8a..756228ddd035 100644 --- a/arch/arm/mach-pnx4008/time.c +++ b/arch/arm/mach-pnx4008/time.c @@ -20,17 +20,15 @@ #include #include #include +#include +#include +#include #include #include #include #include -#include -#include #include - -#include -#include #include /*! Note: all timers are UPCOUNTING */ -- cgit v1.2.3 From ba854e18413d2d827f050984edeb8286c3335895 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Wed, 5 Jul 2006 17:22:52 +0100 Subject: [ARM] 3711/1: AT91 timer update Patch from Andrew Victor The AIC interrupt controller is the same on the Atmel AT91RM9200, AT91SAM9261 and AT91SAM9260 processors. This patch removes any RM9200-specific naming from the IRQ driver, and moves the AT91RM9200's default IRQ priority table into at91rm9200.c. Signed-off-by: Andrew Victor Signed-off-by: Russell King --- arch/arm/mach-at91rm9200/at91rm9200.c | 45 ++++++++++++++++++++++ arch/arm/mach-at91rm9200/generic.h | 8 +++- arch/arm/mach-at91rm9200/irq.c | 70 +++++++--------------------------- include/asm-arm/arch-at91rm9200/irqs.h | 8 ---- 4 files changed, 66 insertions(+), 65 deletions(-) diff --git a/arch/arm/mach-at91rm9200/at91rm9200.c b/arch/arm/mach-at91rm9200/at91rm9200.c index 7e1d072bdd80..0985b1c42c7c 100644 --- a/arch/arm/mach-at91rm9200/at91rm9200.c +++ b/arch/arm/mach-at91rm9200/at91rm9200.c @@ -107,3 +107,48 @@ void __init at91rm9200_map_io(void) iotable_init(at91rm9200_io_desc, ARRAY_SIZE(at91rm9200_io_desc)); } +/* + * The default interrupt priority levels (0 = lowest, 7 = highest). + */ +static unsigned int at91rm9200_default_irq_priority[NR_AIC_IRQS] __initdata = { + 7, /* Advanced Interrupt Controller (FIQ) */ + 7, /* System Peripherals */ + 0, /* Parallel IO Controller A */ + 0, /* Parallel IO Controller B */ + 0, /* Parallel IO Controller C */ + 0, /* Parallel IO Controller D */ + 6, /* USART 0 */ + 6, /* USART 1 */ + 6, /* USART 2 */ + 6, /* USART 3 */ + 0, /* Multimedia Card Interface */ + 4, /* USB Device Port */ + 0, /* Two-Wire Interface */ + 6, /* Serial Peripheral Interface */ + 5, /* Serial Synchronous Controller 0 */ + 5, /* Serial Synchronous Controller 1 */ + 5, /* Serial Synchronous Controller 2 */ + 0, /* Timer Counter 0 */ + 0, /* Timer Counter 1 */ + 0, /* Timer Counter 2 */ + 0, /* Timer Counter 3 */ + 0, /* Timer Counter 4 */ + 0, /* Timer Counter 5 */ + 3, /* USB Host port */ + 3, /* Ethernet MAC */ + 0, /* Advanced Interrupt Controller (IRQ0) */ + 0, /* Advanced Interrupt Controller (IRQ1) */ + 0, /* Advanced Interrupt Controller (IRQ2) */ + 0, /* Advanced Interrupt Controller (IRQ3) */ + 0, /* Advanced Interrupt Controller (IRQ4) */ + 0, /* Advanced Interrupt Controller (IRQ5) */ + 0 /* Advanced Interrupt Controller (IRQ6) */ +}; + +void __init at91rm9200_init_irq(unsigned int priority[NR_AIC_IRQS]) +{ + if (!priority) + priority = at91rm9200_default_irq_priority; + + at91_aic_init(priority); +} diff --git a/arch/arm/mach-at91rm9200/generic.h b/arch/arm/mach-at91rm9200/generic.h index f0d969d7d874..7979d8ab7e07 100644 --- a/arch/arm/mach-at91rm9200/generic.h +++ b/arch/arm/mach-at91rm9200/generic.h @@ -8,13 +8,19 @@ * published by the Free Software Foundation. */ -void at91_gpio_irq_setup(unsigned banks); + /* Interrupts */ +extern void __init at91rm9200_init_irq(unsigned int priority[]); +extern void __init at91_aic_init(unsigned int priority[]); +extern void __init at91_gpio_irq_setup(unsigned banks); + /* Timer */ struct sys_timer; extern struct sys_timer at91rm9200_timer; + /* Memory Map */ extern void __init at91rm9200_map_io(void); + /* Clocks */ extern int __init at91_clock_init(unsigned long main_clock); struct device; extern void __init at91_clock_associate(const char *id, struct device *dev, const char *func); diff --git a/arch/arm/mach-at91rm9200/irq.c b/arch/arm/mach-at91rm9200/irq.c index dcd560dbcfb7..9b0911320417 100644 --- a/arch/arm/mach-at91rm9200/irq.c +++ b/arch/arm/mach-at91rm9200/irq.c @@ -36,58 +36,20 @@ #include "generic.h" -/* - * The default interrupt priority levels (0 = lowest, 7 = highest). - */ -static unsigned int at91rm9200_default_irq_priority[NR_AIC_IRQS] __initdata = { - 7, /* Advanced Interrupt Controller */ - 7, /* System Peripheral */ - 0, /* Parallel IO Controller A */ - 0, /* Parallel IO Controller B */ - 0, /* Parallel IO Controller C */ - 0, /* Parallel IO Controller D */ - 6, /* USART 0 */ - 6, /* USART 1 */ - 6, /* USART 2 */ - 6, /* USART 3 */ - 0, /* Multimedia Card Interface */ - 4, /* USB Device Port */ - 0, /* Two-Wire Interface */ - 6, /* Serial Peripheral Interface */ - 5, /* Serial Synchronous Controller */ - 5, /* Serial Synchronous Controller */ - 5, /* Serial Synchronous Controller */ - 0, /* Timer Counter 0 */ - 0, /* Timer Counter 1 */ - 0, /* Timer Counter 2 */ - 0, /* Timer Counter 3 */ - 0, /* Timer Counter 4 */ - 0, /* Timer Counter 5 */ - 3, /* USB Host port */ - 3, /* Ethernet MAC */ - 0, /* Advanced Interrupt Controller */ - 0, /* Advanced Interrupt Controller */ - 0, /* Advanced Interrupt Controller */ - 0, /* Advanced Interrupt Controller */ - 0, /* Advanced Interrupt Controller */ - 0, /* Advanced Interrupt Controller */ - 0 /* Advanced Interrupt Controller */ -}; - -static void at91rm9200_mask_irq(unsigned int irq) +static void at91_aic_mask_irq(unsigned int irq) { /* Disable interrupt on AIC */ at91_sys_write(AT91_AIC_IDCR, 1 << irq); } -static void at91rm9200_unmask_irq(unsigned int irq) +static void at91_aic_unmask_irq(unsigned int irq) { /* Enable interrupt on AIC */ at91_sys_write(AT91_AIC_IECR, 1 << irq); } -static int at91rm9200_irq_type(unsigned irq, unsigned type) +static int at91_aic_set_type(unsigned irq, unsigned type) { unsigned int smr, srctype; @@ -122,7 +84,7 @@ static int at91rm9200_irq_type(unsigned irq, unsigned type) static u32 wakeups; static u32 backups; -static int at91rm9200_irq_set_wake(unsigned irq, unsigned value) +static int at91_aic_set_wake(unsigned irq, unsigned value) { if (unlikely(irq >= 32)) return -EINVAL; @@ -149,28 +111,24 @@ void at91_irq_resume(void) } #else -#define at91rm9200_irq_set_wake NULL +#define at91_aic_set_wake NULL #endif -static struct irqchip at91rm9200_irq_chip = { - .ack = at91rm9200_mask_irq, - .mask = at91rm9200_mask_irq, - .unmask = at91rm9200_unmask_irq, - .set_type = at91rm9200_irq_type, - .set_wake = at91rm9200_irq_set_wake, +static struct irqchip at91_aic_chip = { + .ack = at91_aic_mask_irq, + .mask = at91_aic_mask_irq, + .unmask = at91_aic_unmask_irq, + .set_type = at91_aic_set_type, + .set_wake = at91_aic_set_wake, }; /* * Initialize the AIC interrupt controller. */ -void __init at91rm9200_init_irq(unsigned int priority[NR_AIC_IRQS]) +void __init at91_aic_init(unsigned int priority[NR_AIC_IRQS]) { unsigned int i; - /* No priority list specified for this board -> use defaults */ - if (priority == NULL) - priority = at91rm9200_default_irq_priority; - /* * The IVR is used by macro get_irqnr_and_base to read and verify. * The irq number is NR_AIC_IRQS when a spurious interrupt has occurred. @@ -178,10 +136,10 @@ void __init at91rm9200_init_irq(unsigned int priority[NR_AIC_IRQS]) for (i = 0; i < NR_AIC_IRQS; i++) { /* Put irq number in Source Vector Register: */ at91_sys_write(AT91_AIC_SVR(i), i); - /* Store the Source Mode Register as defined in table above */ + /* Active Low interrupt, with the specified priority */ at91_sys_write(AT91_AIC_SMR(i), AT91_AIC_SRCTYPE_LOW | priority[i]); - set_irq_chip(i, &at91rm9200_irq_chip); + set_irq_chip(i, &at91_aic_chip); set_irq_handler(i, do_level_IRQ); set_irq_flags(i, IRQF_VALID | IRQF_PROBE); diff --git a/include/asm-arm/arch-at91rm9200/irqs.h b/include/asm-arm/arch-at91rm9200/irqs.h index 2dc93b174a8f..f63842c2c093 100644 --- a/include/asm-arm/arch-at91rm9200/irqs.h +++ b/include/asm-arm/arch-at91rm9200/irqs.h @@ -39,12 +39,4 @@ */ #define NR_IRQS (NR_AIC_IRQS + (4 * 32)) - -#ifndef __ASSEMBLY__ -/* - * Initialize the IRQ controller. - */ -extern void at91rm9200_init_irq(unsigned int priority[]); -#endif - #endif -- cgit v1.2.3 From ba1826e5eced176cc9ec0033ad8ee0f1cd5ad2e4 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 5 Jul 2006 15:36:15 +1000 Subject: [PATCH] powerpc: Fix loss of interrupts with MPIC With the new interrupt rework, an interrupt "host" map() callback can be called after the interrupt is already active. It's called again for an already mapped interrupt to allow changing the trigger setup, and currently this is not guarded with a test of wether the interrupt is requested or not. I plan to change some of this logic to be a bit less lenient against random reconfiguring of live interrupts but just not yet. The ported MPIC driver has a bug where when that happens, it will mask the interrupt. This changes it to preserve the previous masking of the interrupt instead. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Linus Torvalds --- arch/powerpc/sysdev/mpic.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 7d31d7cc392d..9cecebaa0360 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -405,20 +405,22 @@ static void mpic_unmask_irq(unsigned int irq) unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); + unsigned long flags; DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src); + spin_lock_irqsave(&mpic_lock, flags); mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI, mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & ~MPIC_VECPRI_MASK); - /* make sure mask gets to controller before we return to user */ do { if (!loops--) { printk(KERN_ERR "mpic_enable_irq timeout\n"); break; } - } while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK); + } while(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK); + spin_unlock_irqrestore(&mpic_lock, flags); } static void mpic_mask_irq(unsigned int irq) @@ -426,9 +428,11 @@ static void mpic_mask_irq(unsigned int irq) unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); + unsigned long flags; DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src); + spin_lock_irqsave(&mpic_lock, flags); mpic_irq_write(src, MPIC_IRQ_VECTOR_PRI, mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) | MPIC_VECPRI_MASK); @@ -440,6 +444,7 @@ static void mpic_mask_irq(unsigned int irq) break; } } while(!(mpic_irq_read(src, MPIC_IRQ_VECTOR_PRI) & MPIC_VECPRI_MASK)); + spin_unlock_irqrestore(&mpic_lock, flags); } static void mpic_end_irq(unsigned int irq) @@ -624,9 +629,10 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq, struct irq_desc *desc = get_irq_desc(virq); struct irq_chip *chip; struct mpic *mpic = h->host_data; - unsigned int vecpri = MPIC_VECPRI_SENSE_LEVEL | + u32 v, vecpri = MPIC_VECPRI_SENSE_LEVEL | MPIC_VECPRI_POLARITY_NEGATIVE; int level; + unsigned long iflags; pr_debug("mpic: map virq %d, hwirq 0x%lx, flags: 0x%x\n", virq, hw, flags); @@ -668,11 +674,21 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq, } #endif - /* Reconfigure irq */ - vecpri |= MPIC_VECPRI_MASK | hw | (8 << MPIC_VECPRI_PRIORITY_SHIFT); - mpic_irq_write(hw, MPIC_IRQ_VECTOR_PRI, vecpri); - - pr_debug("mpic: mapping as IRQ\n"); + /* Reconfigure irq. We must preserve the mask bit as we can be called + * while the interrupt is still active (This may change in the future + * but for now, it is the case). + */ + spin_lock_irqsave(&mpic_lock, iflags); + v = mpic_irq_read(hw, MPIC_IRQ_VECTOR_PRI); + vecpri = (v & + ~(MPIC_VECPRI_POLARITY_MASK | MPIC_VECPRI_SENSE_MASK)) | + vecpri; + if (vecpri != v) + mpic_irq_write(hw, MPIC_IRQ_VECTOR_PRI, vecpri); + spin_unlock_irqrestore(&mpic_lock, iflags); + + pr_debug("mpic: mapping as IRQ, vecpri = 0x%08x (was 0x%08x)\n", + vecpri, v); set_irq_chip_data(virq, mpic); set_irq_chip_and_handler(virq, chip, handle_fasteoi_irq); @@ -904,8 +920,8 @@ void __init mpic_init(struct mpic *mpic) /* do senses munging */ if (mpic->senses && i < mpic->senses_count) - vecpri = mpic_flags_to_vecpri(mpic->senses[i], - &level); + vecpri |= mpic_flags_to_vecpri(mpic->senses[i], + &level); else vecpri |= MPIC_VECPRI_SENSE_LEVEL; @@ -955,14 +971,17 @@ void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio) void __init mpic_set_serial_int(struct mpic *mpic, int enable) { + unsigned long flags; u32 v; + spin_lock_irqsave(&mpic_lock, flags); v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1); if (enable) v |= MPIC_GREG_GLOBAL_CONF_1_SIE; else v &= ~MPIC_GREG_GLOBAL_CONF_1_SIE; mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v); + spin_unlock_irqrestore(&mpic_lock, flags); } void mpic_irq_set_priority(unsigned int irq, unsigned int pri) -- cgit v1.2.3 From a8e0c51c71fc973b400f6502382063553b82af5a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 5 Jul 2006 11:24:26 +0100 Subject: [PATCH] powerpc: implement missing jiffies64_to_cputime64() asm-powerpc/cputime.h doesn't declare jiffies64_to_cputime64() or cputime64_sub(), and due to CONFIG_VIRT_CPU_ACCOUNTING it's not picking up the definition from asm-generic like x86-64 & friends do. Cc: Dave Jones Cc: Andrew Morton Signed-off-by: David Woodhouse Signed-off-by: Linus Torvalds --- include/asm-powerpc/cputime.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/include/asm-powerpc/cputime.h b/include/asm-powerpc/cputime.h index a21185d47883..310804485208 100644 --- a/include/asm-powerpc/cputime.h +++ b/include/asm-powerpc/cputime.h @@ -43,6 +43,7 @@ typedef u64 cputime64_t; #define cputime64_zero ((cputime64_t)0) #define cputime64_add(__a, __b) ((__a) + (__b)) +#define cputime64_sub(__a, __b) ((__a) - (__b)) #define cputime_to_cputime64(__ct) (__ct) #ifdef __KERNEL__ @@ -74,6 +75,23 @@ static inline cputime_t jiffies_to_cputime(const unsigned long jif) return ct; } +static inline cputime64_t jiffies64_to_cputime64(const u64 jif) +{ + cputime_t ct; + u64 sec; + + /* have to be a little careful about overflow */ + ct = jif % HZ; + sec = jif / HZ; + if (ct) { + ct *= tb_ticks_per_sec; + do_div(ct, HZ); + } + if (sec) + ct += (cputime_t) sec * tb_ticks_per_sec; + return ct; +} + static inline u64 cputime64_to_jiffies64(const cputime_t ct) { return mulhdu(ct, __cputime_jiffies_factor); -- cgit v1.2.3 From e340221acda6bc0bf05a0ff6e6114902c4307670 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Wed, 5 Jul 2006 18:57:48 +0200 Subject: [PATCH] Makefile typo Fix a typo in the toplevel makefile. Signed-off-by: Andreas Schwab Signed-off-by: Linus Torvalds --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 11a850cffd3d..82f76a96cfe6 100644 --- a/Makefile +++ b/Makefile @@ -528,7 +528,7 @@ export MODLIB ifdef INSTALL_MOD_STRIP ifeq ($(INSTALL_MOD_STRIP),1) -mod_strip_cmd = $STRIP) --strip-debug +mod_strip_cmd = $(STRIP) --strip-debug else mod_strip_cmd = $(STRIP) $(INSTALL_MOD_STRIP) endif # INSTALL_MOD_STRIP=1 -- cgit v1.2.3 From f475ae957db66650db66916c62604ac27409d884 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 29 Jun 2006 16:38:32 -0400 Subject: VFS: Allow caller to determine if BSD or posix locks were actually freed Change posix_lock_file_conf(), and flock_lock_file() so that if called with an F_UNLCK argument, and the FL_EXISTS flag they will indicate whether or not any locks were actually freed by returning 0 or -ENOENT. Signed-off-by: Trond Myklebust --- fs/locks.c | 18 ++++++++++++++++-- include/linux/fs.h | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/fs/locks.c b/fs/locks.c index 1ad29c9b6252..50cb0a2b74d9 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -725,6 +725,10 @@ next_task: /* Try to create a FLOCK lock on filp. We always insert new FLOCK locks * at the head of the list, but that's secret knowledge known only to * flock_lock_file and posix_lock_file. + * + * Note that if called with an FL_EXISTS argument, the caller may determine + * whether or not a lock was successfully freed by testing the return + * value for -ENOENT. */ static int flock_lock_file(struct file *filp, struct file_lock *request) { @@ -750,8 +754,11 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) break; } - if (request->fl_type == F_UNLCK) + if (request->fl_type == F_UNLCK) { + if ((request->fl_flags & FL_EXISTS) && !found) + error = -ENOENT; goto out; + } error = -ENOMEM; new_fl = locks_alloc_lock(); @@ -948,8 +955,11 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request error = 0; if (!added) { - if (request->fl_type == F_UNLCK) + if (request->fl_type == F_UNLCK) { + if (request->fl_flags & FL_EXISTS) + error = -ENOENT; goto out; + } if (!new_fl) { error = -ENOLCK; @@ -996,6 +1006,10 @@ static int __posix_lock_file_conf(struct inode *inode, struct file_lock *request * Add a POSIX style lock to a file. * We merge adjacent & overlapping locks whenever possible. * POSIX locks are sorted by owner task, then by starting address + * + * Note that if called with an FL_EXISTS argument, the caller may determine + * whether or not a lock was successfully freed by testing the return + * value for -ENOENT. */ int posix_lock_file(struct file *filp, struct file_lock *fl) { diff --git a/include/linux/fs.h b/include/linux/fs.h index 134b32068246..43aef9b230fd 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -716,6 +716,7 @@ extern spinlock_t files_lock; #define FL_POSIX 1 #define FL_FLOCK 2 #define FL_ACCESS 8 /* not trying to lock, just looking */ +#define FL_EXISTS 16 /* when unlocking, test for existence */ #define FL_LEASE 32 /* lease held on this file */ #define FL_CLOSE 64 /* unlock on close */ #define FL_SLEEP 128 /* A blocking lock */ -- cgit v1.2.3 From 9b07357490e5c7a1c3c2b6f4679d7ee4b4185ecd Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 29 Jun 2006 16:38:34 -0400 Subject: NLM,NFSv4: Don't put UNLOCK requests on the wire unless we hold a lock Use the new behaviour of {flock,posix}_file_lock(F_UNLCK) to determine if we held a lock, and only send the RPC request to the server if this was the case. Signed-off-by: Trond Myklebust --- fs/lockd/clntproc.c | 18 ++++++++++-------- fs/nfs/nfs4proc.c | 31 ++++++++++++------------------- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 5980c45998cc..24c691f5480e 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -454,7 +454,7 @@ static void nlmclnt_locks_init_private(struct file_lock *fl, struct nlm_host *ho fl->fl_ops = &nlmclnt_lock_ops; } -static void do_vfs_lock(struct file_lock *fl) +static int do_vfs_lock(struct file_lock *fl) { int res = 0; switch (fl->fl_flags & (FL_POSIX|FL_FLOCK)) { @@ -467,9 +467,7 @@ static void do_vfs_lock(struct file_lock *fl) default: BUG(); } - if (res < 0) - printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", - __FUNCTION__); + return res; } /* @@ -541,7 +539,8 @@ again: } fl->fl_flags |= FL_SLEEP; /* Ensure the resulting lock will get added to granted list */ - do_vfs_lock(fl); + if (do_vfs_lock(fl) < 0) + printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); up_read(&host->h_rwsem); } status = nlm_stat_to_errno(resp->status); @@ -606,15 +605,19 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) { struct nlm_host *host = req->a_host; struct nlm_res *resp = &req->a_res; - int status; + int status = 0; /* * Note: the server is supposed to either grant us the unlock * request, or to deny it with NLM_LCK_DENIED_GRACE_PERIOD. In either * case, we want to unlock. */ + fl->fl_flags |= FL_EXISTS; down_read(&host->h_rwsem); - do_vfs_lock(fl); + if (do_vfs_lock(fl) == -ENOENT) { + up_read(&host->h_rwsem); + goto out; + } up_read(&host->h_rwsem); if (req->a_flags & RPC_TASK_ASYNC) @@ -624,7 +627,6 @@ nlmclnt_unlock(struct nlm_rqst *req, struct file_lock *fl) if (status < 0) goto out; - status = 0; if (resp->status == NLM_LCK_GRANTED) goto out; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b4916b092194..b8c63757f039 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3144,9 +3144,6 @@ static int do_vfs_lock(struct file *file, struct file_lock *fl) default: BUG(); } - if (res < 0) - printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", - __FUNCTION__); return res; } @@ -3258,8 +3255,6 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl, return ERR_PTR(-ENOMEM); } - /* Unlock _before_ we do the RPC call */ - do_vfs_lock(fl->fl_file, fl); return rpc_run_task(NFS_CLIENT(lsp->ls_state->inode), RPC_TASK_ASYNC, &nfs4_locku_ops, data); } @@ -3270,30 +3265,28 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock * struct rpc_task *task; int status = 0; - /* Is this a delegated lock? */ - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) - goto out_unlock; - /* Is this open_owner holding any locks on the server? */ - if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) - goto out_unlock; - status = nfs4_set_lock_state(state, request); + /* Unlock _before_ we do the RPC call */ + request->fl_flags |= FL_EXISTS; + if (do_vfs_lock(request->fl_file, request) == -ENOENT) + goto out; if (status != 0) - goto out_unlock; + goto out; + /* Is this a delegated lock? */ + if (test_bit(NFS_DELEGATED_STATE, &state->flags)) + goto out; lsp = request->fl_u.nfs4_fl.owner; - status = -ENOMEM; seqid = nfs_alloc_seqid(&lsp->ls_seqid); + status = -ENOMEM; if (seqid == NULL) - goto out_unlock; + goto out; task = nfs4_do_unlck(request, request->fl_file->private_data, lsp, seqid); status = PTR_ERR(task); if (IS_ERR(task)) - goto out_unlock; + goto out; status = nfs4_wait_for_completion_rpc_task(task); rpc_release_task(task); - return status; -out_unlock: - do_vfs_lock(request->fl_file, request); +out: return status; } -- cgit v1.2.3 From 42a2d13eee3c895d22e9d1a52b96d15ca49adabc Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 29 Jun 2006 16:38:36 -0400 Subject: NFSv4: Ensure nfs4_lock_expired() caches delegated locks Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b8c63757f039..8bdfe3ff7925 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3454,10 +3454,10 @@ static int nfs4_lock_reclaim(struct nfs4_state *state, struct file_lock *request struct nfs4_exception exception = { }; int err; - /* Cache the lock if possible... */ - if (test_bit(NFS_DELEGATED_STATE, &state->flags)) - return 0; do { + /* Cache the lock if possible... */ + if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) + return 0; err = _nfs4_do_setlk(state, F_SETLK, request, 1); if (err != -NFS4ERR_DELAY) break; @@ -3476,6 +3476,8 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request if (err != 0) return err; do { + if (test_bit(NFS_DELEGATED_STATE, &state->flags) != 0) + return 0; err = _nfs4_do_setlk(state, F_SETLK, request, 0); if (err != -NFS4ERR_DELAY) break; -- cgit v1.2.3 From f07f18dd6f29f11887b8d9cf7ecb736bf2f7dc62 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 29 Jun 2006 16:38:37 -0400 Subject: VFS: Add support for the FL_ACCESS flag to flock_lock_file() Signed-off-by: Trond Myklebust --- fs/locks.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/locks.c b/fs/locks.c index 50cb0a2b74d9..b0b41a64e10b 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -739,6 +739,8 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) int found = 0; lock_kernel(); + if (request->fl_flags & FL_ACCESS) + goto find_conflict; for_each_lock(inode, before) { struct file_lock *fl = *before; if (IS_POSIX(fl)) @@ -771,6 +773,7 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) if (found) cond_resched(); +find_conflict: for_each_lock(inode, before) { struct file_lock *fl = *before; if (IS_POSIX(fl)) @@ -784,6 +787,8 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) locks_insert_block(fl, request); goto out; } + if (request->fl_flags & FL_ACCESS) + goto out; locks_copy_lock(new_fl, request); locks_insert_lock(&inode->i_flock, new_fl); new_fl = NULL; -- cgit v1.2.3 From 01c3b861cd77b28565a2d18c7caa3ce7f938e35c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 29 Jun 2006 16:38:39 -0400 Subject: NLM,NFSv4: Wait on local locks before we put RPC calls on the wire Use FL_ACCESS flag to test and/or wait for local locks before we try requesting a lock from the server Signed-off-by: Trond Myklebust --- fs/lockd/clntproc.c | 8 +++++++- fs/nfs/nfs4proc.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/fs/lockd/clntproc.c b/fs/lockd/clntproc.c index 24c691f5480e..89ba0df14c22 100644 --- a/fs/lockd/clntproc.c +++ b/fs/lockd/clntproc.c @@ -496,6 +496,7 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) struct nlm_host *host = req->a_host; struct nlm_res *resp = &req->a_res; struct nlm_wait *block = NULL; + unsigned char fl_flags = fl->fl_flags; int status = -ENOLCK; if (!host->h_monitored && nsm_monitor(host) < 0) { @@ -503,6 +504,10 @@ nlmclnt_lock(struct nlm_rqst *req, struct file_lock *fl) host->h_name); goto out; } + fl->fl_flags |= FL_ACCESS; + status = do_vfs_lock(fl); + if (status < 0) + goto out; block = nlmclnt_prepare_block(host, fl); again: @@ -537,8 +542,8 @@ again: up_read(&host->h_rwsem); goto again; } - fl->fl_flags |= FL_SLEEP; /* Ensure the resulting lock will get added to granted list */ + fl->fl_flags = fl_flags | FL_SLEEP; if (do_vfs_lock(fl) < 0) printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); up_read(&host->h_rwsem); @@ -551,6 +556,7 @@ out_unblock: nlmclnt_cancel(host, req->a_args.block, fl); out: nlm_release_call(req); + fl->fl_flags = fl_flags; return status; } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 8bdfe3ff7925..e6ee97f19d81 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3489,29 +3489,42 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request) { struct nfs4_client *clp = state->owner->so_client; + unsigned char fl_flags = request->fl_flags; int status; /* Is this a delegated open? */ - if (NFS_I(state->inode)->delegation_state != 0) { - /* Yes: cache locks! */ - status = do_vfs_lock(request->fl_file, request); - /* ...but avoid races with delegation recall... */ - if (status < 0 || test_bit(NFS_DELEGATED_STATE, &state->flags)) - return status; - } - down_read(&clp->cl_sem); status = nfs4_set_lock_state(state, request); if (status != 0) goto out; + request->fl_flags |= FL_ACCESS; + status = do_vfs_lock(request->fl_file, request); + if (status < 0) + goto out; + down_read(&clp->cl_sem); + if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { + struct nfs_inode *nfsi = NFS_I(state->inode); + /* Yes: cache locks! */ + down_read(&nfsi->rwsem); + /* ...but avoid races with delegation recall... */ + if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { + request->fl_flags = fl_flags & ~FL_SLEEP; + status = do_vfs_lock(request->fl_file, request); + up_read(&nfsi->rwsem); + goto out_unlock; + } + up_read(&nfsi->rwsem); + } status = _nfs4_do_setlk(state, cmd, request, 0); if (status != 0) - goto out; + goto out_unlock; /* Note: we always want to sleep here! */ - request->fl_flags |= FL_SLEEP; + request->fl_flags = fl_flags | FL_SLEEP; if (do_vfs_lock(request->fl_file, request) < 0) printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __FUNCTION__); -out: +out_unlock: up_read(&clp->cl_sem); +out: + request->fl_flags = fl_flags; return status; } -- cgit v1.2.3 From 83715ad54fad5a7ed330110f83e31ae92630e9d9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 5 Jul 2006 13:17:12 -0400 Subject: NFS: Fix NFS page_state usage The introduction of the FLUSH_INVALIDATE argument to nfs_sync_inode_wait() does not clear the nr_unstable page state counter for pages that are being released. Also fix a longstanding similar bug when nfs_commit_list() fails. Signed-off-by: Trond Myklebust --- fs/nfs/write.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/fs/nfs/write.c b/fs/nfs/write.c index bca5734ca9fb..86bac6a5008e 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -578,7 +578,7 @@ static int nfs_wait_on_requests(struct inode *inode, unsigned long idx_start, un return ret; } -static void nfs_cancel_requests(struct list_head *head) +static void nfs_cancel_dirty_list(struct list_head *head) { struct nfs_page *req; while(!list_empty(head)) { @@ -589,6 +589,19 @@ static void nfs_cancel_requests(struct list_head *head) } } +static void nfs_cancel_commit_list(struct list_head *head) +{ + struct nfs_page *req; + + while(!list_empty(head)) { + req = nfs_list_entry(head->next); + nfs_list_remove_request(req); + nfs_inode_remove_request(req); + nfs_clear_page_writeback(req); + dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); + } +} + /* * nfs_scan_dirty - Scan an inode for dirty requests * @inode: NFS inode to scan @@ -1381,6 +1394,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how) nfs_list_remove_request(req); nfs_mark_request_commit(req); nfs_clear_page_writeback(req); + dec_zone_page_state(req->wb_page, NR_UNSTABLE_NFS); } return -ENOMEM; } @@ -1499,7 +1513,7 @@ int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, if (pages != 0) { spin_unlock(&nfsi->req_lock); if (how & FLUSH_INVALIDATE) - nfs_cancel_requests(&head); + nfs_cancel_dirty_list(&head); else ret = nfs_flush_list(inode, &head, pages, how); spin_lock(&nfsi->req_lock); @@ -1512,7 +1526,7 @@ int nfs_sync_inode_wait(struct inode *inode, unsigned long idx_start, break; if (how & FLUSH_INVALIDATE) { spin_unlock(&nfsi->req_lock); - nfs_cancel_requests(&head); + nfs_cancel_commit_list(&head); spin_lock(&nfsi->req_lock); continue; } -- cgit v1.2.3 From bce3481c91801665e17f8daf59ede946129f3d3f Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 5 Jul 2006 13:17:12 -0400 Subject: This fixes a panic doing the first READDIR or READDIRPLUS call when: * the client is ia64 or any platform that actually implements flush_dcache_page(), and * the server returns fsinfo.dtpref >= client's PAGE_SIZE, and * the server does *not* return post-op attributes for the directory in the READDIR reply. Problem diagnosed by Greg Banks Signed-off-by: Trond Myklebust --- net/sunrpc/xdr.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index 49174f0d0a3e..6ac45103a272 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -191,7 +191,6 @@ _shift_data_right_pages(struct page **pages, size_t pgto_base, do { /* Are any pointers crossing a page boundary? */ if (pgto_base == 0) { - flush_dcache_page(*pgto); pgto_base = PAGE_CACHE_SIZE; pgto--; } @@ -211,11 +210,11 @@ _shift_data_right_pages(struct page **pages, size_t pgto_base, vto = kmap_atomic(*pgto, KM_USER0); vfrom = kmap_atomic(*pgfrom, KM_USER1); memmove(vto + pgto_base, vfrom + pgfrom_base, copy); + flush_dcache_page(*pgto); kunmap_atomic(vfrom, KM_USER1); kunmap_atomic(vto, KM_USER0); } while ((len -= copy) != 0); - flush_dcache_page(*pgto); } /* -- cgit v1.2.3 From 4e0641a7ad98fca5646a6be17605bc80f6c4ebde Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 5 Jul 2006 13:05:13 -0400 Subject: NFS: Optimise away an excessive GETATTR call when a file is symlinked In the case when compiling via a symlink tree, we want to ensure that the close-to-open GETATTR call is applied only to the final file, and not to the symlink. Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 3ddda6f7ecc2..e7ffb4deb3e5 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -690,7 +690,9 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd) goto out_force; /* This is an open(2) */ if (nfs_lookup_check_intent(nd, LOOKUP_OPEN) != 0 && - !(server->flags & NFS_MOUNT_NOCTO)) + !(server->flags & NFS_MOUNT_NOCTO) && + (S_ISREG(inode->i_mode) || + S_ISDIR(inode->i_mode))) goto out_force; } return nfs_revalidate_inode(server, inode); -- cgit v1.2.3 From 36e1e84768b1123eb632aff153104855c71684cc Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 07:38:33 -0400 Subject: [netdrvr] epic100: minor cleanups - Remove in-source changelog, it's in the global kernel history. - convert silly and useless version to useful one - replace invariant pci_id_tbl[]::io_size uses with EPIC_TOTAL_SIZE - remove now-unused io_size member from pci_id_tbl[] - current kernel style prefers dev_printk() for the rare ethernet driver messages that cannot print an 'eth%d' prefix. Signed-off-by: Jeff Garzik --- drivers/net/epic100.c | 71 ++++++++------------------------------------------- 1 file changed, 10 insertions(+), 61 deletions(-) diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 9f3e09a3d88c..05c9a26a74e2 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -19,62 +19,15 @@ Information and updates available at http://www.scyld.com/network/epic100.html + [this link no longer provides anything useful -jgarzik] --------------------------------------------------------------------- - Linux kernel-specific changes: - - LK1.1.2 (jgarzik): - * Merge becker version 1.09 (4/08/2000) - - LK1.1.3: - * Major bugfix to 1.09 driver (Francis Romieu) - - LK1.1.4 (jgarzik): - * Merge becker test version 1.09 (5/29/2000) - - LK1.1.5: - * Fix locking (jgarzik) - * Limit 83c175 probe to ethernet-class PCI devices (rgooch) - - LK1.1.6: - * Merge becker version 1.11 - * Move pci_enable_device before any PCI BAR len checks - - LK1.1.7: - * { fill me in } - - LK1.1.8: - * ethtool driver info support (jgarzik) - - LK1.1.9: - * ethtool media get/set support (jgarzik) - - LK1.1.10: - * revert MII transceiver init change (jgarzik) - - LK1.1.11: - * implement ETHTOOL_[GS]SET, _NWAY_RST, _[GS]MSGLVL, _GLINK (jgarzik) - * replace some MII-related magic numbers with constants - - LK1.1.12: - * fix power-up sequence - - LK1.1.13: - * revert version 1.1.12, power-up sequence "fix" - - LK1.1.14 (Kryzsztof Halasa): - * fix spurious bad initializations - * pound phy a la SMSC's app note on the subject - - AC1.1.14ac - * fix power up/down for ethtool that broke in 1.11 - */ #define DRV_NAME "epic100" -#define DRV_VERSION "1.11+LK1.1.14+AC1.1.14" -#define DRV_RELDATE "June 2, 2004" +#define DRV_VERSION "2.0" +#define DRV_RELDATE "June 27, 2006" /* The user-configurable values. These may be modified when a driver module is loaded.*/ @@ -204,19 +157,15 @@ typedef enum { struct epic_chip_info { const char *name; - int io_size; /* Needed for I/O region check or ioremap(). */ int drv_flags; /* Driver use, intended as capability flags. */ }; /* indexed by chip_t */ static const struct epic_chip_info pci_id_tbl[] = { - { "SMSC EPIC/100 83c170", - EPIC_TOTAL_SIZE, TYPE2_INTR | NO_MII | MII_PWRDWN }, - { "SMSC EPIC/100 83c170", - EPIC_TOTAL_SIZE, TYPE2_INTR }, - { "SMSC EPIC/C 83c175", - EPIC_TOTAL_SIZE, TYPE2_INTR | MII_PWRDWN }, + { "SMSC EPIC/100 83c170", TYPE2_INTR | NO_MII | MII_PWRDWN }, + { "SMSC EPIC/100 83c170", TYPE2_INTR }, + { "SMSC EPIC/C 83c175", TYPE2_INTR | MII_PWRDWN }, }; @@ -385,8 +334,8 @@ static int __devinit epic_init_one (struct pci_dev *pdev, goto out; irq = pdev->irq; - if (pci_resource_len(pdev, 0) < pci_id_tbl[chip_idx].io_size) { - printk (KERN_ERR "card %d: no PCI region space\n", card_idx); + if (pci_resource_len(pdev, 0) < EPIC_TOTAL_SIZE) { + dev_printk(KERN_ERR, &pdev->dev, "no PCI region space\n"); ret = -ENODEV; goto err_out_disable; } @@ -401,7 +350,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, dev = alloc_etherdev(sizeof (*ep)); if (!dev) { - printk (KERN_ERR "card %d: no memory for eth device\n", card_idx); + dev_printk(KERN_ERR, &pdev->dev, "no memory for eth device\n"); goto err_out_free_res; } SET_MODULE_OWNER(dev); @@ -413,7 +362,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, ioaddr = pci_resource_start (pdev, 1); ioaddr = (long) ioremap (ioaddr, pci_resource_len (pdev, 1)); if (!ioaddr) { - printk (KERN_ERR DRV_NAME " %d: ioremap failed\n", card_idx); + dev_printk(KERN_ERR, &pdev->dev, "ioremap failed\n"); goto err_out_free_netdev; } #endif -- cgit v1.2.3 From 03a8c6611aa18f919f8700b18f925e9008b616a8 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 07:57:22 -0400 Subject: [netdrvr] Remove Linux-specific changelogs from several Becker template drivers When in-kernel net drivers branched from Donald Becker's vanilla driver set, in the days before BitKeeper and git, a driver changelog was maintained in the driver source code. These days, the kernel's changelog is far superior and much more accurate, so the in-driver changelogs are removed. Another relic of the Becker/kernel split was version numbering, using "foo-LKx.y.z" notation, resulting in weird version numbers like "1.17b-LK1.1.9". These drivers are for older hardware, and see few changes these days, so the version numbers were all bumped to something more simple. Finally, in xircom_tulip_cb specifically, an additional cleanup removes the always-enabled CARDBUS cpp macro. Signed-off-by: Jeff Garzik --- drivers/net/dl2k.c | 43 +------------ drivers/net/hamachi.c | 13 +--- drivers/net/natsemi.c | 117 +--------------------------------- drivers/net/starfire.c | 122 +----------------------------------- drivers/net/sundance.c | 84 +------------------------ drivers/net/tulip/xircom_tulip_cb.c | 27 ++------ drivers/net/via-rhine.c | 110 +------------------------------- drivers/net/yellowfin.c | 30 +-------- 8 files changed, 24 insertions(+), 522 deletions(-) diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 4b6ddb70f921..402961e68c89 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -9,49 +9,10 @@ the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ -/* - Rev Date Description - ========================================================================== - 0.01 2001/05/03 Created DL2000-based linux driver - 0.02 2001/05/21 Added VLAN and hardware checksum support. - 1.00 2001/06/26 Added jumbo frame support. - 1.01 2001/08/21 Added two parameters, rx_coalesce and rx_timeout. - 1.02 2001/10/08 Supported fiber media. - Added flow control parameters. - 1.03 2001/10/12 Changed the default media to 1000mbps_fd for - the fiber devices. - 1.04 2001/11/08 Fixed Tx stopped when tx very busy. - 1.05 2001/11/22 Fixed Tx stopped when unidirectional tx busy. - 1.06 2001/12/13 Fixed disconnect bug at 10Mbps mode. - Fixed tx_full flag incorrect. - Added tx_coalesce paramter. - 1.07 2002/01/03 Fixed miscount of RX frame error. - 1.08 2002/01/17 Fixed the multicast bug. - 1.09 2002/03/07 Move rx-poll-now to re-fill loop. - Added rio_timer() to watch rx buffers. - 1.10 2002/04/16 Fixed miscount of carrier error. - 1.11 2002/05/23 Added ISR schedule scheme - Fixed miscount of rx frame error for DGE-550SX. - Fixed VLAN bug. - 1.12 2002/06/13 Lock tx_coalesce=1 on 10/100Mbps mode. - 1.13 2002/08/13 1. Fix disconnection (many tx:carrier/rx:frame - errs) with some mainboards. - 2. Use definition "DRV_NAME" "DRV_VERSION" - "DRV_RELDATE" for flexibility. - 1.14 2002/08/14 Support ethtool. - 1.15 2002/08/27 Changed the default media to Auto-Negotiation - for the fiber devices. - 1.16 2002/09/04 More power down time for fiber devices auto- - negotiation. - Fix disconnect bug after ifup and ifdown. - 1.17 2002/10/03 Fix RMON statistics overflow. - Always use I/O mapping to access eeprom, - avoid system freezing with some chipsets. -*/ #define DRV_NAME "D-Link DL2000-based linux driver" -#define DRV_VERSION "v1.17b" -#define DRV_RELDATE "2006/03/10" +#define DRV_VERSION "v1.18" +#define DRV_RELDATE "2006/06/27" #include "dl2k.h" #include diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index 7bcd939c6edd..1b212a56933c 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -20,22 +20,15 @@ Support and updates available at http://www.scyld.com/network/hamachi.html + [link no longer provides useful info -jgarzik] or http://www.parl.clemson.edu/~keithu/hamachi.html - - - Linux kernel changelog: - - LK1.0.1: - - fix lack of pci_dev<->dev association - - ethtool support (jgarzik) - */ #define DRV_NAME "hamachi" -#define DRV_VERSION "1.01+LK1.0.1" -#define DRV_RELDATE "5/18/2001" +#define DRV_VERSION "2.0" +#define DRV_RELDATE "June 27, 2006" /* A few user-configurable values. */ diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index 9df2628be1e7..db0475a1102f 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -20,120 +20,9 @@ Support information and updates available at http://www.scyld.com/network/netsemi.html + [link no longer provides useful info -jgarzik] - Linux kernel modifications: - - Version 1.0.1: - - Spinlock fixes - - Bug fixes and better intr performance (Tjeerd) - Version 1.0.2: - - Now reads correct MAC address from eeprom - Version 1.0.3: - - Eliminate redundant priv->tx_full flag - - Call netif_start_queue from dev->tx_timeout - - wmb() in start_tx() to flush data - - Update Tx locking - - Clean up PCI enable (davej) - Version 1.0.4: - - Merge Donald Becker's natsemi.c version 1.07 - Version 1.0.5: - - { fill me in } - Version 1.0.6: - * ethtool support (jgarzik) - * Proper initialization of the card (which sometimes - fails to occur and leaves the card in a non-functional - state). (uzi) - - * Some documented register settings to optimize some - of the 100Mbit autodetection circuitry in rev C cards. (uzi) - - * Polling of the PHY intr for stuff like link state - change and auto- negotiation to finally work properly. (uzi) - - * One-liner removal of a duplicate declaration of - netdev_error(). (uzi) - - Version 1.0.7: (Manfred Spraul) - * pci dma - * SMP locking update - * full reset added into tx_timeout - * correct multicast hash generation (both big and little endian) - [copied from a natsemi driver version - from Myrio Corporation, Greg Smith] - * suspend/resume - - version 1.0.8 (Tim Hockin ) - * ETHTOOL_* support - * Wake on lan support (Erik Gilling) - * MXDMA fixes for serverworks - * EEPROM reload - - version 1.0.9 (Manfred Spraul) - * Main change: fix lack of synchronize - netif_close/netif_suspend against a last interrupt - or packet. - * do not enable superflous interrupts (e.g. the - drivers relies on TxDone - TxIntr not needed) - * wait that the hardware has really stopped in close - and suspend. - * workaround for the (at least) gcc-2.95.1 compiler - problem. Also simplifies the code a bit. - * disable_irq() in tx_timeout - needed to protect - against rx interrupts. - * stop the nic before switching into silent rx mode - for wol (required according to docu). - - version 1.0.10: - * use long for ee_addr (various) - * print pointers properly (DaveM) - * include asm/irq.h (?) - - version 1.0.11: - * check and reset if PHY errors appear (Adrian Sun) - * WoL cleanup (Tim Hockin) - * Magic number cleanup (Tim Hockin) - * Don't reload EEPROM on every reset (Tim Hockin) - * Save and restore EEPROM state across reset (Tim Hockin) - * MDIO Cleanup (Tim Hockin) - * Reformat register offsets/bits (jgarzik) - - version 1.0.12: - * ETHTOOL_* further support (Tim Hockin) - - version 1.0.13: - * ETHTOOL_[G]EEPROM support (Tim Hockin) - - version 1.0.13: - * crc cleanup (Matt Domsch ) - - version 1.0.14: - * Cleanup some messages and autoneg in ethtool (Tim Hockin) - - version 1.0.15: - * Get rid of cable_magic flag - * use new (National provided) solution for cable magic issue - - version 1.0.16: - * call netdev_rx() for RxErrors (Manfred Spraul) - * formatting and cleanups - * change options and full_duplex arrays to be zero - initialized - * enable only the WoL and PHY interrupts in wol mode - - version 1.0.17: - * only do cable_magic on 83815 and early 83816 (Tim Hockin) - * create a function for rx refill (Manfred Spraul) - * combine drain_ring and init_ring (Manfred Spraul) - * oom handling (Manfred Spraul) - * hands_off instead of playing with netif_device_{de,a}ttach - (Manfred Spraul) - * be sure to write the MAC back to the chip (Manfred Spraul) - * lengthen EEPROM timeout, and always warn about timeouts - (Manfred Spraul) - * comments update (Manfred) - * do the right thing on a phy-reset (Manfred and Tim) - TODO: * big endian support with CFG:BEM instead of cpu_to_le32 */ @@ -165,8 +54,8 @@ #include #define DRV_NAME "natsemi" -#define DRV_VERSION "1.07+LK1.0.17" -#define DRV_RELDATE "Sep 27, 2002" +#define DRV_VERSION "2.0" +#define DRV_RELDATE "June 27, 2006" #define RX_OFFSET 2 diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index ed1f59901ff4..24d55b4591b3 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -22,129 +22,13 @@ Support and updates available at http://www.scyld.com/network/starfire.html + [link no longer provides useful info -jgarzik] - ----------------------------------------------------------- - - Linux kernel-specific changes: - - LK1.1.1 (jgarzik): - - Use PCI driver interface - - Fix MOD_xxx races - - softnet fixups - - LK1.1.2 (jgarzik): - - Merge Becker version 0.15 - - LK1.1.3 (Andrew Morton) - - Timer cleanups - - LK1.1.4 (jgarzik): - - Merge Becker version 1.03 - - LK1.2.1 (Ion Badulescu ) - - Support hardware Rx/Tx checksumming - - Use the GFP firmware taken from Adaptec's Netware driver - - LK1.2.2 (Ion Badulescu) - - Backported to 2.2.x - - LK1.2.3 (Ion Badulescu) - - Fix the flaky mdio interface - - More compat clean-ups - - LK1.2.4 (Ion Badulescu) - - More 2.2.x initialization fixes - - LK1.2.5 (Ion Badulescu) - - Several fixes from Manfred Spraul - - LK1.2.6 (Ion Badulescu) - - Fixed ifup/ifdown/ifup problem in 2.4.x - - LK1.2.7 (Ion Badulescu) - - Removed unused code - - Made more functions static and __init - - LK1.2.8 (Ion Badulescu) - - Quell bogus error messages, inform about the Tx threshold - - Removed #ifdef CONFIG_PCI, this driver is PCI only - - LK1.2.9 (Ion Badulescu) - - Merged Jeff Garzik's changes from 2.4.4-pre5 - - Added 2.2.x compatibility stuff required by the above changes - - LK1.2.9a (Ion Badulescu) - - More updates from Jeff Garzik - - LK1.3.0 (Ion Badulescu) - - Merged zerocopy support - - LK1.3.1 (Ion Badulescu) - - Added ethtool support - - Added GPIO (media change) interrupt support - - LK1.3.2 (Ion Badulescu) - - Fixed 2.2.x compatibility issues introduced in 1.3.1 - - Fixed ethtool ioctl returning uninitialized memory - - LK1.3.3 (Ion Badulescu) - - Initialize the TxMode register properly - - Don't dereference dev->priv after freeing it - - LK1.3.4 (Ion Badulescu) - - Fixed initialization timing problems - - Fixed interrupt mask definitions - - LK1.3.5 (jgarzik) - - ethtool NWAY_RST, GLINK, [GS]MSGLVL support - - LK1.3.6: - - Sparc64 support and fixes (Ion Badulescu) - - Better stats and error handling (Ion Badulescu) - - Use new pci_set_mwi() PCI API function (jgarzik) - - LK1.3.7 (Ion Badulescu) - - minimal implementation of tx_timeout() - - correctly shutdown the Rx/Tx engines in netdev_close() - - added calls to netif_carrier_on/off - (patch from Stefan Rompf ) - - VLAN support - - LK1.3.8 (Ion Badulescu) - - adjust DMA burst size on sparc64 - - 64-bit support - - reworked zerocopy support for 64-bit buffers - - working and usable interrupt mitigation/latency - - reduced Tx interrupt frequency for lower interrupt overhead - - LK1.3.9 (Ion Badulescu) - - bugfix for mcast filter - - enable the right kind of Tx interrupts (TxDMADone, not TxDone) - - LK1.4.0 (Ion Badulescu) - - NAPI support - - LK1.4.1 (Ion Badulescu) - - flush PCI posting buffers after disabling Rx interrupts - - put the chip to a D3 slumber on driver unload - - added config option to enable/disable NAPI - - LK1.4.2 (Ion Badulescu) - - finally added firmware (GPL'ed by Adaptec) - - removed compatibility code for 2.2.x - - LK1.4.2.1 (Ion Badulescu) - - fixed 32/64 bit issues on i386 + CONFIG_HIGHMEM - - added 32-bit padding to outgoing skb's, removed previous workaround - -TODO: - fix forced speed/duplexing code (broken a long time ago, when - somebody converted the driver to use the generic MII code) - - fix VLAN support */ #define DRV_NAME "starfire" -#define DRV_VERSION "1.03+LK1.4.2.1" -#define DRV_RELDATE "October 3, 2005" +#define DRV_VERSION "2.0" +#define DRV_RELDATE "June 27, 2006" #include #include diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index 643fceae3db5..de6645196657 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -16,91 +16,13 @@ Support and updates available at http://www.scyld.com/network/sundance.html + [link no longer provides useful info -jgarzik] - - Version LK1.01a (jgarzik): - - Replace some MII-related magic numbers with constants - - Version LK1.02 (D-Link): - - Add new board to PCI ID list - - Fix multicast bug - - Version LK1.03 (D-Link): - - New Rx scheme, reduce Rx congestion - - Option to disable flow control - - Version LK1.04 (D-Link): - - Tx timeout recovery - - More support for ethtool. - - Version LK1.04a: - - Remove unused/constant members from struct pci_id_info - (which then allows removal of 'drv_flags' from private struct) - (jgarzik) - - If no phy is found, fail to load that board (jgarzik) - - Always start phy id scan at id 1 to avoid problems (Donald Becker) - - Autodetect where mii_preable_required is needed, - default to not needed. (Donald Becker) - - Version LK1.04b: - - Remove mii_preamble_required module parameter (Donald Becker) - - Add per-interface mii_preamble_required (setting is autodetected) - (Donald Becker) - - Remove unnecessary cast from void pointer (jgarzik) - - Re-align comments in private struct (jgarzik) - - Version LK1.04c (jgarzik): - - Support bitmapped message levels (NETIF_MSG_xxx), and the - two ethtool ioctls that get/set them - - Don't hand-code MII ethtool support, use standard API/lib - - Version LK1.04d: - - Merge from Donald Becker's sundance.c: (Jason Lunz) - * proper support for variably-sized MTUs - * default to PIO, to fix chip bugs - - Add missing unregister_netdev (Jason Lunz) - - Add CONFIG_SUNDANCE_MMIO config option (jgarzik) - - Better rx buf size calculation (Donald Becker) - - Version LK1.05 (D-Link): - - Fix DFE-580TX packet drop issue (for DL10050C) - - Fix reset_tx logic - - Version LK1.06 (D-Link): - - Fix crash while unloading driver - - Versin LK1.06b (D-Link): - - New tx scheme, adaptive tx_coalesce - - Version LK1.07 (D-Link): - - Fix tx bugs in big-endian machines - - Remove unused max_interrupt_work module parameter, the new - NAPI-like rx scheme doesn't need it. - - Remove redundancy get_stats() in intr_handler(), those - I/O access could affect performance in ARM-based system - - Add Linux software VLAN support - - Version LK1.08 (Philippe De Muyter phdm@macqel.be): - - Fix bug of custom mac address - (StationAddr register only accept word write) - - Version LK1.09 (D-Link): - - Fix the flowctrl bug. - - Set Pause bit in MII ANAR if flow control enabled. - - Version LK1.09a (ICPlus): - - Add the delay time in reading the contents of EEPROM - - Version LK1.10 (Philippe De Muyter phdm@macqel.be): - - Make 'unblock interface after Tx underrun' work - - Version LK1.11 (Pedro Alejandro Lopez-Valencia palopezv at gmail.com): - - Add support for IC Plus Corporation IP100A chipset */ #define DRV_NAME "sundance" -#define DRV_VERSION "1.01+LK1.11" -#define DRV_RELDATE "14-Jun-2006" +#define DRV_VERSION "1.1" +#define DRV_RELDATE "27-Jun-2006" /* The user-configurable values. diff --git a/drivers/net/tulip/xircom_tulip_cb.c b/drivers/net/tulip/xircom_tulip_cb.c index 091ebb7a62f6..17ca7dc42e6f 100644 --- a/drivers/net/tulip/xircom_tulip_cb.c +++ b/drivers/net/tulip/xircom_tulip_cb.c @@ -10,26 +10,11 @@ 410 Severn Ave., Suite 210 Annapolis MD 21403 - ----------------------------------------------------------- - - Linux kernel-specific changes: - - LK1.0 (Ion Badulescu) - - Major cleanup - - Use 2.4 PCI API - - Support ethtool - - Rewrite perfect filter/hash code - - Use interrupts for media changes - - LK1.1 (Ion Badulescu) - - Disallow negotiation of unsupported full-duplex modes */ #define DRV_NAME "xircom_tulip_cb" -#define DRV_VERSION "0.91+LK1.1" -#define DRV_RELDATE "October 11, 2001" - -#define CARDBUS 1 +#define DRV_VERSION "0.92" +#define DRV_RELDATE "June 27, 2006" /* A few user-configurable values. */ @@ -306,10 +291,10 @@ struct xircom_private { struct xircom_tx_desc tx_ring[TX_RING_SIZE]; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; -#ifdef CARDBUS + /* The X3201-3 requires 4-byte aligned tx bufs */ struct sk_buff* tx_aligned_skbuff[TX_RING_SIZE]; -#endif + /* The addresses of receive-in-place skbuffs. */ struct sk_buff* rx_skbuff[RX_RING_SIZE]; u16 setup_frame[PKT_SETUP_SZ / sizeof(u16)]; /* Pseudo-Tx frame to init address table. */ @@ -908,10 +893,8 @@ static void xircom_init_ring(struct net_device *dev) tp->tx_skbuff[i] = NULL; tp->tx_ring[i].status = 0; tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); -#ifdef CARDBUS if (tp->chip_id == X3201_3) tp->tx_aligned_skbuff[i] = dev_alloc_skb(PKT_BUF_SZ); -#endif /* CARDBUS */ } tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); } @@ -931,12 +914,10 @@ xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) entry = tp->cur_tx % TX_RING_SIZE; tp->tx_skbuff[entry] = skb; -#ifdef CARDBUS if (tp->chip_id == X3201_3) { memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); } else -#endif tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 98b6f3207d3d..e48e76ce73a0 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -25,117 +25,13 @@ version. He may or may not be interested in bug reports on this code. You can find his versions at: http://www.scyld.com/network/via-rhine.html - - - Linux kernel version history: - - LK1.1.0: - - Jeff Garzik: softnet 'n stuff - - LK1.1.1: - - Justin Guyett: softnet and locking fixes - - Jeff Garzik: use PCI interface - - LK1.1.2: - - Urban Widmark: minor cleanups, merges from Becker 1.03a/1.04 versions - - LK1.1.3: - - Urban Widmark: use PCI DMA interface (with thanks to the eepro100.c - code) update "Theory of Operation" with - softnet/locking changes - - Dave Miller: PCI DMA and endian fixups - - Jeff Garzik: MOD_xxx race fixes, updated PCI resource allocation - - LK1.1.4: - - Urban Widmark: fix gcc 2.95.2 problem and - remove writel's to fixed address 0x7c - - LK1.1.5: - - Urban Widmark: mdio locking, bounce buffer changes - merges from Beckers 1.05 version - added netif_running_on/off support - - LK1.1.6: - - Urban Widmark: merges from Beckers 1.08b version (VT6102 + mdio) - set netif_running_on/off on startup, del_timer_sync - - LK1.1.7: - - Manfred Spraul: added reset into tx_timeout - - LK1.1.9: - - Urban Widmark: merges from Beckers 1.10 version - (media selection + eeprom reload) - - David Vrabel: merges from D-Link "1.11" version - (disable WOL and PME on startup) - - LK1.1.10: - - Manfred Spraul: use "singlecopy" for unaligned buffers - don't allocate bounce buffers for !ReqTxAlign cards - - LK1.1.11: - - David Woodhouse: Set dev->base_addr before the first time we call - wait_for_reset(). It's a lot happier that way. - Free np->tx_bufs only if we actually allocated it. - - LK1.1.12: - - Martin Eriksson: Allow Memory-Mapped IO to be enabled. - - LK1.1.13 (jgarzik): - - Add ethtool support - - Replace some MII-related magic numbers with constants - - LK1.1.14 (Ivan G.): - - fixes comments for Rhine-III - - removes W_MAX_TIMEOUT (unused) - - adds HasDavicomPhy for Rhine-I (basis: linuxfet driver; my card - is R-I and has Davicom chip, flag is referenced in kernel driver) - - sends chip_id as a parameter to wait_for_reset since np is not - initialized on first call - - changes mmio "else if (chip_id==VT6102)" to "else" so it will work - for Rhine-III's (documentation says same bit is correct) - - transmit frame queue message is off by one - fixed - - adds IntrNormalSummary to "Something Wicked" exclusion list - so normal interrupts will not trigger the message (src: Donald Becker) - (Roger Luethi) - - show confused chip where to continue after Tx error - - location of collision counter is chip specific - - allow selecting backoff algorithm (module parameter) - - LK1.1.15 (jgarzik): - - Use new MII lib helper generic_mii_ioctl - - LK1.1.16 (Roger Luethi) - - Etherleak fix - - Handle Tx buffer underrun - - Fix bugs in full duplex handling - - New reset code uses "force reset" cmd on Rhine-II - - Various clean ups - - LK1.1.17 (Roger Luethi) - - Fix race in via_rhine_start_tx() - - On errors, wait for Tx engine to turn off before scavenging - - Handle Tx descriptor write-back race on Rhine-II - - Force flushing for PCI posted writes - - More reset code changes - - LK1.1.18 (Roger Luethi) - - No filtering multicast in promisc mode (Edward Peng) - - Fix for Rhine-I Tx timeouts - - LK1.1.19 (Roger Luethi) - - Increase Tx threshold for unspecified errors - - LK1.2.0-2.6 (Roger Luethi) - - Massive clean-up - - Rewrite PHY, media handling (remove options, full_duplex, backoff) - - Fix Tx engine race for good - - Craig Brind: Zero padded aligned buffers for short packets. + [link no longer provides useful info -jgarzik] */ #define DRV_NAME "via-rhine" -#define DRV_VERSION "1.2.0-2.6" -#define DRV_RELDATE "June-10-2004" +#define DRV_VERSION "1.4.0" +#define DRV_RELDATE "June-27-2006" /* A few user-configurable values. diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index bbbf7e274a2a..207340fe8df9 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -19,37 +19,13 @@ Support and updates available at http://www.scyld.com/network/yellowfin.html + [link no longer provides useful info -jgarzik] - - Linux kernel changelog: - ----------------------- - - LK1.1.1 (jgarzik): Port to 2.4 kernel - - LK1.1.2 (jgarzik): - * Merge in becker version 1.05 - - LK1.1.3 (jgarzik): - * Various cleanups - * Update yellowfin_timer to correctly calculate duplex. - (suggested by Manfred Spraul) - - LK1.1.4 (val@nmt.edu): - * Fix three endian-ness bugs - * Support dual function SYM53C885E ethernet chip - - LK1.1.5 (val@nmt.edu): - * Fix forced full-duplex bug I introduced - - LK1.1.6 (val@nmt.edu): - * Only print warning on truly "oversized" packets - * Fix theoretical bug on gigabit cards - return to 1.1.3 behavior - */ #define DRV_NAME "yellowfin" -#define DRV_VERSION "1.05+LK1.1.6" -#define DRV_RELDATE "Feb 11, 2002" +#define DRV_VERSION "2.0" +#define DRV_RELDATE "Jun 27, 2006" #define PFX DRV_NAME ": " -- cgit v1.2.3 From c3d8e682b7d10f57d13c86ecbb15806781d6e953 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 08:54:34 -0400 Subject: [netdrvr] Remove Becker-template 'io_size' member, when invariant Becker-derived drivers often have the 'io_size' member in their chip info struct, indicating the minimum required size of the I/O resource (usually a PCI BAR). For many situations, this number is either constant or irrelevant (due to pci_iomap convenience behavior). This change removes the io_size invariant member, and replaces it with a compile-time constant. Drivers updated: fealnx, gt96100eth, winbond-840, yellowfin Additionally, - gt96100eth: unused 'drv_flags' removed from gt96100eth - winbond-840: unused struct match_info removed - winbond-840: mark pci_id_tbl[] const, __devinitdata Signed-off-by: Jeff Garzik --- drivers/net/fealnx.c | 7 +++---- drivers/net/gt96100eth.c | 3 +-- drivers/net/gt96100eth.h | 2 -- drivers/net/tulip/winbond-840.c | 29 +++++++++++++---------------- drivers/net/yellowfin.c | 9 +++++---- 5 files changed, 22 insertions(+), 28 deletions(-) diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index c701951dcd6f..0e6a495549fa 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -146,14 +146,13 @@ enum phy_type_flags { struct chip_info { char *chip_name; - int io_size; int flags; }; static const struct chip_info skel_netdrv_tbl[] = { - {"100/10M Ethernet PCI Adapter", 136, HAS_MII_XCVR}, - {"100/10M Ethernet PCI Adapter", 136, HAS_CHIP_XCVR}, - {"1000/100/10M Ethernet PCI Adapter", 136, HAS_MII_XCVR}, + { "100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, + { "100/10M Ethernet PCI Adapter", HAS_CHIP_XCVR }, + { "1000/100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, }; /* Offsets to the Command and Status Registers. */ diff --git a/drivers/net/gt96100eth.c b/drivers/net/gt96100eth.c index 49dacc6e35aa..2b4db7414475 100644 --- a/drivers/net/gt96100eth.c +++ b/drivers/net/gt96100eth.c @@ -699,7 +699,6 @@ static int __init gt96100_probe1(struct pci_dev *pci, int port_num) memset(gp, 0, sizeof(*gp)); // clear it gp->port_num = port_num; - gp->io_size = GT96100_ETH_IO_SIZE; gp->port_offset = port_num * GT96100_ETH_IO_SIZE; gp->phy_addr = phy_addr; gp->chip_rev = chip_rev; @@ -1531,7 +1530,7 @@ static void gt96100_cleanup_module(void) + sizeof(gt96100_td_t) * TX_RING_SIZE, gp->rx_ring); free_netdev(gtif->dev); - release_region(gtif->iobase, gp->io_size); + release_region(gtif->iobase, GT96100_ETH_IO_SIZE); } } } diff --git a/drivers/net/gt96100eth.h b/drivers/net/gt96100eth.h index 2a8331938b84..3b62a87c7d7f 100644 --- a/drivers/net/gt96100eth.h +++ b/drivers/net/gt96100eth.h @@ -331,7 +331,6 @@ struct gt96100_private { mib_counters_t mib; struct net_device_stats stats; - int io_size; int port_num; // 0 or 1 int chip_rev; u32 port_offset; @@ -340,7 +339,6 @@ struct gt96100_private { u32 last_psr; // last value of the port status register int options; /* User-settable misc. driver options. */ - int drv_flags; struct timer_list timer; spinlock_t lock; /* Serialise access to device */ }; diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c index b4c0d101a7d7..7f414815cc62 100644 --- a/drivers/net/tulip/winbond-840.c +++ b/drivers/net/tulip/winbond-840.c @@ -224,24 +224,21 @@ static const struct pci_device_id w840_pci_tbl[] = { }; MODULE_DEVICE_TABLE(pci, w840_pci_tbl); +enum { + netdev_res_size = 128, /* size of PCI BAR resource */ +}; + struct pci_id_info { const char *name; - struct match_info { - int pci, pci_mask, subsystem, subsystem_mask; - int revision, revision_mask; /* Only 8 bits. */ - } id; - int io_size; /* Needed for I/O region check or ioremap(). */ - int drv_flags; /* Driver use, intended as capability flags. */ + int drv_flags; /* Driver use, intended as capability flags. */ }; -static struct pci_id_info pci_id_tbl[] = { - {"Winbond W89c840", /* Sometime a Level-One switch card. */ - { 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 }, - 128, CanHaveMII | HasBrokenTx | FDXOnNoMII}, - {"Winbond W89c840", { 0x08401050, 0xffffffff, }, - 128, CanHaveMII | HasBrokenTx}, - {"Compex RL100-ATX", { 0x201111F6, 0xffffffff,}, - 128, CanHaveMII | HasBrokenTx}, - {NULL,}, /* 0 terminated list. */ + +static const struct pci_id_info pci_id_tbl[] __devinitdata = { + { /* Sometime a Level-One switch card. */ + "Winbond W89c840", CanHaveMII | HasBrokenTx | FDXOnNoMII}, + { "Winbond W89c840", CanHaveMII | HasBrokenTx}, + { "Compex RL100-ATX", CanHaveMII | HasBrokenTx}, + { } /* terminate list. */ }; /* This driver was written to use PCI memory space, however some x86 systems @@ -399,7 +396,7 @@ static int __devinit w840_probe1 (struct pci_dev *pdev, #ifdef USE_IO_OPS bar = 0; #endif - ioaddr = pci_iomap(pdev, bar, pci_id_tbl[chip_idx].io_size); + ioaddr = pci_iomap(pdev, bar, netdev_res_size); if (!ioaddr) goto err_out_free_res; diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 207340fe8df9..8459a18254a4 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -215,8 +215,11 @@ enum capability_flags { HasMACAddrBug=32, /* Only on early revs. */ DontUseEeprom=64, /* Don't read the MAC from the EEPROm. */ }; + /* The PCI I/O space extent. */ -#define YELLOWFIN_SIZE 0x100 +enum { + YELLOWFIN_SIZE = 0x100, +}; struct pci_id_info { const char *name; @@ -224,16 +227,14 @@ struct pci_id_info { int pci, pci_mask, subsystem, subsystem_mask; int revision, revision_mask; /* Only 8 bits. */ } id; - int io_size; /* Needed for I/O region check or ioremap(). */ int drv_flags; /* Driver use, intended as capability flags. */ }; static const struct pci_id_info pci_id_tbl[] = { {"Yellowfin G-NIC Gigabit Ethernet", { 0x07021000, 0xffffffff}, - YELLOWFIN_SIZE, FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug | DontUseEeprom}, {"Symbios SYM83C885", { 0x07011000, 0xffffffff}, - YELLOWFIN_SIZE, HasMII | DontUseEeprom }, + HasMII | DontUseEeprom }, { } }; -- cgit v1.2.3 From 8ab6f3f706f5cb91bc0793afc95c8809837ece05 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 08:56:23 -0400 Subject: [netdrvr] via-velocity: use netdev_priv() where appropriate Signed-off-by: Jeff Garzik --- drivers/net/via-velocity.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index ba2972ba3757..bbab3972ff86 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -341,7 +341,7 @@ static char __devinit *get_chip_name(enum chip_type chip_id) static void __devexit velocity_remove1(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); #ifdef CONFIG_PM unsigned long flags; @@ -708,7 +708,7 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); - vptr = dev->priv; + vptr = netdev_priv(dev); if (first) { @@ -1728,7 +1728,7 @@ static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_ static int velocity_open(struct net_device *dev) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); int ret; vptr->rx_buf_sz = (dev->mtu <= 1504 ? PKT_BUF_SZ : dev->mtu + 32); @@ -1785,7 +1785,7 @@ err_free_desc_rings: static int velocity_change_mtu(struct net_device *dev, int new_mtu) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); unsigned long flags; int oldmtu = dev->mtu; int ret = 0; @@ -1861,7 +1861,7 @@ static void velocity_shutdown(struct velocity_info *vptr) static int velocity_close(struct net_device *dev) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); netif_stop_queue(dev); velocity_shutdown(vptr); @@ -1894,7 +1894,7 @@ static int velocity_close(struct net_device *dev) static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); int qnum = 0; struct tx_desc *td_ptr; struct velocity_td_info *tdinfo; @@ -2049,7 +2049,7 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) static int velocity_intr(int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = dev_instance; - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); u32 isr_status; int max_count = 0; @@ -2104,7 +2104,7 @@ static int velocity_intr(int irq, void *dev_instance, struct pt_regs *regs) static void velocity_set_multi(struct net_device *dev) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); struct mac_regs __iomem * regs = vptr->mac_regs; u8 rx_mode; int i; @@ -2153,7 +2153,7 @@ static void velocity_set_multi(struct net_device *dev) static struct net_device_stats *velocity_get_stats(struct net_device *dev) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); /* If the hardware is down, don't touch MII */ if(!netif_running(dev)) @@ -2196,7 +2196,7 @@ static struct net_device_stats *velocity_get_stats(struct net_device *dev) static int velocity_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); int ret; /* If we are asked for information and the device is power @@ -2825,7 +2825,7 @@ static void enable_flow_control_ability(struct velocity_info *vptr) static int velocity_ethtool_up(struct net_device *dev) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); if (!netif_running(dev)) pci_set_power_state(vptr->pdev, PCI_D0); return 0; @@ -2841,14 +2841,14 @@ static int velocity_ethtool_up(struct net_device *dev) static void velocity_ethtool_down(struct net_device *dev) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); if (!netif_running(dev)) pci_set_power_state(vptr->pdev, PCI_D3hot); } static int velocity_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); struct mac_regs __iomem * regs = vptr->mac_regs; u32 status; status = check_connection_type(vptr->mac_regs); @@ -2873,7 +2873,7 @@ static int velocity_get_settings(struct net_device *dev, struct ethtool_cmd *cmd static int velocity_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); u32 curr_status; u32 new_status = 0; int ret = 0; @@ -2896,14 +2896,14 @@ static int velocity_set_settings(struct net_device *dev, struct ethtool_cmd *cmd static u32 velocity_get_link(struct net_device *dev) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); struct mac_regs __iomem * regs = vptr->mac_regs; return BYTE_REG_BITS_IS_ON(PHYSR0_LINKGD, ®s->PHYSR0) ? 0 : 1; } static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); strcpy(info->driver, VELOCITY_NAME); strcpy(info->version, VELOCITY_VERSION); strcpy(info->bus_info, pci_name(vptr->pdev)); @@ -2911,7 +2911,7 @@ static void velocity_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP; wol->wolopts |= WAKE_MAGIC; /* @@ -2927,7 +2927,7 @@ static void velocity_ethtool_get_wol(struct net_device *dev, struct ethtool_woli static int velocity_ethtool_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); if (!(wol->wolopts & (WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_ARP))) return -EFAULT; @@ -2992,7 +2992,7 @@ static struct ethtool_ops velocity_ethtool_ops = { static int velocity_mii_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct velocity_info *vptr = dev->priv; + struct velocity_info *vptr = netdev_priv(dev); struct mac_regs __iomem * regs = vptr->mac_regs; unsigned long flags; struct mii_ioctl_data *miidata = if_mii(ifr); -- cgit v1.2.3 From 46009c8bcd5d7c9fcbfc17d0455a471bea5be4d4 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 09:12:38 -0400 Subject: [netdrvr] minor cleanups in Becker-derived drivers - fealnx: convert #define to enum - fealnx, sundance: mark chip info table __devinitdata - fealnx: use dev_printk() during probe - fealnx: formatting cleanups - starfire: remove obsolete comment - sundance, via-rhine: add some whitespace where useful, in tables - sundance: prefer "{ }" table terminator - via-rhine: mark PCI probe table const Signed-off-by: Jeff Garzik --- drivers/net/fealnx.c | 13 ++++++++----- drivers/net/starfire.c | 1 - drivers/net/sundance.c | 22 +++++++++++----------- drivers/net/via-rhine.c | 11 +++++------ 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index 0e6a495549fa..aaf136658765 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -124,7 +124,9 @@ MODULE_PARM_DESC(multicast_filter_limit, "fealnx maximum number of filtered mult MODULE_PARM_DESC(options, "fealnx: Bits 0-3: media type, bit 17: full duplex"); MODULE_PARM_DESC(full_duplex, "fealnx full duplex setting(s) (1)"); -#define MIN_REGION_SIZE 136 +enum { + MIN_REGION_SIZE = 136, +}; /* A chip capabilities table, matching the entries in pci_tbl[] above. */ enum chip_capability_flags { @@ -149,7 +151,7 @@ struct chip_info { int flags; }; -static const struct chip_info skel_netdrv_tbl[] = { +static const struct chip_info skel_netdrv_tbl[] __devinitdata = { { "100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, { "100/10M Ethernet PCI Adapter", HAS_CHIP_XCVR }, { "1000/100/10M Ethernet PCI Adapter", HAS_MII_XCVR }, @@ -503,13 +505,14 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, len = pci_resource_len(pdev, bar); if (len < MIN_REGION_SIZE) { - printk(KERN_ERR "%s: region size %ld too small, aborting\n", - boardname, len); + dev_printk(KERN_ERR, &pdev->dev, + "region size %ld too small, aborting\n", len); return -ENODEV; } i = pci_request_regions(pdev, boardname); - if (i) return i; + if (i) + return i; irq = pdev->irq; diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 24d55b4591b3..c0a62b00ffc8 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -730,7 +730,6 @@ static int __devinit starfire_init_one(struct pci_dev *pdev, goto err_out_free_netdev; } - /* ioremap is borken in Linux-2.2.x/sparc64 */ base = ioremap(ioaddr, io_size); if (!base) { printk(KERN_ERR DRV_NAME " %d: cannot remap %#x @ %#lx, aborting\n", diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index de6645196657..ac17377b3e9f 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -204,15 +204,15 @@ IVc. Errata #define USE_IO_OPS 1 #endif -static struct pci_device_id sundance_pci_tbl[] = { - {0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0}, - {0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1}, - {0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2}, - {0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3}, - {0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, - {0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5}, - {0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6}, - {0,} +static const struct pci_device_id sundance_pci_tbl[] = { + { 0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0 }, + { 0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1 }, + { 0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2 }, + { 0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3 }, + { 0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 }, + { 0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5 }, + { 0x13F0, 0x0200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 6 }, + { } }; MODULE_DEVICE_TABLE(pci, sundance_pci_tbl); @@ -223,7 +223,7 @@ enum { struct pci_id_info { const char *name; }; -static const struct pci_id_info pci_id_tbl[] = { +static const struct pci_id_info pci_id_tbl[] __devinitdata = { {"D-Link DFE-550TX FAST Ethernet Adapter"}, {"D-Link DFE-550FX 100Mbps Fiber-optics Adapter"}, {"D-Link DFE-580TX 4 port Server Adapter"}, @@ -231,7 +231,7 @@ static const struct pci_id_info pci_id_tbl[] = { {"D-Link DL10050-based FAST Ethernet Adapter"}, {"Sundance Technology Alta"}, {"IC Plus Corporation IP100A FAST Ethernet Adapter"}, - {NULL,}, /* 0 terminated list. */ + { } /* terminate list. */ }; /* This driver was written to use PCI memory space, however x86-oriented diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index e48e76ce73a0..d3d0ec970318 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -252,12 +252,11 @@ enum rhine_quirks { /* Beware of PCI posted writes */ #define IOSYNC do { ioread8(ioaddr + StationAddr); } while (0) -static struct pci_device_id rhine_pci_tbl[] = -{ - {0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, /* VT86C100A */ - {0x1106, 0x3065, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, /* VT6102 */ - {0x1106, 0x3106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, /* 6105{,L,LOM} */ - {0x1106, 0x3053, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, /* VT6105M */ +static const struct pci_device_id rhine_pci_tbl[] = { + { 0x1106, 0x3043, PCI_ANY_ID, PCI_ANY_ID, }, /* VT86C100A */ + { 0x1106, 0x3065, PCI_ANY_ID, PCI_ANY_ID, }, /* VT6102 */ + { 0x1106, 0x3106, PCI_ANY_ID, PCI_ANY_ID, }, /* 6105{,L,LOM} */ + { 0x1106, 0x3053, PCI_ANY_ID, PCI_ANY_ID, }, /* VT6105M */ { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, rhine_pci_tbl); -- cgit v1.2.3 From e54f48933f414fa447c26d16524a4c9a8e2facc6 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 09:20:08 -0400 Subject: [netdrvr] via-velocity: misc. cleanups - const-ify pci_device_id table - clean up pci_device_id table with PCI_DEVICE() - don't store internal pointer in pci_device_id table, use pci_device_id::driver_data as an integer index - use dev_printk() for messages where eth%d prefix is unavailable - formatting fixes Signed-off-by: Jeff Garzik --- drivers/net/via-velocity.c | 48 +++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index bbab3972ff86..019dcf2457b4 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -304,10 +304,9 @@ static struct velocity_info_tbl chip_info_table[] = { * device driver. Used for hotplug autoloading. */ -static struct pci_device_id velocity_id_table[] __devinitdata = { - {PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) chip_info_table}, - {0, } +static const struct pci_device_id velocity_id_table[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_612X) }, + { } }; MODULE_DEVICE_TABLE(pci, velocity_id_table); @@ -686,21 +685,23 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi static int first = 1; struct net_device *dev; int i; - struct velocity_info_tbl *info = (struct velocity_info_tbl *) ent->driver_data; + struct velocity_info_tbl *info = &chip_info_table[ent->driver_data]; struct velocity_info *vptr; struct mac_regs __iomem * regs; int ret = -ENOMEM; + /* FIXME: this driver, like almost all other ethernet drivers, + * can support more than MAX_UNITS. + */ if (velocity_nics >= MAX_UNITS) { - printk(KERN_NOTICE VELOCITY_NAME ": already found %d NICs.\n", - velocity_nics); + dev_printk(KERN_NOTICE, &pdev->dev, "already found %d NICs.\n", + velocity_nics); return -ENODEV; } dev = alloc_etherdev(sizeof(struct velocity_info)); - - if (dev == NULL) { - printk(KERN_ERR VELOCITY_NAME ": allocate net device failed.\n"); + if (!dev) { + dev_printk(KERN_ERR, &pdev->dev, "allocate net device failed.\n"); goto out; } @@ -731,13 +732,13 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi ret = velocity_get_pci_info(vptr, pdev); if (ret < 0) { - printk(KERN_ERR VELOCITY_NAME ": Failed to find PCI device.\n"); + /* error message already printed */ goto err_disable; } ret = pci_request_regions(pdev, VELOCITY_NAME); if (ret < 0) { - printk(KERN_ERR VELOCITY_NAME ": Failed to find PCI device.\n"); + dev_printk(KERN_ERR, &pdev->dev, "No PCI resources.\n"); goto err_disable; } @@ -883,8 +884,7 @@ static void __devinit velocity_init_info(struct pci_dev *pdev, struct velocity_i static int __devinit velocity_get_pci_info(struct velocity_info *vptr, struct pci_dev *pdev) { - - if(pci_read_config_byte(pdev, PCI_REVISION_ID, &vptr->rev_id) < 0) + if (pci_read_config_byte(pdev, PCI_REVISION_ID, &vptr->rev_id) < 0) return -EIO; pci_set_master(pdev); @@ -892,24 +892,20 @@ static int __devinit velocity_get_pci_info(struct velocity_info *vptr, struct pc vptr->ioaddr = pci_resource_start(pdev, 0); vptr->memaddr = pci_resource_start(pdev, 1); - if(!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) - { - printk(KERN_ERR "%s: region #0 is not an I/O resource, aborting.\n", - pci_name(pdev)); + if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) { + dev_printk(KERN_ERR, &pdev->dev, + "region #0 is not an I/O resource, aborting.\n"); return -EINVAL; } - if((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) - { - printk(KERN_ERR "%s: region #1 is an I/O resource, aborting.\n", - pci_name(pdev)); + if ((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) { + dev_printk(KERN_ERR, &pdev->dev, + "region #1 is an I/O resource, aborting.\n"); return -EINVAL; } - if(pci_resource_len(pdev, 1) < 256) - { - printk(KERN_ERR "%s: region #1 is too small.\n", - pci_name(pdev)); + if (pci_resource_len(pdev, 1) < 256) { + dev_printk(KERN_ERR, &pdev->dev, "region #1 is too small.\n"); return -EINVAL; } vptr->pdev = pdev; -- cgit v1.2.3 From cabb7667dc150320ccd9d6f64fbd7a34766bc775 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 09:25:28 -0400 Subject: [netdrvr] via-velocity: remove io_size struct member, it is invariant Replace io_size struct members with VELOCITY_IO_SIZE constant. Also, constify chip_info_table[]. Signed-off-by: Jeff Garzik --- drivers/net/via-velocity.c | 20 +++++++++++--------- drivers/net/via-velocity.h | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 019dcf2457b4..7e3b2a318146 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -229,7 +229,8 @@ static int rx_copybreak = 200; module_param(rx_copybreak, int, 0644); MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy-only-tiny-frames"); -static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, struct velocity_info_tbl *info); +static void velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, + const struct velocity_info_tbl *info); static int velocity_get_pci_info(struct velocity_info *, struct pci_dev *pdev); static void velocity_print_info(struct velocity_info *vptr); static int velocity_open(struct net_device *dev); @@ -294,9 +295,9 @@ static void velocity_unregister_notifier(void) * Internal board variants. At the moment we have only one */ -static struct velocity_info_tbl chip_info_table[] = { - {CHIP_TYPE_VT6110, "VIA Networking Velocity Family Gigabit Ethernet Adapter", 256, 1, 0x00FFFFFFUL}, - {0, NULL} +static const struct velocity_info_tbl chip_info_table[] __devinitdata = { + {CHIP_TYPE_VT6110, "VIA Networking Velocity Family Gigabit Ethernet Adapter", 1, 0x00FFFFFFUL}, + { } }; /* @@ -685,7 +686,7 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi static int first = 1; struct net_device *dev; int i; - struct velocity_info_tbl *info = &chip_info_table[ent->driver_data]; + const struct velocity_info_tbl *info = &chip_info_table[ent->driver_data]; struct velocity_info *vptr; struct mac_regs __iomem * regs; int ret = -ENOMEM; @@ -742,7 +743,7 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi goto err_disable; } - regs = ioremap(vptr->memaddr, vptr->io_size); + regs = ioremap(vptr->memaddr, VELOCITY_IO_SIZE); if (regs == NULL) { ret = -EIO; goto err_release_res; @@ -860,13 +861,14 @@ static void __devinit velocity_print_info(struct velocity_info *vptr) * discovered. */ -static void __devinit velocity_init_info(struct pci_dev *pdev, struct velocity_info *vptr, struct velocity_info_tbl *info) +static void __devinit velocity_init_info(struct pci_dev *pdev, + struct velocity_info *vptr, + const struct velocity_info_tbl *info) { memset(vptr, 0, sizeof(struct velocity_info)); vptr->pdev = pdev; vptr->chip_id = info->chip_id; - vptr->io_size = info->io_size; vptr->num_txq = info->txqueue; vptr->multicast_limit = MCAM_SIZE; spin_lock_init(&vptr->lock); @@ -904,7 +906,7 @@ static int __devinit velocity_get_pci_info(struct velocity_info *vptr, struct pc return -EINVAL; } - if (pci_resource_len(pdev, 1) < 256) { + if (pci_resource_len(pdev, 1) < VELOCITY_IO_SIZE) { dev_printk(KERN_ERR, &pdev->dev, "region #1 is too small.\n"); return -EINVAL; } diff --git a/drivers/net/via-velocity.h b/drivers/net/via-velocity.h index f1b2640ebdc6..496c3d597444 100644 --- a/drivers/net/via-velocity.h +++ b/drivers/net/via-velocity.h @@ -31,6 +31,8 @@ #define VELOCITY_FULL_DRV_NAM "VIA Networking Velocity Family Gigabit Ethernet Adapter Driver" #define VELOCITY_VERSION "1.13" +#define VELOCITY_IO_SIZE 256 + #define PKT_BUF_SZ 1540 #define MAX_UNITS 8 @@ -1191,7 +1193,6 @@ enum chip_type { struct velocity_info_tbl { enum chip_type chip_id; char *name; - int io_size; int txqueue; u32 flags; }; @@ -1751,7 +1752,6 @@ struct velocity_info { struct mac_regs __iomem * mac_regs; unsigned long memaddr; unsigned long ioaddr; - u32 io_size; u8 rev_id; -- cgit v1.2.3 From 2e8a538d865de0eb9813c8a0f2284e920299c0cc Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 10:47:51 -0400 Subject: [netdrvr] Use dev_printk() when ethernet interface isn't available For messages prior to register_netdev(), prefer dev_printk() because that prints out both our driver name and our [PCI | whatever] bus id. Updates: 8139{cp,too}, b44, bnx2, cassini, {eepro,epic}100, fealnx, hamachi, ne2k-pci, ns83820, pci-skeleton, r8169. Signed-off-by: Jeff Garzik --- drivers/net/8139cp.c | 38 ++++++++++++++++++++++---------------- drivers/net/8139too.c | 41 +++++++++++++++++++++++++---------------- drivers/net/b44.c | 28 +++++++++++++++------------- drivers/net/bnx2.c | 37 ++++++++++++++++++++++--------------- drivers/net/cassini.c | 20 ++++++++++---------- drivers/net/eepro100.c | 8 +++++--- drivers/net/epic100.c | 23 ++++++++++++----------- drivers/net/fealnx.c | 17 +++++++++-------- drivers/net/hamachi.c | 3 ++- drivers/net/ne2k-pci.c | 12 ++++++++---- drivers/net/ns83820.c | 14 +++++++++----- drivers/net/pci-skeleton.c | 24 +++++++++++++++--------- drivers/net/r8169.c | 43 +++++++++++++++++++------------------------ 13 files changed, 173 insertions(+), 135 deletions(-) diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 1959654cbec8..93b7c904bba9 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -1836,9 +1836,11 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (pdev->vendor == PCI_VENDOR_ID_REALTEK && pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev < 0x20) { - printk(KERN_ERR PFX "pci dev %s (id %04x:%04x rev %02x) is not an 8139C+ compatible chip\n", - pci_name(pdev), pdev->vendor, pdev->device, pci_rev); - printk(KERN_ERR PFX "Try the \"8139too\" driver instead.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "This (id %04x:%04x rev %02x) is not an 8139C+ compatible chip\n", + pdev->vendor, pdev->device, pci_rev); + dev_printk(KERN_ERR, &pdev->dev, + "Try the \"8139too\" driver instead.\n"); return -ENODEV; } @@ -1876,14 +1878,14 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pciaddr = pci_resource_start(pdev, 1); if (!pciaddr) { rc = -EIO; - printk(KERN_ERR PFX "no MMIO resource for pci dev %s\n", - pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, "no MMIO resource\n"); goto err_out_res; } if (pci_resource_len(pdev, 1) < CP_REGS_SIZE) { rc = -EIO; - printk(KERN_ERR PFX "MMIO resource (%llx) too small on pci dev %s\n", - (unsigned long long)pci_resource_len(pdev, 1), pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, + "MMIO resource (%llx) too small\n", + (unsigned long long)pci_resource_len(pdev, 1)); goto err_out_res; } @@ -1897,14 +1899,15 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - printk(KERN_ERR PFX "No usable DMA configuration, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "No usable DMA configuration, aborting.\n"); goto err_out_res; } rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - printk(KERN_ERR PFX "No usable consistent DMA configuration, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "No usable consistent DMA configuration, " + "aborting.\n"); goto err_out_res; } } @@ -1915,9 +1918,10 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) regs = ioremap(pciaddr, CP_REGS_SIZE); if (!regs) { rc = -EIO; - printk(KERN_ERR PFX "Cannot map PCI MMIO (%llx@%llx) on pci dev %s\n", - (unsigned long long)pci_resource_len(pdev, 1), - (unsigned long long)pciaddr, pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot map PCI MMIO (%llx@%llx)\n", + (unsigned long long)pci_resource_len(pdev, 1), + (unsigned long long)pciaddr); goto err_out_res; } dev->base_addr = (unsigned long) regs; @@ -1986,7 +1990,8 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) /* enable busmastering and memory-write-invalidate */ pci_set_master(pdev); - if (cp->wol_enabled) cp_set_d3_state (cp); + if (cp->wol_enabled) + cp_set_d3_state (cp); return 0; @@ -2011,7 +2016,8 @@ static void cp_remove_one (struct pci_dev *pdev) BUG_ON(!dev); unregister_netdev(dev); iounmap(cp->regs); - if (cp->wol_enabled) pci_set_power_state (pdev, PCI_D0); + if (cp->wol_enabled) + pci_set_power_state (pdev, PCI_D0); pci_release_regions(pdev); pci_clear_mwi(pdev); pci_disable_device(pdev); diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 717506b2b13a..5e9ef52a5ca9 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -768,7 +768,8 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* dev and priv zeroed in alloc_etherdev */ dev = alloc_etherdev (sizeof (*tp)); if (dev == NULL) { - printk (KERN_ERR PFX "%s: Unable to alloc new net device\n", pci_name(pdev)); + dev_printk (KERN_ERR, &pdev->dev, + "Unable to alloc new net device\n"); return -ENOMEM; } SET_MODULE_OWNER(dev); @@ -800,31 +801,35 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, #ifdef USE_IO_OPS /* make sure PCI base addr 0 is PIO */ if (!(pio_flags & IORESOURCE_IO)) { - printk (KERN_ERR PFX "%s: region #0 not a PIO resource, aborting\n", pci_name(pdev)); + dev_printk (KERN_ERR, &pdev->dev, + "region #0 not a PIO resource, aborting\n"); rc = -ENODEV; goto err_out; } /* check for weird/broken PCI region reporting */ if (pio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI I/O region size(s), aborting\n", pci_name(pdev)); + dev_printk (KERN_ERR, &pdev->dev, + "Invalid PCI I/O region size(s), aborting\n"); rc = -ENODEV; goto err_out; } #else /* make sure PCI base addr 1 is MMIO */ if (!(mmio_flags & IORESOURCE_MEM)) { - printk (KERN_ERR PFX "%s: region #1 not an MMIO resource, aborting\n", pci_name(pdev)); + dev_printk (KERN_ERR, &pdev->dev, + "region #1 not an MMIO resource, aborting\n"); rc = -ENODEV; goto err_out; } if (mmio_len < RTL_MIN_IO_SIZE) { - printk (KERN_ERR PFX "%s: Invalid PCI mem region size(s), aborting\n", pci_name(pdev)); + dev_printk (KERN_ERR, &pdev->dev, + "Invalid PCI mem region size(s), aborting\n"); rc = -ENODEV; goto err_out; } #endif - rc = pci_request_regions (pdev, "8139too"); + rc = pci_request_regions (pdev, DRV_NAME); if (rc) goto err_out; disable_dev_on_err = 1; @@ -835,7 +840,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, #ifdef USE_IO_OPS ioaddr = ioport_map(pio_start, pio_len); if (!ioaddr) { - printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev)); + dev_printk (KERN_ERR, &pdev->dev, "cannot map PIO, aborting\n"); rc = -EIO; goto err_out; } @@ -846,7 +851,8 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* ioremap MMIO region */ ioaddr = pci_iomap(pdev, 1, 0); if (ioaddr == NULL) { - printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev)); + dev_printk (KERN_ERR, &pdev->dev, + "cannot remap MMIO, aborting\n"); rc = -EIO; goto err_out; } @@ -860,8 +866,8 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* check for missing/broken hardware */ if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { - printk (KERN_ERR PFX "%s: Chip not responding, ignoring board\n", - pci_name(pdev)); + dev_printk (KERN_ERR, &pdev->dev, + "Chip not responding, ignoring board\n"); rc = -EIO; goto err_out; } @@ -875,9 +881,10 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, } /* if unknown chip, assume array element #0, original RTL-8139 in this case */ - printk (KERN_DEBUG PFX "%s: unknown chip version, assuming RTL-8139\n", - pci_name(pdev)); - printk (KERN_DEBUG PFX "%s: TxConfig = 0x%lx\n", pci_name(pdev), RTL_R32 (TxConfig)); + dev_printk (KERN_DEBUG, &pdev->dev, + "unknown chip version, assuming RTL-8139\n"); + dev_printk (KERN_DEBUG, &pdev->dev, + "TxConfig = 0x%lx\n", RTL_R32 (TxConfig)); tp->chipset = 0; match: @@ -954,9 +961,11 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, if (pdev->vendor == PCI_VENDOR_ID_REALTEK && pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { - printk(KERN_INFO PFX "pci dev %s (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", - pci_name(pdev), pdev->vendor, pdev->device, pci_rev); - printk(KERN_INFO PFX "Use the \"8139cp\" driver for improved performance and stability.\n"); + dev_printk(KERN_INFO, &pdev->dev, + "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", + pdev->vendor, pdev->device, pci_rev); + dev_printk(KERN_INFO, &pdev->dev, + "Use the \"8139cp\" driver for improved performance and stability.\n"); } i = rtl8139_init_board (pdev, &dev); diff --git a/drivers/net/b44.c b/drivers/net/b44.c index cd98d31dee8c..5815e3dc1251 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -2120,13 +2120,14 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = pci_enable_device(pdev); if (err) { - printk(KERN_ERR PFX "Cannot enable PCI device, " + dev_printk(KERN_ERR, &pdev->dev, "Cannot enable PCI device, " "aborting.\n"); return err; } if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - printk(KERN_ERR PFX "Cannot find proper PCI device " + dev_printk(KERN_ERR, &pdev->dev, + "Cannot find proper PCI device " "base address, aborting.\n"); err = -ENODEV; goto err_out_disable_pdev; @@ -2134,8 +2135,8 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = pci_request_regions(pdev, DRV_MODULE_NAME); if (err) { - printk(KERN_ERR PFX "Cannot obtain PCI resources, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot obtain PCI resources, aborting.\n"); goto err_out_disable_pdev; } @@ -2143,15 +2144,15 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = pci_set_dma_mask(pdev, (u64) B44_DMA_MASK); if (err) { - printk(KERN_ERR PFX "No usable DMA configuration, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "No usable DMA configuration, aborting.\n"); goto err_out_free_res; } err = pci_set_consistent_dma_mask(pdev, (u64) B44_DMA_MASK); if (err) { - printk(KERN_ERR PFX "No usable DMA configuration, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "No usable DMA configuration, aborting.\n"); goto err_out_free_res; } @@ -2160,7 +2161,8 @@ static int __devinit b44_init_one(struct pci_dev *pdev, dev = alloc_etherdev(sizeof(*bp)); if (!dev) { - printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Etherdev alloc failed, aborting.\n"); err = -ENOMEM; goto err_out_free_res; } @@ -2181,7 +2183,7 @@ static int __devinit b44_init_one(struct pci_dev *pdev, bp->regs = ioremap(b44reg_base, b44reg_len); if (bp->regs == 0UL) { - printk(KERN_ERR PFX "Cannot map device registers, " + dev_printk(KERN_ERR, &pdev->dev, "Cannot map device registers, " "aborting.\n"); err = -ENOMEM; goto err_out_free_dev; @@ -2212,8 +2214,8 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = b44_get_invariants(bp); if (err) { - printk(KERN_ERR PFX "Problem fetching invariants of chip, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Problem fetching invariants of chip, aborting.\n"); goto err_out_iounmap; } @@ -2233,7 +2235,7 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = register_netdev(dev); if (err) { - printk(KERN_ERR PFX "Cannot register net device, " + dev_printk(KERN_ERR, &pdev->dev, "Cannot register net device, " "aborting.\n"); goto err_out_iounmap; } diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 4f4db5ae503b..5502b9aeda53 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -5575,20 +5575,22 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) /* enable device (incl. PCI PM wakeup), and bus-mastering */ rc = pci_enable_device(pdev); if (rc) { - printk(KERN_ERR PFX "Cannot enable PCI device, aborting."); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot enable PCI device, aborting."); goto err_out; } if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - printk(KERN_ERR PFX "Cannot find PCI device base address, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot find PCI device base address, aborting.\n"); rc = -ENODEV; goto err_out_disable; } rc = pci_request_regions(pdev, DRV_MODULE_NAME); if (rc) { - printk(KERN_ERR PFX "Cannot obtain PCI resources, aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot obtain PCI resources, aborting.\n"); goto err_out_disable; } @@ -5596,15 +5598,16 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); if (bp->pm_cap == 0) { - printk(KERN_ERR PFX "Cannot find power management capability, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot find power management capability, aborting.\n"); rc = -EIO; goto err_out_release; } bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX); if (bp->pcix_cap == 0) { - printk(KERN_ERR PFX "Cannot find PCIX capability, aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot find PCIX capability, aborting.\n"); rc = -EIO; goto err_out_release; } @@ -5612,14 +5615,15 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) { bp->flags |= USING_DAC_FLAG; if (pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK) != 0) { - printk(KERN_ERR PFX "pci_set_consistent_dma_mask " - "failed, aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "pci_set_consistent_dma_mask failed, aborting.\n"); rc = -EIO; goto err_out_release; } } else if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) { - printk(KERN_ERR PFX "System does not support DMA, aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "System does not support DMA, aborting.\n"); rc = -EIO; goto err_out_release; } @@ -5639,7 +5643,8 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->regview = ioremap_nocache(dev->base_addr, mem_len); if (!bp->regview) { - printk(KERN_ERR PFX "Cannot map register space, aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot map register space, aborting.\n"); rc = -ENOMEM; goto err_out_release; } @@ -5711,8 +5716,8 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) else if ((CHIP_ID(bp) == CHIP_ID_5706_A1) && !(bp->flags & PCIX_FLAG)) { - printk(KERN_ERR PFX "5706 A1 can only be used in a PCIX bus, " - "aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "5706 A1 can only be used in a PCIX bus, aborting.\n"); goto err_out_unmap; } @@ -5733,7 +5738,8 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) if ((reg & BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK) != BNX2_DEV_INFO_SIGNATURE_MAGIC) { - printk(KERN_ERR PFX "Firmware not running, aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Firmware not running, aborting.\n"); rc = -ENODEV; goto err_out_unmap; } @@ -5895,7 +5901,8 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #endif if ((rc = register_netdev(dev))) { - printk(KERN_ERR PFX "Cannot register net device\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Cannot register net device\n"); if (bp->regview) iounmap(bp->regview); pci_release_regions(pdev); diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index d33130f64700..428e2067738c 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -4887,13 +4887,13 @@ static int __devinit cas_init_one(struct pci_dev *pdev, err = pci_enable_device(pdev); if (err) { - printk(KERN_ERR PFX "Cannot enable PCI device, " + dev_printk(KERN_ERR, &pdev->dev, "Cannot enable PCI device, " "aborting.\n"); return err; } if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - printk(KERN_ERR PFX "Cannot find proper PCI device " + dev_printk(KERN_ERR, &pdev->dev, "Cannot find proper PCI device " "base address, aborting.\n"); err = -ENODEV; goto err_out_disable_pdev; @@ -4901,7 +4901,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, dev = alloc_etherdev(sizeof(*cp)); if (!dev) { - printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); + dev_printk(KERN_ERR, &pdev->dev, "Etherdev alloc failed, aborting.\n"); err = -ENOMEM; goto err_out_disable_pdev; } @@ -4910,7 +4910,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, err = pci_request_regions(pdev, dev->name); if (err) { - printk(KERN_ERR PFX "Cannot obtain PCI resources, " + dev_printk(KERN_ERR, &pdev->dev, "Cannot obtain PCI resources, " "aborting.\n"); goto err_out_free_netdev; } @@ -4941,7 +4941,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, if (pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cas_cacheline_size)) { - printk(KERN_ERR PFX "Could not set PCI cache " + dev_printk(KERN_ERR, &pdev->dev, "Could not set PCI cache " "line size\n"); goto err_write_cacheline; } @@ -4955,7 +4955,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (err < 0) { - printk(KERN_ERR PFX "Unable to obtain 64-bit DMA " + dev_printk(KERN_ERR, &pdev->dev, "Unable to obtain 64-bit DMA " "for consistent allocations\n"); goto err_out_free_res; } @@ -4963,7 +4963,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, } else { err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (err) { - printk(KERN_ERR PFX "No usable DMA configuration, " + dev_printk(KERN_ERR, &pdev->dev, "No usable DMA configuration, " "aborting.\n"); goto err_out_free_res; } @@ -5023,7 +5023,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, /* give us access to cassini registers */ cp->regs = pci_iomap(pdev, 0, casreg_len); if (cp->regs == 0UL) { - printk(KERN_ERR PFX "Cannot map device registers, " + dev_printk(KERN_ERR, &pdev->dev, "Cannot map device registers, " "aborting.\n"); goto err_out_free_res; } @@ -5040,7 +5040,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, pci_alloc_consistent(pdev, sizeof(struct cas_init_block), &cp->block_dvma); if (!cp->init_block) { - printk(KERN_ERR PFX "Cannot allocate init block, " + dev_printk(KERN_ERR, &pdev->dev, "Cannot allocate init block, " "aborting.\n"); goto err_out_iounmap; } @@ -5085,7 +5085,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, dev->features |= NETIF_F_HIGHDMA; if (register_netdev(dev)) { - printk(KERN_ERR PFX "Cannot register net device, " + dev_printk(KERN_ERR, &pdev->dev, "Cannot register net device, " "aborting.\n"); goto err_out_free_consistent; } diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index 2ad327542927..bccb12e03c2d 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -555,12 +555,14 @@ static int __devinit eepro100_init_one (struct pci_dev *pdev, if (!request_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1), "eepro100")) { - printk (KERN_ERR "eepro100: cannot reserve I/O ports\n"); + dev_printk (KERN_ERR, &pdev->dev, + "eepro100: cannot reserve I/O ports\n"); goto err_out_none; } if (!request_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0), "eepro100")) { - printk (KERN_ERR "eepro100: cannot reserve MMIO region\n"); + dev_printk (KERN_ERR, &pdev->dev, + "eepro100: cannot reserve MMIO region\n"); goto err_out_free_pio_region; } @@ -573,7 +575,7 @@ static int __devinit eepro100_init_one (struct pci_dev *pdev, ioaddr = pci_iomap(pdev, pci_bar, 0); if (!ioaddr) { - printk (KERN_ERR "eepro100: cannot remap IO\n"); + dev_printk (KERN_ERR, &pdev->dev, "eepro100: cannot remap IO\n"); goto err_out_free_mmio_region; } diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 05c9a26a74e2..6dd0e9d159de 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -422,8 +422,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, ((u16 *)dev->dev_addr)[i] = le16_to_cpu(inw(ioaddr + LAN0 + i*4)); if (debug > 2) { - printk(KERN_DEBUG DRV_NAME "(%s): EEPROM contents\n", - pci_name(pdev)); + dev_printk(KERN_DEBUG, &pdev->dev, "EEPROM contents:\n"); for (i = 0; i < 64; i++) printk(" %4.4x%s", read_eeprom(ioaddr, i), i % 16 == 15 ? "\n" : ""); @@ -445,21 +444,23 @@ static int __devinit epic_init_one (struct pci_dev *pdev, int mii_status = mdio_read(dev, phy, MII_BMSR); if (mii_status != 0xffff && mii_status != 0x0000) { ep->phys[phy_idx++] = phy; - printk(KERN_INFO DRV_NAME "(%s): MII transceiver #%d control " - "%4.4x status %4.4x.\n", - pci_name(pdev), phy, mdio_read(dev, phy, 0), mii_status); + dev_printk(KERN_INFO, &pdev->dev, + "MII transceiver #%d control " + "%4.4x status %4.4x.\n", + phy, mdio_read(dev, phy, 0), mii_status); } } ep->mii_phy_cnt = phy_idx; if (phy_idx != 0) { phy = ep->phys[0]; ep->mii.advertising = mdio_read(dev, phy, MII_ADVERTISE); - printk(KERN_INFO DRV_NAME "(%s): Autonegotiation advertising %4.4x link " + dev_printk(KERN_INFO, &pdev->dev, + "Autonegotiation advertising %4.4x link " "partner %4.4x.\n", - pci_name(pdev), ep->mii.advertising, mdio_read(dev, phy, 5)); + ep->mii.advertising, mdio_read(dev, phy, 5)); } else if ( ! (ep->chip_flags & NO_MII)) { - printk(KERN_WARNING DRV_NAME "(%s): ***WARNING***: No MII transceiver found!\n", - pci_name(pdev)); + dev_printk(KERN_WARNING, &pdev->dev, + "***WARNING***: No MII transceiver found!\n"); /* Use the known PHY address of the EPII. */ ep->phys[0] = 3; } @@ -474,8 +475,8 @@ static int __devinit epic_init_one (struct pci_dev *pdev, /* The lower four bits are the media type. */ if (duplex) { ep->mii.force_media = ep->mii.full_duplex = 1; - printk(KERN_INFO DRV_NAME "(%s): Forced full duplex operation requested.\n", - pci_name(pdev)); + dev_printk(KERN_INFO, &pdev->dev, + "Forced full duplex operation requested.\n"); } dev->if_port = ep->default_port = option; diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index aaf136658765..d26140ad36e9 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -578,9 +578,9 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, if (mii_status != 0xffff && mii_status != 0x0000) { np->phys[phy_idx++] = phy; - printk(KERN_INFO - "%s: MII PHY found at address %d, status " - "0x%4.4x.\n", dev->name, phy, mii_status); + dev_printk(KERN_INFO, &pdev->dev, + "MII PHY found at address %d, status " + "0x%4.4x.\n", phy, mii_status); /* get phy type */ { unsigned int data; @@ -603,10 +603,10 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, } np->mii_cnt = phy_idx; - if (phy_idx == 0) { - printk(KERN_WARNING "%s: MII PHY not found -- this device may " - "not operate correctly.\n", dev->name); - } + if (phy_idx == 0) + dev_printk(KERN_WARNING, &pdev->dev, + "MII PHY not found -- this device may " + "not operate correctly.\n"); } else { np->phys[0] = 32; /* 89/6/23 add, (begin) */ @@ -632,7 +632,8 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, np->mii.full_duplex = full_duplex[card_idx]; if (np->mii.full_duplex) { - printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name); + dev_printk(KERN_INFO, &pdev->dev, + "Media type forced to Full Duplex.\n"); /* 89/6/13 add, (begin) */ // if (np->PHYType==MarvellPHY) if ((np->PHYType == MarvellPHY) || (np->PHYType == LevelOnePHY)) { diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index 1b212a56933c..409c6aab0411 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -601,7 +601,8 @@ static int __devinit hamachi_init_one (struct pci_dev *pdev, pci_set_master(pdev); i = pci_request_regions(pdev, DRV_NAME); - if (i) return i; + if (i) + return i; irq = pdev->irq; ioaddr = ioremap(base, 0x400); diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c index fa50eb889408..4f226064d55e 100644 --- a/drivers/net/ne2k-pci.c +++ b/drivers/net/ne2k-pci.c @@ -231,12 +231,14 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, irq = pdev->irq; if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) { - printk (KERN_ERR PFX "no I/O resource at PCI BAR #0\n"); + dev_printk (KERN_ERR, &pdev->dev, + "no I/O resource at PCI BAR #0\n"); return -ENODEV; } if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) { - printk (KERN_ERR PFX "I/O resource 0x%x @ 0x%lx busy\n", + dev_printk (KERN_ERR, &pdev->dev, + "I/O resource 0x%x @ 0x%lx busy\n", NE_IO_EXTENT, ioaddr); return -EBUSY; } @@ -263,7 +265,8 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, /* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */ dev = alloc_ei_netdev(); if (!dev) { - printk (KERN_ERR PFX "cannot allocate ethernet device\n"); + dev_printk (KERN_ERR, &pdev->dev, + "cannot allocate ethernet device\n"); goto err_out_free_res; } SET_MODULE_OWNER(dev); @@ -281,7 +284,8 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0) /* Limit wait: '2' avoids jiffy roll-over. */ if (jiffies - reset_start_time > 2) { - printk(KERN_ERR PFX "Card failure (no reset ack).\n"); + dev_printk(KERN_ERR, &pdev->dev, + "Card failure (no reset ack).\n"); goto err_out_free_netdev; } diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index 70429108c40d..d6e7ac2d0c94 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -1832,7 +1832,8 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_ } else if (!pci_set_dma_mask(pci_dev, DMA_32BIT_MASK)) { using_dac = 0; } else { - printk(KERN_WARNING "ns83820.c: pci_set_dma_mask failed!\n"); + dev_printk(KERN_WARNING, &pci_dev->dev, + "pci_set_dma_mask failed!\n"); return -ENODEV; } @@ -1855,7 +1856,8 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_ err = pci_enable_device(pci_dev); if (err) { - printk(KERN_INFO "ns83820: pci_enable_dev failed: %d\n", err); + dev_printk(KERN_INFO, &pci_dev->dev, + "pci_enable_dev failed: %d\n", err); goto out_free; } @@ -1884,8 +1886,9 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_ err = request_irq(pci_dev->irq, ns83820_irq, IRQF_SHARED, DRV_NAME, ndev); if (err) { - printk(KERN_INFO "ns83820: unable to register irq %d\n", - pci_dev->irq); + dev_printk(KERN_INFO, &pci_dev->dev, + "unable to register irq %d, err %d\n", + pci_dev->irq, err); goto out_disable; } @@ -1899,7 +1902,8 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_ rtnl_lock(); err = dev_alloc_name(ndev, ndev->name); if (err < 0) { - printk(KERN_INFO "ns83820: unable to get netdev name: %d\n", err); + dev_printk(KERN_INFO, &pci_dev->dev, + "unable to get netdev name: %d\n", err); goto out_free_irq; } diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c index 3388ee1313ea..9c1ce318b077 100644 --- a/drivers/net/pci-skeleton.c +++ b/drivers/net/pci-skeleton.c @@ -601,7 +601,8 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, /* dev zeroed in alloc_etherdev */ dev = alloc_etherdev (sizeof (*tp)); if (dev == NULL) { - printk (KERN_ERR PFX "unable to alloc new ethernet\n"); + dev_printk (KERN_ERR, &pdev->dev, + "unable to alloc new ethernet\n"); DPRINTK ("EXIT, returning -ENOMEM\n"); return -ENOMEM; } @@ -631,14 +632,16 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, /* make sure PCI base addr 0 is PIO */ if (!(pio_flags & IORESOURCE_IO)) { - printk (KERN_ERR PFX "region #0 not a PIO resource, aborting\n"); + dev_printk (KERN_ERR, &pdev->dev, + "region #0 not a PIO resource, aborting\n"); rc = -ENODEV; goto err_out; } /* make sure PCI base addr 1 is MMIO */ if (!(mmio_flags & IORESOURCE_MEM)) { - printk (KERN_ERR PFX "region #1 not an MMIO resource, aborting\n"); + dev_printk (KERN_ERR, &pdev->dev, + "region #1 not an MMIO resource, aborting\n"); rc = -ENODEV; goto err_out; } @@ -646,12 +649,13 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, /* check for weird/broken PCI region reporting */ if ((pio_len < NETDRV_MIN_IO_SIZE) || (mmio_len < NETDRV_MIN_IO_SIZE)) { - printk (KERN_ERR PFX "Invalid PCI region size(s), aborting\n"); + dev_printk (KERN_ERR, &pdev->dev, + "Invalid PCI region size(s), aborting\n"); rc = -ENODEV; goto err_out; } - rc = pci_request_regions (pdev, "pci-skeleton"); + rc = pci_request_regions (pdev, MODNAME); if (rc) goto err_out; @@ -663,7 +667,8 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, /* ioremap MMIO region */ ioaddr = ioremap (mmio_start, mmio_len); if (ioaddr == NULL) { - printk (KERN_ERR PFX "cannot remap MMIO, aborting\n"); + dev_printk (KERN_ERR, &pdev->dev, + "cannot remap MMIO, aborting\n"); rc = -EIO; goto err_out_free_res; } @@ -699,9 +704,10 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, } /* if unknown chip, assume array element #0, original RTL-8139 in this case */ - printk (KERN_DEBUG PFX "PCI device %s: unknown chip version, assuming RTL-8139\n", - pci_name(pdev)); - printk (KERN_DEBUG PFX "PCI device %s: TxConfig = 0x%lx\n", pci_name(pdev), NETDRV_R32 (TxConfig)); + dev_printk (KERN_DEBUG, &pdev->dev, + "unknown chip version, assuming RTL-8139\n"); + dev_printk (KERN_DEBUG, &pdev->dev, "TxConfig = 0x%lx\n", + NETDRV_R32 (TxConfig)); tp->chipset = 0; match: diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 16a0ef1b1369..ae2a1f8f1454 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -1406,7 +1406,8 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, dev = alloc_etherdev(sizeof (*tp)); if (dev == NULL) { if (netif_msg_drv(&debug)) - printk(KERN_ERR PFX "unable to alloc new ethernet\n"); + dev_printk(KERN_ERR, &pdev->dev, + "unable to alloc new ethernet\n"); goto err_out; } @@ -1418,10 +1419,8 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, /* enable device (incl. PCI PM wakeup and hotplug setup) */ rc = pci_enable_device(pdev); if (rc < 0) { - if (netif_msg_probe(tp)) { - printk(KERN_ERR PFX "%s: enable failure\n", - pci_name(pdev)); - } + if (netif_msg_probe(tp)) + dev_printk(KERN_ERR, &pdev->dev, "enable failure\n"); goto err_out_free_dev; } @@ -1437,37 +1436,33 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command); acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK; } else { - if (netif_msg_probe(tp)) { - printk(KERN_ERR PFX + if (netif_msg_probe(tp)) + dev_printk(KERN_ERR, &pdev->dev, "PowerManagement capability not found.\n"); - } } /* make sure PCI base addr 1 is MMIO */ if (!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) { - if (netif_msg_probe(tp)) { - printk(KERN_ERR PFX + if (netif_msg_probe(tp)) + dev_printk(KERN_ERR, &pdev->dev, "region #1 not an MMIO resource, aborting\n"); - } rc = -ENODEV; goto err_out_mwi; } /* check for weird/broken PCI region reporting */ if (pci_resource_len(pdev, 1) < R8169_REGS_SIZE) { - if (netif_msg_probe(tp)) { - printk(KERN_ERR PFX + if (netif_msg_probe(tp)) + dev_printk(KERN_ERR, &pdev->dev, "Invalid PCI region size(s), aborting\n"); - } rc = -ENODEV; goto err_out_mwi; } rc = pci_request_regions(pdev, MODULENAME); if (rc < 0) { - if (netif_msg_probe(tp)) { - printk(KERN_ERR PFX "%s: could not request regions.\n", - pci_name(pdev)); - } + if (netif_msg_probe(tp)) + dev_printk(KERN_ERR, &pdev->dev, + "could not request regions.\n"); goto err_out_mwi; } @@ -1480,10 +1475,9 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, } else { rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc < 0) { - if (netif_msg_probe(tp)) { - printk(KERN_ERR PFX + if (netif_msg_probe(tp)) + dev_printk(KERN_ERR, &pdev->dev, "DMA configuration failed.\n"); - } goto err_out_free_res; } } @@ -1494,7 +1488,8 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, ioaddr = ioremap(pci_resource_start(pdev, 1), R8169_REGS_SIZE); if (ioaddr == NULL) { if (netif_msg_probe(tp)) - printk(KERN_ERR PFX "cannot remap MMIO, aborting\n"); + dev_printk(KERN_ERR, &pdev->dev, + "cannot remap MMIO, aborting\n"); rc = -EIO; goto err_out_free_res; } @@ -1526,9 +1521,9 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, if (i < 0) { /* Unknown chip: assume array element #0, original RTL-8169 */ if (netif_msg_probe(tp)) { - printk(KERN_DEBUG PFX "PCI device %s: " + dev_printk(KERN_DEBUG, &pdev->dev, "unknown chip version, assuming %s\n", - pci_name(pdev), rtl_chip_info[0].name); + rtl_chip_info[0].name); } i++; } -- cgit v1.2.3 From 9b91cf9daac41eeaaea57ecfe68dc13bb0305fa9 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 27 Jun 2006 11:39:50 -0400 Subject: [netdrvr] use dev_xxx() printk helpers, rather than dev_printk(KERN_xxx, ... Suggested by Jiri Slaby. Signed-off-by: Jeff Garzik --- drivers/net/8139cp.c | 17 +++++++---------- drivers/net/8139too.c | 27 ++++++++++----------------- drivers/net/b44.c | 23 +++++++++-------------- drivers/net/bnx2.c | 29 +++++++++++------------------ drivers/net/cassini.c | 25 ++++++++++--------------- drivers/net/eepro100.c | 8 +++----- drivers/net/epic100.c | 15 +++++++-------- drivers/net/fealnx.c | 9 ++++----- drivers/net/ne2k-pci.c | 11 ++++------- drivers/net/ns83820.c | 12 ++++-------- drivers/net/pci-skeleton.c | 15 +++++---------- drivers/net/r8169.c | 19 ++++++++----------- drivers/net/via-velocity.c | 12 ++++++------ 13 files changed, 88 insertions(+), 134 deletions(-) diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 93b7c904bba9..d2150baa7e35 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -1836,11 +1836,10 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (pdev->vendor == PCI_VENDOR_ID_REALTEK && pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev < 0x20) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "This (id %04x:%04x rev %02x) is not an 8139C+ compatible chip\n", pdev->vendor, pdev->device, pci_rev); - dev_printk(KERN_ERR, &pdev->dev, - "Try the \"8139too\" driver instead.\n"); + dev_err(&pdev->dev, "Try the \"8139too\" driver instead.\n"); return -ENODEV; } @@ -1878,13 +1877,12 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pciaddr = pci_resource_start(pdev, 1); if (!pciaddr) { rc = -EIO; - dev_printk(KERN_ERR, &pdev->dev, "no MMIO resource\n"); + dev_err(&pdev->dev, "no MMIO resource\n"); goto err_out_res; } if (pci_resource_len(pdev, 1) < CP_REGS_SIZE) { rc = -EIO; - dev_printk(KERN_ERR, &pdev->dev, - "MMIO resource (%llx) too small\n", + dev_err(&pdev->dev, "MMIO resource (%llx) too small\n", (unsigned long long)pci_resource_len(pdev, 1)); goto err_out_res; } @@ -1899,13 +1897,13 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n"); goto err_out_res; } rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "No usable consistent DMA configuration, " "aborting.\n"); goto err_out_res; @@ -1918,8 +1916,7 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) regs = ioremap(pciaddr, CP_REGS_SIZE); if (!regs) { rc = -EIO; - dev_printk(KERN_ERR, &pdev->dev, - "Cannot map PCI MMIO (%llx@%llx)\n", + dev_err(&pdev->dev, "Cannot map PCI MMIO (%lx@%lx)\n", (unsigned long long)pci_resource_len(pdev, 1), (unsigned long long)pciaddr); goto err_out_res; diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 5e9ef52a5ca9..cd9718512d1c 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -768,8 +768,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* dev and priv zeroed in alloc_etherdev */ dev = alloc_etherdev (sizeof (*tp)); if (dev == NULL) { - dev_printk (KERN_ERR, &pdev->dev, - "Unable to alloc new net device\n"); + dev_err(&pdev->dev, "Unable to alloc new net device\n"); return -ENOMEM; } SET_MODULE_OWNER(dev); @@ -801,29 +800,25 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, #ifdef USE_IO_OPS /* make sure PCI base addr 0 is PIO */ if (!(pio_flags & IORESOURCE_IO)) { - dev_printk (KERN_ERR, &pdev->dev, - "region #0 not a PIO resource, aborting\n"); + dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); rc = -ENODEV; goto err_out; } /* check for weird/broken PCI region reporting */ if (pio_len < RTL_MIN_IO_SIZE) { - dev_printk (KERN_ERR, &pdev->dev, - "Invalid PCI I/O region size(s), aborting\n"); + dev_err(&pdev->dev, "Invalid PCI I/O region size(s), aborting\n"); rc = -ENODEV; goto err_out; } #else /* make sure PCI base addr 1 is MMIO */ if (!(mmio_flags & IORESOURCE_MEM)) { - dev_printk (KERN_ERR, &pdev->dev, - "region #1 not an MMIO resource, aborting\n"); + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); rc = -ENODEV; goto err_out; } if (mmio_len < RTL_MIN_IO_SIZE) { - dev_printk (KERN_ERR, &pdev->dev, - "Invalid PCI mem region size(s), aborting\n"); + dev_err(&pdev->dev, "Invalid PCI mem region size(s), aborting\n"); rc = -ENODEV; goto err_out; } @@ -840,7 +835,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, #ifdef USE_IO_OPS ioaddr = ioport_map(pio_start, pio_len); if (!ioaddr) { - dev_printk (KERN_ERR, &pdev->dev, "cannot map PIO, aborting\n"); + dev_err(&pdev->dev, "cannot map PIO, aborting\n"); rc = -EIO; goto err_out; } @@ -851,8 +846,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* ioremap MMIO region */ ioaddr = pci_iomap(pdev, 1, 0); if (ioaddr == NULL) { - dev_printk (KERN_ERR, &pdev->dev, - "cannot remap MMIO, aborting\n"); + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); rc = -EIO; goto err_out; } @@ -866,8 +860,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, /* check for missing/broken hardware */ if (RTL_R32 (TxConfig) == 0xFFFFFFFF) { - dev_printk (KERN_ERR, &pdev->dev, - "Chip not responding, ignoring board\n"); + dev_err(&pdev->dev, "Chip not responding, ignoring board\n"); rc = -EIO; goto err_out; } @@ -961,10 +954,10 @@ static int __devinit rtl8139_init_one (struct pci_dev *pdev, if (pdev->vendor == PCI_VENDOR_ID_REALTEK && pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pci_rev >= 0x20) { - dev_printk(KERN_INFO, &pdev->dev, + dev_info(&pdev->dev, "This (id %04x:%04x rev %02x) is an enhanced 8139C+ chip\n", pdev->vendor, pdev->device, pci_rev); - dev_printk(KERN_INFO, &pdev->dev, + dev_info(&pdev->dev, "Use the \"8139cp\" driver for improved performance and stability.\n"); } diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 5815e3dc1251..bea0fc0ede2f 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -2120,13 +2120,13 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = pci_enable_device(pdev); if (err) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot enable PCI device, " + dev_err(&pdev->dev, "Cannot enable PCI device, " "aborting.\n"); return err; } if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "Cannot find proper PCI device " "base address, aborting.\n"); err = -ENODEV; @@ -2135,7 +2135,7 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = pci_request_regions(pdev, DRV_MODULE_NAME); if (err) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting.\n"); goto err_out_disable_pdev; } @@ -2144,15 +2144,13 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = pci_set_dma_mask(pdev, (u64) B44_DMA_MASK); if (err) { - dev_printk(KERN_ERR, &pdev->dev, - "No usable DMA configuration, aborting.\n"); + dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n"); goto err_out_free_res; } err = pci_set_consistent_dma_mask(pdev, (u64) B44_DMA_MASK); if (err) { - dev_printk(KERN_ERR, &pdev->dev, - "No usable DMA configuration, aborting.\n"); + dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n"); goto err_out_free_res; } @@ -2161,8 +2159,7 @@ static int __devinit b44_init_one(struct pci_dev *pdev, dev = alloc_etherdev(sizeof(*bp)); if (!dev) { - dev_printk(KERN_ERR, &pdev->dev, - "Etherdev alloc failed, aborting.\n"); + dev_err(&pdev->dev, "Etherdev alloc failed, aborting.\n"); err = -ENOMEM; goto err_out_free_res; } @@ -2183,8 +2180,7 @@ static int __devinit b44_init_one(struct pci_dev *pdev, bp->regs = ioremap(b44reg_base, b44reg_len); if (bp->regs == 0UL) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot map device registers, " - "aborting.\n"); + dev_err(&pdev->dev, "Cannot map device registers, aborting.\n"); err = -ENOMEM; goto err_out_free_dev; } @@ -2214,7 +2210,7 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = b44_get_invariants(bp); if (err) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "Problem fetching invariants of chip, aborting.\n"); goto err_out_iounmap; } @@ -2235,8 +2231,7 @@ static int __devinit b44_init_one(struct pci_dev *pdev, err = register_netdev(dev); if (err) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot register net device, " - "aborting.\n"); + dev_err(&pdev->dev, "Cannot register net device, aborting.\n"); goto err_out_iounmap; } diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 5502b9aeda53..64b6a72b4f6a 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -5575,13 +5575,12 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) /* enable device (incl. PCI PM wakeup), and bus-mastering */ rc = pci_enable_device(pdev); if (rc) { - dev_printk(KERN_ERR, &pdev->dev, - "Cannot enable PCI device, aborting."); + dev_err(&pdev->dev, "Cannot enable PCI device, aborting."); goto err_out; } if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "Cannot find PCI device base address, aborting.\n"); rc = -ENODEV; goto err_out_disable; @@ -5589,8 +5588,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) rc = pci_request_regions(pdev, DRV_MODULE_NAME); if (rc) { - dev_printk(KERN_ERR, &pdev->dev, - "Cannot obtain PCI resources, aborting.\n"); + dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting.\n"); goto err_out_disable; } @@ -5598,7 +5596,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); if (bp->pm_cap == 0) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "Cannot find power management capability, aborting.\n"); rc = -EIO; goto err_out_release; @@ -5606,8 +5604,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->pcix_cap = pci_find_capability(pdev, PCI_CAP_ID_PCIX); if (bp->pcix_cap == 0) { - dev_printk(KERN_ERR, &pdev->dev, - "Cannot find PCIX capability, aborting.\n"); + dev_err(&pdev->dev, "Cannot find PCIX capability, aborting.\n"); rc = -EIO; goto err_out_release; } @@ -5615,15 +5612,14 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) { bp->flags |= USING_DAC_FLAG; if (pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK) != 0) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "pci_set_consistent_dma_mask failed, aborting.\n"); rc = -EIO; goto err_out_release; } } else if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) { - dev_printk(KERN_ERR, &pdev->dev, - "System does not support DMA, aborting.\n"); + dev_err(&pdev->dev, "System does not support DMA, aborting.\n"); rc = -EIO; goto err_out_release; } @@ -5643,8 +5639,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->regview = ioremap_nocache(dev->base_addr, mem_len); if (!bp->regview) { - dev_printk(KERN_ERR, &pdev->dev, - "Cannot map register space, aborting.\n"); + dev_err(&pdev->dev, "Cannot map register space, aborting.\n"); rc = -ENOMEM; goto err_out_release; } @@ -5716,7 +5711,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) else if ((CHIP_ID(bp) == CHIP_ID_5706_A1) && !(bp->flags & PCIX_FLAG)) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "5706 A1 can only be used in a PCIX bus, aborting.\n"); goto err_out_unmap; } @@ -5738,8 +5733,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) if ((reg & BNX2_DEV_INFO_SIGNATURE_MAGIC_MASK) != BNX2_DEV_INFO_SIGNATURE_MAGIC) { - dev_printk(KERN_ERR, &pdev->dev, - "Firmware not running, aborting.\n"); + dev_err(&pdev->dev, "Firmware not running, aborting.\n"); rc = -ENODEV; goto err_out_unmap; } @@ -5901,8 +5895,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #endif if ((rc = register_netdev(dev))) { - dev_printk(KERN_ERR, &pdev->dev, - "Cannot register net device\n"); + dev_err(&pdev->dev, "Cannot register net device\n"); if (bp->regview) iounmap(bp->regview); pci_release_regions(pdev); diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 428e2067738c..a31544ccb3c4 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -4887,13 +4887,12 @@ static int __devinit cas_init_one(struct pci_dev *pdev, err = pci_enable_device(pdev); if (err) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot enable PCI device, " - "aborting.\n"); + dev_err(&pdev->dev, "Cannot enable PCI device, aborting.\n"); return err; } if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot find proper PCI device " + dev_err(&pdev->dev, "Cannot find proper PCI device " "base address, aborting.\n"); err = -ENODEV; goto err_out_disable_pdev; @@ -4901,7 +4900,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, dev = alloc_etherdev(sizeof(*cp)); if (!dev) { - dev_printk(KERN_ERR, &pdev->dev, "Etherdev alloc failed, aborting.\n"); + dev_err(&pdev->dev, "Etherdev alloc failed, aborting.\n"); err = -ENOMEM; goto err_out_disable_pdev; } @@ -4910,8 +4909,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, err = pci_request_regions(pdev, dev->name); if (err) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot obtain PCI resources, " - "aborting.\n"); + dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting.\n"); goto err_out_free_netdev; } pci_set_master(pdev); @@ -4941,7 +4939,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, if (pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, cas_cacheline_size)) { - dev_printk(KERN_ERR, &pdev->dev, "Could not set PCI cache " + dev_err(&pdev->dev, "Could not set PCI cache " "line size\n"); goto err_write_cacheline; } @@ -4955,7 +4953,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (err < 0) { - dev_printk(KERN_ERR, &pdev->dev, "Unable to obtain 64-bit DMA " + dev_err(&pdev->dev, "Unable to obtain 64-bit DMA " "for consistent allocations\n"); goto err_out_free_res; } @@ -4963,7 +4961,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, } else { err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (err) { - dev_printk(KERN_ERR, &pdev->dev, "No usable DMA configuration, " + dev_err(&pdev->dev, "No usable DMA configuration, " "aborting.\n"); goto err_out_free_res; } @@ -5023,8 +5021,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, /* give us access to cassini registers */ cp->regs = pci_iomap(pdev, 0, casreg_len); if (cp->regs == 0UL) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot map device registers, " - "aborting.\n"); + dev_err(&pdev->dev, "Cannot map device registers, aborting.\n"); goto err_out_free_res; } cp->casreg_len = casreg_len; @@ -5040,8 +5037,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, pci_alloc_consistent(pdev, sizeof(struct cas_init_block), &cp->block_dvma); if (!cp->init_block) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot allocate init block, " - "aborting.\n"); + dev_err(&pdev->dev, "Cannot allocate init block, aborting.\n"); goto err_out_iounmap; } @@ -5085,8 +5081,7 @@ static int __devinit cas_init_one(struct pci_dev *pdev, dev->features |= NETIF_F_HIGHDMA; if (register_netdev(dev)) { - dev_printk(KERN_ERR, &pdev->dev, "Cannot register net device, " - "aborting.\n"); + dev_err(&pdev->dev, "Cannot register net device, aborting.\n"); goto err_out_free_consistent; } diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index bccb12e03c2d..e445988c92ee 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -555,14 +555,12 @@ static int __devinit eepro100_init_one (struct pci_dev *pdev, if (!request_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1), "eepro100")) { - dev_printk (KERN_ERR, &pdev->dev, - "eepro100: cannot reserve I/O ports\n"); + dev_err(&pdev->dev, "eepro100: cannot reserve I/O ports\n"); goto err_out_none; } if (!request_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0), "eepro100")) { - dev_printk (KERN_ERR, &pdev->dev, - "eepro100: cannot reserve MMIO region\n"); + dev_err(&pdev->dev, "eepro100: cannot reserve MMIO region\n"); goto err_out_free_pio_region; } @@ -575,7 +573,7 @@ static int __devinit eepro100_init_one (struct pci_dev *pdev, ioaddr = pci_iomap(pdev, pci_bar, 0); if (!ioaddr) { - dev_printk (KERN_ERR, &pdev->dev, "eepro100: cannot remap IO\n"); + dev_err(&pdev->dev, "eepro100: cannot remap IO\n"); goto err_out_free_mmio_region; } diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 6dd0e9d159de..a67650ccf084 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -335,7 +335,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, irq = pdev->irq; if (pci_resource_len(pdev, 0) < EPIC_TOTAL_SIZE) { - dev_printk(KERN_ERR, &pdev->dev, "no PCI region space\n"); + dev_err(&pdev->dev, "no PCI region space\n"); ret = -ENODEV; goto err_out_disable; } @@ -350,7 +350,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, dev = alloc_etherdev(sizeof (*ep)); if (!dev) { - dev_printk(KERN_ERR, &pdev->dev, "no memory for eth device\n"); + dev_err(&pdev->dev, "no memory for eth device\n"); goto err_out_free_res; } SET_MODULE_OWNER(dev); @@ -362,7 +362,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, ioaddr = pci_resource_start (pdev, 1); ioaddr = (long) ioremap (ioaddr, pci_resource_len (pdev, 1)); if (!ioaddr) { - dev_printk(KERN_ERR, &pdev->dev, "ioremap failed\n"); + dev_err(&pdev->dev, "ioremap failed\n"); goto err_out_free_netdev; } #endif @@ -444,7 +444,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, int mii_status = mdio_read(dev, phy, MII_BMSR); if (mii_status != 0xffff && mii_status != 0x0000) { ep->phys[phy_idx++] = phy; - dev_printk(KERN_INFO, &pdev->dev, + dev_info(&pdev->dev, "MII transceiver #%d control " "%4.4x status %4.4x.\n", phy, mdio_read(dev, phy, 0), mii_status); @@ -454,12 +454,12 @@ static int __devinit epic_init_one (struct pci_dev *pdev, if (phy_idx != 0) { phy = ep->phys[0]; ep->mii.advertising = mdio_read(dev, phy, MII_ADVERTISE); - dev_printk(KERN_INFO, &pdev->dev, + dev_info(&pdev->dev, "Autonegotiation advertising %4.4x link " "partner %4.4x.\n", ep->mii.advertising, mdio_read(dev, phy, 5)); } else if ( ! (ep->chip_flags & NO_MII)) { - dev_printk(KERN_WARNING, &pdev->dev, + dev_warn(&pdev->dev, "***WARNING***: No MII transceiver found!\n"); /* Use the known PHY address of the EPII. */ ep->phys[0] = 3; @@ -475,8 +475,7 @@ static int __devinit epic_init_one (struct pci_dev *pdev, /* The lower four bits are the media type. */ if (duplex) { ep->mii.force_media = ep->mii.full_duplex = 1; - dev_printk(KERN_INFO, &pdev->dev, - "Forced full duplex operation requested.\n"); + dev_info(&pdev->dev, "Forced full duplex requested.\n"); } dev->if_port = ep->default_port = option; diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index d26140ad36e9..97d34fee8c1f 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -505,7 +505,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, len = pci_resource_len(pdev, bar); if (len < MIN_REGION_SIZE) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "region size %ld too small, aborting\n", len); return -ENODEV; } @@ -578,7 +578,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, if (mii_status != 0xffff && mii_status != 0x0000) { np->phys[phy_idx++] = phy; - dev_printk(KERN_INFO, &pdev->dev, + dev_info(&pdev->dev, "MII PHY found at address %d, status " "0x%4.4x.\n", phy, mii_status); /* get phy type */ @@ -604,7 +604,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, np->mii_cnt = phy_idx; if (phy_idx == 0) - dev_printk(KERN_WARNING, &pdev->dev, + dev_warn(&pdev->dev, "MII PHY not found -- this device may " "not operate correctly.\n"); } else { @@ -632,8 +632,7 @@ static int __devinit fealnx_init_one(struct pci_dev *pdev, np->mii.full_duplex = full_duplex[card_idx]; if (np->mii.full_duplex) { - dev_printk(KERN_INFO, &pdev->dev, - "Media type forced to Full Duplex.\n"); + dev_info(&pdev->dev, "Media type forced to Full Duplex.\n"); /* 89/6/13 add, (begin) */ // if (np->PHYType==MarvellPHY) if ((np->PHYType == MarvellPHY) || (np->PHYType == LevelOnePHY)) { diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c index 4f226064d55e..34bdba9eec79 100644 --- a/drivers/net/ne2k-pci.c +++ b/drivers/net/ne2k-pci.c @@ -231,14 +231,12 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, irq = pdev->irq; if (!ioaddr || ((pci_resource_flags (pdev, 0) & IORESOURCE_IO) == 0)) { - dev_printk (KERN_ERR, &pdev->dev, - "no I/O resource at PCI BAR #0\n"); + dev_err(&pdev->dev, "no I/O resource at PCI BAR #0\n"); return -ENODEV; } if (request_region (ioaddr, NE_IO_EXTENT, DRV_NAME) == NULL) { - dev_printk (KERN_ERR, &pdev->dev, - "I/O resource 0x%x @ 0x%lx busy\n", + dev_err(&pdev->dev, "I/O resource 0x%x @ 0x%lx busy\n", NE_IO_EXTENT, ioaddr); return -EBUSY; } @@ -265,8 +263,7 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, /* Allocate net_device, dev->priv; fill in 8390 specific dev fields. */ dev = alloc_ei_netdev(); if (!dev) { - dev_printk (KERN_ERR, &pdev->dev, - "cannot allocate ethernet device\n"); + dev_err(&pdev->dev, "cannot allocate ethernet device\n"); goto err_out_free_res; } SET_MODULE_OWNER(dev); @@ -284,7 +281,7 @@ static int __devinit ne2k_pci_init_one (struct pci_dev *pdev, while ((inb(ioaddr + EN0_ISR) & ENISR_RESET) == 0) /* Limit wait: '2' avoids jiffy roll-over. */ if (jiffies - reset_start_time > 2) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "Card failure (no reset ack).\n"); goto err_out_free_netdev; } diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index d6e7ac2d0c94..d0ed8646902f 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -1832,8 +1832,7 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_ } else if (!pci_set_dma_mask(pci_dev, DMA_32BIT_MASK)) { using_dac = 0; } else { - dev_printk(KERN_WARNING, &pci_dev->dev, - "pci_set_dma_mask failed!\n"); + dev_warn(&pci_dev->dev, "pci_set_dma_mask failed!\n"); return -ENODEV; } @@ -1856,8 +1855,7 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_ err = pci_enable_device(pci_dev); if (err) { - dev_printk(KERN_INFO, &pci_dev->dev, - "pci_enable_dev failed: %d\n", err); + dev_info(&pci_dev->dev, "pci_enable_dev failed: %d\n", err); goto out_free; } @@ -1886,8 +1884,7 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_ err = request_irq(pci_dev->irq, ns83820_irq, IRQF_SHARED, DRV_NAME, ndev); if (err) { - dev_printk(KERN_INFO, &pci_dev->dev, - "unable to register irq %d, err %d\n", + dev_info(&pci_dev->dev, "unable to register irq %d, err %d\n", pci_dev->irq, err); goto out_disable; } @@ -1902,8 +1899,7 @@ static int __devinit ns83820_init_one(struct pci_dev *pci_dev, const struct pci_ rtnl_lock(); err = dev_alloc_name(ndev, ndev->name); if (err < 0) { - dev_printk(KERN_INFO, &pci_dev->dev, - "unable to get netdev name: %d\n", err); + dev_info(&pci_dev->dev, "unable to get netdev name: %d\n", err); goto out_free_irq; } diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c index 9c1ce318b077..e0e293964042 100644 --- a/drivers/net/pci-skeleton.c +++ b/drivers/net/pci-skeleton.c @@ -601,8 +601,7 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, /* dev zeroed in alloc_etherdev */ dev = alloc_etherdev (sizeof (*tp)); if (dev == NULL) { - dev_printk (KERN_ERR, &pdev->dev, - "unable to alloc new ethernet\n"); + dev_err(&pdev->dev, "unable to alloc new ethernet\n"); DPRINTK ("EXIT, returning -ENOMEM\n"); return -ENOMEM; } @@ -632,16 +631,14 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, /* make sure PCI base addr 0 is PIO */ if (!(pio_flags & IORESOURCE_IO)) { - dev_printk (KERN_ERR, &pdev->dev, - "region #0 not a PIO resource, aborting\n"); + dev_err(&pdev->dev, "region #0 not a PIO resource, aborting\n"); rc = -ENODEV; goto err_out; } /* make sure PCI base addr 1 is MMIO */ if (!(mmio_flags & IORESOURCE_MEM)) { - dev_printk (KERN_ERR, &pdev->dev, - "region #1 not an MMIO resource, aborting\n"); + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); rc = -ENODEV; goto err_out; } @@ -649,8 +646,7 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, /* check for weird/broken PCI region reporting */ if ((pio_len < NETDRV_MIN_IO_SIZE) || (mmio_len < NETDRV_MIN_IO_SIZE)) { - dev_printk (KERN_ERR, &pdev->dev, - "Invalid PCI region size(s), aborting\n"); + dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n"); rc = -ENODEV; goto err_out; } @@ -667,8 +663,7 @@ static int __devinit netdrv_init_board (struct pci_dev *pdev, /* ioremap MMIO region */ ioaddr = ioremap (mmio_start, mmio_len); if (ioaddr == NULL) { - dev_printk (KERN_ERR, &pdev->dev, - "cannot remap MMIO, aborting\n"); + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); rc = -EIO; goto err_out_free_res; } diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index ae2a1f8f1454..4c2f575faad7 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -1406,8 +1406,7 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, dev = alloc_etherdev(sizeof (*tp)); if (dev == NULL) { if (netif_msg_drv(&debug)) - dev_printk(KERN_ERR, &pdev->dev, - "unable to alloc new ethernet\n"); + dev_err(&pdev->dev, "unable to alloc new ethernet\n"); goto err_out; } @@ -1420,7 +1419,7 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, rc = pci_enable_device(pdev); if (rc < 0) { if (netif_msg_probe(tp)) - dev_printk(KERN_ERR, &pdev->dev, "enable failure\n"); + dev_err(&pdev->dev, "enable failure\n"); goto err_out_free_dev; } @@ -1437,14 +1436,14 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK; } else { if (netif_msg_probe(tp)) - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "PowerManagement capability not found.\n"); } /* make sure PCI base addr 1 is MMIO */ if (!(pci_resource_flags(pdev, 1) & IORESOURCE_MEM)) { if (netif_msg_probe(tp)) - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); rc = -ENODEV; goto err_out_mwi; @@ -1452,7 +1451,7 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, /* check for weird/broken PCI region reporting */ if (pci_resource_len(pdev, 1) < R8169_REGS_SIZE) { if (netif_msg_probe(tp)) - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n"); rc = -ENODEV; goto err_out_mwi; @@ -1461,8 +1460,7 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, rc = pci_request_regions(pdev, MODULENAME); if (rc < 0) { if (netif_msg_probe(tp)) - dev_printk(KERN_ERR, &pdev->dev, - "could not request regions.\n"); + dev_err(&pdev->dev, "could not request regions.\n"); goto err_out_mwi; } @@ -1476,7 +1474,7 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc < 0) { if (netif_msg_probe(tp)) - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "DMA configuration failed.\n"); goto err_out_free_res; } @@ -1488,8 +1486,7 @@ rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, ioaddr = ioremap(pci_resource_start(pdev, 1), R8169_REGS_SIZE); if (ioaddr == NULL) { if (netif_msg_probe(tp)) - dev_printk(KERN_ERR, &pdev->dev, - "cannot remap MMIO, aborting\n"); + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); rc = -EIO; goto err_out_free_res; } diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 7e3b2a318146..f5b0078eb4ad 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -695,14 +695,14 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi * can support more than MAX_UNITS. */ if (velocity_nics >= MAX_UNITS) { - dev_printk(KERN_NOTICE, &pdev->dev, "already found %d NICs.\n", + dev_notice(&pdev->dev, "already found %d NICs.\n", velocity_nics); return -ENODEV; } dev = alloc_etherdev(sizeof(struct velocity_info)); if (!dev) { - dev_printk(KERN_ERR, &pdev->dev, "allocate net device failed.\n"); + dev_err(&pdev->dev, "allocate net device failed.\n"); goto out; } @@ -739,7 +739,7 @@ static int __devinit velocity_found1(struct pci_dev *pdev, const struct pci_devi ret = pci_request_regions(pdev, VELOCITY_NAME); if (ret < 0) { - dev_printk(KERN_ERR, &pdev->dev, "No PCI resources.\n"); + dev_err(&pdev->dev, "No PCI resources.\n"); goto err_disable; } @@ -895,19 +895,19 @@ static int __devinit velocity_get_pci_info(struct velocity_info *vptr, struct pc vptr->memaddr = pci_resource_start(pdev, 1); if (!(pci_resource_flags(pdev, 0) & IORESOURCE_IO)) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "region #0 is not an I/O resource, aborting.\n"); return -EINVAL; } if ((pci_resource_flags(pdev, 1) & IORESOURCE_IO)) { - dev_printk(KERN_ERR, &pdev->dev, + dev_err(&pdev->dev, "region #1 is an I/O resource, aborting.\n"); return -EINVAL; } if (pci_resource_len(pdev, 1) < VELOCITY_IO_SIZE) { - dev_printk(KERN_ERR, &pdev->dev, "region #1 is too small.\n"); + dev_err(&pdev->dev, "region #1 is too small.\n"); return -EINVAL; } vptr->pdev = pdev; -- cgit v1.2.3 From d3cef4ee2207033b9f231da308d966f357af34da Mon Sep 17 00:00:00 2001 From: Faidon Liambotis Date: Sat, 10 Jun 2006 20:54:58 +0300 Subject: [PATCH] Add two PLX device IDs This patch adds device IDs for Symbol LA-4123 and Global Sun Tech GL24110P to the HostAP PLX driver. This is not tested with real hardware, but there is no reason why it shouldn't work. Please test. Signed-off-by: Faidon Liambotis Signed-off-by: John W. Linville --- drivers/net/wireless/hostap/hostap_plx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c index 49860fa61c30..6dfa041be66d 100644 --- a/drivers/net/wireless/hostap/hostap_plx.c +++ b/drivers/net/wireless/hostap/hostap_plx.c @@ -66,10 +66,12 @@ static struct pci_device_id prism2_plx_id_table[] __devinitdata = { PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"), PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"), PLXDEV(0x126c, 0x8030, "Nortel emobility"), + PLXDEV(0x1562, 0x0001, "Symbol LA-4123"), PLXDEV(0x1385, 0x4100, "Netgear MA301"), PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"), PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"), PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"), + PLXDEV(0x16ab, 0x1100, "Global Sun Tech GL24110P"), PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"), PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"), PLXDEV(0x16ab, 0x1103, "Longshine 8031"), -- cgit v1.2.3 From e8d596972221aea55ae7f5c2bb7d5895039f664d Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 14 Jun 2006 17:51:14 +0200 Subject: [PATCH] bcm43xx: use softmac-suggested TX rate Use Softmac-suggested TX ratecode: ieee80211softmac_suggest_txrate() Signed-off-by: Daniel Drake Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_xmit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c index d8ece28c079f..6dbd855b3647 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c @@ -296,11 +296,14 @@ void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm, u16 control = 0; u16 wsec_rate = 0; u16 encrypt_frame; + const u16 ftype = WLAN_FC_GET_TYPE(le16_to_cpu(wireless_header->frame_ctl)); + const int is_mgt = (ftype == IEEE80211_FTYPE_MGMT); /* Now construct the TX header. */ memset(txhdr, 0, sizeof(*txhdr)); - bitrate = bcm->softmac->txrates.default_rate; + bitrate = ieee80211softmac_suggest_txrate(bcm->softmac, + is_multicast_ether_addr(wireless_header->addr1), is_mgt); ofdm_modulation = !(ieee80211_is_cck_rate(bitrate)); fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate); fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate)); -- cgit v1.2.3 From a1d79aaaa564abc19b7bf3a024bb371fa7648a15 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sat, 17 Jun 2006 15:19:05 +0200 Subject: [PATCH] bcm43xx: workaround init_board vs. IRQ race Place the Init-vs-IRQ workaround before any card register access, because we might not have the wireless core mapped at all times in init. So this will result in a Machine Check caused by a bus error. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_main.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index d8f5600578b4..673ab11d92ab 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -1885,6 +1885,15 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re spin_lock(&bcm->irq_lock); + /* Only accept IRQs, if we are initialized properly. + * This avoids an RX race while initializing. + * We should probably not enable IRQs before we are initialized + * completely, but some careful work is needed to fix this. I think it + * is best to stay with this cheap workaround for now... . + */ + if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) + goto out; + reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); if (reason == 0xffffffff) { /* irq not for us (shared irq) */ @@ -1906,19 +1915,11 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_re bcm43xx_interrupt_ack(bcm, reason); - /* Only accept IRQs, if we are initialized properly. - * This avoids an RX race while initializing. - * We should probably not enable IRQs before we are initialized - * completely, but some careful work is needed to fix this. I think it - * is best to stay with this cheap workaround for now... . - */ - if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) { - /* disable all IRQs. They are enabled again in the bottom half. */ - bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); - /* save the reason code and call our bottom half. */ - bcm->irq_reason = reason; - tasklet_schedule(&bcm->isr_tasklet); - } + /* disable all IRQs. They are enabled again in the bottom half. */ + bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + /* save the reason code and call our bottom half. */ + bcm->irq_reason = reason; + tasklet_schedule(&bcm->isr_tasklet); out: mmiowb(); -- cgit v1.2.3 From 4b301536694facb93f597281580f5ad907d36050 Mon Sep 17 00:00:00 2001 From: Hong Liu Date: Wed, 21 Jun 2006 11:35:08 +0800 Subject: [PATCH] ieee80211: fix not allocating IV+ICV space when usingencryption in ieee80211_tx_frame We should preallocate IV+ICV space when encrypting the frame. Currently no problem shows up just because dev_alloc_skb aligns the data len to SMP_CACHE_BYTES which can be used for ICV. Signed-off-by: Hong Liu Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_tx.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c index de148ae594f3..bf042139c7ab 100644 --- a/net/ieee80211/ieee80211_tx.c +++ b/net/ieee80211/ieee80211_tx.c @@ -562,10 +562,13 @@ int ieee80211_tx_frame(struct ieee80211_device *ieee, struct net_device_stats *stats = &ieee->stats; struct sk_buff *skb_frag; int priority = -1; + int fraglen = total_len; + int headroom = ieee->tx_headroom; + struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx]; spin_lock_irqsave(&ieee->lock, flags); - if (encrypt_mpdu && !ieee->sec.encrypt) + if (encrypt_mpdu && (!ieee->sec.encrypt || !crypt)) encrypt_mpdu = 0; /* If there is no driver handler to take the TXB, dont' bother @@ -581,20 +584,24 @@ int ieee80211_tx_frame(struct ieee80211_device *ieee, goto success; } - if (encrypt_mpdu) + if (encrypt_mpdu) { frame->frame_ctl |= cpu_to_le16(IEEE80211_FCTL_PROTECTED); + fraglen += crypt->ops->extra_mpdu_prefix_len + + crypt->ops->extra_mpdu_postfix_len; + headroom += crypt->ops->extra_mpdu_prefix_len; + } /* When we allocate the TXB we allocate enough space for the reserve * and full fragment bytes (bytes_per_frag doesn't include prefix, * postfix, header, FCS, etc.) */ - txb = ieee80211_alloc_txb(1, total_len, ieee->tx_headroom, GFP_ATOMIC); + txb = ieee80211_alloc_txb(1, fraglen, headroom, GFP_ATOMIC); if (unlikely(!txb)) { printk(KERN_WARNING "%s: Could not allocate TXB\n", ieee->dev->name); goto failed; } txb->encrypted = 0; - txb->payload_size = total_len; + txb->payload_size = fraglen; skb_frag = txb->fragments[0]; -- cgit v1.2.3 From 60d48f1e23c25d09dbe7025ff179b808d72704b3 Mon Sep 17 00:00:00 2001 From: Eric Sesterhenn Date: Wed, 21 Jun 2006 21:05:58 +0200 Subject: [PATCH] skb used after passing to netif_rx in net/ieee80211/ieee80211_rx.c this patch fixes coverity id #913. ieee80211_monitor_rx() passes the skb to netif_rx() and we should not reference it any longer. Signed-off-by: Eric Sesterhenn Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_rx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index 47ccf159372c..dd746e38db5d 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c @@ -402,9 +402,9 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, #endif if (ieee->iw_mode == IW_MODE_MONITOR) { - ieee80211_monitor_rx(ieee, skb, rx_stats); stats->rx_packets++; stats->rx_bytes += skb->len; + ieee80211_monitor_rx(ieee, skb, rx_stats); return 1; } -- cgit v1.2.3 From fc8fd3400b716615933a47be453839d190b235bd Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 25 Jun 2006 21:32:21 -0500 Subject: [PATCH] Convert bcm43xx-softmac to use the ieee80211_is_valid_channel routine The current version of bcm43xx-softmac uses local routines to check if a channel is valid. As noted in the comments, these routines do not take any regulatory information into account. This patch converts the code to use the equivalent routine in ieee80211, which is being converted to know about regulatory information. Signed-Off-By: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_main.h | 24 ------------------------ drivers/net/wireless/bcm43xx/bcm43xx_radio.c | 7 ++----- drivers/net/wireless/bcm43xx/bcm43xx_wx.c | 2 +- 3 files changed, 3 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h index 30a202b258b5..116493671f88 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h @@ -112,30 +112,6 @@ int bcm43xx_channel_to_freq(struct bcm43xx_private *bcm, return bcm43xx_channel_to_freq_bg(channel); } -/* Lightweight function to check if a channel number is valid. - * Note that this does _NOT_ check for geographical restrictions! - */ -static inline -int bcm43xx_is_valid_channel_a(u8 channel) -{ - return (channel >= IEEE80211_52GHZ_MIN_CHANNEL - && channel <= IEEE80211_52GHZ_MAX_CHANNEL); -} -static inline -int bcm43xx_is_valid_channel_bg(u8 channel) -{ - return (channel >= IEEE80211_24GHZ_MIN_CHANNEL - && channel <= IEEE80211_24GHZ_MAX_CHANNEL); -} -static inline -int bcm43xx_is_valid_channel(struct bcm43xx_private *bcm, - u8 channel) -{ - if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) - return bcm43xx_is_valid_channel_a(channel); - return bcm43xx_is_valid_channel_bg(channel); -} - void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf); void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c index af5c0bff1696..bb9c484d7e19 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c @@ -1594,11 +1594,11 @@ int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, u16 r8, tmp; u16 freq; + if (!ieee80211_is_valid_channel(bcm->ieee, channel)) + return -EINVAL; if ((radio->manufact == 0x17F) && (radio->version == 0x2060) && (radio->revision == 1)) { - if (channel > 200) - return -EINVAL; freq = channel2freq_a(channel); r8 = bcm43xx_radio_read16(bcm, 0x0008); @@ -1651,9 +1651,6 @@ int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, TODO(); //TODO: TSSI2dbm workaround bcm43xx_phy_xmitpower(bcm);//FIXME correct? } else { - if ((channel < 1) || (channel > 14)) - return -EINVAL; - if (synthetic_pu_workaround) bcm43xx_synth_pu_workaround(bcm, channel); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c index c35cb3a0777e..5c36e29efff7 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c @@ -119,7 +119,7 @@ static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, channel = bcm43xx_freq_to_channel(bcm, data->freq.m); freq = data->freq.m; } - if (!bcm43xx_is_valid_channel(bcm, channel)) + if (!ieee80211_is_valid_channel(bcm->ieee, channel)) goto out_unlock; if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED) { //ieee80211softmac_disassoc(softmac, $REASON); -- cgit v1.2.3 From 6d41e2651080c717c1b48389fe4171180388f042 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 20 Jun 2006 09:19:18 -0500 Subject: [PATCH] 2.6.17 missing a call to ieee80211softmac_capabilities from ieee80211softmac_assoc_req In commit ba9b28d19a3251bb1dfe6a6f8cc89b96fb85f683, routine ieee80211softmac_capabilities was added to ieee80211softmac_io.c. As denoted by its name, it completes the capabilities IE that is needed in the associate and reassociate requests sent to the AP. For at least one AP, the Linksys WRT54G V5, the capabilities field must set the 'short preamble' bit or the AP refuses to associate. In the commit noted above, there is a call to the new routine from ieee80211softmac_reassoc_req, but not from ieee80211softmac_assoc_req. This patch fixes that oversight. As noted in the subject, v2.6.17 is affected. My bcm43xx card had been unable to associate since I was forced to buy a new AP. I finally was able to get a packet dump and traced the problem to the capabilities info. Although I had heard that a patch was "floating around", I had not seen it before 2.6.17 was released. As this bug does not affect security and I seem to have the only AP affected by it, there should be no problem in leaving it for 2.6.18. Signed-Off-By: Larry Finger Signed-off-by: John W. Linville --- net/ieee80211/softmac/ieee80211softmac_io.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ieee80211/softmac/ieee80211softmac_io.c b/net/ieee80211/softmac/ieee80211softmac_io.c index 09541611e48c..8cc8b20f5cda 100644 --- a/net/ieee80211/softmac/ieee80211softmac_io.c +++ b/net/ieee80211/softmac/ieee80211softmac_io.c @@ -229,6 +229,9 @@ ieee80211softmac_assoc_req(struct ieee80211_assoc_request **pkt, return 0; ieee80211softmac_hdr_3addr(mac, &((*pkt)->header), IEEE80211_STYPE_ASSOC_REQ, net->bssid, net->bssid); + /* Fill in the capabilities */ + (*pkt)->capability = ieee80211softmac_capabilities(mac, net); + /* Fill in Listen Interval (?) */ (*pkt)->listen_interval = cpu_to_le16(10); -- cgit v1.2.3 From 4359219425a0918a72775480e125fbb077de338d Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Fri, 16 Jun 2006 20:50:22 +0100 Subject: [PATCH] bcm43xx: enable shared key authentication I recently patched softmac to enable shared key authentication. This small patch will enable crazy or unfortunate bcm43xx users to use this new capability. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index 673ab11d92ab..e1c5a939bca4 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -3699,6 +3699,10 @@ static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, secinfo->encrypt = sec->encrypt; dprintk(", .encrypt = %d", sec->encrypt); } + if (sec->flags & SEC_AUTH_MODE) { + secinfo->auth_mode = sec->auth_mode; + dprintk(", .auth_mode = %d\n", sec->auth_mode); + } dprintk("\n"); if (bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED && !bcm->ieee->host_encrypt) { -- cgit v1.2.3 From cb74c432e321ed645b6cd88b77edc15f9478efbd Mon Sep 17 00:00:00 2001 From: Joseph Jezak Date: Sun, 11 Jun 2006 12:00:37 -0400 Subject: [PATCH] SoftMAC: Prevent multiple authentication attempts on the same network This patch addresses the "No queue exists" messages commonly seen during authentication and associating. These appear due to scheduling multiple authentication attempts on the same network. To prevent this, I added a flag to stop multiple authentication attempts by the association layer. I also added a check to the wx handler to see if we're connecting to a different network than the one already in progress. This scenario was causing multiple requests on the same network because the network BSSID was not being updated despite the fact that the ESSID changed. Signed-off-by: Joseph Jezak Signed-off-by: John W. Linville --- include/net/ieee80211softmac.h | 1 + net/ieee80211/softmac/ieee80211softmac_assoc.c | 25 ++++++++++++++---- net/ieee80211/softmac/ieee80211softmac_auth.c | 4 +-- net/ieee80211/softmac/ieee80211softmac_wx.c | 36 ++++++++++++++++++++++++-- 4 files changed, 57 insertions(+), 9 deletions(-) diff --git a/include/net/ieee80211softmac.h b/include/net/ieee80211softmac.h index 7a483ab4022f..00ad810eb883 100644 --- a/include/net/ieee80211softmac.h +++ b/include/net/ieee80211softmac.h @@ -104,6 +104,7 @@ struct ieee80211softmac_assoc_info { */ u8 static_essid:1, associating:1, + assoc_wait:1, bssvalid:1, bssfixed:1; diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c index 5e9a90651d04..0af360d9e9a5 100644 --- a/net/ieee80211/softmac/ieee80211softmac_assoc.c +++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c @@ -47,9 +47,7 @@ ieee80211softmac_assoc(struct ieee80211softmac_device *mac, struct ieee80211soft dprintk(KERN_INFO PFX "sent association request!\n"); - /* Change the state to associating */ spin_lock_irqsave(&mac->lock, flags); - mac->associnfo.associating = 1; mac->associated = 0; /* just to make sure */ /* Set a timer for timeout */ @@ -203,6 +201,10 @@ ieee80211softmac_assoc_work(void *d) if (mac->associated) ieee80211softmac_send_disassoc_req(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT); + spin_lock_irqsave(&mac->lock, flags); + mac->associnfo.associating = 1; + spin_unlock_irqrestore(&mac->lock, flags); + /* try to find the requested network in our list, if we found one already */ if (bssvalid || mac->associnfo.bssfixed) found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid); @@ -295,19 +297,32 @@ ieee80211softmac_assoc_work(void *d) memcpy(mac->associnfo.associate_essid.data, found->essid.data, IW_ESSID_MAX_SIZE + 1); /* we found a network! authenticate (if necessary) and associate to it. */ - if (!found->authenticated) { + if (found->authenticating) { + dprintk(KERN_INFO PFX "Already requested authentication, waiting...\n"); + if(!mac->associnfo.assoc_wait) { + mac->associnfo.assoc_wait = 1; + ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL); + } + return; + } + if (!found->authenticated && !found->authenticating) { /* This relies on the fact that _auth_req only queues the work, * otherwise adding the notification would be racy. */ if (!ieee80211softmac_auth_req(mac, found)) { - dprintk(KERN_INFO PFX "cannot associate without being authenticated, requested authentication\n"); - ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL); + if(!mac->associnfo.assoc_wait) { + dprintk(KERN_INFO PFX "Cannot associate without being authenticated, requested authentication\n"); + mac->associnfo.assoc_wait = 1; + ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL); + } } else { printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n"); + mac->associnfo.assoc_wait = 0; ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found); } return; } /* finally! now we can start associating */ + mac->associnfo.assoc_wait = 0; ieee80211softmac_assoc(mac, found); } diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c index 90b8484e509b..ebc33ca6e692 100644 --- a/net/ieee80211/softmac/ieee80211softmac_auth.c +++ b/net/ieee80211/softmac/ieee80211softmac_auth.c @@ -36,8 +36,9 @@ ieee80211softmac_auth_req(struct ieee80211softmac_device *mac, struct ieee80211softmac_auth_queue_item *auth; unsigned long flags; - if (net->authenticating) + if (net->authenticating || net->authenticated) return 0; + net->authenticating = 1; /* Add the network if it's not already added */ ieee80211softmac_add_network(mac, net); @@ -92,7 +93,6 @@ ieee80211softmac_auth_queue(void *data) return; } net->authenticated = 0; - net->authenticating = 1; /* add a timeout call so we eventually give up waiting for an auth reply */ schedule_delayed_work(&auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT); auth->retry--; diff --git a/net/ieee80211/softmac/ieee80211softmac_wx.c b/net/ieee80211/softmac/ieee80211softmac_wx.c index 0e65ff4e33fc..75320b6842ab 100644 --- a/net/ieee80211/softmac/ieee80211softmac_wx.c +++ b/net/ieee80211/softmac/ieee80211softmac_wx.c @@ -70,12 +70,44 @@ ieee80211softmac_wx_set_essid(struct net_device *net_dev, char *extra) { struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); + struct ieee80211softmac_network *n; + struct ieee80211softmac_auth_queue_item *authptr; int length = 0; unsigned long flags; - + + /* Check if we're already associating to this or another network + * If it's another network, cancel and start over with our new network + * If it's our network, ignore the change, we're already doing it! + */ + if((sm->associnfo.associating || sm->associated) && + (data->essid.flags && data->essid.length && extra)) { + /* Get the associating network */ + n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid); + if(n && n->essid.len == (data->essid.length - 1) && + !memcmp(n->essid.data, extra, n->essid.len)) { + dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n", + MAC_ARG(sm->associnfo.bssid)); + return 0; + } else { + dprintk(KERN_INFO PFX "Canceling existing associate request!\n"); + spin_lock_irqsave(&sm->lock,flags); + /* Cancel assoc work */ + cancel_delayed_work(&sm->associnfo.work); + /* We don't have to do this, but it's a little cleaner */ + list_for_each_entry(authptr, &sm->auth_queue, list) + cancel_delayed_work(&authptr->work); + sm->associnfo.bssvalid = 0; + sm->associnfo.bssfixed = 0; + spin_unlock_irqrestore(&sm->lock,flags); + flush_scheduled_work(); + } + } + + spin_lock_irqsave(&sm->lock, flags); - + sm->associnfo.static_essid = 0; + sm->associnfo.assoc_wait = 0; if (data->essid.flags && data->essid.length && extra /*required?*/) { length = min(data->essid.length - 1, IW_ESSID_MAX_SIZE); -- cgit v1.2.3 From 9f101fc5e5a4810a2560841e4292a782b250cb02 Mon Sep 17 00:00:00 2001 From: Joseph Jezak Date: Sun, 11 Jun 2006 12:01:03 -0400 Subject: [PATCH] SoftMAC: Add network to ieee80211softmac_call_events when associate times out The ieee80211softmac_call_events function, when called with event type IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT should pass the network as the third parameter. This patch does that. Signed-off-by: Joseph Jezak Signed-off-by: John W. Linville --- net/ieee80211/softmac/ieee80211softmac_assoc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c index 0af360d9e9a5..7f6ef0eee0ba 100644 --- a/net/ieee80211/softmac/ieee80211softmac_assoc.c +++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c @@ -61,6 +61,7 @@ void ieee80211softmac_assoc_timeout(void *d) { struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d; + struct ieee80211softmac_network *n; unsigned long flags; spin_lock_irqsave(&mac->lock, flags); @@ -73,11 +74,12 @@ ieee80211softmac_assoc_timeout(void *d) mac->associnfo.associating = 0; mac->associnfo.bssvalid = 0; mac->associated = 0; + + n = ieee80211softmac_get_network_by_bssid_locked(mac, mac->associnfo.bssid); spin_unlock_irqrestore(&mac->lock, flags); dprintk(KERN_INFO PFX "assoc request timed out!\n"); - /* FIXME: we need to know the network here. that requires a bit of restructuring */ - ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, NULL); + ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, n); } void -- cgit v1.2.3 From 8f7eb40749f4b0cf9d3892d9680c97b1bfce6de6 Mon Sep 17 00:00:00 2001 From: Horms Date: Mon, 26 Jun 2006 17:44:38 +0900 Subject: [PATCH] CONFIG_WIRELESS_EXT is neccessary after all WARNING: /lib/modules/2.6.17-mm2/kernel/net/ieee80211/ieee80211.ko needs unknown symbol wireless_spy_update Someone removed the `#ifdef CONFIG_WIRELESS_EXT' from around the callsite in net/ieee80211/ieee80211_rx.c and didn't update Kconfig appropriately. The offending patchset seems to be 35c14b855f52c49e4f3d078b9532b056005ed321 which is tittled [PATCH] ieee80211: remove unnecessary CONFIG_WIRELESS_EXT checking After a quick look it seems that wireless_spy_update() lives in net/core/wirless.c, and that file is only compiled if CONFIG_WIRELESS_EXT is set. Perhaps this is Kconig work, but in the mean time here is a reversal of the recent change. Signed-Off-By: Horms Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_rx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index dd746e38db5d..72d4d4e04d42 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c @@ -368,6 +368,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, /* Put this code here so that we avoid duplicating it in all * Rx paths. - Jean II */ +#ifdef CONFIG_WIRELESS_EXT #ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ /* If spy monitoring on */ if (ieee->spy_data.spy_number > 0) { @@ -396,6 +397,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, wireless_spy_update(ieee->dev, hdr->addr2, &wstats); } #endif /* IW_WIRELESS_SPY */ +#endif /* CONFIG_WIRELESS_EXT */ #ifdef NOT_YET hostap_update_rx_stats(local->ap, hdr, rx_stats); -- cgit v1.2.3 From 4a232e725b5cc1bc7fc5b177424a9ff8313b23ad Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Mon, 26 Jun 2006 16:34:29 -0400 Subject: [PATCH] softmac: fix build-break from 881ee6999d66c8fc903b429b73bbe6045b38c549 Signed-off-by: John W. Linville --- net/ieee80211/softmac/ieee80211softmac_assoc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c index 7f6ef0eee0ba..44215ce64d4e 100644 --- a/net/ieee80211/softmac/ieee80211softmac_assoc.c +++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c @@ -303,7 +303,7 @@ ieee80211softmac_assoc_work(void *d) dprintk(KERN_INFO PFX "Already requested authentication, waiting...\n"); if(!mac->associnfo.assoc_wait) { mac->associnfo.assoc_wait = 1; - ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL); + ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL); } return; } @@ -314,7 +314,7 @@ ieee80211softmac_assoc_work(void *d) if(!mac->associnfo.assoc_wait) { dprintk(KERN_INFO PFX "Cannot associate without being authenticated, requested authentication\n"); mac->associnfo.assoc_wait = 1; - ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL); + ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify_auth, NULL, GFP_KERNEL); } } else { printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n"); -- cgit v1.2.3 From e85d0918b54fbd9b38003752f7d665416b06edd8 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Fri, 2 Jun 2006 17:11:32 +0100 Subject: [PATCH] ZyDAS ZD1211 USB-WLAN driver There are 60+ USB wifi adapters available on the market based on the ZyDAS ZD1211 chip. Unlike the predecessor (ZD1201), ZD1211 does not have a hardware MAC, so most data operations are coordinated by the device driver. The ZD1211 chip sits alongside an RF transceiver which is also controlled by the driver. Our driver currently supports 2 RF types, we know of one other available in a few marketed products which we will be supporting soon. Our driver also supports the newer revision of ZD1211, called ZD1211B. The initialization and RF operations are slightly different for the new revision, but the main difference is 802.11e support. Our driver does not support the QoS features yet, but we think we know how to use them. This driver is based on ZyDAS's own GPL driver available from www.zydas.com.tw. ZyDAS engineers have been responsive and supportive of our efforts, so thumbs up to them. Additionally, the firmware is redistributable and they have provided device specs. This driver has been written primarily by Ulrich Kunitz and myself. Graham Gower, Greg KH, Remco and Bryan Rittmeyer have also contributed. The developers of ieee80211 and softmac have made our lives so much easier- thanks! We maintain a small info-page: http://zd1211.ath.cx/wiki/DriverRewrite If there is enough time for review, we would like to aim for inclusion in 2.6.18. The driver works nicely as a STA, and can connect to both open and encrypted networks (we are using software-based encryption for now). We will work towards supporting more advanced features in the future (ad-hoc, master mode, 802.11a, ...). Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/Makefile | 1 + drivers/net/wireless/zd1211rw/Kconfig | 19 + drivers/net/wireless/zd1211rw/Makefile | 11 + drivers/net/wireless/zd1211rw/zd_chip.c | 1615 ++++++++++++++++++++++++++ drivers/net/wireless/zd1211rw/zd_chip.h | 825 +++++++++++++ drivers/net/wireless/zd1211rw/zd_def.h | 48 + drivers/net/wireless/zd1211rw/zd_ieee80211.c | 191 +++ drivers/net/wireless/zd1211rw/zd_ieee80211.h | 85 ++ drivers/net/wireless/zd1211rw/zd_mac.c | 1055 +++++++++++++++++ drivers/net/wireless/zd1211rw/zd_mac.h | 190 +++ drivers/net/wireless/zd1211rw/zd_netdev.c | 267 +++++ drivers/net/wireless/zd1211rw/zd_netdev.h | 45 + drivers/net/wireless/zd1211rw/zd_rf.c | 151 +++ drivers/net/wireless/zd1211rw/zd_rf.h | 82 ++ drivers/net/wireless/zd1211rw/zd_rf_al2230.c | 308 +++++ drivers/net/wireless/zd1211rw/zd_rf_rf2959.c | 279 +++++ drivers/net/wireless/zd1211rw/zd_types.h | 71 ++ drivers/net/wireless/zd1211rw/zd_usb.c | 1316 +++++++++++++++++++++ drivers/net/wireless/zd1211rw/zd_usb.h | 240 ++++ drivers/net/wireless/zd1211rw/zd_util.c | 82 ++ drivers/net/wireless/zd1211rw/zd_util.h | 29 + 22 files changed, 6911 insertions(+) create mode 100644 drivers/net/wireless/zd1211rw/Kconfig create mode 100644 drivers/net/wireless/zd1211rw/Makefile create mode 100644 drivers/net/wireless/zd1211rw/zd_chip.c create mode 100644 drivers/net/wireless/zd1211rw/zd_chip.h create mode 100644 drivers/net/wireless/zd1211rw/zd_def.h create mode 100644 drivers/net/wireless/zd1211rw/zd_ieee80211.c create mode 100644 drivers/net/wireless/zd1211rw/zd_ieee80211.h create mode 100644 drivers/net/wireless/zd1211rw/zd_mac.c create mode 100644 drivers/net/wireless/zd1211rw/zd_mac.h create mode 100644 drivers/net/wireless/zd1211rw/zd_netdev.c create mode 100644 drivers/net/wireless/zd1211rw/zd_netdev.h create mode 100644 drivers/net/wireless/zd1211rw/zd_rf.c create mode 100644 drivers/net/wireless/zd1211rw/zd_rf.h create mode 100644 drivers/net/wireless/zd1211rw/zd_rf_al2230.c create mode 100644 drivers/net/wireless/zd1211rw/zd_rf_rf2959.c create mode 100644 drivers/net/wireless/zd1211rw/zd_types.h create mode 100644 drivers/net/wireless/zd1211rw/zd_usb.c create mode 100644 drivers/net/wireless/zd1211rw/zd_usb.h create mode 100644 drivers/net/wireless/zd1211rw/zd_util.c create mode 100644 drivers/net/wireless/zd1211rw/zd_util.h diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 30ec235e6935..fa9d2c4edc93 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -550,6 +550,7 @@ config USB_ZD1201 source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/bcm43xx/Kconfig" +source "drivers/net/wireless/zd1211rw/Kconfig" # yes, this works even when no drivers are selected config NET_WIRELESS diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 512603de309a..c613af17a159 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_HOSTAP) += hostap/ obj-$(CONFIG_BCM43XX) += bcm43xx/ +obj-$(CONFIG_ZD1211RW) += zd1211rw/ # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig new file mode 100644 index 000000000000..66ed55bc5460 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/Kconfig @@ -0,0 +1,19 @@ +config ZD1211RW + tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" + depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + select FW_LOADER + ---help--- + This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless + chip, present in many USB-wireless adapters. + + Device firmware is required alongside this driver. You can download the + firmware distribution from http://zd1211.ath.cx/get-firmware + +config ZD1211RW_DEBUG + bool "ZyDAS ZD1211 debugging" + depends on ZD1211RW + ---help--- + ZD1211 debugging messages. Choosing Y will result in additional debug + messages being saved to your kernel logs, which may help debug any + problems. + diff --git a/drivers/net/wireless/zd1211rw/Makefile b/drivers/net/wireless/zd1211rw/Makefile new file mode 100644 index 000000000000..500314fc74d2 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_ZD1211RW) += zd1211rw.o + +zd1211rw-objs := zd_chip.o zd_ieee80211.o \ + zd_mac.o zd_netdev.o \ + zd_rf_al2230.o zd_rf_rf2959.o \ + zd_rf.o zd_usb.o zd_util.o + +ifeq ($(CONFIG_ZD1211RW_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif + diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c new file mode 100644 index 000000000000..efc9c4bd826f --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -0,0 +1,1615 @@ +/* zd_chip.c + * + * 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 + */ + +/* This file implements all the hardware specific functions for the ZD1211 + * and ZD1211B chips. Support for the ZD1211B was possible after Timothy + * Legge sent me a ZD1211B device. Thank you Tim. -- Uli + */ + +#include +#include + +#include "zd_def.h" +#include "zd_chip.h" +#include "zd_ieee80211.h" +#include "zd_mac.h" +#include "zd_rf.h" +#include "zd_util.h" + +void zd_chip_init(struct zd_chip *chip, + struct net_device *netdev, + struct usb_interface *intf) +{ + memset(chip, 0, sizeof(*chip)); + mutex_init(&chip->mutex); + zd_usb_init(&chip->usb, netdev, intf); + zd_rf_init(&chip->rf); +} + +void zd_chip_clear(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_clear(&chip->usb); + zd_rf_clear(&chip->rf); + mutex_unlock(&chip->mutex); + mutex_destroy(&chip->mutex); + memset(chip, 0, sizeof(*chip)); +} + +static int scnprint_mac_oui(const u8 *addr, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%02x-%02x-%02x", + addr[0], addr[1], addr[2]); +} + +/* Prints an identifier line, which will support debugging. */ +static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size) +{ + int i = 0; + + i = scnprintf(buffer, size, "zd1211%s chip ", + chip->is_zd1211b ? "b" : ""); + i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += scnprint_mac_oui(chip->e2p_mac, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " "); + i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); + i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c", chip->pa_type, + chip->patch_cck_gain ? 'g' : '-', + chip->patch_cr157 ? '7' : '-', + chip->patch_6m_band_edge ? '6' : '-'); + return i; +} + +static void print_id(struct zd_chip *chip) +{ + char buffer[80]; + + scnprint_id(chip, buffer, sizeof(buffer)); + buffer[sizeof(buffer)-1] = 0; + dev_info(zd_chip_dev(chip), "%s\n", buffer); +} + +/* Read a variable number of 32-bit values. Parameter count is not allowed to + * exceed USB_MAX_IOREAD32_COUNT. + */ +int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr, + unsigned int count) +{ + int r; + int i; + zd_addr_t *a16 = (zd_addr_t *)NULL; + u16 *v16; + unsigned int count16; + + if (count > USB_MAX_IOREAD32_COUNT) + return -EINVAL; + + /* Allocate a single memory block for values and addresses. */ + count16 = 2*count; + a16 = (zd_addr_t *)kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)), + GFP_NOFS); + if (!a16) { + dev_dbg_f(zd_chip_dev(chip), + "error ENOMEM in allocation of a16\n"); + r = -ENOMEM; + goto out; + } + v16 = (u16 *)(a16 + count16); + + for (i = 0; i < count; i++) { + int j = 2*i; + /* We read the high word always first. */ + a16[j] = zd_inc_word(addr[i]); + a16[j+1] = addr[i]; + } + + r = zd_ioread16v_locked(chip, v16, a16, count16); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error: zd_ioread16v_locked. Error number %d\n", r); + goto out; + } + + for (i = 0; i < count; i++) { + int j = 2*i; + values[i] = (v16[j] << 16) | v16[j+1]; + } + +out: + kfree((void *)a16); + return r; +} + +int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int i, j, r; + struct zd_ioreq16 *ioreqs16; + unsigned int count16; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + + if (count == 0) + return 0; + if (count > USB_MAX_IOWRITE32_COUNT) + return -EINVAL; + + /* Allocate a single memory block for values and addresses. */ + count16 = 2*count; + ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_NOFS); + if (!ioreqs16) { + r = -ENOMEM; + dev_dbg_f(zd_chip_dev(chip), + "error %d in ioreqs16 allocation\n", r); + goto out; + } + + for (i = 0; i < count; i++) { + j = 2*i; + /* We write the high word always first. */ + ioreqs16[j].value = ioreqs[i].value >> 16; + ioreqs16[j].addr = zd_inc_word(ioreqs[i].addr); + ioreqs16[j+1].value = ioreqs[i].value; + ioreqs16[j+1].addr = ioreqs[i].addr; + } + + r = zd_usb_iowrite16v(&chip->usb, ioreqs16, count16); +#ifdef DEBUG + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error %d in zd_usb_write16v\n", r); + } +#endif /* DEBUG */ +out: + kfree(ioreqs16); + return r; +} + +int zd_iowrite16a_locked(struct zd_chip *chip, + const struct zd_ioreq16 *ioreqs, unsigned int count) +{ + int r; + unsigned int i, j, t, max; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + for (i = 0; i < count; i += j + t) { + t = 0; + max = count-i; + if (max > USB_MAX_IOWRITE16_COUNT) + max = USB_MAX_IOWRITE16_COUNT; + for (j = 0; j < max; j++) { + if (!ioreqs[i+j].addr) { + t = 1; + break; + } + } + + r = zd_usb_iowrite16v(&chip->usb, &ioreqs[i], j); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error zd_usb_iowrite16v. Error number %d\n", + r); + return r; + } + } + + return 0; +} + +/* Writes a variable number of 32 bit registers. The functions will split + * that in several USB requests. A split can be forced by inserting an IO + * request with an zero address field. + */ +int zd_iowrite32a_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, unsigned int count) +{ + int r; + unsigned int i, j, t, max; + + for (i = 0; i < count; i += j + t) { + t = 0; + max = count-i; + if (max > USB_MAX_IOWRITE32_COUNT) + max = USB_MAX_IOWRITE32_COUNT; + for (j = 0; j < max; j++) { + if (!ioreqs[i+j].addr) { + t = 1; + break; + } + } + + r = _zd_iowrite32v_locked(chip, &ioreqs[i], j); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error _zd_iowrite32v_locked." + " Error number %d\n", r); + return r; + } + } + + return 0; +} + +int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_ioread16_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_ioread32_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_iowrite16_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, value, addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, + u32 *values, unsigned int count) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_ioread32v_locked(chip, values, addresses, count); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count) +{ + int r; + + ZD_ASSERT(!mutex_is_locked(&chip->mutex)); + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, ioreqs, count); + mutex_unlock(&chip->mutex); + return r; +} + +static int read_pod(struct zd_chip *chip, u8 *rf_type) +{ + int r; + u32 value; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &value, E2P_POD); + if (r) + goto error; + dev_dbg_f(zd_chip_dev(chip), "E2P_POD %#010x\n", value); + + /* FIXME: AL2230 handling (Bit 7 in POD) */ + *rf_type = value & 0x0f; + chip->pa_type = (value >> 16) & 0x0f; + chip->patch_cck_gain = (value >> 8) & 0x1; + chip->patch_cr157 = (value >> 13) & 0x1; + chip->patch_6m_band_edge = (value >> 21) & 0x1; + + dev_dbg_f(zd_chip_dev(chip), + "RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d " + "patch 6M %d\n", + zd_rf_name(*rf_type), *rf_type, + chip->pa_type, chip->patch_cck_gain, + chip->patch_cr157, chip->patch_6m_band_edge); + return 0; +error: + *rf_type = 0; + chip->pa_type = 0; + chip->patch_cck_gain = 0; + chip->patch_cr157 = 0; + chip->patch_6m_band_edge = 0; + return r; +} + +static int _read_mac_addr(struct zd_chip *chip, u8 *mac_addr, + const zd_addr_t *addr) +{ + int r; + u32 parts[2]; + + r = zd_ioread32v_locked(chip, parts, (const zd_addr_t *)addr, 2); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error: couldn't read e2p macs. Error number %d\n", r); + return r; + } + + mac_addr[0] = parts[0]; + mac_addr[1] = parts[0] >> 8; + mac_addr[2] = parts[0] >> 16; + mac_addr[3] = parts[0] >> 24; + mac_addr[4] = parts[1]; + mac_addr[5] = parts[1] >> 8; + + return 0; +} + +static int read_e2p_mac_addr(struct zd_chip *chip) +{ + static const zd_addr_t addr[2] = { E2P_MAC_ADDR_P1, E2P_MAC_ADDR_P2 }; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return _read_mac_addr(chip, chip->e2p_mac, (const zd_addr_t *)addr); +} + +/* MAC address: if custom mac addresses are to to be used CR_MAC_ADDR_P1 and + * CR_MAC_ADDR_P2 must be overwritten + */ +void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + mutex_lock(&chip->mutex); + memcpy(mac_addr, chip->e2p_mac, ETH_ALEN); + mutex_unlock(&chip->mutex); +} + +static int read_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + static const zd_addr_t addr[2] = { CR_MAC_ADDR_P1, CR_MAC_ADDR_P2 }; + return _read_mac_addr(chip, mac_addr, (const zd_addr_t *)addr); +} + +int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr) +{ + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + mutex_lock(&chip->mutex); + r = read_mac_addr(chip, mac_addr); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr) +{ + int r; + struct zd_ioreq32 reqs[2] = { + [0] = { .addr = CR_MAC_ADDR_P1 }, + [1] = { .addr = CR_MAC_ADDR_P2 }, + }; + + reqs[0].value = (mac_addr[3] << 24) + | (mac_addr[2] << 16) + | (mac_addr[1] << 8) + | mac_addr[0]; + reqs[1].value = (mac_addr[5] << 8) + | mac_addr[4]; + + dev_dbg_f(zd_chip_dev(chip), + "mac addr " MAC_FMT "\n", MAC_ARG(mac_addr)); + + mutex_lock(&chip->mutex); + r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); +#ifdef DEBUG + { + u8 tmp[ETH_ALEN]; + read_mac_addr(chip, tmp); + } +#endif /* DEBUG */ + mutex_unlock(&chip->mutex); + return r; +} + +int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain) +{ + int r; + u32 value; + + mutex_lock(&chip->mutex); + r = zd_ioread32_locked(chip, &value, E2P_SUBID); + mutex_unlock(&chip->mutex); + if (r) + return r; + + *regdomain = value >> 16; + dev_dbg_f(zd_chip_dev(chip), "regdomain: %#04x\n", *regdomain); + + return 0; +} + +static int read_values(struct zd_chip *chip, u8 *values, size_t count, + zd_addr_t e2p_addr, u32 guard) +{ + int r; + int i; + u32 v; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + for (i = 0;;) { + r = zd_ioread32_locked(chip, &v, e2p_addr+i/2); + if (r) + return r; + v -= guard; + if (i+4 < count) { + values[i++] = v; + values[i++] = v >> 8; + values[i++] = v >> 16; + values[i++] = v >> 24; + continue; + } + for (;i < count; i++) + values[i] = v >> (8*(i%3)); + return 0; + } +} + +static int read_pwr_cal_values(struct zd_chip *chip) +{ + return read_values(chip, chip->pwr_cal_values, + E2P_CHANNEL_COUNT, E2P_PWR_CAL_VALUE1, + 0); +} + +static int read_pwr_int_values(struct zd_chip *chip) +{ + return read_values(chip, chip->pwr_int_values, + E2P_CHANNEL_COUNT, E2P_PWR_INT_VALUE1, + E2P_PWR_INT_GUARD); +} + +static int read_ofdm_cal_values(struct zd_chip *chip) +{ + int r; + int i; + static const zd_addr_t addresses[] = { + E2P_36M_CAL_VALUE1, + E2P_48M_CAL_VALUE1, + E2P_54M_CAL_VALUE1, + }; + + for (i = 0; i < 3; i++) { + r = read_values(chip, chip->ofdm_cal_values[i], + E2P_CHANNEL_COUNT, addresses[i], 0); + if (r) + return r; + } + return 0; +} + +static int read_cal_int_tables(struct zd_chip *chip) +{ + int r; + + r = read_pwr_cal_values(chip); + if (r) + return r; + r = read_pwr_int_values(chip); + if (r) + return r; + r = read_ofdm_cal_values(chip); + if (r) + return r; + return 0; +} + +/* phy means physical registers */ +int zd_chip_lock_phy_regs(struct zd_chip *chip) +{ + int r; + u32 tmp; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &tmp, CR_REG1); + if (r) { + dev_err(zd_chip_dev(chip), "error ioread32(CR_REG1): %d\n", r); + return r; + } + + dev_dbg_f(zd_chip_dev(chip), + "CR_REG1: 0x%02x -> 0x%02x\n", tmp, tmp & ~UNLOCK_PHY_REGS); + tmp &= ~UNLOCK_PHY_REGS; + + r = zd_iowrite32_locked(chip, tmp, CR_REG1); + if (r) + dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); + return r; +} + +int zd_chip_unlock_phy_regs(struct zd_chip *chip) +{ + int r; + u32 tmp; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &tmp, CR_REG1); + if (r) { + dev_err(zd_chip_dev(chip), + "error ioread32(CR_REG1): %d\n", r); + return r; + } + + dev_dbg_f(zd_chip_dev(chip), + "CR_REG1: 0x%02x -> 0x%02x\n", tmp, tmp | UNLOCK_PHY_REGS); + tmp |= UNLOCK_PHY_REGS; + + r = zd_iowrite32_locked(chip, tmp, CR_REG1); + if (r) + dev_err(zd_chip_dev(chip), "error iowrite32(CR_REG1): %d\n", r); + return r; +} + +/* CR157 can be optionally patched by the EEPROM */ +static int patch_cr157(struct zd_chip *chip) +{ + int r; + u32 value; + + if (!chip->patch_cr157) + return 0; + + r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); + if (r) + return r; + + dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value >> 8); + return zd_iowrite32_locked(chip, value >> 8, CR157); +} + +/* + * 6M band edge can be optionally overwritten for certain RF's + * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge + * bit (for AL2230, AL2230S) + */ +static int patch_6m_band_edge(struct zd_chip *chip, int channel) +{ + struct zd_ioreq16 ioreqs[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR47, 0x1e }, + }; + + if (!chip->patch_6m_band_edge || !chip->rf.patch_6m_band_edge) + return 0; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1 || channel == 11) + ioreqs[0].value = 0x12; + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211_hw_reset_phy(struct zd_chip *chip) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR0, 0x0a }, { CR1, 0x06 }, { CR2, 0x26 }, + { CR3, 0x38 }, { CR4, 0x80 }, { CR9, 0xa0 }, + { CR10, 0x81 }, { CR11, 0x00 }, { CR12, 0x7f }, + { CR13, 0x8c }, { CR14, 0x80 }, { CR15, 0x3d }, + { CR16, 0x20 }, { CR17, 0x1e }, { CR18, 0x0a }, + { CR19, 0x48 }, { CR20, 0x0c }, { CR21, 0x0c }, + { CR22, 0x23 }, { CR23, 0x90 }, { CR24, 0x14 }, + { CR25, 0x40 }, { CR26, 0x10 }, { CR27, 0x19 }, + { CR28, 0x7f }, { CR29, 0x80 }, { CR30, 0x4b }, + { CR31, 0x60 }, { CR32, 0x43 }, { CR33, 0x08 }, + { CR34, 0x06 }, { CR35, 0x0a }, { CR36, 0x00 }, + { CR37, 0x00 }, { CR38, 0x38 }, { CR39, 0x0c }, + { CR40, 0x84 }, { CR41, 0x2a }, { CR42, 0x80 }, + { CR43, 0x10 }, { CR44, 0x12 }, { CR46, 0xff }, + { CR47, 0x1E }, { CR48, 0x26 }, { CR49, 0x5b }, + { CR64, 0xd0 }, { CR65, 0x04 }, { CR66, 0x58 }, + { CR67, 0xc9 }, { CR68, 0x88 }, { CR69, 0x41 }, + { CR70, 0x23 }, { CR71, 0x10 }, { CR72, 0xff }, + { CR73, 0x32 }, { CR74, 0x30 }, { CR75, 0x65 }, + { CR76, 0x41 }, { CR77, 0x1b }, { CR78, 0x30 }, + { CR79, 0x68 }, { CR80, 0x64 }, { CR81, 0x64 }, + { CR82, 0x00 }, { CR83, 0x00 }, { CR84, 0x00 }, + { CR85, 0x02 }, { CR86, 0x00 }, { CR87, 0x00 }, + { CR88, 0xff }, { CR89, 0xfc }, { CR90, 0x00 }, + { CR91, 0x00 }, { CR92, 0x00 }, { CR93, 0x08 }, + { CR94, 0x00 }, { CR95, 0x00 }, { CR96, 0xff }, + { CR97, 0xe7 }, { CR98, 0x00 }, { CR99, 0x00 }, + { CR100, 0x00 }, { CR101, 0xae }, { CR102, 0x02 }, + { CR103, 0x00 }, { CR104, 0x03 }, { CR105, 0x65 }, + { CR106, 0x04 }, { CR107, 0x00 }, { CR108, 0x0a }, + { CR109, 0xaa }, { CR110, 0xaa }, { CR111, 0x25 }, + { CR112, 0x25 }, { CR113, 0x00 }, { CR119, 0x1e }, + { CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 }, + { }, + { CR5, 0x00 }, { CR6, 0x00 }, { CR7, 0x00 }, + { CR8, 0x00 }, { CR9, 0x20 }, { CR12, 0xf0 }, + { CR20, 0x0e }, { CR21, 0x0e }, { CR27, 0x10 }, + { CR44, 0x33 }, { CR47, 0x1E }, { CR83, 0x24 }, + { CR84, 0x04 }, { CR85, 0x00 }, { CR86, 0x0C }, + { CR87, 0x12 }, { CR88, 0x0C }, { CR89, 0x00 }, + { CR90, 0x10 }, { CR91, 0x08 }, { CR93, 0x00 }, + { CR94, 0x01 }, { CR95, 0x00 }, { CR96, 0x50 }, + { CR97, 0x37 }, { CR98, 0x35 }, { CR101, 0x13 }, + { CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 }, + { CR105, 0x12 }, { CR109, 0x27 }, { CR110, 0x27 }, + { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, + { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, + { CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f }, + { CR123, 0x27 }, { CR125, 0xaa }, { CR127, 0x03 }, + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR131, 0x0C }, { CR136, 0xdf }, { CR137, 0x40 }, + { CR138, 0xa0 }, { CR139, 0xb0 }, { CR140, 0x99 }, + { CR141, 0x82 }, { CR142, 0x54 }, { CR143, 0x1c }, + { CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x4c }, + { CR149, 0x50 }, { CR150, 0x0e }, { CR151, 0x18 }, + { CR160, 0xfe }, { CR161, 0xee }, { CR162, 0xaa }, + { CR163, 0xfa }, { CR164, 0xfa }, { CR165, 0xea }, + { CR166, 0xbe }, { CR167, 0xbe }, { CR168, 0x6a }, + { CR169, 0xba }, { CR170, 0xba }, { CR171, 0xba }, + /* Note: CR204 must lead the CR203 */ + { CR204, 0x7d }, + { }, + { CR203, 0x30 }, + }; + + int r, t; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + goto unlock; + + r = patch_cr157(chip); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + return r; +} + +static int zd1211b_hw_reset_phy(struct zd_chip *chip) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR0, 0x14 }, { CR1, 0x06 }, { CR2, 0x26 }, + { CR3, 0x38 }, { CR4, 0x80 }, { CR9, 0xe0 }, + { CR10, 0x81 }, + /* power control { { CR11, 1 << 6 }, */ + { CR11, 0x00 }, + { CR12, 0xf0 }, { CR13, 0x8c }, { CR14, 0x80 }, + { CR15, 0x3d }, { CR16, 0x20 }, { CR17, 0x1e }, + { CR18, 0x0a }, { CR19, 0x48 }, + { CR20, 0x10 }, /* Org:0x0E, ComTrend:RalLink AP */ + { CR21, 0x0e }, { CR22, 0x23 }, { CR23, 0x90 }, + { CR24, 0x14 }, { CR25, 0x40 }, { CR26, 0x10 }, + { CR27, 0x10 }, { CR28, 0x7f }, { CR29, 0x80 }, + { CR30, 0x49 }, /* jointly decoder, no ASIC */ + { CR31, 0x60 }, { CR32, 0x43 }, { CR33, 0x08 }, + { CR34, 0x06 }, { CR35, 0x0a }, { CR36, 0x00 }, + { CR37, 0x00 }, { CR38, 0x38 }, { CR39, 0x0c }, + { CR40, 0x84 }, { CR41, 0x2a }, { CR42, 0x80 }, + { CR43, 0x10 }, { CR44, 0x33 }, { CR46, 0xff }, + { CR47, 0x1E }, { CR48, 0x26 }, { CR49, 0x5b }, + { CR64, 0xd0 }, { CR65, 0x04 }, { CR66, 0x58 }, + { CR67, 0xc9 }, { CR68, 0x88 }, { CR69, 0x41 }, + { CR70, 0x23 }, { CR71, 0x10 }, { CR72, 0xff }, + { CR73, 0x32 }, { CR74, 0x30 }, { CR75, 0x65 }, + { CR76, 0x41 }, { CR77, 0x1b }, { CR78, 0x30 }, + { CR79, 0xf0 }, { CR80, 0x64 }, { CR81, 0x64 }, + { CR82, 0x00 }, { CR83, 0x24 }, { CR84, 0x04 }, + { CR85, 0x00 }, { CR86, 0x0c }, { CR87, 0x12 }, + { CR88, 0x0c }, { CR89, 0x00 }, { CR90, 0x58 }, + { CR91, 0x04 }, { CR92, 0x00 }, { CR93, 0x00 }, + { CR94, 0x01 }, + { CR95, 0x20 }, /* ZD1211B */ + { CR96, 0x50 }, { CR97, 0x37 }, { CR98, 0x35 }, + { CR99, 0x00 }, { CR100, 0x01 }, { CR101, 0x13 }, + { CR102, 0x27 }, { CR103, 0x27 }, { CR104, 0x18 }, + { CR105, 0x12 }, { CR106, 0x04 }, { CR107, 0x00 }, + { CR108, 0x0a }, { CR109, 0x27 }, { CR110, 0x27 }, + { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, + { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, + { CR117, 0xfc }, { CR118, 0xfa }, { CR119, 0x1e }, + { CR125, 0x90 }, { CR126, 0x00 }, { CR127, 0x00 }, + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR131, 0x0c }, { CR136, 0xdf }, { CR137, 0xa0 }, + { CR138, 0xa8 }, { CR139, 0xb4 }, { CR140, 0x98 }, + { CR141, 0x82 }, { CR142, 0x53 }, { CR143, 0x1c }, + { CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x40 }, + { CR149, 0x40 }, /* Org:0x50 ComTrend:RalLink AP */ + { CR150, 0x14 }, /* Org:0x0E ComTrend:RalLink AP */ + { CR151, 0x18 }, { CR159, 0x70 }, { CR160, 0xfe }, + { CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa }, + { CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe }, + { CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba }, + { CR170, 0xba }, { CR171, 0xba }, + /* Note: CR204 must lead the CR203 */ + { CR204, 0x7d }, + {}, + { CR203, 0x30 }, + }; + + int r, t; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + goto unlock; + + r = patch_cr157(chip); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + return r; +} + +static int hw_reset_phy(struct zd_chip *chip) +{ + return chip->is_zd1211b ? zd1211b_hw_reset_phy(chip) : + zd1211_hw_reset_phy(chip); +} + +static int zd1211_hw_init_hmac(struct zd_chip *chip) +{ + static const struct zd_ioreq32 ioreqs[] = { + { CR_ACK_TIMEOUT_EXT, 0x20 }, + { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, + { CR_ZD1211_RETRY_MAX, 0x2 }, + { CR_SNIFFER_ON, 0 }, + { CR_RX_FILTER, AP_RX_FILTER }, + { CR_GROUP_HASH_P1, 0x00 }, + { CR_GROUP_HASH_P2, 0x80000000 }, + { CR_REG1, 0xa4 }, + { CR_ADDA_PWR_DWN, 0x7f }, + { CR_BCN_PLCP_CFG, 0x00f00401 }, + { CR_PHY_DELAY, 0x00 }, + { CR_ACK_TIMEOUT_EXT, 0x80 }, + { CR_ADDA_PWR_DWN, 0x00 }, + { CR_ACK_TIME_80211, 0x100 }, + { CR_IFS_VALUE, 0x547c032 }, + { CR_RX_PE_DELAY, 0x70 }, + { CR_PS_CTRL, 0x10000000 }, + { CR_RTS_CTS_RATE, 0x02030203 }, + { CR_RX_THRESHOLD, 0x000c0640 }, + { CR_AFTER_PNP, 0x1 }, + { CR_WEP_PROTECT, 0x114 }, + }; + + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +#ifdef DEBUG + if (r) { + dev_err(zd_chip_dev(chip), + "error in zd_iowrite32a_locked. Error number %d\n", r); + } +#endif /* DEBUG */ + return r; +} + +static int zd1211b_hw_init_hmac(struct zd_chip *chip) +{ + static const struct zd_ioreq32 ioreqs[] = { + { CR_ACK_TIMEOUT_EXT, 0x20 }, + { CR_ADDA_MBIAS_WARMTIME, 0x30000808 }, + { CR_ZD1211B_RETRY_MAX, 0x02020202 }, + { CR_ZD1211B_TX_PWR_CTL4, 0x007f003f }, + { CR_ZD1211B_TX_PWR_CTL3, 0x007f003f }, + { CR_ZD1211B_TX_PWR_CTL2, 0x003f001f }, + { CR_ZD1211B_TX_PWR_CTL1, 0x001f000f }, + { CR_ZD1211B_AIFS_CTL1, 0x00280028 }, + { CR_ZD1211B_AIFS_CTL2, 0x008C003C }, + { CR_ZD1211B_TXOP, 0x01800824 }, + { CR_SNIFFER_ON, 0 }, + { CR_RX_FILTER, AP_RX_FILTER }, + { CR_GROUP_HASH_P1, 0x00 }, + { CR_GROUP_HASH_P2, 0x80000000 }, + { CR_REG1, 0xa4 }, + { CR_ADDA_PWR_DWN, 0x7f }, + { CR_BCN_PLCP_CFG, 0x00f00401 }, + { CR_PHY_DELAY, 0x00 }, + { CR_ACK_TIMEOUT_EXT, 0x80 }, + { CR_ADDA_PWR_DWN, 0x00 }, + { CR_ACK_TIME_80211, 0x100 }, + { CR_IFS_VALUE, 0x547c032 }, + { CR_RX_PE_DELAY, 0x70 }, + { CR_PS_CTRL, 0x10000000 }, + { CR_RTS_CTS_RATE, 0x02030203 }, + { CR_RX_THRESHOLD, 0x000c0640 }, + { CR_AFTER_PNP, 0x1 }, + { CR_WEP_PROTECT, 0x114 }, + }; + + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error in zd_iowrite32a_locked. Error number %d\n", r); + } + return r; +} + +static int hw_init_hmac(struct zd_chip *chip) +{ + return chip->is_zd1211b ? + zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip); +} + +struct aw_pt_bi { + u32 atim_wnd_period; + u32 pre_tbtt; + u32 beacon_interval; +}; + +static int get_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) +{ + int r; + static const zd_addr_t aw_pt_bi_addr[] = + { CR_ATIM_WND_PERIOD, CR_PRE_TBTT, CR_BCN_INTERVAL }; + u32 values[3]; + + r = zd_ioread32v_locked(chip, values, (const zd_addr_t *)aw_pt_bi_addr, + ARRAY_SIZE(aw_pt_bi_addr)); + if (r) { + memset(s, 0, sizeof(*s)); + return r; + } + + s->atim_wnd_period = values[0]; + s->pre_tbtt = values[1]; + s->beacon_interval = values[2]; + dev_dbg_f(zd_chip_dev(chip), "aw %u pt %u bi %u\n", + s->atim_wnd_period, s->pre_tbtt, s->beacon_interval); + return 0; +} + +static int set_aw_pt_bi(struct zd_chip *chip, struct aw_pt_bi *s) +{ + struct zd_ioreq32 reqs[3]; + + if (s->beacon_interval <= 5) + s->beacon_interval = 5; + if (s->pre_tbtt < 4 || s->pre_tbtt >= s->beacon_interval) + s->pre_tbtt = s->beacon_interval - 1; + if (s->atim_wnd_period >= s->pre_tbtt) + s->atim_wnd_period = s->pre_tbtt - 1; + + reqs[0].addr = CR_ATIM_WND_PERIOD; + reqs[0].value = s->atim_wnd_period; + reqs[1].addr = CR_PRE_TBTT; + reqs[1].value = s->pre_tbtt; + reqs[2].addr = CR_BCN_INTERVAL; + reqs[2].value = s->beacon_interval; + + dev_dbg_f(zd_chip_dev(chip), + "aw %u pt %u bi %u\n", s->atim_wnd_period, s->pre_tbtt, + s->beacon_interval); + return zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs)); +} + + +static int set_beacon_interval(struct zd_chip *chip, u32 interval) +{ + int r; + struct aw_pt_bi s; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = get_aw_pt_bi(chip, &s); + if (r) + return r; + s.beacon_interval = interval; + return set_aw_pt_bi(chip, &s); +} + +int zd_set_beacon_interval(struct zd_chip *chip, u32 interval) +{ + int r; + + mutex_lock(&chip->mutex); + r = set_beacon_interval(chip, interval); + mutex_unlock(&chip->mutex); + return r; +} + +static int hw_init(struct zd_chip *chip) +{ + int r; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = hw_reset_phy(chip); + if (r) + return r; + + r = hw_init_hmac(chip); + if (r) + return r; + r = set_beacon_interval(chip, 100); + if (r) + return r; + return 0; +} + +#ifdef DEBUG +static int dump_cr(struct zd_chip *chip, const zd_addr_t addr, + const char *addr_string) +{ + int r; + u32 value; + + r = zd_ioread32_locked(chip, &value, addr); + if (r) { + dev_dbg_f(zd_chip_dev(chip), + "error reading %s. Error number %d\n", addr_string, r); + return r; + } + + dev_dbg_f(zd_chip_dev(chip), "%s %#010x\n", + addr_string, (unsigned int)value); + return 0; +} + +static int test_init(struct zd_chip *chip) +{ + int r; + + r = dump_cr(chip, CR_AFTER_PNP, "CR_AFTER_PNP"); + if (r) + return r; + r = dump_cr(chip, CR_GPI_EN, "CR_GPI_EN"); + if (r) + return r; + return dump_cr(chip, CR_INTERRUPT, "CR_INTERRUPT"); +} + +static void dump_fw_registers(struct zd_chip *chip) +{ + static const zd_addr_t addr[4] = { + FW_FIRMWARE_VER, FW_USB_SPEED, FW_FIX_TX_RATE, + FW_LINK_STATUS + }; + + int r; + u16 values[4]; + + r = zd_ioread16v_locked(chip, values, (const zd_addr_t*)addr, + ARRAY_SIZE(addr)); + if (r) { + dev_dbg_f(zd_chip_dev(chip), "error %d zd_ioread16v_locked\n", + r); + return; + } + + dev_dbg_f(zd_chip_dev(chip), "FW_FIRMWARE_VER %#06hx\n", values[0]); + dev_dbg_f(zd_chip_dev(chip), "FW_USB_SPEED %#06hx\n", values[1]); + dev_dbg_f(zd_chip_dev(chip), "FW_FIX_TX_RATE %#06hx\n", values[2]); + dev_dbg_f(zd_chip_dev(chip), "FW_LINK_STATUS %#06hx\n", values[3]); +} +#endif /* DEBUG */ + +static int print_fw_version(struct zd_chip *chip) +{ + int r; + u16 version; + + r = zd_ioread16_locked(chip, &version, FW_FIRMWARE_VER); + if (r) + return r; + + dev_info(zd_chip_dev(chip),"firmware version %04hx\n", version); + return 0; +} + +static int set_mandatory_rates(struct zd_chip *chip, enum ieee80211_std std) +{ + u32 rates; + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + /* This sets the mandatory rates, which only depend from the standard + * that the device is supporting. Until further notice we should try + * to support 802.11g also for full speed USB. + */ + switch (std) { + case IEEE80211B: + rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M; + break; + case IEEE80211G: + rates = CR_RATE_1M|CR_RATE_2M|CR_RATE_5_5M|CR_RATE_11M| + CR_RATE_6M|CR_RATE_12M|CR_RATE_24M; + break; + default: + return -EINVAL; + } + return zd_iowrite32_locked(chip, rates, CR_MANDATORY_RATE_TBL); +} + +int zd_chip_enable_hwint(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, HWINT_ENABLED, CR_INTERRUPT); + mutex_unlock(&chip->mutex); + return r; +} + +static int disable_hwint(struct zd_chip *chip) +{ + return zd_iowrite32_locked(chip, HWINT_DISABLED, CR_INTERRUPT); +} + +int zd_chip_disable_hwint(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = disable_hwint(chip); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_init_hw(struct zd_chip *chip, u8 device_type) +{ + int r; + u8 rf_type; + + dev_dbg_f(zd_chip_dev(chip), "\n"); + + mutex_lock(&chip->mutex); + chip->is_zd1211b = (device_type == DEVICE_ZD1211B) != 0; + +#ifdef DEBUG + r = test_init(chip); + if (r) + goto out; +#endif + r = zd_iowrite32_locked(chip, 1, CR_AFTER_PNP); + if (r) + goto out; + + r = zd_usb_init_hw(&chip->usb); + if (r) + goto out; + + /* GPI is always disabled, also in the other driver. + */ + r = zd_iowrite32_locked(chip, 0, CR_GPI_EN); + if (r) + goto out; + r = zd_iowrite32_locked(chip, CWIN_SIZE, CR_CWMIN_CWMAX); + if (r) + goto out; + /* Currently we support IEEE 802.11g for full and high speed USB. + * It might be discussed, whether we should suppport pure b mode for + * full speed USB. + */ + r = set_mandatory_rates(chip, IEEE80211G); + if (r) + goto out; + /* Disabling interrupts is certainly a smart thing here. + */ + r = disable_hwint(chip); + if (r) + goto out; + r = read_pod(chip, &rf_type); + if (r) + goto out; + r = hw_init(chip); + if (r) + goto out; + r = zd_rf_init_hw(&chip->rf, rf_type); + if (r) + goto out; + + r = print_fw_version(chip); + if (r) + goto out; + +#ifdef DEBUG + dump_fw_registers(chip); + r = test_init(chip); + if (r) + goto out; +#endif /* DEBUG */ + + r = read_e2p_mac_addr(chip); + if (r) + goto out; + + r = read_cal_int_tables(chip); + if (r) + goto out; + + print_id(chip); +out: + mutex_unlock(&chip->mutex); + return r; +} + +static int update_pwr_int(struct zd_chip *chip, u8 channel) +{ + u8 value = chip->pwr_int_values[channel - 1]; + dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_int %#04x\n", + channel, value); + return zd_iowrite32_locked(chip, value, CR31); +} + +static int update_pwr_cal(struct zd_chip *chip, u8 channel) +{ + u8 value = chip->pwr_cal_values[channel-1]; + dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_cal %#04x\n", + channel, value); + return zd_iowrite32_locked(chip, value, CR68); +} + +static int update_ofdm_cal(struct zd_chip *chip, u8 channel) +{ + struct zd_ioreq32 ioreqs[3]; + + ioreqs[0].addr = CR67; + ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1]; + ioreqs[1].addr = CR66; + ioreqs[1].value = chip->ofdm_cal_values[OFDM_48M_INDEX][channel-1]; + ioreqs[2].addr = CR65; + ioreqs[2].value = chip->ofdm_cal_values[OFDM_54M_INDEX][channel-1]; + + dev_dbg_f(zd_chip_dev(chip), + "channel %d ofdm_cal 36M %#04x 48M %#04x 54M %#04x\n", + channel, ioreqs[0].value, ioreqs[1].value, ioreqs[2].value); + return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int update_channel_integration_and_calibration(struct zd_chip *chip, + u8 channel) +{ + int r; + + r = update_pwr_int(chip, channel); + if (r) + return r; + if (chip->is_zd1211b) { + static const struct zd_ioreq32 ioreqs[] = { + { CR69, 0x28 }, + {}, + { CR69, 0x2a }, + }; + + r = update_ofdm_cal(chip, channel); + if (r) + return r; + r = update_pwr_cal(chip, channel); + if (r) + return r; + r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + } + + return 0; +} + +/* The CCK baseband gain can be optionally patched by the EEPROM */ +static int patch_cck_gain(struct zd_chip *chip) +{ + int r; + u32 value; + + if (!chip->patch_cck_gain) + return 0; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); + if (r) + return r; + dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff); + return zd_iowrite32_locked(chip, value & 0xff, CR47); +} + +int zd_chip_set_channel(struct zd_chip *chip, u8 channel) +{ + int r, t; + + mutex_lock(&chip->mutex); + r = zd_chip_lock_phy_regs(chip); + if (r) + goto out; + r = zd_rf_set_channel(&chip->rf, channel); + if (r) + goto unlock; + r = update_channel_integration_and_calibration(chip, channel); + if (r) + goto unlock; + r = patch_cck_gain(chip); + if (r) + goto unlock; + r = patch_6m_band_edge(chip, channel); + if (r) + goto unlock; + r = zd_iowrite32_locked(chip, 0, CR_CONFIG_PHILIPS); +unlock: + t = zd_chip_unlock_phy_regs(chip); + if (t && !r) + r = t; +out: + mutex_unlock(&chip->mutex); + return r; +} + +u8 zd_chip_get_channel(struct zd_chip *chip) +{ + u8 channel; + + mutex_lock(&chip->mutex); + channel = chip->rf.channel; + mutex_unlock(&chip->mutex); + return channel; +} + +static u16 led_mask(int led) +{ + switch (led) { + case 1: + return LED1; + case 2: + return LED2; + default: + return 0; + } +} + +static int read_led_reg(struct zd_chip *chip, u16 *status) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_ioread16_locked(chip, status, CR_LED); +} + +static int write_led_reg(struct zd_chip *chip, u16 status) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_iowrite16_locked(chip, status, CR_LED); +} + +int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status) +{ + int r, ret; + u16 mask = led_mask(led); + u16 reg; + + if (!mask) + return -EINVAL; + mutex_lock(&chip->mutex); + r = read_led_reg(chip, ®); + if (r) + return r; + switch (status) { + case LED_STATUS: + return (reg & mask) ? LED_ON : LED_OFF; + case LED_OFF: + reg &= ~mask; + ret = LED_OFF; + break; + case LED_FLIP: + reg ^= mask; + ret = (reg&mask) ? LED_ON : LED_OFF; + break; + case LED_ON: + reg |= mask; + ret = LED_ON; + break; + default: + return -EINVAL; + } + r = write_led_reg(chip, reg); + if (r) { + ret = r; + goto out; + } +out: + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_led_flip(struct zd_chip *chip, int led, + const unsigned int *phases_msecs, unsigned int count) +{ + int i, r; + enum led_status status; + + r = zd_chip_led_status(chip, led, LED_STATUS); + if (r) + return r; + status = r; + for (i = 0; i < count; i++) { + r = zd_chip_led_status(chip, led, LED_FLIP); + if (r < 0) + goto out; + msleep(phases_msecs[i]); + } + +out: + zd_chip_led_status(chip, led, status); + return r; +} + +int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates) +{ + int r; + + if (cr_rates & ~(CR_RATES_80211B|CR_RATES_80211G)) + return -EINVAL; + + mutex_lock(&chip->mutex); + r = zd_iowrite32_locked(chip, cr_rates, CR_BASIC_RATE_TBL); + mutex_unlock(&chip->mutex); + return r; +} + +static int ofdm_qual_db(u8 status_quality, u8 rate, unsigned int size) +{ + static const u16 constants[] = { + 715, 655, 585, 540, 470, 410, 360, 315, + 270, 235, 205, 175, 150, 125, 105, 85, + 65, 50, 40, 25, 15 + }; + + int i; + u32 x; + + /* It seems that their quality parameter is somehow per signal + * and is now transferred per bit. + */ + switch (rate) { + case ZD_OFDM_RATE_6M: + case ZD_OFDM_RATE_12M: + case ZD_OFDM_RATE_24M: + size *= 2; + break; + case ZD_OFDM_RATE_9M: + case ZD_OFDM_RATE_18M: + case ZD_OFDM_RATE_36M: + case ZD_OFDM_RATE_54M: + size *= 4; + size /= 3; + break; + case ZD_OFDM_RATE_48M: + size *= 3; + size /= 2; + break; + default: + return -EINVAL; + } + + x = (10000 * status_quality)/size; + for (i = 0; i < ARRAY_SIZE(constants); i++) { + if (x > constants[i]) + break; + } + + return i; +} + +static unsigned int log10times100(unsigned int x) +{ + static const u8 log10[] = { + 0, + 0, 30, 47, 60, 69, 77, 84, 90, 95, 100, + 104, 107, 111, 114, 117, 120, 123, 125, 127, 130, + 132, 134, 136, 138, 139, 141, 143, 144, 146, 147, + 149, 150, 151, 153, 154, 155, 156, 157, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, + 170, 171, 172, 173, 174, 174, 175, 176, 177, 177, + 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, + 185, 185, 186, 186, 187, 188, 188, 189, 189, 190, + 190, 191, 191, 192, 192, 193, 193, 194, 194, 195, + 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, + 200, 200, 201, 201, 202, 202, 202, 203, 203, 204, + 204, 204, 205, 205, 206, 206, 206, 207, 207, 207, + 208, 208, 208, 209, 209, 210, 210, 210, 211, 211, + 211, 212, 212, 212, 213, 213, 213, 213, 214, 214, + 214, 215, 215, 215, 216, 216, 216, 217, 217, 217, + 217, 218, 218, 218, 219, 219, 219, 219, 220, 220, + 220, 220, 221, 221, 221, 222, 222, 222, 222, 223, + 223, 223, 223, 224, 224, 224, 224, + }; + + return x < ARRAY_SIZE(log10) ? log10[x] : 225; +} + +enum { + MAX_CCK_EVM_DB = 45, +}; + +static int cck_evm_db(u8 status_quality) +{ + return (20 * log10times100(status_quality)) / 100; +} + +static int cck_snr_db(u8 status_quality) +{ + int r = MAX_CCK_EVM_DB - cck_evm_db(status_quality); + ZD_ASSERT(r >= 0); + return r; +} + +static int rx_qual_db(const void *rx_frame, unsigned int size, + const struct rx_status *status) +{ + return (status->frame_status&ZD_RX_OFDM) ? + ofdm_qual_db(status->signal_quality_ofdm, + zd_ofdm_plcp_header_rate(rx_frame), + size) : + cck_snr_db(status->signal_quality_cck); +} + +u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size, + const struct rx_status *status) +{ + int r = rx_qual_db(rx_frame, size, status); + if (r < 0) + r = 0; + r = (r * 100) / 14; + if (r > 100) + r = 100; + return r; +} + +u8 zd_rx_strength_percent(u8 rssi) +{ + int r = (rssi*100) / 30; + if (r > 100) + r = 100; + return (u8) r; +} + +u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status) +{ + static const u16 ofdm_rates[] = { + [ZD_OFDM_RATE_6M] = 60, + [ZD_OFDM_RATE_9M] = 90, + [ZD_OFDM_RATE_12M] = 120, + [ZD_OFDM_RATE_18M] = 180, + [ZD_OFDM_RATE_24M] = 240, + [ZD_OFDM_RATE_36M] = 360, + [ZD_OFDM_RATE_48M] = 480, + [ZD_OFDM_RATE_54M] = 540, + }; + u16 rate; + if (status->frame_status & ZD_RX_OFDM) { + u8 ofdm_rate = zd_ofdm_plcp_header_rate(rx_frame); + rate = ofdm_rates[ofdm_rate & 0xf]; + } else { + u8 cck_rate = zd_cck_plcp_header_rate(rx_frame); + switch (cck_rate) { + case ZD_CCK_SIGNAL_1M: + rate = 10; + break; + case ZD_CCK_SIGNAL_2M: + rate = 20; + break; + case ZD_CCK_SIGNAL_5M5: + rate = 55; + break; + case ZD_CCK_SIGNAL_11M: + rate = 110; + break; + default: + rate = 0; + } + } + + return rate; +} + +int zd_chip_switch_radio_on(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_switch_radio_on(&chip->rf); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_switch_radio_off(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_switch_radio_off(&chip->rf); + mutex_unlock(&chip->mutex); + return r; +} + +int zd_chip_enable_int(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_usb_enable_int(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_int(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_disable_int(&chip->usb); + mutex_unlock(&chip->mutex); +} + +int zd_chip_enable_rx(struct zd_chip *chip) +{ + int r; + + mutex_lock(&chip->mutex); + r = zd_usb_enable_rx(&chip->usb); + mutex_unlock(&chip->mutex); + return r; +} + +void zd_chip_disable_rx(struct zd_chip *chip) +{ + mutex_lock(&chip->mutex); + zd_usb_disable_rx(&chip->usb); + mutex_unlock(&chip->mutex); +} + +int zd_rfwritev_locked(struct zd_chip *chip, + const u32* values, unsigned int count, u8 bits) +{ + int r; + unsigned int i; + + for (i = 0; i < count; i++) { + r = zd_rfwrite_locked(chip, values[i], bits); + if (r) + return r; + } + + return 0; +} diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h new file mode 100644 index 000000000000..805121093ab5 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -0,0 +1,825 @@ +/* zd_chip.h + * + * 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 + */ + +#ifndef _ZD_CHIP_H +#define _ZD_CHIP_H + +#include "zd_types.h" +#include "zd_rf.h" +#include "zd_usb.h" + +/* Header for the Media Access Controller (MAC) and the Baseband Processor + * (BBP). It appears that the ZD1211 wraps the old ZD1205 with USB glue and + * adds a processor for handling the USB protocol. + */ + +/* 8-bit hardware registers */ +#define CR0 CTL_REG(0x0000) +#define CR1 CTL_REG(0x0004) +#define CR2 CTL_REG(0x0008) +#define CR3 CTL_REG(0x000C) + +#define CR5 CTL_REG(0x0010) +/* bit 5: if set short preamble used + * bit 6: filter band - Japan channel 14 on, else off + */ +#define CR6 CTL_REG(0x0014) +#define CR7 CTL_REG(0x0018) +#define CR8 CTL_REG(0x001C) + +#define CR4 CTL_REG(0x0020) + +#define CR9 CTL_REG(0x0024) +/* bit 2: antenna switch (together with CR10) */ +#define CR10 CTL_REG(0x0028) +/* bit 1: antenna switch (together with CR9) + * RF2959 controls with CR11 radion on and off + */ +#define CR11 CTL_REG(0x002C) +/* bit 6: TX power control for OFDM + * RF2959 controls with CR10 radio on and off + */ +#define CR12 CTL_REG(0x0030) +#define CR13 CTL_REG(0x0034) +#define CR14 CTL_REG(0x0038) +#define CR15 CTL_REG(0x003C) +#define CR16 CTL_REG(0x0040) +#define CR17 CTL_REG(0x0044) +#define CR18 CTL_REG(0x0048) +#define CR19 CTL_REG(0x004C) +#define CR20 CTL_REG(0x0050) +#define CR21 CTL_REG(0x0054) +#define CR22 CTL_REG(0x0058) +#define CR23 CTL_REG(0x005C) +#define CR24 CTL_REG(0x0060) /* CCA threshold */ +#define CR25 CTL_REG(0x0064) +#define CR26 CTL_REG(0x0068) +#define CR27 CTL_REG(0x006C) +#define CR28 CTL_REG(0x0070) +#define CR29 CTL_REG(0x0074) +#define CR30 CTL_REG(0x0078) +#define CR31 CTL_REG(0x007C) /* TX power control for RF in CCK mode */ +#define CR32 CTL_REG(0x0080) +#define CR33 CTL_REG(0x0084) +#define CR34 CTL_REG(0x0088) +#define CR35 CTL_REG(0x008C) +#define CR36 CTL_REG(0x0090) +#define CR37 CTL_REG(0x0094) +#define CR38 CTL_REG(0x0098) +#define CR39 CTL_REG(0x009C) +#define CR40 CTL_REG(0x00A0) +#define CR41 CTL_REG(0x00A4) +#define CR42 CTL_REG(0x00A8) +#define CR43 CTL_REG(0x00AC) +#define CR44 CTL_REG(0x00B0) +#define CR45 CTL_REG(0x00B4) +#define CR46 CTL_REG(0x00B8) +#define CR47 CTL_REG(0x00BC) /* CCK baseband gain + * (patch value might be in EEPROM) + */ +#define CR48 CTL_REG(0x00C0) +#define CR49 CTL_REG(0x00C4) +#define CR50 CTL_REG(0x00C8) +#define CR51 CTL_REG(0x00CC) /* TX power control for RF in 6-36M modes */ +#define CR52 CTL_REG(0x00D0) /* TX power control for RF in 48M mode */ +#define CR53 CTL_REG(0x00D4) /* TX power control for RF in 54M mode */ +#define CR54 CTL_REG(0x00D8) +#define CR55 CTL_REG(0x00DC) +#define CR56 CTL_REG(0x00E0) +#define CR57 CTL_REG(0x00E4) +#define CR58 CTL_REG(0x00E8) +#define CR59 CTL_REG(0x00EC) +#define CR60 CTL_REG(0x00F0) +#define CR61 CTL_REG(0x00F4) +#define CR62 CTL_REG(0x00F8) +#define CR63 CTL_REG(0x00FC) +#define CR64 CTL_REG(0x0100) +#define CR65 CTL_REG(0x0104) /* OFDM 54M calibration */ +#define CR66 CTL_REG(0x0108) /* OFDM 48M calibration */ +#define CR67 CTL_REG(0x010C) /* OFDM 36M calibration */ +#define CR68 CTL_REG(0x0110) /* CCK calibration */ +#define CR69 CTL_REG(0x0114) +#define CR70 CTL_REG(0x0118) +#define CR71 CTL_REG(0x011C) +#define CR72 CTL_REG(0x0120) +#define CR73 CTL_REG(0x0124) +#define CR74 CTL_REG(0x0128) +#define CR75 CTL_REG(0x012C) +#define CR76 CTL_REG(0x0130) +#define CR77 CTL_REG(0x0134) +#define CR78 CTL_REG(0x0138) +#define CR79 CTL_REG(0x013C) +#define CR80 CTL_REG(0x0140) +#define CR81 CTL_REG(0x0144) +#define CR82 CTL_REG(0x0148) +#define CR83 CTL_REG(0x014C) +#define CR84 CTL_REG(0x0150) +#define CR85 CTL_REG(0x0154) +#define CR86 CTL_REG(0x0158) +#define CR87 CTL_REG(0x015C) +#define CR88 CTL_REG(0x0160) +#define CR89 CTL_REG(0x0164) +#define CR90 CTL_REG(0x0168) +#define CR91 CTL_REG(0x016C) +#define CR92 CTL_REG(0x0170) +#define CR93 CTL_REG(0x0174) +#define CR94 CTL_REG(0x0178) +#define CR95 CTL_REG(0x017C) +#define CR96 CTL_REG(0x0180) +#define CR97 CTL_REG(0x0184) +#define CR98 CTL_REG(0x0188) +#define CR99 CTL_REG(0x018C) +#define CR100 CTL_REG(0x0190) +#define CR101 CTL_REG(0x0194) +#define CR102 CTL_REG(0x0198) +#define CR103 CTL_REG(0x019C) +#define CR104 CTL_REG(0x01A0) +#define CR105 CTL_REG(0x01A4) +#define CR106 CTL_REG(0x01A8) +#define CR107 CTL_REG(0x01AC) +#define CR108 CTL_REG(0x01B0) +#define CR109 CTL_REG(0x01B4) +#define CR110 CTL_REG(0x01B8) +#define CR111 CTL_REG(0x01BC) +#define CR112 CTL_REG(0x01C0) +#define CR113 CTL_REG(0x01C4) +#define CR114 CTL_REG(0x01C8) +#define CR115 CTL_REG(0x01CC) +#define CR116 CTL_REG(0x01D0) +#define CR117 CTL_REG(0x01D4) +#define CR118 CTL_REG(0x01D8) +#define CR119 CTL_REG(0x01DC) +#define CR120 CTL_REG(0x01E0) +#define CR121 CTL_REG(0x01E4) +#define CR122 CTL_REG(0x01E8) +#define CR123 CTL_REG(0x01EC) +#define CR124 CTL_REG(0x01F0) +#define CR125 CTL_REG(0x01F4) +#define CR126 CTL_REG(0x01F8) +#define CR127 CTL_REG(0x01FC) +#define CR128 CTL_REG(0x0200) +#define CR129 CTL_REG(0x0204) +#define CR130 CTL_REG(0x0208) +#define CR131 CTL_REG(0x020C) +#define CR132 CTL_REG(0x0210) +#define CR133 CTL_REG(0x0214) +#define CR134 CTL_REG(0x0218) +#define CR135 CTL_REG(0x021C) +#define CR136 CTL_REG(0x0220) +#define CR137 CTL_REG(0x0224) +#define CR138 CTL_REG(0x0228) +#define CR139 CTL_REG(0x022C) +#define CR140 CTL_REG(0x0230) +#define CR141 CTL_REG(0x0234) +#define CR142 CTL_REG(0x0238) +#define CR143 CTL_REG(0x023C) +#define CR144 CTL_REG(0x0240) +#define CR145 CTL_REG(0x0244) +#define CR146 CTL_REG(0x0248) +#define CR147 CTL_REG(0x024C) +#define CR148 CTL_REG(0x0250) +#define CR149 CTL_REG(0x0254) +#define CR150 CTL_REG(0x0258) +#define CR151 CTL_REG(0x025C) +#define CR152 CTL_REG(0x0260) +#define CR153 CTL_REG(0x0264) +#define CR154 CTL_REG(0x0268) +#define CR155 CTL_REG(0x026C) +#define CR156 CTL_REG(0x0270) +#define CR157 CTL_REG(0x0274) +#define CR158 CTL_REG(0x0278) +#define CR159 CTL_REG(0x027C) +#define CR160 CTL_REG(0x0280) +#define CR161 CTL_REG(0x0284) +#define CR162 CTL_REG(0x0288) +#define CR163 CTL_REG(0x028C) +#define CR164 CTL_REG(0x0290) +#define CR165 CTL_REG(0x0294) +#define CR166 CTL_REG(0x0298) +#define CR167 CTL_REG(0x029C) +#define CR168 CTL_REG(0x02A0) +#define CR169 CTL_REG(0x02A4) +#define CR170 CTL_REG(0x02A8) +#define CR171 CTL_REG(0x02AC) +#define CR172 CTL_REG(0x02B0) +#define CR173 CTL_REG(0x02B4) +#define CR174 CTL_REG(0x02B8) +#define CR175 CTL_REG(0x02BC) +#define CR176 CTL_REG(0x02C0) +#define CR177 CTL_REG(0x02C4) +#define CR178 CTL_REG(0x02C8) +#define CR179 CTL_REG(0x02CC) +#define CR180 CTL_REG(0x02D0) +#define CR181 CTL_REG(0x02D4) +#define CR182 CTL_REG(0x02D8) +#define CR183 CTL_REG(0x02DC) +#define CR184 CTL_REG(0x02E0) +#define CR185 CTL_REG(0x02E4) +#define CR186 CTL_REG(0x02E8) +#define CR187 CTL_REG(0x02EC) +#define CR188 CTL_REG(0x02F0) +#define CR189 CTL_REG(0x02F4) +#define CR190 CTL_REG(0x02F8) +#define CR191 CTL_REG(0x02FC) +#define CR192 CTL_REG(0x0300) +#define CR193 CTL_REG(0x0304) +#define CR194 CTL_REG(0x0308) +#define CR195 CTL_REG(0x030C) +#define CR196 CTL_REG(0x0310) +#define CR197 CTL_REG(0x0314) +#define CR198 CTL_REG(0x0318) +#define CR199 CTL_REG(0x031C) +#define CR200 CTL_REG(0x0320) +#define CR201 CTL_REG(0x0324) +#define CR202 CTL_REG(0x0328) +#define CR203 CTL_REG(0x032C) /* I2C bus template value & flash control */ +#define CR204 CTL_REG(0x0330) +#define CR205 CTL_REG(0x0334) +#define CR206 CTL_REG(0x0338) +#define CR207 CTL_REG(0x033C) +#define CR208 CTL_REG(0x0340) +#define CR209 CTL_REG(0x0344) +#define CR210 CTL_REG(0x0348) +#define CR211 CTL_REG(0x034C) +#define CR212 CTL_REG(0x0350) +#define CR213 CTL_REG(0x0354) +#define CR214 CTL_REG(0x0358) +#define CR215 CTL_REG(0x035C) +#define CR216 CTL_REG(0x0360) +#define CR217 CTL_REG(0x0364) +#define CR218 CTL_REG(0x0368) +#define CR219 CTL_REG(0x036C) +#define CR220 CTL_REG(0x0370) +#define CR221 CTL_REG(0x0374) +#define CR222 CTL_REG(0x0378) +#define CR223 CTL_REG(0x037C) +#define CR224 CTL_REG(0x0380) +#define CR225 CTL_REG(0x0384) +#define CR226 CTL_REG(0x0388) +#define CR227 CTL_REG(0x038C) +#define CR228 CTL_REG(0x0390) +#define CR229 CTL_REG(0x0394) +#define CR230 CTL_REG(0x0398) +#define CR231 CTL_REG(0x039C) +#define CR232 CTL_REG(0x03A0) +#define CR233 CTL_REG(0x03A4) +#define CR234 CTL_REG(0x03A8) +#define CR235 CTL_REG(0x03AC) +#define CR236 CTL_REG(0x03B0) + +#define CR240 CTL_REG(0x03C0) +/* bit 7: host-controlled RF register writes + * CR241-CR245: for hardware controlled writing of RF bits, not needed for + * USB + */ +#define CR241 CTL_REG(0x03C4) +#define CR242 CTL_REG(0x03C8) +#define CR243 CTL_REG(0x03CC) +#define CR244 CTL_REG(0x03D0) +#define CR245 CTL_REG(0x03D4) + +#define CR251 CTL_REG(0x03EC) /* only used for activation and deactivation of + * Airoha RFs AL2230 and AL7230B + */ +#define CR252 CTL_REG(0x03F0) +#define CR253 CTL_REG(0x03F4) +#define CR254 CTL_REG(0x03F8) +#define CR255 CTL_REG(0x03FC) + +#define CR_MAX_PHY_REG 255 + +/* Taken from the ZYDAS driver, not all of them are relevant for the ZSD1211 + * driver. + */ + +#define CR_RF_IF_CLK CTL_REG(0x0400) +#define CR_RF_IF_DATA CTL_REG(0x0404) +#define CR_PE1_PE2 CTL_REG(0x0408) +#define CR_PE2_DLY CTL_REG(0x040C) +#define CR_LE1 CTL_REG(0x0410) +#define CR_LE2 CTL_REG(0x0414) +/* Seems to enable/disable GPI (General Purpose IO?) */ +#define CR_GPI_EN CTL_REG(0x0418) +#define CR_RADIO_PD CTL_REG(0x042C) +#define CR_RF2948_PD CTL_REG(0x042C) +#define CR_ENABLE_PS_MANUAL_AGC CTL_REG(0x043C) +#define CR_CONFIG_PHILIPS CTL_REG(0x0440) +#define CR_SA2400_SER_AP CTL_REG(0x0444) +#define CR_I2C_WRITE CTL_REG(0x0444) +#define CR_SA2400_SER_RP CTL_REG(0x0448) +#define CR_RADIO_PE CTL_REG(0x0458) +#define CR_RST_BUS_MASTER CTL_REG(0x045C) +#define CR_RFCFG CTL_REG(0x0464) +#define CR_HSTSCHG CTL_REG(0x046C) +#define CR_PHY_ON CTL_REG(0x0474) +#define CR_RX_DELAY CTL_REG(0x0478) +#define CR_RX_PE_DELAY CTL_REG(0x047C) +#define CR_GPIO_1 CTL_REG(0x0490) +#define CR_GPIO_2 CTL_REG(0x0494) +#define CR_EncryBufMux CTL_REG(0x04A8) +#define CR_PS_CTRL CTL_REG(0x0500) +#define CR_ADDA_PWR_DWN CTL_REG(0x0504) +#define CR_ADDA_MBIAS_WARMTIME CTL_REG(0x0508) +#define CR_MAC_PS_STATE CTL_REG(0x050C) + +#define CR_INTERRUPT CTL_REG(0x0510) +#define INT_TX_COMPLETE 0x00000001 +#define INT_RX_COMPLETE 0x00000002 +#define INT_RETRY_FAIL 0x00000004 +#define INT_WAKEUP 0x00000008 +#define INT_DTIM_NOTIFY 0x00000020 +#define INT_CFG_NEXT_BCN 0x00000040 +#define INT_BUS_ABORT 0x00000080 +#define INT_TX_FIFO_READY 0x00000100 +#define INT_UART 0x00000200 +#define INT_TX_COMPLETE_EN 0x00010000 +#define INT_RX_COMPLETE_EN 0x00020000 +#define INT_RETRY_FAIL_EN 0x00040000 +#define INT_WAKEUP_EN 0x00080000 +#define INT_DTIM_NOTIFY_EN 0x00200000 +#define INT_CFG_NEXT_BCN_EN 0x00400000 +#define INT_BUS_ABORT_EN 0x00800000 +#define INT_TX_FIFO_READY_EN 0x01000000 +#define INT_UART_EN 0x02000000 + +#define CR_TSF_LOW_PART CTL_REG(0x0514) +#define CR_TSF_HIGH_PART CTL_REG(0x0518) + +/* Following three values are in time units (1024us) + * Following condition must be met: + * atim < tbtt < bcn + */ +#define CR_ATIM_WND_PERIOD CTL_REG(0x051C) +#define CR_BCN_INTERVAL CTL_REG(0x0520) +#define CR_PRE_TBTT CTL_REG(0x0524) +/* in units of TU(1024us) */ + +/* for UART support */ +#define CR_UART_RBR_THR_DLL CTL_REG(0x0540) +#define CR_UART_DLM_IER CTL_REG(0x0544) +#define CR_UART_IIR_FCR CTL_REG(0x0548) +#define CR_UART_LCR CTL_REG(0x054c) +#define CR_UART_MCR CTL_REG(0x0550) +#define CR_UART_LSR CTL_REG(0x0554) +#define CR_UART_MSR CTL_REG(0x0558) +#define CR_UART_ECR CTL_REG(0x055c) +#define CR_UART_STATUS CTL_REG(0x0560) + +#define CR_PCI_TX_ADDR_P1 CTL_REG(0x0600) +#define CR_PCI_TX_AddR_P2 CTL_REG(0x0604) +#define CR_PCI_RX_AddR_P1 CTL_REG(0x0608) +#define CR_PCI_RX_AddR_P2 CTL_REG(0x060C) + +/* must be overwritten if custom MAC address will be used */ +#define CR_MAC_ADDR_P1 CTL_REG(0x0610) +#define CR_MAC_ADDR_P2 CTL_REG(0x0614) +#define CR_BSSID_P1 CTL_REG(0x0618) +#define CR_BSSID_P2 CTL_REG(0x061C) +#define CR_BCN_PLCP_CFG CTL_REG(0x0620) +#define CR_GROUP_HASH_P1 CTL_REG(0x0624) +#define CR_GROUP_HASH_P2 CTL_REG(0x0628) +#define CR_RX_TIMEOUT CTL_REG(0x062C) + +/* Basic rates supported by the BSS. When producing ACK or CTS messages, the + * device will use a rate in this table that is less than or equal to the rate + * of the incoming frame which prompted the response */ +#define CR_BASIC_RATE_TBL CTL_REG(0x0630) +#define CR_RATE_1M 0x0001 /* 802.11b */ +#define CR_RATE_2M 0x0002 /* 802.11b */ +#define CR_RATE_5_5M 0x0004 /* 802.11b */ +#define CR_RATE_11M 0x0008 /* 802.11b */ +#define CR_RATE_6M 0x0100 /* 802.11g */ +#define CR_RATE_9M 0x0200 /* 802.11g */ +#define CR_RATE_12M 0x0400 /* 802.11g */ +#define CR_RATE_18M 0x0800 /* 802.11g */ +#define CR_RATE_24M 0x1000 /* 802.11g */ +#define CR_RATE_36M 0x2000 /* 802.11g */ +#define CR_RATE_48M 0x4000 /* 802.11g */ +#define CR_RATE_54M 0x8000 /* 802.11g */ +#define CR_RATES_80211G 0xff00 +#define CR_RATES_80211B 0x000f + +/* Mandatory rates required in the BSS. When producing ACK or CTS messages, if + * the device could not find an appropriate rate in CR_BASIC_RATE_TBL, it will + * look for a rate in this table that is less than or equal to the rate of + * the incoming frame. */ +#define CR_MANDATORY_RATE_TBL CTL_REG(0x0634) +#define CR_RTS_CTS_RATE CTL_REG(0x0638) + +#define CR_WEP_PROTECT CTL_REG(0x063C) +#define CR_RX_THRESHOLD CTL_REG(0x0640) + +/* register for controlling the LEDS */ +#define CR_LED CTL_REG(0x0644) +/* masks for controlling LEDs */ +#define LED1 0x0100 +#define LED2 0x0200 + +/* Seems to indicate that the configuration is over. + */ +#define CR_AFTER_PNP CTL_REG(0x0648) +#define CR_ACK_TIME_80211 CTL_REG(0x0658) + +#define CR_RX_OFFSET CTL_REG(0x065c) + +#define CR_PHY_DELAY CTL_REG(0x066C) +#define CR_BCN_FIFO CTL_REG(0x0670) +#define CR_SNIFFER_ON CTL_REG(0x0674) + +#define CR_ENCRYPTION_TYPE CTL_REG(0x0678) +#define NO_WEP 0 +#define WEP64 1 +#define WEP128 5 +#define WEP256 6 +#define ENC_SNIFFER 8 + +#define CR_ZD1211_RETRY_MAX CTL_REG(0x067C) + +#define CR_REG1 CTL_REG(0x0680) +/* Setting the bit UNLOCK_PHY_REGS disallows the write access to physical + * registers, so one could argue it is a LOCK bit. But calling it + * LOCK_PHY_REGS makes it confusing. + */ +#define UNLOCK_PHY_REGS 0x0080 + +#define CR_DEVICE_STATE CTL_REG(0x0684) +#define CR_UNDERRUN_CNT CTL_REG(0x0688) + +#define CR_RX_FILTER CTL_REG(0x068c) +#define RX_FILTER_ASSOC_RESPONSE 0x0002 +#define RX_FILTER_PROBE_RESPONSE 0x0020 +#define RX_FILTER_BEACON 0x0100 +#define RX_FILTER_AUTH 0x0800 +/* Sniff modus sets filter to 0xfffff */ + +#define CR_ACK_TIMEOUT_EXT CTL_REG(0x0690) +#define CR_BCN_FIFO_SEMAPHORE CTL_REG(0x0694) +#define CR_IFS_VALUE CTL_REG(0x0698) +#define CR_RX_TIME_OUT CTL_REG(0x069C) +#define CR_TOTAL_RX_FRM CTL_REG(0x06A0) +#define CR_CRC32_CNT CTL_REG(0x06A4) +#define CR_CRC16_CNT CTL_REG(0x06A8) +#define CR_DECRYPTION_ERR_UNI CTL_REG(0x06AC) +#define CR_RX_FIFO_OVERRUN CTL_REG(0x06B0) + +#define CR_DECRYPTION_ERR_MUL CTL_REG(0x06BC) + +#define CR_NAV_CNT CTL_REG(0x06C4) +#define CR_NAV_CCA CTL_REG(0x06C8) +#define CR_RETRY_CNT CTL_REG(0x06CC) + +#define CR_READ_TCB_ADDR CTL_REG(0x06E8) +#define CR_READ_RFD_ADDR CTL_REG(0x06EC) +#define CR_CWMIN_CWMAX CTL_REG(0x06F0) +#define CR_TOTAL_TX_FRM CTL_REG(0x06F4) + +/* CAM: Continuous Access Mode (power management) */ +#define CR_CAM_MODE CTL_REG(0x0700) +#define CR_CAM_ROLL_TB_LOW CTL_REG(0x0704) +#define CR_CAM_ROLL_TB_HIGH CTL_REG(0x0708) +#define CR_CAM_ADDRESS CTL_REG(0x070C) +#define CR_CAM_DATA CTL_REG(0x0710) + +#define CR_ROMDIR CTL_REG(0x0714) + +#define CR_DECRY_ERR_FLG_LOW CTL_REG(0x0714) +#define CR_DECRY_ERR_FLG_HIGH CTL_REG(0x0718) + +#define CR_WEPKEY0 CTL_REG(0x0720) +#define CR_WEPKEY1 CTL_REG(0x0724) +#define CR_WEPKEY2 CTL_REG(0x0728) +#define CR_WEPKEY3 CTL_REG(0x072C) +#define CR_WEPKEY4 CTL_REG(0x0730) +#define CR_WEPKEY5 CTL_REG(0x0734) +#define CR_WEPKEY6 CTL_REG(0x0738) +#define CR_WEPKEY7 CTL_REG(0x073C) +#define CR_WEPKEY8 CTL_REG(0x0740) +#define CR_WEPKEY9 CTL_REG(0x0744) +#define CR_WEPKEY10 CTL_REG(0x0748) +#define CR_WEPKEY11 CTL_REG(0x074C) +#define CR_WEPKEY12 CTL_REG(0x0750) +#define CR_WEPKEY13 CTL_REG(0x0754) +#define CR_WEPKEY14 CTL_REG(0x0758) +#define CR_WEPKEY15 CTL_REG(0x075c) +#define CR_TKIP_MODE CTL_REG(0x0760) + +#define CR_EEPROM_PROTECT0 CTL_REG(0x0758) +#define CR_EEPROM_PROTECT1 CTL_REG(0x075C) + +#define CR_DBG_FIFO_RD CTL_REG(0x0800) +#define CR_DBG_SELECT CTL_REG(0x0804) +#define CR_FIFO_Length CTL_REG(0x0808) + + +#define CR_RSSI_MGC CTL_REG(0x0810) + +#define CR_PON CTL_REG(0x0818) +#define CR_RX_ON CTL_REG(0x081C) +#define CR_TX_ON CTL_REG(0x0820) +#define CR_CHIP_EN CTL_REG(0x0824) +#define CR_LO_SW CTL_REG(0x0828) +#define CR_TXRX_SW CTL_REG(0x082C) +#define CR_S_MD CTL_REG(0x0830) + +#define CR_USB_DEBUG_PORT CTL_REG(0x0888) + +#define CR_ZD1211B_TX_PWR_CTL1 CTL_REG(0x0b00) +#define CR_ZD1211B_TX_PWR_CTL2 CTL_REG(0x0b04) +#define CR_ZD1211B_TX_PWR_CTL3 CTL_REG(0x0b08) +#define CR_ZD1211B_TX_PWR_CTL4 CTL_REG(0x0b0c) +#define CR_ZD1211B_AIFS_CTL1 CTL_REG(0x0b10) +#define CR_ZD1211B_AIFS_CTL2 CTL_REG(0x0b14) +#define CR_ZD1211B_TXOP CTL_REG(0x0b20) +#define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28) + +#define AP_RX_FILTER 0x0400feff +#define STA_RX_FILTER 0x0000ffff + +#define CWIN_SIZE 0x007f043f + + +#define HWINT_ENABLED 0x004f0000 +#define HWINT_DISABLED 0 + +#define E2P_PWR_INT_GUARD 8 +#define E2P_CHANNEL_COUNT 14 + +/* If you compare this addresses with the ZYDAS orignal driver, please notify + * that we use word mapping for the EEPROM. + */ + +/* + * Upper 16 bit contains the regulatory domain. + */ +#define E2P_SUBID E2P_REG(0x00) +#define E2P_POD E2P_REG(0x02) +#define E2P_MAC_ADDR_P1 E2P_REG(0x04) +#define E2P_MAC_ADDR_P2 E2P_REG(0x06) +#define E2P_PWR_CAL_VALUE1 E2P_REG(0x08) +#define E2P_PWR_CAL_VALUE2 E2P_REG(0x0a) +#define E2P_PWR_CAL_VALUE3 E2P_REG(0x0c) +#define E2P_PWR_CAL_VALUE4 E2P_REG(0x0e) +#define E2P_PWR_INT_VALUE1 E2P_REG(0x10) +#define E2P_PWR_INT_VALUE2 E2P_REG(0x12) +#define E2P_PWR_INT_VALUE3 E2P_REG(0x14) +#define E2P_PWR_INT_VALUE4 E2P_REG(0x16) + +/* Contains a bit for each allowed channel. It gives for Europe (ETSI 0x30) + * also only 11 channels. */ +#define E2P_ALLOWED_CHANNEL E2P_REG(0x18) + +#define E2P_PHY_REG E2P_REG(0x1a) +#define E2P_DEVICE_VER E2P_REG(0x20) +#define E2P_36M_CAL_VALUE1 E2P_REG(0x28) +#define E2P_36M_CAL_VALUE2 E2P_REG(0x2a) +#define E2P_36M_CAL_VALUE3 E2P_REG(0x2c) +#define E2P_36M_CAL_VALUE4 E2P_REG(0x2e) +#define E2P_11A_INT_VALUE1 E2P_REG(0x30) +#define E2P_11A_INT_VALUE2 E2P_REG(0x32) +#define E2P_11A_INT_VALUE3 E2P_REG(0x34) +#define E2P_11A_INT_VALUE4 E2P_REG(0x36) +#define E2P_48M_CAL_VALUE1 E2P_REG(0x38) +#define E2P_48M_CAL_VALUE2 E2P_REG(0x3a) +#define E2P_48M_CAL_VALUE3 E2P_REG(0x3c) +#define E2P_48M_CAL_VALUE4 E2P_REG(0x3e) +#define E2P_48M_INT_VALUE1 E2P_REG(0x40) +#define E2P_48M_INT_VALUE2 E2P_REG(0x42) +#define E2P_48M_INT_VALUE3 E2P_REG(0x44) +#define E2P_48M_INT_VALUE4 E2P_REG(0x46) +#define E2P_54M_CAL_VALUE1 E2P_REG(0x48) /* ??? */ +#define E2P_54M_CAL_VALUE2 E2P_REG(0x4a) +#define E2P_54M_CAL_VALUE3 E2P_REG(0x4c) +#define E2P_54M_CAL_VALUE4 E2P_REG(0x4e) +#define E2P_54M_INT_VALUE1 E2P_REG(0x50) +#define E2P_54M_INT_VALUE2 E2P_REG(0x52) +#define E2P_54M_INT_VALUE3 E2P_REG(0x54) +#define E2P_54M_INT_VALUE4 E2P_REG(0x56) + +/* All 16 bit values */ +#define FW_FIRMWARE_VER FW_REG(0) +/* non-zero if USB high speed connection */ +#define FW_USB_SPEED FW_REG(1) +#define FW_FIX_TX_RATE FW_REG(2) +/* Seems to be able to control LEDs over the firmware */ +#define FW_LINK_STATUS FW_REG(3) +#define FW_SOFT_RESET FW_REG(4) +#define FW_FLASH_CHK FW_REG(5) + +enum { + CR_BASE_OFFSET = 0x9000, + FW_START_OFFSET = 0xee00, + FW_BASE_ADDR_OFFSET = FW_START_OFFSET + 0x1d, + EEPROM_START_OFFSET = 0xf800, + EEPROM_SIZE = 0x800, /* words */ + LOAD_CODE_SIZE = 0xe, /* words */ + LOAD_VECT_SIZE = 0x10000 - 0xfff7, /* words */ + EEPROM_REGS_OFFSET = LOAD_CODE_SIZE + LOAD_VECT_SIZE, + E2P_BASE_OFFSET = EEPROM_START_OFFSET + + EEPROM_REGS_OFFSET, +}; + +#define FW_REG_TABLE_ADDR USB_ADDR(FW_START_OFFSET + 0x1d) + +enum { + /* indices for ofdm_cal_values */ + OFDM_36M_INDEX = 0, + OFDM_48M_INDEX = 1, + OFDM_54M_INDEX = 2, +}; + +struct zd_chip { + struct zd_usb usb; + struct zd_rf rf; + struct mutex mutex; + u8 e2p_mac[ETH_ALEN]; + /* EepSetPoint in the vendor driver */ + u8 pwr_cal_values[E2P_CHANNEL_COUNT]; + /* integration values in the vendor driver */ + u8 pwr_int_values[E2P_CHANNEL_COUNT]; + /* SetPointOFDM in the vendor driver */ + u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT]; + u8 pa_type:4, patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1, + is_zd1211b:1; +}; + +static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb) +{ + return container_of(usb, struct zd_chip, usb); +} + +static inline struct zd_chip *zd_rf_to_chip(struct zd_rf *rf) +{ + return container_of(rf, struct zd_chip, rf); +} + +#define zd_chip_dev(chip) (&(chip)->usb.intf->dev) + +void zd_chip_init(struct zd_chip *chip, + struct net_device *netdev, + struct usb_interface *intf); +void zd_chip_clear(struct zd_chip *chip); +int zd_chip_init_hw(struct zd_chip *chip, u8 device_type); +int zd_chip_reset(struct zd_chip *chip); + +static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values, + const zd_addr_t *addresses, + unsigned int count) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_ioread16v(&chip->usb, values, addresses, count); +} + +static inline int zd_ioread16_locked(struct zd_chip *chip, u16 *value, + const zd_addr_t addr) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_ioread16(&chip->usb, value, addr); +} + +int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, + const zd_addr_t *addresses, unsigned int count); + +static inline int zd_ioread32_locked(struct zd_chip *chip, u32 *value, + const zd_addr_t addr) +{ + return zd_ioread32v_locked(chip, value, (const zd_addr_t *)&addr, 1); +} + +static inline int zd_iowrite16_locked(struct zd_chip *chip, u16 value, + zd_addr_t addr) +{ + struct zd_ioreq16 ioreq; + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + ioreq.addr = addr; + ioreq.value = value; + + return zd_usb_iowrite16v(&chip->usb, &ioreq, 1); +} + +int zd_iowrite16a_locked(struct zd_chip *chip, + const struct zd_ioreq16 *ioreqs, unsigned int count); + +int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count); + +static inline int zd_iowrite32_locked(struct zd_chip *chip, u32 value, + zd_addr_t addr) +{ + struct zd_ioreq32 ioreq; + + ioreq.addr = addr; + ioreq.value = value; + + return _zd_iowrite32v_locked(chip, &ioreq, 1); +} + +int zd_iowrite32a_locked(struct zd_chip *chip, + const struct zd_ioreq32 *ioreqs, unsigned int count); + +static inline int zd_rfwrite_locked(struct zd_chip *chip, u32 value, u8 bits) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + return zd_usb_rfwrite(&chip->usb, value, bits); +} + +int zd_rfwritev_locked(struct zd_chip *chip, + const u32* values, unsigned int count, u8 bits); + +/* Locking functions for reading and writing registers. + * The different parameters are intentional. + */ +int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value); +int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value); +int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value); +int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value); +int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses, + u32 *values, unsigned int count); +int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, + unsigned int count); + +int zd_chip_set_channel(struct zd_chip *chip, u8 channel); +static inline u8 _zd_chip_get_channel(struct zd_chip *chip) +{ + return chip->rf.channel; +} +u8 zd_chip_get_channel(struct zd_chip *chip); +int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain); +void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr); +int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr); +int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr); +int zd_chip_switch_radio_on(struct zd_chip *chip); +int zd_chip_switch_radio_off(struct zd_chip *chip); +int zd_chip_enable_int(struct zd_chip *chip); +void zd_chip_disable_int(struct zd_chip *chip); +int zd_chip_enable_rx(struct zd_chip *chip); +void zd_chip_disable_rx(struct zd_chip *chip); +int zd_chip_enable_hwint(struct zd_chip *chip); +int zd_chip_disable_hwint(struct zd_chip *chip); + +static inline int zd_get_encryption_type(struct zd_chip *chip, u32 *type) +{ + return zd_ioread32(chip, CR_ENCRYPTION_TYPE, type); +} + +static inline int zd_set_encryption_type(struct zd_chip *chip, u32 type) +{ + return zd_iowrite32(chip, CR_ENCRYPTION_TYPE, type); +} + +static inline int zd_chip_get_basic_rates(struct zd_chip *chip, u16 *cr_rates) +{ + return zd_ioread16(chip, CR_BASIC_RATE_TBL, cr_rates); +} + +int zd_chip_set_basic_rates(struct zd_chip *chip, u16 cr_rates); + +static inline int zd_chip_set_rx_filter(struct zd_chip *chip, u32 filter) +{ + return zd_iowrite32(chip, CR_RX_FILTER, filter); +} + +int zd_chip_lock_phy_regs(struct zd_chip *chip); +int zd_chip_unlock_phy_regs(struct zd_chip *chip); + +enum led_status { + LED_OFF = 0, + LED_ON = 1, + LED_FLIP = 2, + LED_STATUS = 3, +}; + +int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status); +int zd_chip_led_flip(struct zd_chip *chip, int led, + const unsigned int *phases_msecs, unsigned int count); + +int zd_set_beacon_interval(struct zd_chip *chip, u32 interval); + +static inline int zd_get_beacon_interval(struct zd_chip *chip, u32 *interval) +{ + return zd_ioread32(chip, CR_BCN_INTERVAL, interval); +} + +struct rx_status; + +u8 zd_rx_qual_percent(const void *rx_frame, unsigned int size, + const struct rx_status *status); +u8 zd_rx_strength_percent(u8 rssi); + +u16 zd_rx_rate(const void *rx_frame, const struct rx_status *status); + +#endif /* _ZD_CHIP_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_def.h b/drivers/net/wireless/zd1211rw/zd_def.h new file mode 100644 index 000000000000..465906812fc4 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_def.h @@ -0,0 +1,48 @@ +/* zd_def.h + * + * 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 + */ + +#ifndef _ZD_DEF_H +#define _ZD_DEF_H + +#include +#include +#include +#include + +#define dev_printk_f(level, dev, fmt, args...) \ + dev_printk(level, dev, "%s() " fmt, __func__, ##args) + +#ifdef DEBUG +# define dev_dbg_f(dev, fmt, args...) \ + dev_printk_f(KERN_DEBUG, dev, fmt, ## args) +#else +# define dev_dbg_f(dev, fmt, args...) do { (void)(dev); } while (0) +#endif /* DEBUG */ + +#ifdef DEBUG +# define ZD_ASSERT(x) \ +do { \ + if (!(x)) { \ + pr_debug("%s:%d ASSERT %s VIOLATED!\n", \ + __FILE__, __LINE__, __stringify(x)); \ + } \ +} while (0) +#else +# define ZD_ASSERT(x) do { } while (0) +#endif + +#endif /* _ZD_DEF_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_ieee80211.c b/drivers/net/wireless/zd1211rw/zd_ieee80211.c new file mode 100644 index 000000000000..66905f7b61ff --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_ieee80211.c @@ -0,0 +1,191 @@ +/* zd_ieee80211.c + * + * 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 + */ + +/* + * A lot of this code is generic and should be moved into the upper layers + * at some point. + */ + +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_ieee80211.h" +#include "zd_mac.h" + +static const struct channel_range channel_ranges[] = { + [0] = { 0, 0}, + [ZD_REGDOMAIN_FCC] = { 1, 12}, + [ZD_REGDOMAIN_IC] = { 1, 12}, + [ZD_REGDOMAIN_ETSI] = { 1, 14}, + [ZD_REGDOMAIN_JAPAN] = { 1, 14}, + [ZD_REGDOMAIN_SPAIN] = { 1, 14}, + [ZD_REGDOMAIN_FRANCE] = { 1, 14}, + [ZD_REGDOMAIN_JAPAN_ADD] = {14, 15}, +}; + +const struct channel_range *zd_channel_range(u8 regdomain) +{ + if (regdomain >= ARRAY_SIZE(channel_ranges)) + regdomain = 0; + return &channel_ranges[regdomain]; +} + +int zd_regdomain_supports_channel(u8 regdomain, u8 channel) +{ + const struct channel_range *range = zd_channel_range(regdomain); + return range->start <= channel && channel < range->end; +} + +int zd_regdomain_supported(u8 regdomain) +{ + const struct channel_range *range = zd_channel_range(regdomain); + return range->start != 0; +} + +/* Stores channel frequencies in MHz. */ +static const u16 channel_frequencies[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, 2472, 2484, +}; + +#define NUM_CHANNELS ARRAY_SIZE(channel_frequencies) + +static int compute_freq(struct iw_freq *freq, u32 mhz, u32 hz) +{ + u32 factor; + + freq->e = 0; + if (mhz >= 1000000000U) { + pr_debug("zd1211 mhz %u to large\n", mhz); + freq->m = 0; + return -EINVAL; + } + + factor = 1000; + while (mhz >= factor) { + + freq->e += 1; + factor *= 10; + } + + factor /= 1000U; + freq->m = mhz * (1000000U/factor) + hz/factor; + + return 0; +} + +int zd_channel_to_freq(struct iw_freq *freq, u8 channel) +{ + if (channel > NUM_CHANNELS) { + freq->m = 0; + freq->e = 0; + return -EINVAL; + } + if (!channel) { + freq->m = 0; + freq->e = 0; + return -EINVAL; + } + return compute_freq(freq, channel_frequencies[channel-1], 0); +} + +static int freq_to_mhz(const struct iw_freq *freq) +{ + u32 factor; + int e; + + /* Such high frequencies are not supported. */ + if (freq->e > 6) + return -EINVAL; + + factor = 1; + for (e = freq->e; e > 0; --e) { + factor *= 10; + } + factor = 1000000U / factor; + + if (freq->m % factor) { + return -EINVAL; + } + + return freq->m / factor; +} + +int zd_find_channel(u8 *channel, const struct iw_freq *freq) +{ + int i, r; + u32 mhz; + + if (!(freq->flags & IW_FREQ_FIXED)) + return 0; + + if (freq->m < 1000) { + if (freq->m > NUM_CHANNELS || freq->m == 0) + return -EINVAL; + *channel = freq->m; + return 1; + } + + r = freq_to_mhz(freq); + if (r < 0) + return r; + mhz = r; + + for (i = 0; i < NUM_CHANNELS; i++) { + if (mhz == channel_frequencies[i]) { + *channel = i+1; + return 1; + } + } + + return -EINVAL; +} + +int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain) +{ + struct ieee80211_geo geo; + const struct channel_range *range; + int i; + u8 channel; + + dev_dbg(zd_mac_dev(zd_netdev_mac(ieee->dev)), + "regdomain %#04x\n", regdomain); + + range = zd_channel_range(regdomain); + if (range->start == 0) { + dev_err(zd_mac_dev(zd_netdev_mac(ieee->dev)), + "zd1211 regdomain %#04x not supported\n", + regdomain); + return -EINVAL; + } + + memset(&geo, 0, sizeof(geo)); + + for (i = 0, channel = range->start; channel < range->end; channel++) { + struct ieee80211_channel *chan = &geo.bg[i++]; + chan->freq = channel_frequencies[channel - 1]; + chan->channel = channel; + } + + geo.bg_channels = i; + memcpy(geo.name, "XX ", 4); + ieee80211_set_geo(ieee, &geo); + return 0; +} diff --git a/drivers/net/wireless/zd1211rw/zd_ieee80211.h b/drivers/net/wireless/zd1211rw/zd_ieee80211.h new file mode 100644 index 000000000000..36329890dfec --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_ieee80211.h @@ -0,0 +1,85 @@ +#ifndef _ZD_IEEE80211_H +#define _ZD_IEEE80211_H + +#include +#include "zd_types.h" + +/* Additional definitions from the standards. + */ + +#define ZD_REGDOMAIN_FCC 0x10 +#define ZD_REGDOMAIN_IC 0x20 +#define ZD_REGDOMAIN_ETSI 0x30 +#define ZD_REGDOMAIN_SPAIN 0x31 +#define ZD_REGDOMAIN_FRANCE 0x32 +#define ZD_REGDOMAIN_JAPAN_ADD 0x40 +#define ZD_REGDOMAIN_JAPAN 0x41 + +enum { + MIN_CHANNEL24 = 1, + MAX_CHANNEL24 = 14, +}; + +struct channel_range { + u8 start; + u8 end; /* exclusive (channel must be less than end) */ +}; + +struct iw_freq; + +int zd_geo_init(struct ieee80211_device *ieee, u8 regdomain); + +const struct channel_range *zd_channel_range(u8 regdomain); +int zd_regdomain_supports_channel(u8 regdomain, u8 channel); +int zd_regdomain_supported(u8 regdomain); + +/* for 2.4 GHz band */ +int zd_channel_to_freq(struct iw_freq *freq, u8 channel); +int zd_find_channel(u8 *channel, const struct iw_freq *freq); + +#define ZD_PLCP_SERVICE_LENGTH_EXTENSION 0x80 + +struct ofdm_plcp_header { + u8 prefix[3]; + __le16 service; +} __attribute__((packed)); + +static inline u8 zd_ofdm_plcp_header_rate( + const struct ofdm_plcp_header *header) +{ + return header->prefix[0] & 0xf; +} + +#define ZD_OFDM_RATE_6M 0xb +#define ZD_OFDM_RATE_9M 0xf +#define ZD_OFDM_RATE_12M 0xa +#define ZD_OFDM_RATE_18M 0xe +#define ZD_OFDM_RATE_24M 0x9 +#define ZD_OFDM_RATE_36M 0xd +#define ZD_OFDM_RATE_48M 0x8 +#define ZD_OFDM_RATE_54M 0xc + +struct cck_plcp_header { + u8 signal; + u8 service; + __le16 length; + __le16 crc16; +} __attribute__((packed)); + +static inline u8 zd_cck_plcp_header_rate(const struct cck_plcp_header *header) +{ + return header->signal; +} + +#define ZD_CCK_SIGNAL_1M 0x0a +#define ZD_CCK_SIGNAL_2M 0x14 +#define ZD_CCK_SIGNAL_5M5 0x37 +#define ZD_CCK_SIGNAL_11M 0x6e + +enum ieee80211_std { + IEEE80211B = 0x01, + IEEE80211A = 0x02, + IEEE80211G = 0x04, +}; + +#endif /* _ZD_IEEE80211_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c new file mode 100644 index 000000000000..bbe067ec7de1 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -0,0 +1,1055 @@ +/* zd_mac.c + * + * 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 + */ + +#include +#include +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_chip.h" +#include "zd_mac.h" +#include "zd_ieee80211.h" +#include "zd_netdev.h" +#include "zd_rf.h" +#include "zd_util.h" + +static void ieee_init(struct ieee80211_device *ieee); +static void softmac_init(struct ieee80211softmac_device *sm); + +int zd_mac_init(struct zd_mac *mac, + struct net_device *netdev, + struct usb_interface *intf) +{ + struct ieee80211_device *ieee = zd_netdev_ieee80211(netdev); + + memset(mac, 0, sizeof(*mac)); + spin_lock_init(&mac->lock); + mac->netdev = netdev; + + ieee_init(ieee); + softmac_init(ieee80211_priv(netdev)); + zd_chip_init(&mac->chip, netdev, intf); + return 0; +} + +static int reset_channel(struct zd_mac *mac) +{ + int r; + unsigned long flags; + const struct channel_range *range; + + spin_lock_irqsave(&mac->lock, flags); + range = zd_channel_range(mac->regdomain); + if (!range->start) { + r = -EINVAL; + goto out; + } + mac->requested_channel = range->start; + r = 0; +out: + spin_unlock_irqrestore(&mac->lock, flags); + return r; +} + +int zd_mac_init_hw(struct zd_mac *mac, u8 device_type) +{ + int r; + struct zd_chip *chip = &mac->chip; + u8 addr[ETH_ALEN]; + u8 default_regdomain; + + r = zd_chip_enable_int(chip); + if (r) + goto out; + r = zd_chip_init_hw(chip, device_type); + if (r) + goto disable_int; + + zd_get_e2p_mac_addr(chip, addr); + r = zd_write_mac_addr(chip, addr); + if (r) + goto disable_int; + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&mac->lock); + memcpy(mac->netdev->dev_addr, addr, ETH_ALEN); + spin_unlock_irq(&mac->lock); + + r = zd_read_regdomain(chip, &default_regdomain); + if (r) + goto disable_int; + if (!zd_regdomain_supported(default_regdomain)) { + dev_dbg_f(zd_mac_dev(mac), + "Regulatory Domain %#04x is not supported.\n", + default_regdomain); + r = -EINVAL; + goto disable_int; + } + spin_lock_irq(&mac->lock); + mac->regdomain = mac->default_regdomain = default_regdomain; + spin_unlock_irq(&mac->lock); + r = reset_channel(mac); + if (r) + goto disable_int; + + r = zd_set_encryption_type(chip, NO_WEP); + if (r) + goto disable_int; + + r = zd_geo_init(zd_mac_to_ieee80211(mac), mac->regdomain); + if (r) + goto disable_int; + + r = 0; +disable_int: + zd_chip_disable_int(chip); +out: + return r; +} + +void zd_mac_clear(struct zd_mac *mac) +{ + /* Aquire the lock. */ + spin_lock(&mac->lock); + spin_unlock(&mac->lock); + zd_chip_clear(&mac->chip); + memset(mac, 0, sizeof(*mac)); +} + +static int reset_mode(struct zd_mac *mac) +{ + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); + struct zd_ioreq32 ioreqs[3] = { + { CR_RX_FILTER, RX_FILTER_BEACON|RX_FILTER_PROBE_RESPONSE| + RX_FILTER_AUTH|RX_FILTER_ASSOC_RESPONSE }, + { CR_SNIFFER_ON, 0U }, + { CR_ENCRYPTION_TYPE, NO_WEP }, + }; + + if (ieee->iw_mode == IW_MODE_MONITOR) { + ioreqs[0].value = 0xffffffff; + ioreqs[1].value = 0x1; + ioreqs[2].value = ENC_SNIFFER; + } + + return zd_iowrite32a(&mac->chip, ioreqs, 3); +} + +int zd_mac_open(struct net_device *netdev) +{ + struct zd_mac *mac = zd_netdev_mac(netdev); + struct zd_chip *chip = &mac->chip; + int r; + + r = zd_chip_enable_int(chip); + if (r < 0) + goto out; + + r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G); + if (r < 0) + goto disable_int; + r = reset_mode(mac); + if (r) + goto disable_int; + r = zd_chip_switch_radio_on(chip); + if (r < 0) + goto disable_int; + r = zd_chip_set_channel(chip, mac->requested_channel); + if (r < 0) + goto disable_radio; + r = zd_chip_enable_rx(chip); + if (r < 0) + goto disable_radio; + r = zd_chip_enable_hwint(chip); + if (r < 0) + goto disable_rx; + + ieee80211softmac_start(netdev); + return 0; +disable_rx: + zd_chip_disable_rx(chip); +disable_radio: + zd_chip_switch_radio_off(chip); +disable_int: + zd_chip_disable_int(chip); +out: + return r; +} + +int zd_mac_stop(struct net_device *netdev) +{ + struct zd_mac *mac = zd_netdev_mac(netdev); + struct zd_chip *chip = &mac->chip; + + /* + * The order here deliberately is a little different from the open() + * method, since we need to make sure there is no opportunity for RX + * frames to be processed by softmac after we have stopped it. + */ + + zd_chip_disable_rx(chip); + ieee80211softmac_stop(netdev); + + zd_chip_disable_hwint(chip); + zd_chip_switch_radio_off(chip); + zd_chip_disable_int(chip); + + return 0; +} + +int zd_mac_set_mac_address(struct net_device *netdev, void *p) +{ + int r; + unsigned long flags; + struct sockaddr *addr = p; + struct zd_mac *mac = zd_netdev_mac(netdev); + struct zd_chip *chip = &mac->chip; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + dev_dbg_f(zd_mac_dev(mac), + "Setting MAC to " MAC_FMT "\n", MAC_ARG(addr->sa_data)); + + r = zd_write_mac_addr(chip, addr->sa_data); + if (r) + return r; + + spin_lock_irqsave(&mac->lock, flags); + memcpy(netdev->dev_addr, addr->sa_data, ETH_ALEN); + spin_unlock_irqrestore(&mac->lock, flags); + + return 0; +} + +int zd_mac_set_regdomain(struct zd_mac *mac, u8 regdomain) +{ + int r; + u8 channel; + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&mac->lock); + if (regdomain == 0) { + regdomain = mac->default_regdomain; + } + if (!zd_regdomain_supported(regdomain)) { + spin_unlock_irq(&mac->lock); + return -EINVAL; + } + mac->regdomain = regdomain; + channel = mac->requested_channel; + spin_unlock_irq(&mac->lock); + + r = zd_geo_init(zd_mac_to_ieee80211(mac), regdomain); + if (r) + return r; + if (!zd_regdomain_supports_channel(regdomain, channel)) { + r = reset_channel(mac); + if (r) + return r; + } + + return 0; +} + +u8 zd_mac_get_regdomain(struct zd_mac *mac) +{ + unsigned long flags; + u8 regdomain; + + spin_lock_irqsave(&mac->lock, flags); + regdomain = mac->regdomain; + spin_unlock_irqrestore(&mac->lock, flags); + return regdomain; +} + +static void set_channel(struct net_device *netdev, u8 channel) +{ + struct zd_mac *mac = zd_netdev_mac(netdev); + + dev_dbg_f(zd_mac_dev(mac), "channel %d\n", channel); + + zd_chip_set_channel(&mac->chip, channel); +} + +/* TODO: Should not work in Managed mode. */ +int zd_mac_request_channel(struct zd_mac *mac, u8 channel) +{ + unsigned long lock_flags; + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); + + if (ieee->iw_mode == IW_MODE_INFRA) + return -EPERM; + + spin_lock_irqsave(&mac->lock, lock_flags); + if (!zd_regdomain_supports_channel(mac->regdomain, channel)) { + spin_unlock_irqrestore(&mac->lock, lock_flags); + return -EINVAL; + } + mac->requested_channel = channel; + spin_unlock_irqrestore(&mac->lock, lock_flags); + if (netif_running(mac->netdev)) + return zd_chip_set_channel(&mac->chip, channel); + else + return 0; +} + +int zd_mac_get_channel(struct zd_mac *mac, u8 *channel, u8 *flags) +{ + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); + + *channel = zd_chip_get_channel(&mac->chip); + if (ieee->iw_mode != IW_MODE_INFRA) { + spin_lock_irq(&mac->lock); + *flags = *channel == mac->requested_channel ? + MAC_FIXED_CHANNEL : 0; + spin_unlock(&mac->lock); + } else { + *flags = 0; + } + dev_dbg_f(zd_mac_dev(mac), "channel %u flags %u\n", *channel, *flags); + return 0; +} + +/* If wrong rate is given, we are falling back to the slowest rate: 1MBit/s */ +static u8 cs_typed_rate(u8 cs_rate) +{ + static const u8 typed_rates[16] = { + [ZD_CS_CCK_RATE_1M] = ZD_CS_CCK|ZD_CS_CCK_RATE_1M, + [ZD_CS_CCK_RATE_2M] = ZD_CS_CCK|ZD_CS_CCK_RATE_2M, + [ZD_CS_CCK_RATE_5_5M] = ZD_CS_CCK|ZD_CS_CCK_RATE_5_5M, + [ZD_CS_CCK_RATE_11M] = ZD_CS_CCK|ZD_CS_CCK_RATE_11M, + [ZD_OFDM_RATE_6M] = ZD_CS_OFDM|ZD_OFDM_RATE_6M, + [ZD_OFDM_RATE_9M] = ZD_CS_OFDM|ZD_OFDM_RATE_9M, + [ZD_OFDM_RATE_12M] = ZD_CS_OFDM|ZD_OFDM_RATE_12M, + [ZD_OFDM_RATE_18M] = ZD_CS_OFDM|ZD_OFDM_RATE_18M, + [ZD_OFDM_RATE_24M] = ZD_CS_OFDM|ZD_OFDM_RATE_24M, + [ZD_OFDM_RATE_36M] = ZD_CS_OFDM|ZD_OFDM_RATE_36M, + [ZD_OFDM_RATE_48M] = ZD_CS_OFDM|ZD_OFDM_RATE_48M, + [ZD_OFDM_RATE_54M] = ZD_CS_OFDM|ZD_OFDM_RATE_54M, + }; + + ZD_ASSERT(ZD_CS_RATE_MASK == 0x0f); + return typed_rates[cs_rate & ZD_CS_RATE_MASK]; +} + +/* Fallback to lowest rate, if rate is unknown. */ +static u8 rate_to_cs_rate(u8 rate) +{ + switch (rate) { + case IEEE80211_CCK_RATE_2MB: + return ZD_CS_CCK_RATE_2M; + case IEEE80211_CCK_RATE_5MB: + return ZD_CS_CCK_RATE_5_5M; + case IEEE80211_CCK_RATE_11MB: + return ZD_CS_CCK_RATE_11M; + case IEEE80211_OFDM_RATE_6MB: + return ZD_OFDM_RATE_6M; + case IEEE80211_OFDM_RATE_9MB: + return ZD_OFDM_RATE_9M; + case IEEE80211_OFDM_RATE_12MB: + return ZD_OFDM_RATE_12M; + case IEEE80211_OFDM_RATE_18MB: + return ZD_OFDM_RATE_18M; + case IEEE80211_OFDM_RATE_24MB: + return ZD_OFDM_RATE_24M; + case IEEE80211_OFDM_RATE_36MB: + return ZD_OFDM_RATE_36M; + case IEEE80211_OFDM_RATE_48MB: + return ZD_OFDM_RATE_48M; + case IEEE80211_OFDM_RATE_54MB: + return ZD_OFDM_RATE_54M; + } + return ZD_CS_CCK_RATE_1M; +} + +int zd_mac_set_mode(struct zd_mac *mac, u32 mode) +{ + struct ieee80211_device *ieee; + + switch (mode) { + case IW_MODE_AUTO: + case IW_MODE_ADHOC: + case IW_MODE_INFRA: + mac->netdev->type = ARPHRD_ETHER; + break; + case IW_MODE_MONITOR: + mac->netdev->type = ARPHRD_IEEE80211_RADIOTAP; + break; + default: + dev_dbg_f(zd_mac_dev(mac), "wrong mode %u\n", mode); + return -EINVAL; + } + + ieee = zd_mac_to_ieee80211(mac); + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&ieee->lock); + ieee->iw_mode = mode; + spin_unlock_irq(&ieee->lock); + + if (netif_running(mac->netdev)) + return reset_mode(mac); + + return 0; +} + +int zd_mac_get_mode(struct zd_mac *mac, u32 *mode) +{ + unsigned long flags; + struct ieee80211_device *ieee; + + ieee = zd_mac_to_ieee80211(mac); + spin_lock_irqsave(&ieee->lock, flags); + *mode = ieee->iw_mode; + spin_unlock_irqrestore(&ieee->lock, flags); + return 0; +} + +int zd_mac_get_range(struct zd_mac *mac, struct iw_range *range) +{ + int i; + const struct channel_range *channel_range; + u8 regdomain; + + memset(range, 0, sizeof(*range)); + + /* FIXME: Not so important and depends on the mode. For 802.11g + * usually this value is used. It seems to be that Bit/s number is + * given here. + */ + range->throughput = 27 * 1000 * 1000; + + range->max_qual.qual = 100; + range->max_qual.level = 100; + + /* FIXME: Needs still to be tuned. */ + range->avg_qual.qual = 71; + range->avg_qual.level = 80; + + /* FIXME: depends on standard? */ + range->min_rts = 256; + range->max_rts = 2346; + + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + + range->max_encoding_tokens = WEP_KEYS; + range->num_encoding_sizes = 2; + range->encoding_size[0] = 5; + range->encoding_size[1] = WEP_KEY_LEN; + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 20; + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&mac->lock); + regdomain = mac->regdomain; + spin_unlock_irq(&mac->lock); + channel_range = zd_channel_range(regdomain); + + range->num_channels = channel_range->end - channel_range->start; + range->old_num_channels = range->num_channels; + range->num_frequency = range->num_channels; + range->old_num_frequency = range->num_frequency; + + for (i = 0; i < range->num_frequency; i++) { + struct iw_freq *freq = &range->freq[i]; + freq->i = channel_range->start + i; + zd_channel_to_freq(freq, freq->i); + } + + return 0; +} + +static int zd_calc_tx_length_us(u8 *service, u8 cs_rate, u16 tx_length) +{ + static const u8 rate_divisor[] = { + [ZD_CS_CCK_RATE_1M] = 1, + [ZD_CS_CCK_RATE_2M] = 2, + [ZD_CS_CCK_RATE_5_5M] = 11, /* bits must be doubled */ + [ZD_CS_CCK_RATE_11M] = 11, + [ZD_OFDM_RATE_6M] = 6, + [ZD_OFDM_RATE_9M] = 9, + [ZD_OFDM_RATE_12M] = 12, + [ZD_OFDM_RATE_18M] = 18, + [ZD_OFDM_RATE_24M] = 24, + [ZD_OFDM_RATE_36M] = 36, + [ZD_OFDM_RATE_48M] = 48, + [ZD_OFDM_RATE_54M] = 54, + }; + + u32 bits = (u32)tx_length * 8; + u32 divisor; + + divisor = rate_divisor[cs_rate]; + if (divisor == 0) + return -EINVAL; + + switch (cs_rate) { + case ZD_CS_CCK_RATE_5_5M: + bits = (2*bits) + 10; /* round up to the next integer */ + break; + case ZD_CS_CCK_RATE_11M: + if (service) { + u32 t = bits % 11; + *service &= ~ZD_PLCP_SERVICE_LENGTH_EXTENSION; + if (0 < t && t <= 3) { + *service |= ZD_PLCP_SERVICE_LENGTH_EXTENSION; + } + } + bits += 10; /* round up to the next integer */ + break; + } + + return bits/divisor; +} + +enum { + R2M_SHORT_PREAMBLE = 0x01, + R2M_11A = 0x02, +}; + +static u8 cs_rate_to_modulation(u8 cs_rate, int flags) +{ + u8 modulation; + + modulation = cs_typed_rate(cs_rate); + if (flags & R2M_SHORT_PREAMBLE) { + switch (ZD_CS_RATE(modulation)) { + case ZD_CS_CCK_RATE_2M: + case ZD_CS_CCK_RATE_5_5M: + case ZD_CS_CCK_RATE_11M: + modulation |= ZD_CS_CCK_PREA_SHORT; + return modulation; + } + } + if (flags & R2M_11A) { + if (ZD_CS_TYPE(modulation) == ZD_CS_OFDM) + modulation |= ZD_CS_OFDM_MODE_11A; + } + return modulation; +} + +static void cs_set_modulation(struct zd_mac *mac, struct zd_ctrlset *cs, + struct ieee80211_hdr_4addr *hdr) +{ + struct ieee80211softmac_device *softmac = ieee80211_priv(mac->netdev); + u16 ftype = WLAN_FC_GET_TYPE(le16_to_cpu(hdr->frame_ctl)); + u8 rate, cs_rate; + int is_mgt = (ftype == IEEE80211_FTYPE_MGMT) != 0; + + /* FIXME: 802.11a? short preamble? */ + rate = ieee80211softmac_suggest_txrate(softmac, + is_multicast_ether_addr(hdr->addr1), is_mgt); + + cs_rate = rate_to_cs_rate(rate); + cs->modulation = cs_rate_to_modulation(cs_rate, 0); +} + +static void cs_set_control(struct zd_mac *mac, struct zd_ctrlset *cs, + struct ieee80211_hdr_4addr *header) +{ + unsigned int tx_length = le16_to_cpu(cs->tx_length); + u16 fctl = le16_to_cpu(header->frame_ctl); + u16 ftype = WLAN_FC_GET_TYPE(fctl); + u16 stype = WLAN_FC_GET_STYPE(fctl); + + /* + * CONTROL: + * - start at 0x00 + * - if fragment 0, enable bit 0 + * - if backoff needed, enable bit 0 + * - if burst (backoff not needed) disable bit 0 + * - if multicast, enable bit 1 + * - if PS-POLL frame, enable bit 2 + * - if in INDEPENDENT_BSS mode and zd1205_DestPowerSave, then enable + * bit 4 (FIXME: wtf) + * - if frag_len > RTS threshold, set bit 5 as long if it isnt + * multicast or mgt + * - if bit 5 is set, and we are in OFDM mode, unset bit 5 and set bit + * 7 + */ + + cs->control = 0; + + /* First fragment */ + if (WLAN_GET_SEQ_FRAG(le16_to_cpu(header->seq_ctl)) == 0) + cs->control |= ZD_CS_NEED_RANDOM_BACKOFF; + + /* Multicast */ + if (is_multicast_ether_addr(header->addr1)) + cs->control |= ZD_CS_MULTICAST; + + /* PS-POLL */ + if (stype == IEEE80211_STYPE_PSPOLL) + cs->control |= ZD_CS_PS_POLL_FRAME; + + if (!is_multicast_ether_addr(header->addr1) && + ftype != IEEE80211_FTYPE_MGMT && + tx_length > zd_netdev_ieee80211(mac->netdev)->rts) + { + /* FIXME: check the logic */ + if (ZD_CS_TYPE(cs->modulation) == ZD_CS_OFDM) { + /* 802.11g */ + cs->control |= ZD_CS_SELF_CTS; + } else { /* 802.11b */ + cs->control |= ZD_CS_RTS; + } + } + + /* FIXME: Management frame? */ +} + +static int fill_ctrlset(struct zd_mac *mac, + struct ieee80211_txb *txb, + int frag_num) +{ + int r; + struct sk_buff *skb = txb->fragments[frag_num]; + struct ieee80211_hdr_4addr *hdr = + (struct ieee80211_hdr_4addr *) skb->data; + unsigned int frag_len = skb->len + IEEE80211_FCS_LEN; + unsigned int next_frag_len; + unsigned int packet_length; + struct zd_ctrlset *cs = (struct zd_ctrlset *) + skb_push(skb, sizeof(struct zd_ctrlset)); + + if (frag_num+1 < txb->nr_frags) { + next_frag_len = txb->fragments[frag_num+1]->len + + IEEE80211_FCS_LEN; + } else { + next_frag_len = 0; + } + ZD_ASSERT(frag_len <= 0xffff); + ZD_ASSERT(next_frag_len <= 0xffff); + + cs_set_modulation(mac, cs, hdr); + + cs->tx_length = cpu_to_le16(frag_len); + + cs_set_control(mac, cs, hdr); + + packet_length = frag_len + sizeof(struct zd_ctrlset) + 10; + ZD_ASSERT(packet_length <= 0xffff); + /* ZD1211B: Computing the length difference this way, gives us + * flexibility to compute the packet length. + */ + cs->packet_length = cpu_to_le16(mac->chip.is_zd1211b ? + packet_length - frag_len : packet_length); + + /* + * CURRENT LENGTH: + * - transmit frame length in microseconds + * - seems to be derived from frame length + * - see Cal_Us_Service() in zdinlinef.h + * - if macp->bTxBurstEnable is enabled, then multiply by 4 + * - bTxBurstEnable is never set in the vendor driver + * + * SERVICE: + * - "for PLCP configuration" + * - always 0 except in some situations at 802.11b 11M + * - see line 53 of zdinlinef.h + */ + cs->service = 0; + r = zd_calc_tx_length_us(&cs->service, ZD_CS_RATE(cs->modulation), + le16_to_cpu(cs->tx_length)); + if (r < 0) + return r; + cs->current_length = cpu_to_le16(r); + + if (next_frag_len == 0) { + cs->next_frame_length = 0; + } else { + r = zd_calc_tx_length_us(NULL, ZD_CS_RATE(cs->modulation), + next_frag_len); + if (r < 0) + return r; + cs->next_frame_length = cpu_to_le16(r); + } + + return 0; +} + +static int zd_mac_tx(struct zd_mac *mac, struct ieee80211_txb *txb, int pri) +{ + int i, r; + + for (i = 0; i < txb->nr_frags; i++) { + struct sk_buff *skb = txb->fragments[i]; + + r = fill_ctrlset(mac, txb, i); + if (r) + return r; + r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len); + if (r) + return r; + } + + /* FIXME: shouldn't this be handled by the upper layers? */ + mac->netdev->trans_start = jiffies; + + ieee80211_txb_free(txb); + return 0; +} + +struct zd_rt_hdr { + struct ieee80211_radiotap_header rt_hdr; + u8 rt_flags; + u16 rt_channel; + u16 rt_chbitmask; + u16 rt_rate; +}; + +static void fill_rt_header(void *buffer, struct zd_mac *mac, + const struct ieee80211_rx_stats *stats, + const struct rx_status *status) +{ + struct zd_rt_hdr *hdr = buffer; + + hdr->rt_hdr.it_version = PKTHDR_RADIOTAP_VERSION; + hdr->rt_hdr.it_pad = 0; + hdr->rt_hdr.it_len = cpu_to_le16(sizeof(struct zd_rt_hdr)); + hdr->rt_hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | + (1 << IEEE80211_RADIOTAP_CHANNEL) | + (1 << IEEE80211_RADIOTAP_RATE)); + + hdr->rt_flags = 0; + if (status->decryption_type & (ZD_RX_WEP64|ZD_RX_WEP128|ZD_RX_WEP256)) + hdr->rt_flags |= IEEE80211_RADIOTAP_F_WEP; + + /* FIXME: 802.11a */ + hdr->rt_channel = cpu_to_le16(ieee80211chan2mhz( + _zd_chip_get_channel(&mac->chip))); + hdr->rt_chbitmask = cpu_to_le16(IEEE80211_CHAN_2GHZ | + ((status->frame_status & ZD_RX_FRAME_MODULATION_MASK) == + ZD_RX_OFDM ? IEEE80211_CHAN_OFDM : IEEE80211_CHAN_CCK)); + + hdr->rt_rate = stats->rate / 5; +} + +/* Returns 1 if the data packet is for us and 0 otherwise. */ +static int is_data_packet_for_us(struct ieee80211_device *ieee, + struct ieee80211_hdr_4addr *hdr) +{ + struct net_device *netdev = ieee->dev; + u16 fc = le16_to_cpu(hdr->frame_ctl); + + ZD_ASSERT(WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA); + + switch (ieee->iw_mode) { + case IW_MODE_ADHOC: + if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != 0 || + memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) != 0) + return 0; + break; + case IW_MODE_AUTO: + case IW_MODE_INFRA: + if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != + IEEE80211_FCTL_FROMDS || + memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) != 0) + return 0; + break; + default: + ZD_ASSERT(ieee->iw_mode != IW_MODE_MONITOR); + return 0; + } + + return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 || + is_multicast_ether_addr(hdr->addr1) || + (netdev->flags & IFF_PROMISC); +} + +/* Filters receiving packets. If it returns 1 send it to ieee80211_rx, if 0 + * return. If an error is detected -EINVAL is returned. ieee80211_rx_mgt() is + * called here. + * + * It has been based on ieee80211_rx_any. + */ +static int filter_rx(struct ieee80211_device *ieee, + const u8 *buffer, unsigned int length, + struct ieee80211_rx_stats *stats) +{ + struct ieee80211_hdr_4addr *hdr; + u16 fc; + + if (ieee->iw_mode == IW_MODE_MONITOR) + return 1; + + hdr = (struct ieee80211_hdr_4addr *)buffer; + fc = le16_to_cpu(hdr->frame_ctl); + if ((fc & IEEE80211_FCTL_VERS) != 0) + return -EINVAL; + + switch (WLAN_FC_GET_TYPE(fc)) { + case IEEE80211_FTYPE_MGMT: + if (length < sizeof(struct ieee80211_hdr_3addr)) + return -EINVAL; + ieee80211_rx_mgt(ieee, hdr, stats); + return 0; + case IEEE80211_FTYPE_CTL: + /* Ignore invalid short buffers */ + return 0; + case IEEE80211_FTYPE_DATA: + if (length < sizeof(struct ieee80211_hdr_3addr)) + return -EINVAL; + return is_data_packet_for_us(ieee, hdr); + } + + return -EINVAL; +} + +static void update_qual_rssi(struct zd_mac *mac, u8 qual_percent, u8 rssi) +{ + unsigned long flags; + + spin_lock_irqsave(&mac->lock, flags); + mac->qual_average = (7 * mac->qual_average + qual_percent) / 8; + mac->rssi_average = (7 * mac->rssi_average + rssi) / 8; + spin_unlock_irqrestore(&mac->lock, flags); +} + +static int fill_rx_stats(struct ieee80211_rx_stats *stats, + const struct rx_status **pstatus, + struct zd_mac *mac, + const u8 *buffer, unsigned int length) +{ + const struct rx_status *status; + + *pstatus = status = zd_tail(buffer, length, sizeof(struct rx_status)); + if (status->frame_status & ZD_RX_ERROR) { + /* FIXME: update? */ + return -EINVAL; + } + memset(stats, 0, sizeof(struct ieee80211_rx_stats)); + stats->len = length - (ZD_PLCP_HEADER_SIZE + IEEE80211_FCS_LEN + + + sizeof(struct rx_status)); + /* FIXME: 802.11a */ + stats->freq = IEEE80211_24GHZ_BAND; + stats->received_channel = _zd_chip_get_channel(&mac->chip); + stats->rssi = zd_rx_strength_percent(status->signal_strength); + stats->signal = zd_rx_qual_percent(buffer, + length - sizeof(struct rx_status), + status); + stats->mask = IEEE80211_STATMASK_RSSI | IEEE80211_STATMASK_SIGNAL; + stats->rate = zd_rx_rate(buffer, status); + if (stats->rate) + stats->mask |= IEEE80211_STATMASK_RATE; + + update_qual_rssi(mac, stats->signal, stats->rssi); + return 0; +} + +int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length) +{ + int r; + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); + struct ieee80211_rx_stats stats; + const struct rx_status *status; + struct sk_buff *skb; + + if (length < ZD_PLCP_HEADER_SIZE + IEEE80211_1ADDR_LEN + + IEEE80211_FCS_LEN + sizeof(struct rx_status)) + return -EINVAL; + + r = fill_rx_stats(&stats, &status, mac, buffer, length); + if (r) + return r; + + length -= ZD_PLCP_HEADER_SIZE+IEEE80211_FCS_LEN+ + sizeof(struct rx_status); + buffer += ZD_PLCP_HEADER_SIZE; + + r = filter_rx(ieee, buffer, length, &stats); + if (r <= 0) + return r; + + skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length); + if (!skb) + return -ENOMEM; + if (ieee->iw_mode == IW_MODE_MONITOR) + fill_rt_header(skb_put(skb, sizeof(struct zd_rt_hdr)), mac, + &stats, status); + memcpy(skb_put(skb, length), buffer, length); + + r = ieee80211_rx(ieee, skb, &stats); + if (!r) { + ZD_ASSERT(in_irq()); + dev_kfree_skb_irq(skb); + } + return 0; +} + +static int netdev_tx(struct ieee80211_txb *txb, struct net_device *netdev, + int pri) +{ + return zd_mac_tx(zd_netdev_mac(netdev), txb, pri); +} + +static void set_security(struct net_device *netdev, + struct ieee80211_security *sec) +{ + struct ieee80211_device *ieee = zd_netdev_ieee80211(netdev); + struct ieee80211_security *secinfo = &ieee->sec; + int keyidx; + + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n"); + + for (keyidx = 0; keyidxflags & (1<encode_alg[keyidx] = sec->encode_alg[keyidx]; + secinfo->key_sizes[keyidx] = sec->key_sizes[keyidx]; + memcpy(secinfo->keys[keyidx], sec->keys[keyidx], + SCM_KEY_LEN); + } + + if (sec->flags & SEC_ACTIVE_KEY) { + secinfo->active_key = sec->active_key; + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), + " .active_key = %d\n", sec->active_key); + } + if (sec->flags & SEC_UNICAST_GROUP) { + secinfo->unicast_uses_group = sec->unicast_uses_group; + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), + " .unicast_uses_group = %d\n", + sec->unicast_uses_group); + } + if (sec->flags & SEC_LEVEL) { + secinfo->level = sec->level; + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), + " .level = %d\n", sec->level); + } + if (sec->flags & SEC_ENABLED) { + secinfo->enabled = sec->enabled; + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), + " .enabled = %d\n", sec->enabled); + } + if (sec->flags & SEC_ENCRYPT) { + secinfo->encrypt = sec->encrypt; + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), + " .encrypt = %d\n", sec->encrypt); + } + if (sec->flags & SEC_AUTH_MODE) { + secinfo->auth_mode = sec->auth_mode; + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), + " .auth_mode = %d\n", sec->auth_mode); + } +} + +static void ieee_init(struct ieee80211_device *ieee) +{ + ieee->mode = IEEE_B | IEEE_G; + ieee->freq_band = IEEE80211_24GHZ_BAND; + ieee->modulation = IEEE80211_OFDM_MODULATION | IEEE80211_CCK_MODULATION; + ieee->tx_headroom = sizeof(struct zd_ctrlset); + ieee->set_security = set_security; + ieee->hard_start_xmit = netdev_tx; + + /* Software encryption/decryption for now */ + ieee->host_build_iv = 0; + ieee->host_encrypt = 1; + ieee->host_decrypt = 1; + + /* FIXME: default to managed mode, until ieee80211 and zd1211rw can + * correctly support AUTO */ + ieee->iw_mode = IW_MODE_INFRA; +} + +static void softmac_init(struct ieee80211softmac_device *sm) +{ + sm->set_channel = set_channel; +} + +struct iw_statistics *zd_mac_get_wireless_stats(struct net_device *ndev) +{ + struct zd_mac *mac = zd_netdev_mac(ndev); + struct iw_statistics *iw_stats = &mac->iw_stats; + + memset(iw_stats, 0, sizeof(struct iw_statistics)); + /* We are not setting the status, because ieee->state is not updated + * at all and this driver doesn't track authentication state. + */ + spin_lock_irq(&mac->lock); + iw_stats->qual.qual = mac->qual_average; + iw_stats->qual.level = mac->rssi_average; + iw_stats->qual.updated = IW_QUAL_QUAL_UPDATED|IW_QUAL_LEVEL_UPDATED| + IW_QUAL_NOISE_INVALID; + spin_unlock_irq(&mac->lock); + /* TODO: update counter */ + return iw_stats; +} + +#ifdef DEBUG +static const char* decryption_types[] = { + [ZD_RX_NO_WEP] = "none", + [ZD_RX_WEP64] = "WEP64", + [ZD_RX_TKIP] = "TKIP", + [ZD_RX_AES] = "AES", + [ZD_RX_WEP128] = "WEP128", + [ZD_RX_WEP256] = "WEP256", +}; + +static const char *decryption_type_string(u8 type) +{ + const char *s; + + if (type < ARRAY_SIZE(decryption_types)) { + s = decryption_types[type]; + } else { + s = NULL; + } + return s ? s : "unknown"; +} + +static int is_ofdm(u8 frame_status) +{ + return (frame_status & ZD_RX_OFDM); +} + +void zd_dump_rx_status(const struct rx_status *status) +{ + const char* modulation; + u8 quality; + + if (is_ofdm(status->frame_status)) { + modulation = "ofdm"; + quality = status->signal_quality_ofdm; + } else { + modulation = "cck"; + quality = status->signal_quality_cck; + } + pr_debug("rx status %s strength %#04x qual %#04x decryption %s\n", + modulation, status->signal_strength, quality, + decryption_type_string(status->decryption_type)); + if (status->frame_status & ZD_RX_ERROR) { + pr_debug("rx error %s%s%s%s%s%s\n", + (status->frame_status & ZD_RX_TIMEOUT_ERROR) ? + "timeout " : "", + (status->frame_status & ZD_RX_FIFO_OVERRUN_ERROR) ? + "fifo " : "", + (status->frame_status & ZD_RX_DECRYPTION_ERROR) ? + "decryption " : "", + (status->frame_status & ZD_RX_CRC32_ERROR) ? + "crc32 " : "", + (status->frame_status & ZD_RX_NO_ADDR1_MATCH_ERROR) ? + "addr1 " : "", + (status->frame_status & ZD_RX_CRC16_ERROR) ? + "crc16" : ""); + } +} +#endif /* DEBUG */ diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h new file mode 100644 index 000000000000..71e382c589ee --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_mac.h @@ -0,0 +1,190 @@ +/* zd_mac.c + * + * 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 + */ + +#ifndef _ZD_MAC_H +#define _ZD_MAC_H + +#include +#include +#include +#include + +#include "zd_chip.h" +#include "zd_netdev.h" + +struct zd_ctrlset { + u8 modulation; + __le16 tx_length; + u8 control; + /* stores only the difference to tx_length on ZD1211B */ + __le16 packet_length; + __le16 current_length; + u8 service; + __le16 next_frame_length; +} __attribute__((packed)); + +#define ZD_CS_RESERVED_SIZE 25 + +/* zd_crtlset field modulation */ +#define ZD_CS_RATE_MASK 0x0f +#define ZD_CS_TYPE_MASK 0x10 +#define ZD_CS_RATE(modulation) ((modulation) & ZD_CS_RATE_MASK) +#define ZD_CS_TYPE(modulation) ((modulation) & ZD_CS_TYPE_MASK) + +#define ZD_CS_CCK 0x00 +#define ZD_CS_OFDM 0x10 + +#define ZD_CS_CCK_RATE_1M 0x00 +#define ZD_CS_CCK_RATE_2M 0x01 +#define ZD_CS_CCK_RATE_5_5M 0x02 +#define ZD_CS_CCK_RATE_11M 0x03 +/* The rates for OFDM are encoded as in the PLCP header. Use ZD_OFDM_RATE_*. + */ + +/* bit 5 is preamble (when in CCK mode), or a/g selection (when in OFDM mode) */ +#define ZD_CS_CCK_PREA_LONG 0x00 +#define ZD_CS_CCK_PREA_SHORT 0x20 +#define ZD_CS_OFDM_MODE_11G 0x00 +#define ZD_CS_OFDM_MODE_11A 0x20 + +/* zd_ctrlset control field */ +#define ZD_CS_NEED_RANDOM_BACKOFF 0x01 +#define ZD_CS_MULTICAST 0x02 + +#define ZD_CS_FRAME_TYPE_MASK 0x0c +#define ZD_CS_DATA_FRAME 0x00 +#define ZD_CS_PS_POLL_FRAME 0x04 +#define ZD_CS_MANAGEMENT_FRAME 0x08 +#define ZD_CS_NO_SEQUENCE_CTL_FRAME 0x0c + +#define ZD_CS_WAKE_DESTINATION 0x10 +#define ZD_CS_RTS 0x20 +#define ZD_CS_ENCRYPT 0x40 +#define ZD_CS_SELF_CTS 0x80 + +/* Incoming frames are prepended by a PLCP header */ +#define ZD_PLCP_HEADER_SIZE 5 + +struct rx_length_info { + __le16 length[3]; + __le16 tag; +} __attribute__((packed)); + +#define RX_LENGTH_INFO_TAG 0x697e + +struct rx_status { + /* rssi */ + u8 signal_strength; + u8 signal_quality_cck; + u8 signal_quality_ofdm; + u8 decryption_type; + u8 frame_status; +} __attribute__((packed)); + +/* rx_status field decryption_type */ +#define ZD_RX_NO_WEP 0 +#define ZD_RX_WEP64 1 +#define ZD_RX_TKIP 2 +#define ZD_RX_AES 4 +#define ZD_RX_WEP128 5 +#define ZD_RX_WEP256 6 + +/* rx_status field frame_status */ +#define ZD_RX_FRAME_MODULATION_MASK 0x01 +#define ZD_RX_CCK 0x00 +#define ZD_RX_OFDM 0x01 + +#define ZD_RX_TIMEOUT_ERROR 0x02 +#define ZD_RX_FIFO_OVERRUN_ERROR 0x04 +#define ZD_RX_DECRYPTION_ERROR 0x08 +#define ZD_RX_CRC32_ERROR 0x10 +#define ZD_RX_NO_ADDR1_MATCH_ERROR 0x20 +#define ZD_RX_CRC16_ERROR 0x40 +#define ZD_RX_ERROR 0x80 + +enum mac_flags { + MAC_FIXED_CHANNEL = 0x01, +}; + +struct zd_mac { + struct net_device *netdev; + struct zd_chip chip; + spinlock_t lock; + /* Unlocked reading possible */ + struct iw_statistics iw_stats; + u8 qual_average; + u8 rssi_average; + u8 regdomain; + u8 default_regdomain; + u8 requested_channel; +}; + +static inline struct ieee80211_device *zd_mac_to_ieee80211(struct zd_mac *mac) +{ + return zd_netdev_ieee80211(mac->netdev); +} + +static inline struct zd_mac *zd_netdev_mac(struct net_device *netdev) +{ + return ieee80211softmac_priv(netdev); +} + +static inline struct zd_mac *zd_chip_to_mac(struct zd_chip *chip) +{ + return container_of(chip, struct zd_mac, chip); +} + +static inline struct zd_mac *zd_usb_to_mac(struct zd_usb *usb) +{ + return zd_chip_to_mac(zd_usb_to_chip(usb)); +} + +#define zd_mac_dev(mac) (zd_chip_dev(&(mac)->chip)) + +int zd_mac_init(struct zd_mac *mac, + struct net_device *netdev, + struct usb_interface *intf); +void zd_mac_clear(struct zd_mac *mac); + +int zd_mac_init_hw(struct zd_mac *mac, u8 device_type); + +int zd_mac_open(struct net_device *netdev); +int zd_mac_stop(struct net_device *netdev); +int zd_mac_set_mac_address(struct net_device *dev, void *p); + +int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length); + +int zd_mac_set_regdomain(struct zd_mac *zd_mac, u8 regdomain); +u8 zd_mac_get_regdomain(struct zd_mac *zd_mac); + +int zd_mac_request_channel(struct zd_mac *mac, u8 channel); +int zd_mac_get_channel(struct zd_mac *mac, u8 *channel, u8 *flags); + +int zd_mac_set_mode(struct zd_mac *mac, u32 mode); +int zd_mac_get_mode(struct zd_mac *mac, u32 *mode); + +int zd_mac_get_range(struct zd_mac *mac, struct iw_range *range); + +struct iw_statistics *zd_mac_get_wireless_stats(struct net_device *ndev); + +#ifdef DEBUG +void zd_dump_rx_status(const struct rx_status *status); +#else +#define zd_dump_rx_status(status) +#endif /* DEBUG */ + +#endif /* _ZD_MAC_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.c b/drivers/net/wireless/zd1211rw/zd_netdev.c new file mode 100644 index 000000000000..9df232c2c863 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_netdev.c @@ -0,0 +1,267 @@ +/* zd_netdev.c + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_netdev.h" +#include "zd_mac.h" +#include "zd_ieee80211.h" + +/* Region 0 means reset regdomain to default. */ +static int zd_set_regdomain(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + const u8 *regdomain = (u8 *)req; + return zd_mac_set_regdomain(zd_netdev_mac(netdev), *regdomain); +} + +static int zd_get_regdomain(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + u8 *regdomain = (u8 *)req; + if (!regdomain) + return -EINVAL; + *regdomain = zd_mac_get_regdomain(zd_netdev_mac(netdev)); + return 0; +} + +static const struct iw_priv_args zd_priv_args[] = { + { + .cmd = ZD_PRIV_SET_REGDOMAIN, + .set_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + .name = "set_regdomain", + }, + { + .cmd = ZD_PRIV_GET_REGDOMAIN, + .get_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + .name = "get_regdomain", + }, +}; + +#define PRIV_OFFSET(x) [(x)-SIOCIWFIRSTPRIV] + +static const iw_handler zd_priv_handler[] = { + PRIV_OFFSET(ZD_PRIV_SET_REGDOMAIN) = zd_set_regdomain, + PRIV_OFFSET(ZD_PRIV_GET_REGDOMAIN) = zd_get_regdomain, +}; + +static int iw_get_name(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + /* FIXME: check whether 802.11a will also supported, add also + * zd1211B, if we support it. + */ + strlcpy(req->name, "802.11g zd1211", IFNAMSIZ); + return 0; +} + +static int iw_set_freq(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + int r; + struct zd_mac *mac = zd_netdev_mac(netdev); + struct iw_freq *freq = &req->freq; + u8 channel; + + r = zd_find_channel(&channel, freq); + if (r < 0) + return r; + r = zd_mac_request_channel(mac, channel); + return r; +} + +static int iw_get_freq(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + int r; + struct zd_mac *mac = zd_netdev_mac(netdev); + struct iw_freq *freq = &req->freq; + u8 channel; + u8 flags; + + r = zd_mac_get_channel(mac, &channel, &flags); + if (r) + return r; + + freq->flags = (flags & MAC_FIXED_CHANNEL) ? + IW_FREQ_FIXED : IW_FREQ_AUTO; + dev_dbg_f(zd_mac_dev(mac), "channel %s\n", + (flags & MAC_FIXED_CHANNEL) ? "fixed" : "auto"); + return zd_channel_to_freq(freq, channel); +} + +static int iw_set_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + return zd_mac_set_mode(zd_netdev_mac(netdev), req->mode); +} + +static int iw_get_mode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + return zd_mac_get_mode(zd_netdev_mac(netdev), &req->mode); +} + +static int iw_get_range(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *req, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + + dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n"); + req->data.length = sizeof(*range); + return zd_mac_get_range(zd_netdev_mac(netdev), range); +} + +static int iw_set_encode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + return ieee80211_wx_set_encode(zd_netdev_ieee80211(netdev), info, + data, extra); +} + +static int iw_get_encode(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + return ieee80211_wx_get_encode(zd_netdev_ieee80211(netdev), info, + data, extra); +} + +static int iw_set_encodeext(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + return ieee80211_wx_set_encodeext(zd_netdev_ieee80211(netdev), info, + data, extra); +} + +static int iw_get_encodeext(struct net_device *netdev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + return ieee80211_wx_get_encodeext(zd_netdev_ieee80211(netdev), info, + data, extra); +} + +#define WX(x) [(x)-SIOCIWFIRST] + +static const iw_handler zd_standard_iw_handlers[] = { + WX(SIOCGIWNAME) = iw_get_name, + WX(SIOCSIWFREQ) = iw_set_freq, + WX(SIOCGIWFREQ) = iw_get_freq, + WX(SIOCSIWMODE) = iw_set_mode, + WX(SIOCGIWMODE) = iw_get_mode, + WX(SIOCGIWRANGE) = iw_get_range, + WX(SIOCSIWENCODE) = iw_set_encode, + WX(SIOCGIWENCODE) = iw_get_encode, + WX(SIOCSIWENCODEEXT) = iw_set_encodeext, + WX(SIOCGIWENCODEEXT) = iw_get_encodeext, + WX(SIOCSIWAUTH) = ieee80211_wx_set_auth, + WX(SIOCGIWAUTH) = ieee80211_wx_get_auth, + WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan, + WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results, + WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid, + WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid, + WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap, + WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap, + WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate, + WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate, + WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie, + WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie, + WX(SIOCSIWMLME) = ieee80211softmac_wx_set_mlme, +}; + +static const struct iw_handler_def iw_handler_def = { + .standard = zd_standard_iw_handlers, + .num_standard = ARRAY_SIZE(zd_standard_iw_handlers), + .private = zd_priv_handler, + .num_private = ARRAY_SIZE(zd_priv_handler), + .private_args = zd_priv_args, + .num_private_args = ARRAY_SIZE(zd_priv_args), + .get_wireless_stats = zd_mac_get_wireless_stats, +}; + +struct net_device *zd_netdev_alloc(struct usb_interface *intf) +{ + int r; + struct net_device *netdev; + struct zd_mac *mac; + + netdev = alloc_ieee80211softmac(sizeof(struct zd_mac)); + if (!netdev) { + dev_dbg_f(&intf->dev, "out of memory\n"); + return NULL; + } + + mac = zd_netdev_mac(netdev); + r = zd_mac_init(mac, netdev, intf); + if (r) { + usb_set_intfdata(intf, NULL); + free_ieee80211(netdev); + return NULL; + } + + SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &intf->dev); + + dev_dbg_f(&intf->dev, "netdev->flags %#06hx\n", netdev->flags); + dev_dbg_f(&intf->dev, "netdev->features %#010lx\n", netdev->features); + + netdev->open = zd_mac_open; + netdev->stop = zd_mac_stop; + /* netdev->get_stats = */ + /* netdev->set_multicast_list = */ + netdev->set_mac_address = zd_mac_set_mac_address; + netdev->wireless_handlers = &iw_handler_def; + /* netdev->ethtool_ops = */ + + return netdev; +} + +void zd_netdev_free(struct net_device *netdev) +{ + if (!netdev) + return; + + zd_mac_clear(zd_netdev_mac(netdev)); + free_ieee80211(netdev); +} + +void zd_netdev_disconnect(struct net_device *netdev) +{ + unregister_netdev(netdev); +} diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.h b/drivers/net/wireless/zd1211rw/zd_netdev.h new file mode 100644 index 000000000000..374a957073c1 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_netdev.h @@ -0,0 +1,45 @@ +/* zd_netdev.h: Header for net device related functions. + * + * 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 + */ + +#ifndef _ZD_NETDEV_H +#define _ZD_NETDEV_H + +#include +#include +#include + +#define ZD_PRIV_SET_REGDOMAIN (SIOCIWFIRSTPRIV) +#define ZD_PRIV_GET_REGDOMAIN (SIOCIWFIRSTPRIV+1) + +static inline struct ieee80211_device *zd_netdev_ieee80211( + struct net_device *ndev) +{ + return netdev_priv(ndev); +} + +static inline struct net_device *zd_ieee80211_to_netdev( + struct ieee80211_device *ieee) +{ + return ieee->dev; +} + +struct net_device *zd_netdev_alloc(struct usb_interface *intf); +void zd_netdev_free(struct net_device *netdev); + +void zd_netdev_disconnect(struct net_device *netdev); + +#endif /* _ZD_NETDEV_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c new file mode 100644 index 000000000000..d3770d2c61bc --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_rf.c @@ -0,0 +1,151 @@ +/* zd_rf.c + * + * 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 + */ + +#include +#include + +#include "zd_def.h" +#include "zd_rf.h" +#include "zd_ieee80211.h" +#include "zd_chip.h" + +static const char *rfs[] = { + [0] = "unknown RF0", + [1] = "unknown RF1", + [UW2451_RF] = "UW2451_RF", + [UCHIP_RF] = "UCHIP_RF", + [AL2230_RF] = "AL2230_RF", + [AL7230B_RF] = "AL7230B_RF", + [THETA_RF] = "THETA_RF", + [AL2210_RF] = "AL2210_RF", + [MAXIM_NEW_RF] = "MAXIM_NEW_RF", + [UW2453_RF] = "UW2453_RF", + [AL2230S_RF] = "AL2230S_RF", + [RALINK_RF] = "RALINK_RF", + [INTERSIL_RF] = "INTERSIL_RF", + [RF2959_RF] = "RF2959_RF", + [MAXIM_NEW2_RF] = "MAXIM_NEW2_RF", + [PHILIPS_RF] = "PHILIPS_RF", +}; + +const char *zd_rf_name(u8 type) +{ + if (type & 0xf0) + type = 0; + return rfs[type]; +} + +void zd_rf_init(struct zd_rf *rf) +{ + memset(rf, 0, sizeof(*rf)); +} + +void zd_rf_clear(struct zd_rf *rf) +{ + memset(rf, 0, sizeof(*rf)); +} + +int zd_rf_init_hw(struct zd_rf *rf, u8 type) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + switch (type) { + case RF2959_RF: + r = zd_rf_init_rf2959(rf); + if (r) + return r; + break; + case AL2230_RF: + r = zd_rf_init_al2230(rf); + if (r) + return r; + break; + default: + dev_err(zd_chip_dev(chip), + "RF %s %#x is not supported\n", zd_rf_name(type), type); + rf->type = 0; + return -ENODEV; + } + + rf->type = type; + + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->init_hw(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%s", zd_rf_name(rf->type)); +} + +int zd_rf_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + + ZD_ASSERT(mutex_is_locked(&zd_rf_to_chip(rf)->mutex)); + if (channel < MIN_CHANNEL24) + return -EINVAL; + if (channel > MAX_CHANNEL24) + return -EINVAL; + dev_dbg_f(zd_chip_dev(zd_rf_to_chip(rf)), "channel: %d\n", channel); + + r = rf->set_channel(rf, channel); + if (r >= 0) + rf->channel = channel; + return r; +} + +int zd_switch_radio_on(struct zd_rf *rf) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->switch_radio_on(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} + +int zd_switch_radio_off(struct zd_rf *rf) +{ + int r, t; + struct zd_chip *chip = zd_rf_to_chip(rf); + + /* TODO: move phy regs handling to zd_chip */ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_chip_lock_phy_regs(chip); + if (r) + return r; + t = rf->switch_radio_off(rf); + r = zd_chip_unlock_phy_regs(chip); + if (t) + r = t; + return r; +} diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h new file mode 100644 index 000000000000..ea30f693fcc8 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_rf.h @@ -0,0 +1,82 @@ +/* zd_rf.h + * + * 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 + */ + +#ifndef _ZD_RF_H +#define _ZD_RF_H + +#include "zd_types.h" + +#define UW2451_RF 0x2 +#define UCHIP_RF 0x3 +#define AL2230_RF 0x4 +#define AL7230B_RF 0x5 /* a,b,g */ +#define THETA_RF 0x6 +#define AL2210_RF 0x7 +#define MAXIM_NEW_RF 0x8 +#define UW2453_RF 0x9 +#define AL2230S_RF 0xa +#define RALINK_RF 0xb +#define INTERSIL_RF 0xc +#define RF2959_RF 0xd +#define MAXIM_NEW2_RF 0xe +#define PHILIPS_RF 0xf + +#define RF_CHANNEL(ch) [(ch)-1] + +/* Provides functions of the RF transceiver. */ + +enum { + RF_REG_BITS = 6, + RF_VALUE_BITS = 18, + RF_RV_BITS = RF_REG_BITS + RF_VALUE_BITS, +}; + +struct zd_rf { + u8 type; + + u8 channel; + /* + * Whether this RF should patch the 6M band edge + * (assuming E2P_POD agrees) + */ + u8 patch_6m_band_edge:1; + + /* RF-specific functions */ + int (*init_hw)(struct zd_rf *rf); + int (*set_channel)(struct zd_rf *rf, u8 channel); + int (*switch_radio_on)(struct zd_rf *rf); + int (*switch_radio_off)(struct zd_rf *rf); +}; + +const char *zd_rf_name(u8 type); +void zd_rf_init(struct zd_rf *rf); +void zd_rf_clear(struct zd_rf *rf); +int zd_rf_init_hw(struct zd_rf *rf, u8 type); + +int zd_rf_scnprint_id(struct zd_rf *rf, char *buffer, size_t size); + +int zd_rf_set_channel(struct zd_rf *rf, u8 channel); + +int zd_switch_radio_on(struct zd_rf *rf); +int zd_switch_radio_off(struct zd_rf *rf); + +/* Functions for individual RF chips */ + +int zd_rf_init_rf2959(struct zd_rf *rf); +int zd_rf_init_al2230(struct zd_rf *rf); + +#endif /* _ZD_RF_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c new file mode 100644 index 000000000000..0948b25f660d --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c @@ -0,0 +1,308 @@ +/* zd_rf_al2230.c: Functions for the AL2230 RF controller + * + * 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 + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +static const u32 al2230_table[][3] = { + RF_CHANNEL( 1) = { 0x03f790, 0x033331, 0x00000d, }, + RF_CHANNEL( 2) = { 0x03f790, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 3) = { 0x03e790, 0x033331, 0x00000d, }, + RF_CHANNEL( 4) = { 0x03e790, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 5) = { 0x03f7a0, 0x033331, 0x00000d, }, + RF_CHANNEL( 6) = { 0x03f7a0, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 7) = { 0x03e7a0, 0x033331, 0x00000d, }, + RF_CHANNEL( 8) = { 0x03e7a0, 0x0b3331, 0x00000d, }, + RF_CHANNEL( 9) = { 0x03f7b0, 0x033331, 0x00000d, }, + RF_CHANNEL(10) = { 0x03f7b0, 0x0b3331, 0x00000d, }, + RF_CHANNEL(11) = { 0x03e7b0, 0x033331, 0x00000d, }, + RF_CHANNEL(12) = { 0x03e7b0, 0x0b3331, 0x00000d, }, + RF_CHANNEL(13) = { 0x03f7c0, 0x033331, 0x00000d, }, + RF_CHANNEL(14) = { 0x03e7c0, 0x066661, 0x00000d, }, +}; + +static int zd1211_al2230_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { CR15, 0x20 }, { CR23, 0x40 }, { CR24, 0x20 }, + { CR26, 0x11 }, { CR28, 0x3e }, { CR29, 0x00 }, + { CR44, 0x33 }, { CR106, 0x2a }, { CR107, 0x1a }, + { CR109, 0x09 }, { CR110, 0x27 }, { CR111, 0x2b }, + { CR112, 0x2b }, { CR119, 0x0a }, { CR10, 0x89 }, + /* for newest (3rd cut) AL2300 */ + { CR17, 0x28 }, + { CR26, 0x93 }, { CR34, 0x30 }, + /* for newest (3rd cut) AL2300 */ + { CR35, 0x3e }, + { CR41, 0x24 }, { CR44, 0x32 }, + /* for newest (3rd cut) AL2300 */ + { CR46, 0x96 }, + { CR47, 0x1e }, { CR79, 0x58 }, { CR80, 0x30 }, + { CR81, 0x30 }, { CR87, 0x0a }, { CR89, 0x04 }, + { CR92, 0x0a }, { CR99, 0x28 }, { CR100, 0x00 }, + { CR101, 0x13 }, { CR102, 0x27 }, { CR106, 0x24 }, + { CR107, 0x2a }, { CR109, 0x09 }, { CR110, 0x13 }, + { CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 }, + { CR114, 0x27 }, + /* for newest (3rd cut) AL2300 */ + { CR115, 0x24 }, + { CR116, 0x24 }, { CR117, 0xf4 }, { CR118, 0xfc }, + { CR119, 0x10 }, { CR120, 0x4f }, { CR121, 0x77 }, + { CR122, 0xe0 }, { CR137, 0x88 }, { CR252, 0xff }, + { CR253, 0xff }, + + /* These following happen separately in the vendor driver */ + { }, + + /* shdnb(PLL_ON)=0 */ + { CR251, 0x2f }, + /* shdnb(PLL_ON)=1 */ + { CR251, 0x3f }, + { CR138, 0x28 }, { CR203, 0x06 }, + }; + + static const u32 rv[] = { + /* Channel 1 */ + 0x03f790, + 0x033331, + 0x00000d, + + 0x0b3331, + 0x03b812, + 0x00fff3, + 0x000da4, + 0x0f4dc5, /* fix freq shift, 0x04edc5 */ + 0x0805b6, + 0x011687, + 0x000688, + 0x0403b9, /* external control TX power (CR31) */ + 0x00dbba, + 0x00099b, + 0x0bdffc, + 0x00000d, + 0x00500f, + + /* These writes happen separately in the vendor driver */ + 0x00d00f, + 0x004c0f, + 0x00540f, + 0x00700f, + 0x00500f, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); + if (r) + return r; + + return 0; +} + +static int zd1211b_al2230_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs1[] = { + { CR10, 0x89 }, { CR15, 0x20 }, + { CR17, 0x2B }, /* for newest(3rd cut) AL2230 */ + { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x93 }, + { CR28, 0x3e }, { CR29, 0x00 }, + { CR33, 0x28 }, /* 5621 */ + { CR34, 0x30 }, + { CR35, 0x3e }, /* for newest(3rd cut) AL2230 */ + { CR41, 0x24 }, { CR44, 0x32 }, + { CR46, 0x99 }, /* for newest(3rd cut) AL2230 */ + { CR47, 0x1e }, + + /* ZD1211B 05.06.10 */ + { CR48, 0x00 }, { CR49, 0x00 }, { CR51, 0x01 }, + { CR52, 0x80 }, { CR53, 0x7e }, { CR65, 0x00 }, + { CR66, 0x00 }, { CR67, 0x00 }, { CR68, 0x00 }, + { CR69, 0x28 }, + + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR87, 0x0a }, { CR89, 0x04 }, + { CR91, 0x00 }, /* 5621 */ + { CR92, 0x0a }, + { CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { CR99, 0x00 }, /* 5621 */ + { CR101, 0x13 }, { CR102, 0x27 }, + { CR106, 0x24 }, /* for newest(3rd cut) AL2230 */ + { CR107, 0x2a }, + { CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { CR110, 0x1f }, /* 4804, for 1212 new algorithm */ + { CR111, 0x1f }, { CR112, 0x1f }, { CR113, 0x27 }, + { CR114, 0x27 }, + { CR115, 0x26 }, /* 24->26 at 4902 for newest(3rd cut) AL2230 */ + { CR116, 0x24 }, + { CR117, 0xfa }, /* for 1211b */ + { CR118, 0xfa }, /* for 1211b */ + { CR119, 0x10 }, + { CR120, 0x4f }, + { CR121, 0x6c }, /* for 1211b */ + { CR122, 0xfc }, /* E0->FC at 4902 */ + { CR123, 0x57 }, /* 5623 */ + { CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { CR126, 0x6c }, /* 5614 */ + { CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { CR137, 0x50 }, /* 5614 */ + { CR138, 0xa8 }, + { CR144, 0xac }, /* 5621 */ + { CR150, 0x0d }, { CR252, 0x00 }, { CR253, 0x00 }, + }; + + static const u32 rv1[] = { + /* channel 1 */ + 0x03f790, + 0x033331, + 0x00000d, + + 0x0b3331, + 0x03b812, + 0x00fff3, + 0x0005a4, + 0x0f4dc5, /* fix freq shift 0x044dc5 */ + 0x0805b6, + 0x0146c7, + 0x000688, + 0x0403b9, /* External control TX power (CR31) */ + 0x00dbba, + 0x00099b, + 0x0bdffc, + 0x00000d, + 0x00580f, + }; + + static const struct zd_ioreq16 ioreqs2[] = { + { CR47, 0x1e }, { CR_RFCFG, 0x03 }, + }; + + static const u32 rv2[] = { + 0x00880f, + 0x00080f, + }; + + static const struct zd_ioreq16 ioreqs3[] = { + { CR_RFCFG, 0x00 }, { CR47, 0x1e }, { CR251, 0x7f }, + }; + + static const u32 rv3[] = { + 0x00d80f, + 0x00780f, + 0x00580f, + }; + + static const struct zd_ioreq16 ioreqs4[] = { + { CR138, 0x28 }, { CR203, 0x06 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); + if (r) + return r; + r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + if (r) + return r; + r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); + if (r) + return r; + r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); + if (r) + return r; + r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); + if (r) + return r; + return zd_iowrite16a_locked(chip, ioreqs4, ARRAY_SIZE(ioreqs4)); +} + +static int al2230_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = al2230_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR138, 0x28 }, + { CR203, 0x06 }, + }; + + r = zd_rfwritev_locked(chip, rv, 3, RF_RV_BITS); + if (r) + return r; + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211_al2230_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x3f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int zd1211b_al2230_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int al2230_switch_radio_off(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x04 }, + { CR251, 0x2f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_al2230(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + rf->set_channel = al2230_set_channel; + rf->switch_radio_off = al2230_switch_radio_off; + if (chip->is_zd1211b) { + rf->init_hw = zd1211b_al2230_init_hw; + rf->switch_radio_on = zd1211b_al2230_switch_radio_on; + } else { + rf->init_hw = zd1211_al2230_init_hw; + rf->switch_radio_on = zd1211_al2230_switch_radio_on; + } + rf->patch_6m_band_edge = 1; + return 0; +} diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c new file mode 100644 index 000000000000..58247271cc24 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c @@ -0,0 +1,279 @@ +/* zd_rf_rfmd.c: Functions for the RFMD RF controller + * + * 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 + */ + +#include + +#include "zd_rf.h" +#include "zd_usb.h" +#include "zd_chip.h" + +static u32 rf2959_table[][2] = { + RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, + RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, + RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, + RF_CHANNEL( 4) = { 0x1819a9, 0x1e6666 }, + RF_CHANNEL( 5) = { 0x1819b9, 0x1e6666 }, + RF_CHANNEL( 6) = { 0x1819c9, 0x1e6666 }, + RF_CHANNEL( 7) = { 0x1819d9, 0x1e6666 }, + RF_CHANNEL( 8) = { 0x1819e9, 0x1e6666 }, + RF_CHANNEL( 9) = { 0x1819f9, 0x1e6666 }, + RF_CHANNEL(10) = { 0x181a09, 0x1e6666 }, + RF_CHANNEL(11) = { 0x181a19, 0x1e6666 }, + RF_CHANNEL(12) = { 0x181a29, 0x1e6666 }, + RF_CHANNEL(13) = { 0x181a39, 0x1e6666 }, + RF_CHANNEL(14) = { 0x181a60, 0x1c0000 }, +}; + +#if 0 +static int bits(u32 rw, int from, int to) +{ + rw &= ~(0xffffffffU << (to+1)); + rw >>= from; + return rw; +} + +static int bit(u32 rw, int bit) +{ + return bits(rw, bit, bit); +} + +static void dump_regwrite(u32 rw) +{ + int reg = bits(rw, 18, 22); + int rw_flag = bits(rw, 23, 23); + PDEBUG("rf2959 %#010x reg %d rw %d", rw, reg, rw_flag); + + switch (reg) { + case 0: + PDEBUG("reg0 CFG1 ref_sel %d hybernate %d rf_vco_reg_en %d" + " if_vco_reg_en %d if_vga_en %d", + bits(rw, 14, 15), bit(rw, 3), bit(rw, 2), bit(rw, 1), + bit(rw, 0)); + break; + case 1: + PDEBUG("reg1 IFPLL1 pll_en1 %d kv_en1 %d vtc_en1 %d lpf1 %d" + " cpl1 %d pdp1 %d autocal_en1 %d ld_en1 %d ifloopr %d" + " ifloopc %d dac1 %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), + bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), + bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0, 3)); + break; + case 2: + PDEBUG("reg2 IFPLL2 n1 %d num1 %d", + bits(rw, 6, 17), bits(rw, 0, 5)); + break; + case 3: + PDEBUG("reg3 IFPLL3 num %d", bits(rw, 0, 17)); + break; + case 4: + PDEBUG("reg4 IFPLL4 dn1 %#04x ct_def1 %d kv_def1 %d", + bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); + break; + case 5: + PDEBUG("reg5 RFPLL1 pll_en %d kv_en %d vtc_en %d lpf %d cpl %d" + " pdp %d autocal_en %d ld_en %d rfloopr %d rfloopc %d" + " dac %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), bit(rw, 14), + bit(rw, 13), bit(rw, 12), bit(rw, 11), bit(rw, 10), + bits(rw, 7, 9), bits(rw, 4, 6), bits(rw, 0,3)); + break; + case 6: + PDEBUG("reg6 RFPLL2 n %d num %d", + bits(rw, 6, 17), bits(rw, 0, 5)); + break; + case 7: + PDEBUG("reg7 RFPLL3 num2 %d", bits(rw, 0, 17)); + break; + case 8: + PDEBUG("reg8 RFPLL4 dn %#06x ct_def %d kv_def %d", + bits(rw, 8, 16), bits(rw, 4, 7), bits(rw, 0, 3)); + break; + case 9: + PDEBUG("reg9 CAL1 tvco %d tlock %d m_ct_value %d ld_window %d", + bits(rw, 13, 17), bits(rw, 8, 12), bits(rw, 3, 7), + bits(rw, 0, 2)); + break; + case 10: + PDEBUG("reg10 TXRX1 rxdcfbbyps %d pcontrol %d txvgc %d" + " rxlpfbw %d txlpfbw %d txdiffmode %d txenmode %d" + " intbiasen %d tybypass %d", + bit(rw, 17), bits(rw, 15, 16), bits(rw, 10, 14), + bits(rw, 7, 9), bits(rw, 4, 6), bit(rw, 3), bit(rw, 2), + bit(rw, 1), bit(rw, 0)); + break; + case 11: + PDEBUG("reg11 PCNT1 mid_bias %d p_desired %d pc_offset %d" + " tx_delay %d", + bits(rw, 15, 17), bits(rw, 9, 14), bits(rw, 3, 8), + bits(rw, 0, 2)); + break; + case 12: + PDEBUG("reg12 PCNT2 max_power %d mid_power %d min_power %d", + bits(rw, 12, 17), bits(rw, 6, 11), bits(rw, 0, 5)); + break; + case 13: + PDEBUG("reg13 VCOT1 rfpll vco comp %d ifpll vco comp %d" + " lobias %d if_biasbuf %d if_biasvco %d rf_biasbuf %d" + " rf_biasvco %d", + bit(rw, 17), bit(rw, 16), bit(rw, 15), + bits(rw, 8, 9), bits(rw, 5, 7), bits(rw, 3, 4), + bits(rw, 0, 2)); + break; + case 14: + PDEBUG("reg14 IQCAL rx_acal %d rx_pcal %d" + " tx_acal %d tx_pcal %d", + bits(rw, 13, 17), bits(rw, 9, 12), bits(rw, 4, 8), + bits(rw, 0, 3)); + break; + } +} +#endif /* 0 */ + +static int rf2959_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs[] = { + { CR2, 0x1E }, { CR9, 0x20 }, { CR10, 0x89 }, + { CR11, 0x00 }, { CR15, 0xD0 }, { CR17, 0x68 }, + { CR19, 0x4a }, { CR20, 0x0c }, { CR21, 0x0E }, + { CR23, 0x48 }, + /* normal size for cca threshold */ + { CR24, 0x14 }, + /* { CR24, 0x20 }, */ + { CR26, 0x90 }, { CR27, 0x30 }, { CR29, 0x20 }, + { CR31, 0xb2 }, { CR32, 0x43 }, { CR33, 0x28 }, + { CR38, 0x30 }, { CR34, 0x0f }, { CR35, 0xF0 }, + { CR41, 0x2a }, { CR46, 0x7F }, { CR47, 0x1E }, + { CR51, 0xc5 }, { CR52, 0xc5 }, { CR53, 0xc5 }, + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR82, 0x00 }, { CR83, 0x24 }, { CR84, 0x04 }, + { CR85, 0x00 }, { CR86, 0x10 }, { CR87, 0x2A }, + { CR88, 0x10 }, { CR89, 0x24 }, { CR90, 0x18 }, + /* { CR91, 0x18 }, */ + /* should solve continous CTS frame problems */ + { CR91, 0x00 }, + { CR92, 0x0a }, { CR93, 0x00 }, { CR94, 0x01 }, + { CR95, 0x00 }, { CR96, 0x40 }, { CR97, 0x37 }, + { CR98, 0x05 }, { CR99, 0x28 }, { CR100, 0x00 }, + { CR101, 0x13 }, { CR102, 0x27 }, { CR103, 0x27 }, + { CR104, 0x18 }, { CR105, 0x12 }, + /* normal size */ + { CR106, 0x1a }, + /* { CR106, 0x22 }, */ + { CR107, 0x24 }, { CR108, 0x0a }, { CR109, 0x13 }, + { CR110, 0x2F }, { CR111, 0x27 }, { CR112, 0x27 }, + { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x40 }, + { CR116, 0x40 }, { CR117, 0xF0 }, { CR118, 0xF0 }, + { CR119, 0x16 }, + /* no TX continuation */ + { CR122, 0x00 }, + /* { CR122, 0xff }, */ + { CR127, 0x03 }, { CR131, 0x08 }, { CR138, 0x28 }, + { CR148, 0x44 }, { CR150, 0x10 }, { CR169, 0xBB }, + { CR170, 0xBB }, + }; + + static const u32 rv[] = { + 0x000007, /* REG0(CFG1) */ + 0x07dd43, /* REG1(IFPLL1) */ + 0x080959, /* REG2(IFPLL2) */ + 0x0e6666, + 0x116a57, /* REG4 */ + 0x17dd43, /* REG5 */ + 0x1819f9, /* REG6 */ + 0x1e6666, + 0x214554, + 0x25e7fa, + 0x27fffa, + /* The Zydas driver somehow forgets to set this value. It's + * only set for Japan. We are using internal power control + * for now. + */ + 0x294128, /* internal power */ + /* 0x28252c, */ /* External control TX power */ + /* CR31_CCK, CR51_6-36M, CR52_48M, CR53_54M */ + 0x2c0000, + 0x300000, + 0x340000, /* REG13(0xD) */ + 0x381e0f, /* REG14(0xE) */ + /* Bogus, RF2959's data sheet doesn't know register 27, which is + * actually referenced here. The commented 0x11 is 17. + */ + 0x6c180f, /* REG27(0x11) */ + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); +} + +static int rf2959_set_channel(struct zd_rf *rf, u8 channel) +{ + int i, r; + u32 *rv = rf2959_table[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + for (i = 0; i < 2; i++) { + r = zd_rfwrite_locked(chip, rv[i], RF_RV_BITS); + if (r) + return r; + } + return 0; +} + +static int rf2959_switch_radio_on(struct zd_rf *rf) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR10, 0x89 }, + { CR11, 0x00 }, + }; + struct zd_chip *chip = zd_rf_to_chip(rf); + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +static int rf2959_switch_radio_off(struct zd_rf *rf) +{ + static const struct zd_ioreq16 ioreqs[] = { + { CR10, 0x15 }, + { CR11, 0x81 }, + }; + struct zd_chip *chip = zd_rf_to_chip(rf); + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + +int zd_rf_init_rf2959(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + + if (chip->is_zd1211b) { + dev_err(zd_chip_dev(chip), + "RF2959 is currently not supported for ZD1211B" + " devices\n"); + return -ENODEV; + } + rf->init_hw = rf2959_init_hw; + rf->set_channel = rf2959_set_channel; + rf->switch_radio_on = rf2959_switch_radio_on; + rf->switch_radio_off = rf2959_switch_radio_off; + return 0; +} diff --git a/drivers/net/wireless/zd1211rw/zd_types.h b/drivers/net/wireless/zd1211rw/zd_types.h new file mode 100644 index 000000000000..0155a1584ed3 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_types.h @@ -0,0 +1,71 @@ +/* zd_types.h + * + * 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 + */ + +#ifndef _ZD_TYPES_H +#define _ZD_TYPES_H + +#include + +/* We have three register spaces mapped into the overall USB address space of + * 64K words (16-bit values). There is the control register space of + * double-word registers, the eeprom register space and the firmware register + * space. The control register space is byte mapped, the others are word + * mapped. + * + * For that reason, we are using byte offsets for control registers and word + * offsets for everything else. + */ + +typedef u32 __nocast zd_addr_t; + +enum { + ADDR_BASE_MASK = 0xff000000, + ADDR_OFFSET_MASK = 0x0000ffff, + ADDR_ZERO_MASK = 0x00ff0000, + NULL_BASE = 0x00000000, + USB_BASE = 0x01000000, + CR_BASE = 0x02000000, + CR_MAX_OFFSET = 0x0b30, + E2P_BASE = 0x03000000, + E2P_MAX_OFFSET = 0x007e, + FW_BASE = 0x04000000, + FW_MAX_OFFSET = 0x0005, +}; + +#define ZD_ADDR_BASE(addr) ((u32)(addr) & ADDR_BASE_MASK) +#define ZD_OFFSET(addr) ((u32)(addr) & ADDR_OFFSET_MASK) + +#define ZD_ADDR(base, offset) \ + ((zd_addr_t)(((base) & ADDR_BASE_MASK) | ((offset) & ADDR_OFFSET_MASK))) + +#define ZD_NULL_ADDR ((zd_addr_t)0) +#define USB_REG(offset) ZD_ADDR(USB_BASE, offset) /* word addressing */ +#define CTL_REG(offset) ZD_ADDR(CR_BASE, offset) /* byte addressing */ +#define E2P_REG(offset) ZD_ADDR(E2P_BASE, offset) /* word addressing */ +#define FW_REG(offset) ZD_ADDR(FW_BASE, offset) /* word addressing */ + +static inline zd_addr_t zd_inc_word(zd_addr_t addr) +{ + u32 base = ZD_ADDR_BASE(addr); + u32 offset = ZD_OFFSET(addr); + + offset += base == CR_BASE ? 2 : 1; + + return base | offset; +} + +#endif /* _ZD_TYPES_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c new file mode 100644 index 000000000000..ce1cb2c6aa8d --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -0,0 +1,1316 @@ +/* zd_usb.c + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_netdev.h" +#include "zd_mac.h" +#include "zd_usb.h" +#include "zd_util.h" + +static struct usb_device_id usb_ids[] = { + /* ZD1211 */ + { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, + /* ZD1211B */ + { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, + {} +}; + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB driver for devices with the ZD1211 chip."); +MODULE_AUTHOR("Ulrich Kunitz"); +MODULE_AUTHOR("Daniel Drake"); +MODULE_VERSION("1.0"); +MODULE_DEVICE_TABLE(usb, usb_ids); + +#define FW_ZD1211_PREFIX "zd1211/zd1211_" +#define FW_ZD1211B_PREFIX "zd1211/zd1211b_" + +/* register address handling */ + +#ifdef DEBUG +static int check_addr(struct zd_usb *usb, zd_addr_t addr) +{ + u32 base = ZD_ADDR_BASE(addr); + u32 offset = ZD_OFFSET(addr); + + if ((u32)addr & ADDR_ZERO_MASK) + goto invalid_address; + switch (base) { + case USB_BASE: + break; + case CR_BASE: + if (offset > CR_MAX_OFFSET) { + dev_dbg(zd_usb_dev(usb), + "CR offset %#010x larger than" + " CR_MAX_OFFSET %#10x\n", + offset, CR_MAX_OFFSET); + goto invalid_address; + } + if (offset & 1) { + dev_dbg(zd_usb_dev(usb), + "CR offset %#010x is not a multiple of 2\n", + offset); + goto invalid_address; + } + break; + case E2P_BASE: + if (offset > E2P_MAX_OFFSET) { + dev_dbg(zd_usb_dev(usb), + "E2P offset %#010x larger than" + " E2P_MAX_OFFSET %#010x\n", + offset, E2P_MAX_OFFSET); + goto invalid_address; + } + break; + case FW_BASE: + if (!usb->fw_base_offset) { + dev_dbg(zd_usb_dev(usb), + "ERROR: fw base offset has not been set\n"); + return -EAGAIN; + } + if (offset > FW_MAX_OFFSET) { + dev_dbg(zd_usb_dev(usb), + "FW offset %#10x is larger than" + " FW_MAX_OFFSET %#010x\n", + offset, FW_MAX_OFFSET); + goto invalid_address; + } + break; + default: + dev_dbg(zd_usb_dev(usb), + "address has unsupported base %#010x\n", addr); + goto invalid_address; + } + + return 0; +invalid_address: + dev_dbg(zd_usb_dev(usb), + "ERROR: invalid address: %#010x\n", addr); + return -EINVAL; +} +#endif /* DEBUG */ + +static u16 usb_addr(struct zd_usb *usb, zd_addr_t addr) +{ + u32 base; + u16 offset; + + base = ZD_ADDR_BASE(addr); + offset = ZD_OFFSET(addr); + + ZD_ASSERT(check_addr(usb, addr) == 0); + + switch (base) { + case CR_BASE: + offset += CR_BASE_OFFSET; + break; + case E2P_BASE: + offset += E2P_BASE_OFFSET; + break; + case FW_BASE: + offset += usb->fw_base_offset; + break; + } + + return offset; +} + +/* USB device initialization */ + +static int request_fw_file( + const struct firmware **fw, const char *name, struct device *device) +{ + int r; + + dev_dbg_f(device, "fw name %s\n", name); + + r = request_firmware(fw, name, device); + if (r) + dev_err(device, + "Could not load firmware file %s. Error number %d\n", + name, r); + return r; +} + +static inline u16 get_bcdDevice(const struct usb_device *udev) +{ + return le16_to_cpu(udev->descriptor.bcdDevice); +} + +enum upload_code_flags { + REBOOT = 1, +}; + +/* Ensures that MAX_TRANSFER_SIZE is even. */ +#define MAX_TRANSFER_SIZE (USB_MAX_TRANSFER_SIZE & ~1) + +static int upload_code(struct usb_device *udev, + const u8 *data, size_t size, u16 code_offset, int flags) +{ + u8 *p; + int r; + + /* USB request blocks need "kmalloced" buffers. + */ + p = kmalloc(MAX_TRANSFER_SIZE, GFP_KERNEL); + if (!p) { + dev_err(&udev->dev, "out of memory\n"); + r = -ENOMEM; + goto error; + } + + size &= ~1; + while (size > 0) { + size_t transfer_size = size <= MAX_TRANSFER_SIZE ? + size : MAX_TRANSFER_SIZE; + + dev_dbg_f(&udev->dev, "transfer size %zu\n", transfer_size); + + memcpy(p, data, transfer_size); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_FIRMWARE_DOWNLOAD, + USB_DIR_OUT | USB_TYPE_VENDOR, + code_offset, 0, p, transfer_size, 1000 /* ms */); + if (r < 0) { + dev_err(&udev->dev, + "USB control request for firmware upload" + " failed. Error number %d\n", r); + goto error; + } + transfer_size = r & ~1; + + size -= transfer_size; + data += transfer_size; + code_offset += transfer_size/sizeof(u16); + } + + if (flags & REBOOT) { + u8 ret; + + r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_FIRMWARE_CONFIRM, + USB_DIR_IN | USB_TYPE_VENDOR, + 0, 0, &ret, sizeof(ret), 5000 /* ms */); + if (r != sizeof(ret)) { + dev_err(&udev->dev, + "control request firmeware confirmation failed." + " Return value %d\n", r); + if (r >= 0) + r = -ENODEV; + goto error; + } + if (ret & 0x80) { + dev_err(&udev->dev, + "Internal error while downloading." + " Firmware confirm return value %#04x\n", + (unsigned int)ret); + r = -ENODEV; + goto error; + } + dev_dbg_f(&udev->dev, "firmware confirm return value %#04x\n", + (unsigned int)ret); + } + + r = 0; +error: + kfree(p); + return r; +} + +static u16 get_word(const void *data, u16 offset) +{ + const __le16 *p = data; + return le16_to_cpu(p[offset]); +} + +static char *get_fw_name(char *buffer, size_t size, u8 device_type, + const char* postfix) +{ + scnprintf(buffer, size, "%s%s", + device_type == DEVICE_ZD1211B ? + FW_ZD1211B_PREFIX : FW_ZD1211_PREFIX, + postfix); + return buffer; +} + +static int upload_firmware(struct usb_device *udev, u8 device_type) +{ + int r; + u16 fw_bcdDevice; + u16 bcdDevice; + const struct firmware *ub_fw = NULL; + const struct firmware *uph_fw = NULL; + char fw_name[128]; + + bcdDevice = get_bcdDevice(udev); + + r = request_fw_file(&ub_fw, + get_fw_name(fw_name, sizeof(fw_name), device_type, "ub"), + &udev->dev); + if (r) + goto error; + + fw_bcdDevice = get_word(ub_fw->data, EEPROM_REGS_OFFSET); + + /* FIXME: do we have any reason to perform the kludge that the vendor + * driver does when there is a version mismatch? (their driver uploads + * different firmwares and stuff) + */ + if (fw_bcdDevice != bcdDevice) { + dev_info(&udev->dev, + "firmware device id %#06x and actual device id " + "%#06x differ, continuing anyway\n", + fw_bcdDevice, bcdDevice); + } else { + dev_dbg_f(&udev->dev, + "firmware device id %#06x is equal to the " + "actual device id\n", fw_bcdDevice); + } + + + r = request_fw_file(&uph_fw, + get_fw_name(fw_name, sizeof(fw_name), device_type, "uphr"), + &udev->dev); + if (r) + goto error; + + r = upload_code(udev, uph_fw->data, uph_fw->size, FW_START_OFFSET, + REBOOT); + if (r) { + dev_err(&udev->dev, + "Could not upload firmware code uph. Error number %d\n", + r); + } + + /* FALL-THROUGH */ +error: + release_firmware(ub_fw); + release_firmware(uph_fw); + return r; +} + +static void disable_read_regs_int(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + ZD_ASSERT(in_interrupt()); + spin_lock(&intr->lock); + intr->read_regs_enabled = 0; + spin_unlock(&intr->lock); +} + +#define urb_dev(urb) (&(urb)->dev->dev) + +static inline void handle_regs_int(struct urb *urb) +{ + struct zd_usb *usb = urb->context; + struct zd_usb_interrupt *intr = &usb->intr; + int len; + + ZD_ASSERT(in_interrupt()); + spin_lock(&intr->lock); + + if (intr->read_regs_enabled) { + intr->read_regs.length = len = urb->actual_length; + + if (len > sizeof(intr->read_regs.buffer)) + len = sizeof(intr->read_regs.buffer); + memcpy(intr->read_regs.buffer, urb->transfer_buffer, len); + intr->read_regs_enabled = 0; + complete(&intr->read_regs.completion); + goto out; + } + + dev_dbg_f(urb_dev(urb), "regs interrupt ignored\n"); +out: + spin_unlock(&intr->lock); +} + +static inline void handle_retry_failed_int(struct urb *urb) +{ + dev_dbg_f(urb_dev(urb), "retry failed interrupt\n"); +} + + +static void int_urb_complete(struct urb *urb, struct pt_regs *pt_regs) +{ + int r; + struct usb_int_header *hdr; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + goto kfree; + case -EPIPE: + usb_clear_halt(urb->dev, EP_INT_IN); + /* FALL-THROUGH */ + default: + goto resubmit; + } + + if (urb->actual_length < sizeof(hdr)) { + dev_dbg_f(urb_dev(urb), "error: urb %p to small\n", urb); + goto resubmit; + } + + hdr = urb->transfer_buffer; + if (hdr->type != USB_INT_TYPE) { + dev_dbg_f(urb_dev(urb), "error: urb %p wrong type\n", urb); + goto resubmit; + } + + switch (hdr->id) { + case USB_INT_ID_REGS: + handle_regs_int(urb); + break; + case USB_INT_ID_RETRY_FAILED: + handle_retry_failed_int(urb); + break; + default: + dev_dbg_f(urb_dev(urb), "error: urb %p unknown id %x\n", urb, + (unsigned int)hdr->id); + goto resubmit; + } + +resubmit: + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + dev_dbg_f(urb_dev(urb), "resubmit urb %p\n", urb); + goto kfree; + } + return; +kfree: + kfree(urb->transfer_buffer); +} + +static inline int int_urb_interval(struct usb_device *udev) +{ + switch (udev->speed) { + case USB_SPEED_HIGH: + return 4; + case USB_SPEED_LOW: + return 10; + case USB_SPEED_FULL: + default: + return 1; + } +} + +static inline int usb_int_enabled(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + + spin_lock_irqsave(&intr->lock, flags); + urb = intr->urb; + spin_unlock_irqrestore(&intr->lock, flags); + return urb != NULL; +} + +int zd_usb_enable_int(struct zd_usb *usb) +{ + int r; + struct usb_device *udev; + struct zd_usb_interrupt *intr = &usb->intr; + void *transfer_buffer = NULL; + struct urb *urb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + urb = usb_alloc_urb(0, GFP_NOFS); + if (!urb) { + r = -ENOMEM; + goto out; + } + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&intr->lock); + if (intr->urb) { + spin_unlock_irq(&intr->lock); + r = 0; + goto error_free_urb; + } + intr->urb = urb; + spin_unlock_irq(&intr->lock); + + /* TODO: make it a DMA buffer */ + r = -ENOMEM; + transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_NOFS); + if (!transfer_buffer) { + dev_dbg_f(zd_usb_dev(usb), + "couldn't allocate transfer_buffer\n"); + goto error_set_urb_null; + } + + udev = zd_usb_to_usbdev(usb); + usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, EP_INT_IN), + transfer_buffer, USB_MAX_EP_INT_BUFFER, + int_urb_complete, usb, + intr->interval); + + dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); + r = usb_submit_urb(urb, GFP_NOFS); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "Couldn't submit urb. Error number %d\n", r); + goto error; + } + + return 0; +error: + kfree(transfer_buffer); +error_set_urb_null: + spin_lock_irq(&intr->lock); + intr->urb = NULL; + spin_unlock_irq(&intr->lock); +error_free_urb: + usb_free_urb(urb); +out: + return r; +} + +void zd_usb_disable_int(struct zd_usb *usb) +{ + unsigned long flags; + struct zd_usb_interrupt *intr = &usb->intr; + struct urb *urb; + + spin_lock_irqsave(&intr->lock, flags); + urb = intr->urb; + if (!urb) { + spin_unlock_irqrestore(&intr->lock, flags); + return; + } + intr->urb = NULL; + spin_unlock_irqrestore(&intr->lock, flags); + + usb_kill_urb(urb); + dev_dbg_f(zd_usb_dev(usb), "urb %p killed\n", urb); + usb_free_urb(urb); +} + +static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, + unsigned int length) +{ + int i; + struct zd_mac *mac = zd_usb_to_mac(usb); + const struct rx_length_info *length_info; + + if (length < sizeof(struct rx_length_info)) { + /* It's not a complete packet anyhow. */ + return; + } + length_info = (struct rx_length_info *) + (buffer + length - sizeof(struct rx_length_info)); + + /* It might be that three frames are merged into a single URB + * transaction. We have to check for the length info tag. + * + * While testing we discovered that length_info might be unaligned, + * because if USB transactions are merged, the last packet will not + * be padded. Unaligned access might also happen if the length_info + * structure is not present. + */ + if (get_unaligned(&length_info->tag) == RX_LENGTH_INFO_TAG) { + unsigned int l, k, n; + for (i = 0, l = 0;; i++) { + k = le16_to_cpu(get_unaligned( + &length_info->length[i])); + n = l+k; + if (n > length) + return; + zd_mac_rx(mac, buffer+l, k); + if (i >= 2) + return; + l = (n+3) & ~3; + } + } else { + zd_mac_rx(mac, buffer, length); + } +} + +static void rx_urb_complete(struct urb *urb, struct pt_regs *pt_regs) +{ + struct zd_usb *usb; + struct zd_usb_rx *rx; + const u8 *buffer; + unsigned int length; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + return; + case -EPIPE: + usb_clear_halt(urb->dev, EP_DATA_IN); + /* FALL-THROUGH */ + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } + + buffer = urb->transfer_buffer; + length = urb->actual_length; + usb = urb->context; + rx = &usb->rx; + + if (length%rx->usb_packet_size > rx->usb_packet_size-4) { + /* If there is an old first fragment, we don't care. */ + dev_dbg_f(urb_dev(urb), "*** first fragment ***\n"); + ZD_ASSERT(length <= ARRAY_SIZE(rx->fragment)); + spin_lock(&rx->lock); + memcpy(rx->fragment, buffer, length); + rx->fragment_length = length; + spin_unlock(&rx->lock); + goto resubmit; + } + + spin_lock(&rx->lock); + if (rx->fragment_length > 0) { + /* We are on a second fragment, we believe */ + ZD_ASSERT(length + rx->fragment_length <= + ARRAY_SIZE(rx->fragment)); + dev_dbg_f(urb_dev(urb), "*** second fragment ***\n"); + memcpy(rx->fragment+rx->fragment_length, buffer, length); + handle_rx_packet(usb, rx->fragment, + rx->fragment_length + length); + rx->fragment_length = 0; + spin_unlock(&rx->lock); + } else { + spin_unlock(&rx->lock); + handle_rx_packet(usb, buffer, length); + } + +resubmit: + usb_submit_urb(urb, GFP_ATOMIC); +} + +struct urb *alloc_urb(struct zd_usb *usb) +{ + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + void *buffer; + + urb = usb_alloc_urb(0, GFP_NOFS); + if (!urb) + return NULL; + buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_NOFS, + &urb->transfer_dma); + if (!buffer) { + usb_free_urb(urb); + return NULL; + } + + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, EP_DATA_IN), + buffer, USB_MAX_RX_SIZE, + rx_urb_complete, usb); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return urb; +} + +void free_urb(struct urb *urb) +{ + if (!urb) + return; + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); +} + +int zd_usb_enable_rx(struct zd_usb *usb) +{ + int i, r; + struct zd_usb_rx *rx = &usb->rx; + struct urb **urbs; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + r = -ENOMEM; + urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_NOFS); + if (!urbs) + goto error; + for (i = 0; i < URBS_COUNT; i++) { + urbs[i] = alloc_urb(usb); + if (!urbs[i]) + goto error; + } + + ZD_ASSERT(!irqs_disabled()); + spin_lock_irq(&rx->lock); + if (rx->urbs) { + spin_unlock_irq(&rx->lock); + r = 0; + goto error; + } + rx->urbs = urbs; + rx->urbs_count = URBS_COUNT; + spin_unlock_irq(&rx->lock); + + for (i = 0; i < URBS_COUNT; i++) { + r = usb_submit_urb(urbs[i], GFP_NOFS); + if (r) + goto error_submit; + } + + return 0; +error_submit: + for (i = 0; i < URBS_COUNT; i++) { + usb_kill_urb(urbs[i]); + } + spin_lock_irq(&rx->lock); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irq(&rx->lock); +error: + if (urbs) { + for (i = 0; i < URBS_COUNT; i++) + free_urb(urbs[i]); + } + return r; +} + +void zd_usb_disable_rx(struct zd_usb *usb) +{ + int i; + unsigned long flags; + struct urb **urbs; + unsigned int count; + struct zd_usb_rx *rx = &usb->rx; + + spin_lock_irqsave(&rx->lock, flags); + urbs = rx->urbs; + count = rx->urbs_count; + spin_unlock_irqrestore(&rx->lock, flags); + if (!urbs) + return; + + for (i = 0; i < count; i++) { + usb_kill_urb(urbs[i]); + free_urb(urbs[i]); + } + kfree(urbs); + + spin_lock_irqsave(&rx->lock, flags); + rx->urbs = NULL; + rx->urbs_count = 0; + spin_unlock_irqrestore(&rx->lock, flags); +} + +static void tx_urb_complete(struct urb *urb, struct pt_regs *pt_regs) +{ + int r; + + switch (urb->status) { + case 0: + break; + case -ESHUTDOWN: + case -EINVAL: + case -ENODEV: + case -ENOENT: + case -ECONNRESET: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + break; + case -EPIPE: + usb_clear_halt(urb->dev, EP_DATA_OUT); + /* FALL-THROUGH */ + default: + dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); + goto resubmit; + } +free_urb: + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + return; +resubmit: + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) { + dev_dbg_f(urb_dev(urb), "error resubmit urb %p %d\n", urb, r); + goto free_urb; + } +} + +/* Puts the frame on the USB endpoint. It doesn't wait for + * completion. The frame must contain the control set. + */ +int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length) +{ + int r; + struct usb_device *udev = zd_usb_to_usbdev(usb); + struct urb *urb; + void *buffer; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + r = -ENOMEM; + goto out; + } + + buffer = usb_buffer_alloc(zd_usb_to_usbdev(usb), length, GFP_ATOMIC, + &urb->transfer_dma); + if (!buffer) { + r = -ENOMEM; + goto error_free_urb; + } + memcpy(buffer, frame, length); + + usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, EP_DATA_OUT), + buffer, length, tx_urb_complete, NULL); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r) + goto error; + return 0; +error: + usb_buffer_free(zd_usb_to_usbdev(usb), length, buffer, + urb->transfer_dma); +error_free_urb: + usb_free_urb(urb); +out: + return r; +} + +static inline void init_usb_interrupt(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock_init(&intr->lock); + intr->interval = int_urb_interval(zd_usb_to_usbdev(usb)); + init_completion(&intr->read_regs.completion); + intr->read_regs.cr_int_addr = cpu_to_le16(usb_addr(usb, CR_INTERRUPT)); +} + +static inline void init_usb_rx(struct zd_usb *usb) +{ + struct zd_usb_rx *rx = &usb->rx; + spin_lock_init(&rx->lock); + if (interface_to_usbdev(usb->intf)->speed == USB_SPEED_HIGH) { + rx->usb_packet_size = 512; + } else { + rx->usb_packet_size = 64; + } + ZD_ASSERT(rx->fragment_length == 0); +} + +static inline void init_usb_tx(struct zd_usb *usb) +{ + /* FIXME: at this point we will allocate a fixed number of urb's for + * use in a cyclic scheme */ +} + +void zd_usb_init(struct zd_usb *usb, struct net_device *netdev, + struct usb_interface *intf) +{ + memset(usb, 0, sizeof(*usb)); + usb->intf = usb_get_intf(intf); + usb_set_intfdata(usb->intf, netdev); + init_usb_interrupt(usb); + init_usb_tx(usb); + init_usb_rx(usb); +} + +int zd_usb_init_hw(struct zd_usb *usb) +{ + int r; + struct zd_chip *chip = zd_usb_to_chip(usb); + + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + r = zd_ioread16_locked(chip, &usb->fw_base_offset, + USB_REG((u16)FW_BASE_ADDR_OFFSET)); + if (r) + return r; + dev_dbg_f(zd_usb_dev(usb), "fw_base_offset: %#06hx\n", + usb->fw_base_offset); + + return 0; +} + +void zd_usb_clear(struct zd_usb *usb) +{ + usb_set_intfdata(usb->intf, NULL); + usb_put_intf(usb->intf); + memset(usb, 0, sizeof(*usb)); + /* FIXME: usb_interrupt, usb_tx, usb_rx? */ +} + +static const char *speed(enum usb_device_speed speed) +{ + switch (speed) { + case USB_SPEED_LOW: + return "low"; + case USB_SPEED_FULL: + return "full"; + case USB_SPEED_HIGH: + return "high"; + default: + return "unknown speed"; + } +} + +static int scnprint_id(struct usb_device *udev, char *buffer, size_t size) +{ + return scnprintf(buffer, size, "%04hx:%04hx v%04hx %s", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + get_bcdDevice(udev), + speed(udev->speed)); +} + +int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size) +{ + struct usb_device *udev = interface_to_usbdev(usb->intf); + return scnprint_id(udev, buffer, size); +} + +#ifdef DEBUG +static void print_id(struct usb_device *udev) +{ + char buffer[40]; + + scnprint_id(udev, buffer, sizeof(buffer)); + buffer[sizeof(buffer)-1] = 0; + dev_dbg_f(&udev->dev, "%s\n", buffer); +} +#else +#define print_id(udev) do { } while (0) +#endif + +static int probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int r; + struct usb_device *udev = interface_to_usbdev(intf); + struct net_device *netdev = NULL; + + print_id(udev); + + switch (udev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + break; + default: + dev_dbg_f(&intf->dev, "Unknown USB speed\n"); + r = -ENODEV; + goto error; + } + + netdev = zd_netdev_alloc(intf); + if (netdev == NULL) { + r = -ENOMEM; + goto error; + } + + r = upload_firmware(udev, id->driver_info); + if (r) { + dev_err(&intf->dev, + "couldn't load firmware. Error number %d\n", r); + goto error; + } + + r = usb_reset_configuration(udev); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't reset configuration. Error number %d\n", r); + goto error; + } + + /* At this point the interrupt endpoint is not generally enabled. We + * save the USB bandwidth until the network device is opened. But + * notify that the initialization of the MAC will require the + * interrupts to be temporary enabled. + */ + r = zd_mac_init_hw(zd_netdev_mac(netdev), id->driver_info); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't initialize mac. Error number %d\n", r); + goto error; + } + + r = register_netdev(netdev); + if (r) { + dev_dbg_f(&intf->dev, + "couldn't register netdev. Error number %d\n", r); + goto error; + } + + dev_dbg_f(&intf->dev, "successful\n"); + dev_info(&intf->dev,"%s\n", netdev->name); + return 0; +error: + usb_reset_device(interface_to_usbdev(intf)); + zd_netdev_free(netdev); + return r; +} + +static void disconnect(struct usb_interface *intf) +{ + struct net_device *netdev = zd_intf_to_netdev(intf); + struct zd_mac *mac = zd_netdev_mac(netdev); + struct zd_usb *usb = &mac->chip.usb; + + dev_dbg_f(zd_usb_dev(usb), "\n"); + + zd_netdev_disconnect(netdev); + + /* Just in case something has gone wrong! */ + zd_usb_disable_rx(usb); + zd_usb_disable_int(usb); + + /* If the disconnect has been caused by a removal of the + * driver module, the reset allows reloading of the driver. If the + * reset will not be executed here, the upload of the firmware in the + * probe function caused by the reloading of the driver will fail. + */ + usb_reset_device(interface_to_usbdev(intf)); + + /* If somebody still waits on this lock now, this is an error. */ + zd_netdev_free(netdev); + dev_dbg(&intf->dev, "disconnected\n"); +} + +static struct usb_driver driver = { + .name = "zd1211rw", + .id_table = usb_ids, + .probe = probe, + .disconnect = disconnect, +}; + +static int __init usb_init(void) +{ + int r; + + pr_debug("usb_init()\n"); + + r = usb_register(&driver); + if (r) { + printk(KERN_ERR "usb_register() failed. Error number %d\n", r); + return r; + } + + pr_debug("zd1211rw initialized\n"); + return 0; +} + +static void __exit usb_exit(void) +{ + pr_debug("usb_exit()\n"); + usb_deregister(&driver); +} + +module_init(usb_init); +module_exit(usb_exit); + +static int usb_int_regs_length(unsigned int count) +{ + return sizeof(struct usb_int_regs) + count * sizeof(struct reg_data); +} + +static void prepare_read_regs_int(struct zd_usb *usb) +{ + struct zd_usb_interrupt *intr = &usb->intr; + + spin_lock(&intr->lock); + intr->read_regs_enabled = 1; + INIT_COMPLETION(intr->read_regs.completion); + spin_unlock(&intr->lock); +} + +static int get_results(struct zd_usb *usb, u16 *values, + struct usb_req_read_regs *req, unsigned int count) +{ + int r; + int i; + struct zd_usb_interrupt *intr = &usb->intr; + struct read_regs_int *rr = &intr->read_regs; + struct usb_int_regs *regs = (struct usb_int_regs *)rr->buffer; + + spin_lock(&intr->lock); + + r = -EIO; + /* The created block size seems to be larger than expected. + * However results appear to be correct. + */ + if (rr->length < usb_int_regs_length(count)) { + dev_dbg_f(zd_usb_dev(usb), + "error: actual length %d less than expected %d\n", + rr->length, usb_int_regs_length(count)); + goto error_unlock; + } + if (rr->length > sizeof(rr->buffer)) { + dev_dbg_f(zd_usb_dev(usb), + "error: actual length %d exceeds buffer size %zu\n", + rr->length, sizeof(rr->buffer)); + goto error_unlock; + } + + for (i = 0; i < count; i++) { + struct reg_data *rd = ®s->regs[i]; + if (rd->addr != req->addr[i]) { + dev_dbg_f(zd_usb_dev(usb), + "rd[%d] addr %#06hx expected %#06hx\n", i, + le16_to_cpu(rd->addr), + le16_to_cpu(req->addr[i])); + goto error_unlock; + } + values[i] = le16_to_cpu(rd->value); + } + + r = 0; +error_unlock: + spin_unlock(&intr->lock); + return r; +} + +int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, + const zd_addr_t *addresses, unsigned int count) +{ + int r; + int i, req_len, actual_req_len; + struct usb_device *udev; + struct usb_req_read_regs *req = NULL; + unsigned long timeout; + + if (count < 1) { + dev_dbg_f(zd_usb_dev(usb), "error: count is zero\n"); + return -EINVAL; + } + if (count > USB_MAX_IOREAD16_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: count %u exceeds possible max %u\n", + count, USB_MAX_IOREAD16_COUNT); + return -EINVAL; + } + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + if (!usb_int_enabled(usb)) { + dev_dbg_f(zd_usb_dev(usb), + "error: usb interrupt not enabled\n"); + return -EWOULDBLOCK; + } + + req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16); + req = kmalloc(req_len, GFP_NOFS); + if (!req) + return -ENOMEM; + req->id = cpu_to_le16(USB_REQ_READ_REGS); + for (i = 0; i < count; i++) + req->addr[i] = cpu_to_le16(usb_addr(usb, addresses[i])); + + udev = zd_usb_to_usbdev(usb); + prepare_read_regs_int(usb); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT), + req, req_len, &actual_req_len, 1000 /* ms */); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in usb_bulk_msg(). Error number %d\n", r); + goto error; + } + if (req_len != actual_req_len) { + dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()\n" + " req_len %d != actual_req_len %d\n", + req_len, actual_req_len); + r = -EIO; + goto error; + } + + timeout = wait_for_completion_timeout(&usb->intr.read_regs.completion, + msecs_to_jiffies(1000)); + if (!timeout) { + disable_read_regs_int(usb); + dev_dbg_f(zd_usb_dev(usb), "read timed out\n"); + r = -ETIMEDOUT; + goto error; + } + + r = get_results(usb, values, req, count); +error: + kfree(req); + return r; +} + +int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count) +{ + int r; + struct usb_device *udev; + struct usb_req_write_regs *req = NULL; + int i, req_len, actual_req_len; + + if (count == 0) + return 0; + if (count > USB_MAX_IOWRITE16_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: count %u exceeds possible max %u\n", + count, USB_MAX_IOWRITE16_COUNT); + return -EINVAL; + } + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + + req_len = sizeof(struct usb_req_write_regs) + + count * sizeof(struct reg_data); + req = kmalloc(req_len, GFP_NOFS); + if (!req) + return -ENOMEM; + + req->id = cpu_to_le16(USB_REQ_WRITE_REGS); + for (i = 0; i < count; i++) { + struct reg_data *rw = &req->reg_writes[i]; + rw->addr = cpu_to_le16(usb_addr(usb, ioreqs[i].addr)); + rw->value = cpu_to_le16(ioreqs[i].value); + } + + udev = zd_usb_to_usbdev(usb); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT), + req, req_len, &actual_req_len, 1000 /* ms */); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in usb_bulk_msg(). Error number %d\n", r); + goto error; + } + if (req_len != actual_req_len) { + dev_dbg_f(zd_usb_dev(usb), + "error in usb_bulk_msg()" + " req_len %d != actual_req_len %d\n", + req_len, actual_req_len); + r = -EIO; + goto error; + } + + /* FALL-THROUGH with r == 0 */ +error: + kfree(req); + return r; +} + +int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) +{ + int r; + struct usb_device *udev; + struct usb_req_rfwrite *req = NULL; + int i, req_len, actual_req_len; + u16 bit_value_template; + + if (in_atomic()) { + dev_dbg_f(zd_usb_dev(usb), + "error: io in atomic context not supported\n"); + return -EWOULDBLOCK; + } + if (bits < USB_MIN_RFWRITE_BIT_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: bits %d are smaller than" + " USB_MIN_RFWRITE_BIT_COUNT %d\n", + bits, USB_MIN_RFWRITE_BIT_COUNT); + return -EINVAL; + } + if (bits > USB_MAX_RFWRITE_BIT_COUNT) { + dev_dbg_f(zd_usb_dev(usb), + "error: bits %d exceed USB_MAX_RFWRITE_BIT_COUNT %d\n", + bits, USB_MAX_RFWRITE_BIT_COUNT); + return -EINVAL; + } +#ifdef DEBUG + if (value & (~0UL << bits)) { + dev_dbg_f(zd_usb_dev(usb), + "error: value %#09x has bits >= %d set\n", + value, bits); + return -EINVAL; + } +#endif /* DEBUG */ + + dev_dbg_f(zd_usb_dev(usb), "value %#09x bits %d\n", value, bits); + + r = zd_usb_ioread16(usb, &bit_value_template, CR203); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error %d: Couldn't read CR203\n", r); + goto out; + } + bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); + + req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16); + req = kmalloc(req_len, GFP_NOFS); + if (!req) + return -ENOMEM; + + req->id = cpu_to_le16(USB_REQ_WRITE_RF); + /* 1: 3683a, but not used in ZYDAS driver */ + req->value = cpu_to_le16(2); + req->bits = cpu_to_le16(bits); + + for (i = 0; i < bits; i++) { + u16 bv = bit_value_template; + if (value & (1 << (bits-1-i))) + bv |= RF_DATA; + req->bit_values[i] = cpu_to_le16(bv); + } + + udev = zd_usb_to_usbdev(usb); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, EP_REGS_OUT), + req, req_len, &actual_req_len, 1000 /* ms */); + if (r) { + dev_dbg_f(zd_usb_dev(usb), + "error in usb_bulk_msg(). Error number %d\n", r); + goto out; + } + if (req_len != actual_req_len) { + dev_dbg_f(zd_usb_dev(usb), "error in usb_bulk_msg()" + " req_len %d != actual_req_len %d\n", + req_len, actual_req_len); + r = -EIO; + goto out; + } + + /* FALL-THROUGH with r == 0 */ +out: + kfree(req); + return r; +} diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h new file mode 100644 index 000000000000..d6420283bd5a --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_usb.h @@ -0,0 +1,240 @@ +/* zd_usb.h: Header for USB interface implemented by ZD1211 chip + * + * 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 + */ + +#ifndef _ZD_USB_H +#define _ZD_USB_H + +#include +#include +#include +#include +#include + +#include "zd_def.h" +#include "zd_types.h" + +enum devicetype { + DEVICE_ZD1211 = 0, + DEVICE_ZD1211B = 1, +}; + +enum endpoints { + EP_CTRL = 0, + EP_DATA_OUT = 1, + EP_DATA_IN = 2, + EP_INT_IN = 3, + EP_REGS_OUT = 4, +}; + +enum { + USB_MAX_TRANSFER_SIZE = 4096, /* bytes */ + /* FIXME: The original driver uses this value. We have to check, + * whether the MAX_TRANSFER_SIZE is sufficient and this needs only be + * used if one combined frame is split over two USB transactions. + */ + USB_MAX_RX_SIZE = 4800, /* bytes */ + USB_MAX_IOWRITE16_COUNT = 15, + USB_MAX_IOWRITE32_COUNT = USB_MAX_IOWRITE16_COUNT/2, + USB_MAX_IOREAD16_COUNT = 15, + USB_MAX_IOREAD32_COUNT = USB_MAX_IOREAD16_COUNT/2, + USB_MIN_RFWRITE_BIT_COUNT = 16, + USB_MAX_RFWRITE_BIT_COUNT = 28, + USB_MAX_EP_INT_BUFFER = 64, + USB_ZD1211B_BCD_DEVICE = 0x4810, +}; + +enum control_requests { + USB_REQ_WRITE_REGS = 0x21, + USB_REQ_READ_REGS = 0x22, + USB_REQ_WRITE_RF = 0x23, + USB_REQ_PROG_FLASH = 0x24, + USB_REQ_EEPROM_START = 0x0128, /* ? request is a byte */ + USB_REQ_EEPROM_MID = 0x28, + USB_REQ_EEPROM_END = 0x0228, /* ? request is a byte */ + USB_REQ_FIRMWARE_DOWNLOAD = 0x30, + USB_REQ_FIRMWARE_CONFIRM = 0x31, + USB_REQ_FIRMWARE_READ_DATA = 0x32, +}; + +struct usb_req_read_regs { + __le16 id; + __le16 addr[0]; +} __attribute__((packed)); + +struct reg_data { + __le16 addr; + __le16 value; +} __attribute__((packed)); + +struct usb_req_write_regs { + __le16 id; + struct reg_data reg_writes[0]; +} __attribute__((packed)); + +enum { + RF_IF_LE = 0x02, + RF_CLK = 0x04, + RF_DATA = 0x08, +}; + +struct usb_req_rfwrite { + __le16 id; + __le16 value; + /* 1: 3683a */ + /* 2: other (default) */ + __le16 bits; + /* RF2595: 24 */ + __le16 bit_values[0]; + /* (CR203 & ~(RF_IF_LE | RF_CLK | RF_DATA)) | (bit ? RF_DATA : 0) */ +} __attribute__((packed)); + +/* USB interrupt */ + +enum usb_int_id { + USB_INT_TYPE = 0x01, + USB_INT_ID_REGS = 0x90, + USB_INT_ID_RETRY_FAILED = 0xa0, +}; + +enum usb_int_flags { + USB_INT_READ_REGS_EN = 0x01, +}; + +struct usb_int_header { + u8 type; /* must always be 1 */ + u8 id; +} __attribute__((packed)); + +struct usb_int_regs { + struct usb_int_header hdr; + struct reg_data regs[0]; +} __attribute__((packed)); + +struct usb_int_retry_fail { + struct usb_int_header hdr; + u8 new_rate; + u8 _dummy; + u8 addr[ETH_ALEN]; + u8 ibss_wakeup_dest; +} __attribute__((packed)); + +struct read_regs_int { + struct completion completion; + /* Stores the USB int structure and contains the USB address of the + * first requested register before request. + */ + u8 buffer[USB_MAX_EP_INT_BUFFER]; + int length; + __le16 cr_int_addr; +}; + +struct zd_ioreq16 { + zd_addr_t addr; + u16 value; +}; + +struct zd_ioreq32 { + zd_addr_t addr; + u32 value; +}; + +struct zd_usb_interrupt { + struct read_regs_int read_regs; + spinlock_t lock; + struct urb *urb; + int interval; + u8 read_regs_enabled:1; +}; + +static inline struct usb_int_regs *get_read_regs(struct zd_usb_interrupt *intr) +{ + return (struct usb_int_regs *)intr->read_regs.buffer; +} + +#define URBS_COUNT 5 + +struct zd_usb_rx { + spinlock_t lock; + u8 fragment[2*USB_MAX_RX_SIZE]; + unsigned int fragment_length; + unsigned int usb_packet_size; + struct urb **urbs; + int urbs_count; +}; + +struct zd_usb_tx { + spinlock_t lock; +}; + +/* Contains the usb parts. The structure doesn't require a lock, because intf + * and fw_base_offset, will not be changed after initialization. + */ +struct zd_usb { + struct zd_usb_interrupt intr; + struct zd_usb_rx rx; + struct zd_usb_tx tx; + struct usb_interface *intf; + u16 fw_base_offset; +}; + +#define zd_usb_dev(usb) (&usb->intf->dev) + +static inline struct usb_device *zd_usb_to_usbdev(struct zd_usb *usb) +{ + return interface_to_usbdev(usb->intf); +} + +static inline struct net_device *zd_intf_to_netdev(struct usb_interface *intf) +{ + return usb_get_intfdata(intf); +} + +static inline struct net_device *zd_usb_to_netdev(struct zd_usb *usb) +{ + return zd_intf_to_netdev(usb->intf); +} + +void zd_usb_init(struct zd_usb *usb, struct net_device *netdev, + struct usb_interface *intf); +int zd_usb_init_hw(struct zd_usb *usb); +void zd_usb_clear(struct zd_usb *usb); + +int zd_usb_scnprint_id(struct zd_usb *usb, char *buffer, size_t size); + +int zd_usb_enable_int(struct zd_usb *usb); +void zd_usb_disable_int(struct zd_usb *usb); + +int zd_usb_enable_rx(struct zd_usb *usb); +void zd_usb_disable_rx(struct zd_usb *usb); + +int zd_usb_tx(struct zd_usb *usb, const u8 *frame, unsigned int length); + +int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, + const zd_addr_t *addresses, unsigned int count); + +static inline int zd_usb_ioread16(struct zd_usb *usb, u16 *value, + const zd_addr_t addr) +{ + return zd_usb_ioread16v(usb, value, (const zd_addr_t *)&addr, 1); +} + +int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, + unsigned int count); + +int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits); + +#endif /* _ZD_USB_H */ diff --git a/drivers/net/wireless/zd1211rw/zd_util.c b/drivers/net/wireless/zd1211rw/zd_util.c new file mode 100644 index 000000000000..d20036c15d11 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_util.c @@ -0,0 +1,82 @@ +/* zd_util.c + * + * 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 + * + * Utility program + */ + +#include "zd_def.h" +#include "zd_util.h" + +#ifdef DEBUG +static char hex(u8 v) +{ + v &= 0xf; + return (v < 10 ? '0' : 'a' - 10) + v; +} + +static char hex_print(u8 c) +{ + return (0x20 <= c && c < 0x7f) ? c : '.'; +} + +static void dump_line(const u8 *bytes, size_t size) +{ + char c; + size_t i; + + size = size <= 8 ? size : 8; + printk(KERN_DEBUG "zd1211 %p ", bytes); + for (i = 0; i < 8; i++) { + switch (i) { + case 1: + case 5: + c = '.'; + break; + case 3: + c = ':'; + break; + default: + c = ' '; + } + if (i < size) { + printk("%c%c%c", hex(bytes[i] >> 4), hex(bytes[i]), c); + } else { + printk(" %c", c); + } + } + + for (i = 0; i < size; i++) + printk("%c", hex_print(bytes[i])); + printk("\n"); +} + +void zd_hexdump(const void *bytes, size_t size) +{ + size_t i = 0; + + do { + dump_line((u8 *)bytes + i, size-i); + i += 8; + } while (i < size); +} +#endif /* DEBUG */ + +void *zd_tail(const void *buffer, size_t buffer_size, size_t tail_size) +{ + if (buffer_size < tail_size) + return NULL; + return (u8 *)buffer + (buffer_size - tail_size); +} diff --git a/drivers/net/wireless/zd1211rw/zd_util.h b/drivers/net/wireless/zd1211rw/zd_util.h new file mode 100644 index 000000000000..ce26f7adea92 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_util.h @@ -0,0 +1,29 @@ +/* zd_util.h + * + * 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 + */ + +#ifndef _ZD_UTIL_H +#define _ZD_UTIL_H + +void *zd_tail(const void *buffer, size_t buffer_size, size_t tail_size); + +#ifdef DEBUG +void zd_hexdump(const void *bytes, size_t size); +#else +#define zd_hexdump(bytes, size) +#endif /* DEBUG */ + +#endif /* _ZD_UTIL_H */ -- cgit v1.2.3 From c9a4b35df9a5e24065e2dd80aa18a381da62339d Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 11 Jun 2006 23:18:54 +0100 Subject: [PATCH] zd1211rw: disable TX queue during stop This avoids some potential races. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_mac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index bbe067ec7de1..3bdc54d128d0 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -197,6 +197,8 @@ int zd_mac_stop(struct net_device *netdev) struct zd_mac *mac = zd_netdev_mac(netdev); struct zd_chip *chip = &mac->chip; + netif_stop_queue(netdev); + /* * The order here deliberately is a little different from the open() * method, since we need to make sure there is no opportunity for RX -- cgit v1.2.3 From 0c639b31068e8e111ec330a3634d95e20c11aab6 Mon Sep 17 00:00:00 2001 From: Kim Phillips Date: Wed, 28 Jun 2006 21:13:23 -0500 Subject: [PATCH] Add support for the Cicada 8201 PHY Add support for the Cicada 8201 PHY, a.k.a Vitesse VSC8201. This PHY is present on the MPC8349mITX. Signed-off-by: Kim Phillips Signed-off-by: Andy Fleming Signed-off-by: Jeff Garzik --- drivers/net/phy/cicada.c | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c index 3efb715c28dc..ae60e6e4107c 100644 --- a/drivers/net/phy/cicada.c +++ b/drivers/net/phy/cicada.c @@ -103,7 +103,22 @@ static int cis820x_config_intr(struct phy_device *phydev) return err; } -/* Cicada 820x */ +/* Cicada 8201, a.k.a Vitesse VSC8201 */ +static struct phy_driver cis8201_driver = { + .phy_id = 0x000fc410, + .name = "Cicada Cis8201", + .phy_id_mask = 0x000ffff0, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &cis820x_config_init, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &cis820x_ack_interrupt, + .config_intr = &cis820x_config_intr, + .driver = { .owner = THIS_MODULE,}, +}; + +/* Cicada 8204 */ static struct phy_driver cis8204_driver = { .phy_id = 0x000fc440, .name = "Cicada Cis8204", @@ -118,15 +133,30 @@ static struct phy_driver cis8204_driver = { .driver = { .owner = THIS_MODULE,}, }; -static int __init cis8204_init(void) +static int __init cicada_init(void) { - return phy_driver_register(&cis8204_driver); + int ret; + + ret = phy_driver_register(&cis8204_driver); + if (ret) + goto err1; + + ret = phy_driver_register(&cis8201_driver); + if (ret) + goto err2; + return 0; + +err2: + phy_driver_unregister(&cis8204_driver); +err1: + return ret; } -static void __exit cis8204_exit(void) +static void __exit cicada_exit(void) { phy_driver_unregister(&cis8204_driver); + phy_driver_unregister(&cis8201_driver); } -module_init(cis8204_init); -module_exit(cis8204_exit); +module_init(cicada_init); +module_exit(cicada_exit); -- cgit v1.2.3 From dcaf9769801ff49268f5a7a8376045e251700dcf Mon Sep 17 00:00:00 2001 From: Don Fry Date: Thu, 29 Jun 2006 13:52:29 -0700 Subject: [PATCH] pcnet32: Fix Section mismatch error Fix Section mismatch error. Tested ia32 and ppc64. Signed-off-by: Don Fry Signed-off-by: Jeff Garzik --- drivers/net/pcnet32.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index d768f3d1ac28..5d9be50fd982 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -277,7 +277,6 @@ struct pcnet32_private { u32 phymask; }; -static void pcnet32_probe_vlbus(void); static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *); static int pcnet32_probe1(unsigned long, int, struct pci_dev *); static int pcnet32_open(struct net_device *); @@ -952,7 +951,7 @@ static struct ethtool_ops pcnet32_ethtool_ops = { /* only probes for non-PCI devices, the rest are handled by * pci_register_driver via pcnet32_probe_pci */ -static void __devinit pcnet32_probe_vlbus(void) +static void __devinit pcnet32_probe_vlbus(unsigned int *pcnet32_portlist) { unsigned int *port, ioaddr; @@ -2730,7 +2729,7 @@ static int __init pcnet32_init_module(void) /* should we find any remaining VLbus devices ? */ if (pcnet32vlb) - pcnet32_probe_vlbus(); + pcnet32_probe_vlbus(pcnet32_portlist); if (cards_found && (pcnet32_debug & NETIF_MSG_PROBE)) printk(KERN_INFO PFX "%d cards_found.\n", cards_found); -- cgit v1.2.3 From f2622a2b16f27d0087c913115dc15ee70797d58b Mon Sep 17 00:00:00 2001 From: Don Fry Date: Thu, 29 Jun 2006 13:52:58 -0700 Subject: [PATCH] pcnet32: Use PCI_DEVICE macro Jon Mason wrote on Thu, 12 Jan 2006 17:07:49 -0600: This patch adds the PCI_DEVICE macro to the pcnet32 driver. This has been tested on my opteron with my "trident" adapter. Don Fry modified it slightly and tested on ia32 and ppc64. Signed-off-by: Jon Mason Signed-off-by: Don Fry Signed-off-by: Jeff Garzik --- drivers/net/pcnet32.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 5d9be50fd982..971bd6e91e4f 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -58,18 +58,15 @@ static const char *const version = * PCI device identifiers for "new style" Linux PCI Device Drivers */ static struct pci_device_id pcnet32_pci_tbl[] = { - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE_HOME, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE_HOME), }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE), }, /* * Adapters that were sold with IBM's RS/6000 or pSeries hardware have * the incorrect vendor id. */ - { PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE, - PCI_ANY_ID, PCI_ANY_ID, - PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, 0}, + { PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE), + .class = (PCI_CLASS_NETWORK_ETHERNET << 8), .class_mask = 0xffff00, }, { } /* terminate list */ }; -- cgit v1.2.3 From 6dcd60c2c78ca87163730472ddea6aa1a7754b61 Mon Sep 17 00:00:00 2001 From: Don Fry Date: Thu, 29 Jun 2006 13:53:23 -0700 Subject: [PATCH] pcnet32: Fix off-by-one in get_ringparam Fix off-by-one in pcnet32_get_ringparam Signed-off-by: Don Fry Signed-off-by: Jeff Garzik --- drivers/net/pcnet32.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 971bd6e91e4f..5fcd4d94871d 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -515,10 +515,10 @@ static void pcnet32_get_ringparam(struct net_device *dev, { struct pcnet32_private *lp = dev->priv; - ering->tx_max_pending = TX_MAX_RING_SIZE - 1; - ering->tx_pending = lp->tx_ring_size - 1; - ering->rx_max_pending = RX_MAX_RING_SIZE - 1; - ering->rx_pending = lp->rx_ring_size - 1; + ering->tx_max_pending = TX_MAX_RING_SIZE; + ering->tx_pending = lp->tx_ring_size; + ering->rx_max_pending = RX_MAX_RING_SIZE; + ering->rx_pending = lp->rx_ring_size; } static int pcnet32_set_ringparam(struct net_device *dev, -- cgit v1.2.3 From 12fa30f35b52e85b4c37a2ef3c3320c158d510fa Mon Sep 17 00:00:00 2001 From: Don Fry Date: Thu, 29 Jun 2006 13:53:48 -0700 Subject: [PATCH] pcnet32: Use kcalloc instead of kmalloc and memset On 2006-03-08 Eric Sesterhenn wrote: converts drivers/net to kzalloc usage. Don Fry modified it to use netif_msg_drv. Tested ia32 and ppc64. Signed-off-by: Eric Sesterhenn Signed-off-by: Don Fry Signed-off-by: Jeff Garzik --- drivers/net/pcnet32.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 5fcd4d94871d..f89b7a1e24d6 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -1432,7 +1432,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, char *name) lp->tx_ring_size, &lp->tx_ring_dma_addr); if (lp->tx_ring == NULL) { - if (pcnet32_debug & NETIF_MSG_DRV) + if (netif_msg_drv(lp)) printk("\n" KERN_ERR PFX "%s: Consistent memory allocation failed.\n", name); @@ -1444,52 +1444,48 @@ static int pcnet32_alloc_ring(struct net_device *dev, char *name) lp->rx_ring_size, &lp->rx_ring_dma_addr); if (lp->rx_ring == NULL) { - if (pcnet32_debug & NETIF_MSG_DRV) + if (netif_msg_drv(lp)) printk("\n" KERN_ERR PFX "%s: Consistent memory allocation failed.\n", name); return -ENOMEM; } - lp->tx_dma_addr = kmalloc(sizeof(dma_addr_t) * lp->tx_ring_size, + lp->tx_dma_addr = kcalloc(lp->tx_ring_size, sizeof(dma_addr_t), GFP_ATOMIC); if (!lp->tx_dma_addr) { - if (pcnet32_debug & NETIF_MSG_DRV) + if (netif_msg_drv(lp)) printk("\n" KERN_ERR PFX "%s: Memory allocation failed.\n", name); return -ENOMEM; } - memset(lp->tx_dma_addr, 0, sizeof(dma_addr_t) * lp->tx_ring_size); - lp->rx_dma_addr = kmalloc(sizeof(dma_addr_t) * lp->rx_ring_size, + lp->rx_dma_addr = kcalloc(lp->rx_ring_size, sizeof(dma_addr_t), GFP_ATOMIC); if (!lp->rx_dma_addr) { - if (pcnet32_debug & NETIF_MSG_DRV) + if (netif_msg_drv(lp)) printk("\n" KERN_ERR PFX "%s: Memory allocation failed.\n", name); return -ENOMEM; } - memset(lp->rx_dma_addr, 0, sizeof(dma_addr_t) * lp->rx_ring_size); - lp->tx_skbuff = kmalloc(sizeof(struct sk_buff *) * lp->tx_ring_size, + lp->tx_skbuff = kcalloc(lp->tx_ring_size, sizeof(struct sk_buff *), GFP_ATOMIC); if (!lp->tx_skbuff) { - if (pcnet32_debug & NETIF_MSG_DRV) + if (netif_msg_drv(lp)) printk("\n" KERN_ERR PFX "%s: Memory allocation failed.\n", name); return -ENOMEM; } - memset(lp->tx_skbuff, 0, sizeof(struct sk_buff *) * lp->tx_ring_size); - lp->rx_skbuff = kmalloc(sizeof(struct sk_buff *) * lp->rx_ring_size, + lp->rx_skbuff = kcalloc(lp->rx_ring_size, sizeof(struct sk_buff *), GFP_ATOMIC); if (!lp->rx_skbuff) { - if (pcnet32_debug & NETIF_MSG_DRV) + if (netif_msg_drv(lp)) printk("\n" KERN_ERR PFX "%s: Memory allocation failed.\n", name); return -ENOMEM; } - memset(lp->rx_skbuff, 0, sizeof(struct sk_buff *) * lp->rx_ring_size); return 0; } -- cgit v1.2.3 From 06c878500893c315795fcf944ecbd85c3d023040 Mon Sep 17 00:00:00 2001 From: Don Fry Date: Thu, 29 Jun 2006 13:54:38 -0700 Subject: [PATCH] pcnet32: Handle memory allocation failures cleanly when resizing tx/rx rings Fix pcnet32_set_ringparam to handle memory allocation errors without leaving the adapter in an inoperative state and null pointers waiting to be dereferenced. Tested ia32 and ppc64. Signed-off-by: Don Fry Signed-off-by: Jeff Garzik --- drivers/net/pcnet32.c | 275 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 251 insertions(+), 24 deletions(-) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index f89b7a1e24d6..e79c3b6bee13 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -185,6 +185,23 @@ static int homepna[MAX_UNITS]; #define PCNET32_TOTAL_SIZE 0x20 +#define CSR0 0 +#define CSR0_INIT 0x1 +#define CSR0_START 0x2 +#define CSR0_STOP 0x4 +#define CSR0_TXPOLL 0x8 +#define CSR0_INTEN 0x40 +#define CSR0_IDON 0x0100 +#define CSR0_NORMAL (CSR0_START | CSR0_INTEN) +#define PCNET32_INIT_LOW 1 +#define PCNET32_INIT_HIGH 2 +#define CSR3 3 +#define CSR4 4 +#define CSR5 5 +#define CSR5_SUSPEND 0x0001 +#define CSR15 15 +#define PCNET32_MC_FILTER 8 + /* The PCNET32 Rx and Tx ring descriptors. */ struct pcnet32_rx_head { u32 base; @@ -415,6 +432,219 @@ static struct pcnet32_access pcnet32_dwio = { .reset = pcnet32_dwio_reset }; +static void pcnet32_netif_stop(struct net_device *dev) +{ + dev->trans_start = jiffies; + netif_poll_disable(dev); + netif_tx_disable(dev); +} + +static void pcnet32_netif_start(struct net_device *dev) +{ + netif_wake_queue(dev); + netif_poll_enable(dev); +} + +/* + * Allocate space for the new sized tx ring. + * Free old resources + * Save new resources. + * Any failure keeps old resources. + * Must be called with lp->lock held. + */ +static void pcnet32_realloc_tx_ring(struct net_device *dev, + struct pcnet32_private *lp, + unsigned int size) +{ + dma_addr_t new_ring_dma_addr; + dma_addr_t *new_dma_addr_list; + struct pcnet32_tx_head *new_tx_ring; + struct sk_buff **new_skb_list; + + pcnet32_purge_tx_ring(dev); + + new_tx_ring = pci_alloc_consistent(lp->pci_dev, + sizeof(struct pcnet32_tx_head) * + (1 << size), + &new_ring_dma_addr); + if (new_tx_ring == NULL) { + if (netif_msg_drv(lp)) + printk("\n" KERN_ERR + "%s: Consistent memory allocation failed.\n", + dev->name); + return; + } + memset(new_tx_ring, 0, sizeof(struct pcnet32_tx_head) * (1 << size)); + + new_dma_addr_list = kcalloc((1 << size), sizeof(dma_addr_t), + GFP_ATOMIC); + if (!new_dma_addr_list) { + if (netif_msg_drv(lp)) + printk("\n" KERN_ERR + "%s: Memory allocation failed.\n", dev->name); + goto free_new_tx_ring; + } + + new_skb_list = kcalloc((1 << size), sizeof(struct sk_buff *), + GFP_ATOMIC); + if (!new_skb_list) { + if (netif_msg_drv(lp)) + printk("\n" KERN_ERR + "%s: Memory allocation failed.\n", dev->name); + goto free_new_lists; + } + + kfree(lp->tx_skbuff); + kfree(lp->tx_dma_addr); + pci_free_consistent(lp->pci_dev, + sizeof(struct pcnet32_tx_head) * + lp->tx_ring_size, lp->tx_ring, + lp->tx_ring_dma_addr); + + lp->tx_ring_size = (1 << size); + lp->tx_mod_mask = lp->tx_ring_size - 1; + lp->tx_len_bits = (size << 12); + lp->tx_ring = new_tx_ring; + lp->tx_ring_dma_addr = new_ring_dma_addr; + lp->tx_dma_addr = new_dma_addr_list; + lp->tx_skbuff = new_skb_list; + return; + + free_new_lists: + kfree(new_dma_addr_list); + free_new_tx_ring: + pci_free_consistent(lp->pci_dev, + sizeof(struct pcnet32_tx_head) * + (1 << size), + new_tx_ring, + new_ring_dma_addr); + return; +} + +/* + * Allocate space for the new sized rx ring. + * Re-use old receive buffers. + * alloc extra buffers + * free unneeded buffers + * free unneeded buffers + * Save new resources. + * Any failure keeps old resources. + * Must be called with lp->lock held. + */ +static void pcnet32_realloc_rx_ring(struct net_device *dev, + struct pcnet32_private *lp, + unsigned int size) +{ + dma_addr_t new_ring_dma_addr; + dma_addr_t *new_dma_addr_list; + struct pcnet32_rx_head *new_rx_ring; + struct sk_buff **new_skb_list; + int new, overlap; + + new_rx_ring = pci_alloc_consistent(lp->pci_dev, + sizeof(struct pcnet32_rx_head) * + (1 << size), + &new_ring_dma_addr); + if (new_rx_ring == NULL) { + if (netif_msg_drv(lp)) + printk("\n" KERN_ERR + "%s: Consistent memory allocation failed.\n", + dev->name); + return; + } + memset(new_rx_ring, 0, sizeof(struct pcnet32_rx_head) * (1 << size)); + + new_dma_addr_list = kcalloc((1 << size), sizeof(dma_addr_t), + GFP_ATOMIC); + if (!new_dma_addr_list) { + if (netif_msg_drv(lp)) + printk("\n" KERN_ERR + "%s: Memory allocation failed.\n", dev->name); + goto free_new_rx_ring; + } + + new_skb_list = kcalloc((1 << size), sizeof(struct sk_buff *), + GFP_ATOMIC); + if (!new_skb_list) { + if (netif_msg_drv(lp)) + printk("\n" KERN_ERR + "%s: Memory allocation failed.\n", dev->name); + goto free_new_lists; + } + + /* first copy the current receive buffers */ + overlap = min(size, lp->rx_ring_size); + for (new = 0; new < overlap; new++) { + new_rx_ring[new] = lp->rx_ring[new]; + new_dma_addr_list[new] = lp->rx_dma_addr[new]; + new_skb_list[new] = lp->rx_skbuff[new]; + } + /* now allocate any new buffers needed */ + for (; new < size; new++ ) { + struct sk_buff *rx_skbuff; + new_skb_list[new] = dev_alloc_skb(PKT_BUF_SZ); + if (!(rx_skbuff = new_skb_list[new])) { + /* keep the original lists and buffers */ + if (netif_msg_drv(lp)) + printk(KERN_ERR + "%s: pcnet32_realloc_rx_ring dev_alloc_skb failed.\n", + dev->name); + goto free_all_new; + } + skb_reserve(rx_skbuff, 2); + + new_dma_addr_list[new] = + pci_map_single(lp->pci_dev, rx_skbuff->data, + PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE); + new_rx_ring[new].base = (u32) le32_to_cpu(new_dma_addr_list[new]); + new_rx_ring[new].buf_length = le16_to_cpu(2 - PKT_BUF_SZ); + new_rx_ring[new].status = le16_to_cpu(0x8000); + } + /* and free any unneeded buffers */ + for (; new < lp->rx_ring_size; new++) { + if (lp->rx_skbuff[new]) { + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[new], + PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE); + dev_kfree_skb(lp->rx_skbuff[new]); + } + } + + kfree(lp->rx_skbuff); + kfree(lp->rx_dma_addr); + pci_free_consistent(lp->pci_dev, + sizeof(struct pcnet32_rx_head) * + lp->rx_ring_size, lp->rx_ring, + lp->rx_ring_dma_addr); + + lp->rx_ring_size = (1 << size); + lp->rx_mod_mask = lp->rx_ring_size - 1; + lp->rx_len_bits = (size << 4); + lp->rx_ring = new_rx_ring; + lp->rx_ring_dma_addr = new_ring_dma_addr; + lp->rx_dma_addr = new_dma_addr_list; + lp->rx_skbuff = new_skb_list; + return; + + free_all_new: + for (; --new >= lp->rx_ring_size; ) { + if (new_skb_list[new]) { + pci_unmap_single(lp->pci_dev, new_dma_addr_list[new], + PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE); + dev_kfree_skb(new_skb_list[new]); + } + } + kfree(new_skb_list); + free_new_lists: + kfree(new_dma_addr_list); + free_new_rx_ring: + pci_free_consistent(lp->pci_dev, + sizeof(struct pcnet32_rx_head) * + (1 << size), + new_rx_ring, + new_ring_dma_addr); + return; +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void pcnet32_poll_controller(struct net_device *dev) { @@ -526,56 +756,53 @@ static int pcnet32_set_ringparam(struct net_device *dev, { struct pcnet32_private *lp = dev->priv; unsigned long flags; + unsigned int size; + ulong ioaddr = dev->base_addr; int i; if (ering->rx_mini_pending || ering->rx_jumbo_pending) return -EINVAL; if (netif_running(dev)) - pcnet32_close(dev); + pcnet32_netif_stop(dev); spin_lock_irqsave(&lp->lock, flags); - pcnet32_free_ring(dev); - lp->tx_ring_size = - min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE); - lp->rx_ring_size = - min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE); + lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */ + + size = min(ering->tx_pending, (unsigned int)TX_MAX_RING_SIZE); /* set the minimum ring size to 4, to allow the loopback test to work * unchanged. */ for (i = 2; i <= PCNET32_LOG_MAX_TX_BUFFERS; i++) { - if (lp->tx_ring_size <= (1 << i)) + if (size <= (1 << i)) break; } - lp->tx_ring_size = (1 << i); - lp->tx_mod_mask = lp->tx_ring_size - 1; - lp->tx_len_bits = (i << 12); - + if ((1 << i) != lp->tx_ring_size) + pcnet32_realloc_tx_ring(dev, lp, i); + + size = min(ering->rx_pending, (unsigned int)RX_MAX_RING_SIZE); for (i = 2; i <= PCNET32_LOG_MAX_RX_BUFFERS; i++) { - if (lp->rx_ring_size <= (1 << i)) + if (size <= (1 << i)) break; } - lp->rx_ring_size = (1 << i); - lp->rx_mod_mask = lp->rx_ring_size - 1; - lp->rx_len_bits = (i << 4); + if ((1 << i) != lp->rx_ring_size) + pcnet32_realloc_rx_ring(dev, lp, i); + + dev->weight = lp->rx_ring_size / 2; - if (pcnet32_alloc_ring(dev, dev->name)) { - pcnet32_free_ring(dev); - spin_unlock_irqrestore(&lp->lock, flags); - return -ENOMEM; + if (netif_running(dev)) { + pcnet32_netif_start(dev); + pcnet32_restart(dev, CSR0_NORMAL); } spin_unlock_irqrestore(&lp->lock, flags); - if (pcnet32_debug & NETIF_MSG_DRV) - printk(KERN_INFO PFX + if (netif_msg_drv(lp)) + printk(KERN_INFO "%s: Ring Param Settings: RX: %d, TX: %d\n", dev->name, lp->rx_ring_size, lp->tx_ring_size); - if (netif_running(dev)) - pcnet32_open(dev); - return 0; } -- cgit v1.2.3 From df27f4a610e22e8c8c740286368cc13e0600f22c Mon Sep 17 00:00:00 2001 From: Don Fry Date: Thu, 29 Jun 2006 13:55:02 -0700 Subject: [PATCH] pcnet32: Suspend the chip rather than restart when changing multicast/promisc Suspend the chip if possible rather than stop and discard all tx and rx frames, when changing the mcast list or entering/leaving promiscuous mode. Created common pcnet32_suspend routine. Tested ia32 and ppc64 Signed-off-by: Don Fry Signed-off-by: Jeff Garzik --- drivers/net/pcnet32.c | 95 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index e79c3b6bee13..fe0558242835 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -1062,6 +1062,43 @@ static int pcnet32_phys_id(struct net_device *dev, u32 data) return 0; } +/* + * lp->lock must be held. + */ +static int pcnet32_suspend(struct net_device *dev, unsigned long *flags, + int can_sleep) +{ + int csr5; + struct pcnet32_private *lp = dev->priv; + struct pcnet32_access *a = &lp->a; + ulong ioaddr = dev->base_addr; + int ticks; + + /* set SUSPEND (SPND) - CSR5 bit 0 */ + csr5 = a->read_csr(ioaddr, CSR5); + a->write_csr(ioaddr, CSR5, csr5 | CSR5_SUSPEND); + + /* poll waiting for bit to be set */ + ticks = 0; + while (!(a->read_csr(ioaddr, CSR5) & CSR5_SUSPEND)) { + spin_unlock_irqrestore(&lp->lock, *flags); + if (can_sleep) + msleep(1); + else + mdelay(1); + spin_lock_irqsave(&lp->lock, *flags); + ticks++; + if (ticks > 200) { + if (netif_msg_hw(lp)) + printk(KERN_DEBUG + "%s: Error getting into suspend!\n", + dev->name); + return 0; + } + } + return 1; +} + #define PCNET32_REGS_PER_PHY 32 #define PCNET32_MAX_PHYS 32 static int pcnet32_get_regs_len(struct net_device *dev) @@ -1080,32 +1117,13 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, struct pcnet32_private *lp = dev->priv; struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; - int ticks; unsigned long flags; spin_lock_irqsave(&lp->lock, flags); - csr0 = a->read_csr(ioaddr, 0); - if (!(csr0 & 0x0004)) { /* If not stopped */ - /* set SUSPEND (SPND) - CSR5 bit 0 */ - a->write_csr(ioaddr, 5, 0x0001); - - /* poll waiting for bit to be set */ - ticks = 0; - while (!(a->read_csr(ioaddr, 5) & 0x0001)) { - spin_unlock_irqrestore(&lp->lock, flags); - mdelay(1); - spin_lock_irqsave(&lp->lock, flags); - ticks++; - if (ticks > 200) { - if (netif_msg_hw(lp)) - printk(KERN_DEBUG - "%s: Error getting into suspend!\n", - dev->name); - break; - } - } - } + csr0 = a->read_csr(ioaddr, CSR0); + if (!(csr0 & CSR0_STOP)) /* If not stopped */ + pcnet32_suspend(dev, &flags, 1); /* read address PROM */ for (i = 0; i < 16; i += 2) @@ -1142,9 +1160,12 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, } } - if (!(csr0 & 0x0004)) { /* If not stopped */ + if (!(csr0 & CSR0_STOP)) { /* If not stopped */ + int csr5; + /* clear SUSPEND (SPND) - CSR5 bit 0 */ - a->write_csr(ioaddr, 5, 0x0000); + csr5 = a->read_csr(ioaddr, CSR5); + a->write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND)); } spin_unlock_irqrestore(&lp->lock, flags); @@ -2652,6 +2673,7 @@ static void pcnet32_load_multicast(struct net_device *dev) volatile struct pcnet32_init_block *ib = &lp->init_block; volatile u16 *mcast_table = (u16 *) & ib->filter; struct dev_mc_list *dmi = dev->mc_list; + unsigned long ioaddr = dev->base_addr; char *addrs; int i; u32 crc; @@ -2660,6 +2682,10 @@ static void pcnet32_load_multicast(struct net_device *dev) if (dev->flags & IFF_ALLMULTI) { ib->filter[0] = 0xffffffff; ib->filter[1] = 0xffffffff; + lp->a.write_csr(ioaddr, PCNET32_MC_FILTER, 0xffff); + lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+1, 0xffff); + lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+2, 0xffff); + lp->a.write_csr(ioaddr, PCNET32_MC_FILTER+3, 0xffff); return; } /* clear the multicast filter */ @@ -2681,6 +2707,9 @@ static void pcnet32_load_multicast(struct net_device *dev) le16_to_cpu(le16_to_cpu(mcast_table[crc >> 4]) | (1 << (crc & 0xf))); } + for (i = 0; i < 4; i++) + lp->a.write_csr(ioaddr, PCNET32_MC_FILTER + i, + le16_to_cpu(mcast_table[i])); return; } @@ -2691,8 +2720,11 @@ static void pcnet32_set_multicast_list(struct net_device *dev) { unsigned long ioaddr = dev->base_addr, flags; struct pcnet32_private *lp = dev->priv; + int csr15, suspended; spin_lock_irqsave(&lp->lock, flags); + suspended = pcnet32_suspend(dev, &flags, 0); + csr15 = lp->a.read_csr(ioaddr, CSR15); if (dev->flags & IFF_PROMISC) { /* Log any net taps. */ if (netif_msg_hw(lp)) @@ -2701,15 +2733,24 @@ static void pcnet32_set_multicast_list(struct net_device *dev) lp->init_block.mode = le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) << 7); + lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000); } else { lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); + lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff); pcnet32_load_multicast(dev); } - lp->a.write_csr(ioaddr, 0, 0x0004); /* Temporarily stop the lance. */ - pcnet32_restart(dev, 0x0042); /* Resume normal operation */ - netif_wake_queue(dev); + if (suspended) { + int csr5; + /* clear SUSPEND (SPND) - CSR5 bit 0 */ + csr5 = lp->a.read_csr(ioaddr, CSR5); + lp->a.write_csr(ioaddr, CSR5, csr5 & (~CSR5_SUSPEND)); + } else { + lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); + pcnet32_restart(dev, CSR0_NORMAL); + netif_wake_queue(dev); + } spin_unlock_irqrestore(&lp->lock, flags); } -- cgit v1.2.3 From ac5bfe40f94cc8df512d247a5588897b0bc6dbea Mon Sep 17 00:00:00 2001 From: Don Fry Date: Thu, 29 Jun 2006 13:55:27 -0700 Subject: [PATCH] pcnet32: Cleanup rx buffers after loopback test. More cleanup to pcnet32_loopback_test to release receive buffers if device is not up. Created common routine to free rx buffers. Tested ia32 and ppc64 Signed-off-by: Don Fry Signed-off-by: Jeff Garzik --- drivers/net/pcnet32.c | 102 +++++++++++++++++++++----------------------------- 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index fe0558242835..4daafe303358 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -645,6 +645,25 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev, return; } +static void pcnet32_purge_rx_ring(struct net_device *dev) +{ + struct pcnet32_private *lp = dev->priv; + int i; + + /* free all allocated skbuffs */ + for (i = 0; i < lp->rx_ring_size; i++) { + lp->rx_ring[i].status = 0; /* CPU owns buffer */ + wmb(); /* Make sure adapter sees owner change */ + if (lp->rx_skbuff[i]) { + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], + PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(lp->rx_skbuff[i]); + } + lp->rx_skbuff[i] = NULL; + lp->rx_dma_addr[i] = 0; + } +} + #ifdef CONFIG_NET_POLL_CONTROLLER static void pcnet32_poll_controller(struct net_device *dev) { @@ -856,29 +875,27 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) unsigned long flags; unsigned long ticks; - *data1 = 1; /* status of test, default to fail */ rc = 1; /* default to fail */ if (netif_running(dev)) pcnet32_close(dev); spin_lock_irqsave(&lp->lock, flags); + lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* stop the chip */ + + numbuffs = min(numbuffs, (int)min(lp->rx_ring_size, lp->tx_ring_size)); /* Reset the PCNET32 */ lp->a.reset(ioaddr); + lp->a.write_csr(ioaddr, CSR4, 0x0915); /* switch pcnet32 to 32bit mode */ lp->a.write_bcr(ioaddr, 20, 2); - lp->init_block.mode = - le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); - lp->init_block.filter[0] = 0; - lp->init_block.filter[1] = 0; - /* purge & init rings but don't actually restart */ pcnet32_restart(dev, 0x0000); - lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */ + lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */ /* Initialize Transmit buffers. */ size = data_len + 15; @@ -920,14 +937,15 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) } } - x = a->read_bcr(ioaddr, 32); /* set internal loopback in BSR32 */ - x = x | 0x0002; - a->write_bcr(ioaddr, 32, x); + x = a->read_bcr(ioaddr, 32); /* set internal loopback in BCR32 */ + a->write_bcr(ioaddr, 32, x | 0x0002); - lp->a.write_csr(ioaddr, 15, 0x0044); /* set int loopback in CSR15 */ + /* set int loopback in CSR15 */ + x = a->read_csr(ioaddr, CSR15) & 0xfffc; + lp->a.write_csr(ioaddr, CSR15, x | 0x0044); teststatus = le16_to_cpu(0x8000); - lp->a.write_csr(ioaddr, 0, 0x0002); /* Set STRT bit */ + lp->a.write_csr(ioaddr, CSR0, CSR0_START); /* Set STRT bit */ /* Check status of descriptors */ for (x = 0; x < numbuffs; x++) { @@ -935,7 +953,7 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) rmb(); while ((lp->rx_ring[x].status & teststatus) && (ticks < 200)) { spin_unlock_irqrestore(&lp->lock, flags); - mdelay(1); + msleep(1); spin_lock_irqsave(&lp->lock, flags); rmb(); ticks++; @@ -948,7 +966,7 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) } } - lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */ + lp->a.write_csr(ioaddr, CSR0, CSR0_STOP); /* Set STOP bit */ wmb(); if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) { printk(KERN_DEBUG "%s: RX loopback packets:\n", dev->name); @@ -981,25 +999,24 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) } x++; } - if (!rc) { - *data1 = 0; - } clean_up: + *data1 = rc; pcnet32_purge_tx_ring(dev); - x = a->read_csr(ioaddr, 15) & 0xFFFF; - a->write_csr(ioaddr, 15, (x & ~0x0044)); /* reset bits 6 and 2 */ - x = a->read_bcr(ioaddr, 32); /* reset internal loopback */ - x = x & ~0x0002; - a->write_bcr(ioaddr, 32, x); + x = a->read_csr(ioaddr, CSR15); + a->write_csr(ioaddr, CSR15, (x & ~0x0044)); /* reset bits 6 and 2 */ - spin_unlock_irqrestore(&lp->lock, flags); + x = a->read_bcr(ioaddr, 32); /* reset internal loopback */ + a->write_bcr(ioaddr, 32, (x & ~0x0002)); if (netif_running(dev)) { + spin_unlock_irqrestore(&lp->lock, flags); pcnet32_open(dev); } else { + pcnet32_purge_rx_ring(dev); lp->a.write_bcr(ioaddr, 20, 4); /* return to 16bit mode */ + spin_unlock_irqrestore(&lp->lock, flags); } return (rc); @@ -1997,16 +2014,7 @@ static int pcnet32_open(struct net_device *dev) err_free_ring: /* free any allocated skbuffs */ - for (i = 0; i < lp->rx_ring_size; i++) { - lp->rx_ring[i].status = 0; - if (lp->rx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], - PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE); - dev_kfree_skb(lp->rx_skbuff[i]); - } - lp->rx_skbuff[i] = NULL; - lp->rx_dma_addr[i] = 0; - } + pcnet32_purge_rx_ring(dev); /* * Switch back to 16bit mode to avoid problems with dumb @@ -2588,7 +2596,6 @@ static int pcnet32_close(struct net_device *dev) { unsigned long ioaddr = dev->base_addr; struct pcnet32_private *lp = dev->priv; - int i; unsigned long flags; del_timer_sync(&lp->watchdog_timer); @@ -2619,31 +2626,8 @@ static int pcnet32_close(struct net_device *dev) spin_lock_irqsave(&lp->lock, flags); - /* free all allocated skbuffs */ - for (i = 0; i < lp->rx_ring_size; i++) { - lp->rx_ring[i].status = 0; - wmb(); /* Make sure adapter sees owner change */ - if (lp->rx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], - PKT_BUF_SZ - 2, PCI_DMA_FROMDEVICE); - dev_kfree_skb(lp->rx_skbuff[i]); - } - lp->rx_skbuff[i] = NULL; - lp->rx_dma_addr[i] = 0; - } - - for (i = 0; i < lp->tx_ring_size; i++) { - lp->tx_ring[i].status = 0; /* CPU owns buffer */ - wmb(); /* Make sure adapter sees owner change */ - if (lp->tx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i], - lp->tx_skbuff[i]->len, - PCI_DMA_TODEVICE); - dev_kfree_skb(lp->tx_skbuff[i]); - } - lp->tx_skbuff[i] = NULL; - lp->tx_dma_addr[i] = 0; - } + pcnet32_purge_rx_ring(dev); + pcnet32_purge_tx_ring(dev); spin_unlock_irqrestore(&lp->lock, flags); -- cgit v1.2.3 From 3a10ccebe928691d16a001687552228d32ff7910 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 30 Jun 2006 02:25:06 -0700 Subject: [PATCH] lock validator: fix ns83820.c irq-flags bug Barry K. Nathan reported the following lockdep warning: [ 197.343948] BUG: warning at kernel/lockdep.c:1856/trace_hardirqs_on() [ 197.345928] [] show_trace_log_lvl+0x5b/0x105 [ 197.346359] [] show_trace+0x1b/0x20 [ 197.346759] [] dump_stack+0x1f/0x24 [ 197.347159] [] trace_hardirqs_on+0xfb/0x185 [ 197.348873] [] _spin_unlock_irq+0x24/0x2d [ 197.350620] [] do_tx_done+0x171/0x179 [ns83820] [ 197.350895] [] ns83820_irq+0x149/0x20b [ns83820] [ 197.351166] [] handle_IRQ_event+0x1d/0x52 [ 197.353216] [] handle_level_irq+0x97/0xe1 [ 197.355157] [] do_IRQ+0x8b/0xac [ 197.355612] [] common_interrupt+0x25/0x2c this is caused because the ns83820 driver re-enables irq flags in hardirq context. While legal in theory, in practice it should only be done if the hardware is really old and has some very high overhead in its ISR. (such as PIO IDE) For modern hardware, running ISRs with irqs enabled is discouraged, because 1) new hardware is fast enough to not cause latency problems 2) allowing the nesting of hardware interrupts only 'spreads out' the handling of the current ISR, causing extra cachemisses that would otherwise not happen. Furthermore, on architectures where ISRs share the kernel stacks, enabling interrupts in ISRs introduces a much higher kernel-stack-nesting and thus kernel-stack-overflow risk. 3) not managing irq-flags via the _irqsave / _irqrestore variants is dangerous: it's easy to forget whether one function nests inside another, and irq flags might be mismanaged. In the few cases where re-enabling interrupts in an ISR is considered useful (and unavoidable), it has to be taught to the lock validator explicitly (because the lock validator needs the "no ISR ever enables hardirqs" artificial simplification to keep the IRQ/softirq locking dependencies manageable). This teaching is done via the explicit use local_irq_enable_in_hardirq(). On a stock kernel this maps to local_irq_enable(). If the lock validator is enabled then this does not enable interrupts. Now, the analysis of drivers/net/ns83820.c's irq flags use: the irq-enabling in irq context seems intentional, but i dont think it's justified. Furthermore, the driver suffers from problem #3 above too, in ns83820_tx_timeout() it disables irqs via local_irq_save(), but then it calls do_tx_done() which does a spin_unlock_irq(), re-enabling for a function that does not expect it! While currently this bug seems harmless (only some debug printout seems to be affected by it), it's nevertheless something to be fixed. So this patch makes the ns83820 ISR irq-flags-safe, and cleans up do_tx_done() use and locking to avoid the ns83820_tx_timeout() bug. From: Arjan van de Ven ns83820_mib_isr takes the misc_lock in IRQ context. All other places that do this in the ISR already use _irqsave versions, make this consistent at least. At some point in the future someone should audit the driver to see if all _irqsave's in the ISR can go away, this is generally an iffy/fragile proposition though; for now get it safe, simple and consistent. From: Arjan van de Ven ok this is a real driver deadlock: The ns83820 driver enabled interrupts (by unlocking the misc_lock with _irq) while still holding the rx_info.lock, which is required to be irq safe since it's used in the ISR like this: writel(1, dev->base + IER); spin_unlock_irq(&dev->misc_lock); kick_rx(ndev); spin_unlock_irq(&dev->rx_info.lock); This is can cause a deadlock if an irq was pending at the first spin_unlock_irq already, or if one would hit during kick_rx(). Simply remove the first _irq solves this Signed-off-by: Ingo Molnar Cc: Benjamin LaHaise Cc: Jeff Garzik Signed-off-by: Arjan van de Ven Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- drivers/net/ns83820.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index d0ed8646902f..0e76859c90a2 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -803,7 +803,7 @@ static int ns83820_setup_rx(struct net_device *ndev) writel(dev->IMR_cache, dev->base + IMR); writel(1, dev->base + IER); - spin_unlock_irq(&dev->misc_lock); + spin_unlock(&dev->misc_lock); kick_rx(ndev); @@ -1012,8 +1012,6 @@ static void do_tx_done(struct net_device *ndev) struct ns83820 *dev = PRIV(ndev); u32 cmdsts, tx_done_idx, *desc; - spin_lock_irq(&dev->tx_lock); - dprintk("do_tx_done(%p)\n", ndev); tx_done_idx = dev->tx_done_idx; desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); @@ -1069,7 +1067,6 @@ static void do_tx_done(struct net_device *ndev) netif_start_queue(ndev); netif_wake_queue(ndev); } - spin_unlock_irq(&dev->tx_lock); } static void ns83820_cleanup_tx(struct ns83820 *dev) @@ -1281,11 +1278,13 @@ static struct ethtool_ops ops = { .get_link = ns83820_get_link }; +/* this function is called in irq context from the ISR */ static void ns83820_mib_isr(struct ns83820 *dev) { - spin_lock(&dev->misc_lock); + unsigned long flags; + spin_lock_irqsave(&dev->misc_lock, flags); ns83820_update_stats(dev); - spin_unlock(&dev->misc_lock); + spin_unlock_irqrestore(&dev->misc_lock, flags); } static void ns83820_do_isr(struct net_device *ndev, u32 isr); @@ -1307,6 +1306,8 @@ static irqreturn_t ns83820_irq(int foo, void *data, struct pt_regs *regs) static void ns83820_do_isr(struct net_device *ndev, u32 isr) { struct ns83820 *dev = PRIV(ndev); + unsigned long flags; + #ifdef DEBUG if (isr & ~(ISR_PHY | ISR_RXDESC | ISR_RXEARLY | ISR_RXOK | ISR_RXERR | ISR_TXIDLE | ISR_TXOK | ISR_TXDESC)) Dprintk("odd isr? 0x%08x\n", isr); @@ -1321,10 +1322,10 @@ static void ns83820_do_isr(struct net_device *ndev, u32 isr) if ((ISR_RXDESC | ISR_RXOK) & isr) { prefetch(dev->rx_info.next_rx_desc); - spin_lock_irq(&dev->misc_lock); + spin_lock_irqsave(&dev->misc_lock, flags); dev->IMR_cache &= ~(ISR_RXDESC | ISR_RXOK); writel(dev->IMR_cache, dev->base + IMR); - spin_unlock_irq(&dev->misc_lock); + spin_unlock_irqrestore(&dev->misc_lock, flags); tasklet_schedule(&dev->rx_tasklet); //rx_irq(ndev); @@ -1370,16 +1371,18 @@ static void ns83820_do_isr(struct net_device *ndev, u32 isr) * work has accumulated */ if ((ISR_TXDESC | ISR_TXIDLE | ISR_TXOK | ISR_TXERR) & isr) { + spin_lock_irqsave(&dev->tx_lock, flags); do_tx_done(ndev); + spin_unlock_irqrestore(&dev->tx_lock, flags); /* Disable TxOk if there are no outstanding tx packets. */ if ((dev->tx_done_idx == dev->tx_free_idx) && (dev->IMR_cache & ISR_TXOK)) { - spin_lock_irq(&dev->misc_lock); + spin_lock_irqsave(&dev->misc_lock, flags); dev->IMR_cache &= ~ISR_TXOK; writel(dev->IMR_cache, dev->base + IMR); - spin_unlock_irq(&dev->misc_lock); + spin_unlock_irqrestore(&dev->misc_lock, flags); } } @@ -1390,10 +1393,10 @@ static void ns83820_do_isr(struct net_device *ndev, u32 isr) * nature are expected, we must enable TxOk. */ if ((ISR_TXIDLE & isr) && (dev->tx_done_idx != dev->tx_free_idx)) { - spin_lock_irq(&dev->misc_lock); + spin_lock_irqsave(&dev->misc_lock, flags); dev->IMR_cache |= ISR_TXOK; writel(dev->IMR_cache, dev->base + IMR); - spin_unlock_irq(&dev->misc_lock); + spin_unlock_irqrestore(&dev->misc_lock, flags); } /* MIB interrupt: one of the statistics counters is about to overflow */ @@ -1455,7 +1458,7 @@ static void ns83820_tx_timeout(struct net_device *ndev) u32 tx_done_idx, *desc; unsigned long flags; - local_irq_save(flags); + spin_lock_irqsave(&dev->tx_lock, flags); tx_done_idx = dev->tx_done_idx; desc = dev->tx_descs + (tx_done_idx * DESC_SIZE); @@ -1482,7 +1485,7 @@ static void ns83820_tx_timeout(struct net_device *ndev) ndev->name, tx_done_idx, dev->tx_free_idx, le32_to_cpu(desc[DESC_CMDSTS])); - local_irq_restore(flags); + spin_unlock_irqrestore(&dev->tx_lock, flags); } static void ns83820_tx_watch(unsigned long data) -- cgit v1.2.3 From 5b552b16420d11ec59d301494477713ab5cc1f43 Mon Sep 17 00:00:00 2001 From: Andreas Mohr Date: Fri, 30 Jun 2006 02:25:07 -0700 Subject: [PATCH] NI5010 netcard cleanup - updated MAINTAINERS entry to new format - updated Jan-Pascal's (ACKed) and my email address - driver cleanup/modernization (runtime-, not hardware-tested) [bunk@stusta.de: build fix] Signed-off-by: Andreas Mohr Cc: Jeff Garzik Cc: Jan-Pascal van Best Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- MAINTAINERS | 7 ++++--- drivers/net/ni5010.c | 52 ++++++++++++++++++++++++---------------------------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5f76a4f5cd4b..196a31c97524 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2039,9 +2039,10 @@ L: linux-kernel@vger.kernel.org S: Maintained NI5010 NETWORK DRIVER -P: Jan-Pascal van Best and Andreas Mohr -M: Jan-Pascal van Best -M: Andreas Mohr <100.30936@germany.net> +P: Jan-Pascal van Best +M: janpascal@vanbest.org +P: Andreas Mohr +M: andi@lisas.de L: netdev@vger.kernel.org S: Maintained diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c index a68bf474f6ed..d4be207d321a 100644 --- a/drivers/net/ni5010.c +++ b/drivers/net/ni5010.c @@ -1,17 +1,12 @@ /* ni5010.c: A network driver for the MiCom-Interlan NI5010 ethercard. * - * Copyright 1996,1997 Jan-Pascal van Best and Andreas Mohr. + * Copyright 1996,1997,2006 Jan-Pascal van Best and Andreas Mohr. * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * The authors may be reached as: - * jvbest@wi.leidenuniv.nl a.mohr@mailto.de - * or by snail mail as - * Jan-Pascal van Best Andreas Mohr - * Klikspaanweg 58-4 Stauferstr. 6 - * 2324 LZ Leiden D-71272 Renningen - * The Netherlands Germany + * janpascal@vanbest.org andi@lisas.de * * Sources: * Donald Becker's "skeleton.c" @@ -27,8 +22,9 @@ * 970503 v0.93: Fixed auto-irq failure on warm reboot (JB) * 970623 v1.00: First kernel version (AM) * 970814 v1.01: Added detection of onboard receive buffer size (AM) + * 060611 v1.02: slight cleanup: email addresses, driver modernization. * Bugs: - * - None known... + * - not SMP-safe (no locking of I/O accesses) * - Note that you have to patch ifconfig for the new /proc/net/dev * format. It gives incorrect stats otherwise. * @@ -39,7 +35,7 @@ * Complete merge with Andreas' driver * Implement ring buffers (Is this useful? You can't squeeze * too many packet in a 2k buffer!) - * Implement DMA (Again, is this useful? Some docs says DMA is + * Implement DMA (Again, is this useful? Some docs say DMA is * slower than programmed I/O) * * Compile with: @@ -47,7 +43,7 @@ * -DMODULE -c ni5010.c * * Insert with e.g.: - * insmod ni5010.o io=0x300 irq=5 + * insmod ni5010.ko io=0x300 irq=5 */ #include @@ -69,15 +65,15 @@ #include "ni5010.h" -static const char *boardname = "NI5010"; -static char *version = - "ni5010.c: v1.00 06/23/97 Jan-Pascal van Best and Andreas Mohr\n"; +static const char boardname[] = "NI5010"; +static char version[] __initdata = + "ni5010.c: v1.02 20060611 Jan-Pascal van Best and Andreas Mohr\n"; /* bufsize_rcv == 0 means autoprobing */ static unsigned int bufsize_rcv; -#define jumpered_interrupts /* IRQ line jumpered on board */ -#undef jumpered_dma /* No DMA used */ +#define JUMPERED_INTERRUPTS /* IRQ line jumpered on board */ +#undef JUMPERED_DMA /* No DMA used */ #undef FULL_IODETECT /* Only detect in portlist */ #ifndef FULL_IODETECT @@ -281,7 +277,7 @@ static int __init ni5010_probe1(struct net_device *dev, int ioaddr) PRINTK2((KERN_DEBUG "%s: I/O #4 passed!\n", dev->name)); -#ifdef jumpered_interrupts +#ifdef JUMPERED_INTERRUPTS if (dev->irq == 0xff) ; else if (dev->irq < 2) { @@ -305,7 +301,7 @@ static int __init ni5010_probe1(struct net_device *dev, int ioaddr) } else if (dev->irq == 2) { dev->irq = 9; } -#endif /* jumpered_irq */ +#endif /* JUMPERED_INTERRUPTS */ PRINTK2((KERN_DEBUG "%s: I/O #9 passed!\n", dev->name)); /* DMA is not supported (yet?), so no use detecting it */ @@ -334,7 +330,7 @@ static int __init ni5010_probe1(struct net_device *dev, int ioaddr) outw(0, IE_GP); /* Point GP at start of packet */ outb(0, IE_RBUF); /* set buffer byte 0 to 0 again */ } - printk("// bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE); + printk("-> bufsize rcv/xmt=%d/%d\n", bufsize_rcv, NI5010_BUFSIZE); memset(dev->priv, 0, sizeof(struct ni5010_local)); dev->open = ni5010_open; @@ -354,11 +350,9 @@ static int __init ni5010_probe1(struct net_device *dev, int ioaddr) outb(0xff, EDLC_XCLR); /* Kill all pending xmt interrupts */ printk(KERN_INFO "%s: NI5010 found at 0x%x, using IRQ %d", dev->name, ioaddr, dev->irq); - if (dev->dma) printk(" & DMA %d", dev->dma); + if (dev->dma) + printk(" & DMA %d", dev->dma); printk(".\n"); - - printk(KERN_INFO "Join the NI5010 driver development team!\n"); - printk(KERN_INFO "Mail to a.mohr@mailto.de or jvbest@wi.leidenuniv.nl\n"); return 0; out: release_region(dev->base_addr, NI5010_IO_EXTENT); @@ -371,7 +365,7 @@ out: * * This routine should set everything up anew at each open, even * registers that "should" only need to be set once at boot, so that - * there is non-reboot way to recover if something goes wrong. + * there is a non-reboot way to recover if something goes wrong. */ static int ni5010_open(struct net_device *dev) @@ -390,13 +384,13 @@ static int ni5010_open(struct net_device *dev) * Always allocate the DMA channel after the IRQ, * and clean up on failure. */ -#ifdef jumpered_dma +#ifdef JUMPERED_DMA if (request_dma(dev->dma, cardname)) { printk(KERN_WARNING "%s: Cannot get dma %#2x\n", dev->name, dev->dma); free_irq(dev->irq, NULL); return -EAGAIN; } -#endif /* jumpered_dma */ +#endif /* JUMPERED_DMA */ PRINTK3((KERN_DEBUG "%s: passed open() #2\n", dev->name)); /* Reset the hardware here. Don't forget to set the station address. */ @@ -633,7 +627,7 @@ static int ni5010_close(struct net_device *dev) int ioaddr = dev->base_addr; PRINTK2((KERN_DEBUG "%s: entering ni5010_close\n", dev->name)); -#ifdef jumpered_interrupts +#ifdef JUMPERED_INTERRUPTS free_irq(dev->irq, NULL); #endif /* Put card in held-RESET state */ @@ -771,7 +765,7 @@ module_param(irq, int, 0); MODULE_PARM_DESC(io, "ni5010 I/O base address"); MODULE_PARM_DESC(irq, "ni5010 IRQ number"); -int init_module(void) +static int __init ni5010_init_module(void) { PRINTK2((KERN_DEBUG "%s: entering init_module\n", boardname)); /* @@ -792,13 +786,15 @@ int init_module(void) return 0; } -void cleanup_module(void) +static void __exit ni5010_cleanup_module(void) { PRINTK2((KERN_DEBUG "%s: entering cleanup_module\n", boardname)); unregister_netdev(dev_ni5010); release_region(dev_ni5010->base_addr, NI5010_IO_EXTENT); free_netdev(dev_ni5010); } +module_init(ni5010_init_module); +module_exit(ni5010_cleanup_module); #endif /* MODULE */ MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 22db99bd32646aa33051e23420ff3517cc70cad5 Mon Sep 17 00:00:00 2001 From: Paul Fulghum Date: Fri, 30 Jun 2006 02:27:19 -0700 Subject: [PATCH] remove dead entry in net wan Kconfig Remove dead entry from net wan Kconfig and net wan Makefile.. This entry is left over from 2.4 where synclink used syncppp driver directly. synclink drivers now use generic HDLC Signed-off-by: Paul Fulghum Signed-off-by: Andrew Morton Signed-off-by: Jeff Garzik --- drivers/net/wan/Kconfig | 12 ------------ drivers/net/wan/Makefile | 1 - 2 files changed, 13 deletions(-) diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index b5328b0ff927..54b8e492ef97 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -134,18 +134,6 @@ config SEALEVEL_4021 The driver will be compiled as a module: the module will be called sealevel. -config SYNCLINK_SYNCPPP - tristate "SyncLink HDLC/SYNCPPP support" - depends on WAN - help - Enables HDLC/SYNCPPP support for the SyncLink WAN driver. - - Normally the SyncLink WAN driver works with the main PPP driver - and pppd program. - HDLC/SYNCPPP support allows use of the Cisco HDLC/PPP driver - . The SyncLink WAN driver (in - character devices) must also be enabled. - # Generic HDLC config HDLC tristate "Generic HDLC layer" diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile index 823c6d5ab90d..316ca6869d5e 100644 --- a/drivers/net/wan/Makefile +++ b/drivers/net/wan/Makefile @@ -28,7 +28,6 @@ obj-$(CONFIG_COSA) += syncppp.o cosa.o obj-$(CONFIG_FARSYNC) += syncppp.o farsync.o obj-$(CONFIG_DSCC4) += dscc4.o obj-$(CONFIG_LANMEDIA) += syncppp.o -obj-$(CONFIG_SYNCLINK_SYNCPPP) += syncppp.o obj-$(CONFIG_X25_ASY) += x25_asy.o obj-$(CONFIG_LANMEDIA) += lmc/ -- cgit v1.2.3 From b07db75a9f542c1a1745fcb047fd743843db8daa Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Fri, 30 Jun 2006 13:56:13 +0100 Subject: [PATCH] Fix freeing of net device Plus optical sugar. Signed-off-by: Ralf Baechle Signed-off-by: Jeff Garzik --- drivers/net/declance.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/declance.c b/drivers/net/declance.c index 2038ca7e49ce..6ad5796121c8 100644 --- a/drivers/net/declance.c +++ b/drivers/net/declance.c @@ -703,8 +703,8 @@ static irqreturn_t lance_dma_merr_int(const int irq, void *dev_id, return IRQ_HANDLED; } -static irqreturn_t -lance_interrupt(const int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t lance_interrupt(const int irq, void *dev_id, + struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_id; struct lance_private *lp = netdev_priv(dev); @@ -1253,7 +1253,7 @@ static int __init dec_lance_init(const int type, const int slot) return 0; err_out_free_dev: - kfree(dev); + free_netdev(dev); err_out: return ret; @@ -1299,6 +1299,7 @@ static void __exit dec_lance_cleanup(void) while (root_lance_dev) { struct net_device *dev = root_lance_dev; struct lance_private *lp = netdev_priv(dev); + unregister_netdev(dev); #ifdef CONFIG_TC if (lp->slot >= 0) -- cgit v1.2.3 From ec590970f0f45a11c06b9525cd718dbdde22b4a3 Mon Sep 17 00:00:00 2001 From: "brice@myri.com" Date: Mon, 3 Jul 2006 18:02:31 -0400 Subject: [PATCH] myri10ge - Drop unused pm_state The pm_state field in the myri10ge_priv structure is unused. Drop it. Signed-off-by: Brice Goglin Signed-off-by: Jeff Garzik --- drivers/net/myri10ge/myri10ge.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 72aad42db7b4..82ee0ed62cd9 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -188,7 +188,6 @@ struct myri10ge_priv { int vendor_specific_offset; u32 devctl; u16 msi_flags; - u32 pm_state[16]; u32 read_dma; u32 write_dma; u32 read_write_dma; -- cgit v1.2.3 From 18ac544306855aa366d34d29755f4596acd7a58d Mon Sep 17 00:00:00 2001 From: "brice@myri.com" Date: Mon, 3 Jul 2006 18:02:32 -0400 Subject: [PATCH] myri10ge - Drop ununsed nvidia chipset id The workaround for the AER capability of the nVidia chipset has been removed, we don't need this PCI id anymore. Drop it. Signed-off-by: Brice Goglin Signed-off-by: Jeff Garzik --- drivers/net/myri10ge/myri10ge.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 82ee0ed62cd9..8345e4f25793 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -2196,8 +2196,6 @@ static int myri10ge_change_mtu(struct net_device *dev, int new_mtu) * any other device, except if forced with myri10ge_ecrc_enable > 1. */ -#define PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_PCIE 0x005d - static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp) { struct pci_dev *bridge = mgp->pdev->bus->self; -- cgit v1.2.3 From d6020787488e837d3c2b24eb1b8cf9849058d340 Mon Sep 17 00:00:00 2001 From: Brice Goglin Date: Mon, 3 Jul 2006 18:41:30 -0400 Subject: [PATCH] myri10ge - Use dev_info() when printing parameters after probe Displaying the interface name when listing the device parameters at the end of myri10ge_probe is not a good idea since udev might rename the interface soon afterwards. Print the bus id instead, using dev_info(). Signed-off-by: Brice Goglin Signed-off-by: Jeff Garzik --- drivers/net/myri10ge/myri10ge.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 8345e4f25793..abb5dd57c73f 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -2734,11 +2734,10 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_err(&pdev->dev, "register_netdev failed: %d\n", status); goto abort_with_irq; } - - printk(KERN_INFO "myri10ge: %s: %s IRQ %d, tx bndry %d, fw %s, WC %s\n", - netdev->name, (mgp->msi_enabled ? "MSI" : "xPIC"), - pdev->irq, mgp->tx.boundary, mgp->fw_name, - (mgp->mtrr >= 0 ? "Enabled" : "Disabled")); + dev_info(dev, "%s IRQ %d, tx bndry %d, fw %s, WC %s\n", + (mgp->msi_enabled ? "MSI" : "xPIC"), + pdev->irq, mgp->tx.boundary, mgp->fw_name, + (mgp->mtrr >= 0 ? "Enabled" : "Disabled")); return 0; -- cgit v1.2.3 From 2c1a108890c5b57cf3f7d7909f55c4fae0f52f19 Mon Sep 17 00:00:00 2001 From: Brice Goglin Date: Mon, 3 Jul 2006 18:16:46 -0400 Subject: [PATCH] myri10ge - Export more parameters to ethtool Add the IRQ line, the tx_boundary, and whether Write-combining and MSI are enabled to the list of parameters that are exported to ethtool. Signed-off-by: Brice Goglin Signed-off-by: Jeff Garzik --- drivers/net/myri10ge/myri10ge.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index abb5dd57c73f..f4c8fd373b9b 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -1288,6 +1288,7 @@ static const char myri10ge_gstrings_stats[][ETH_GSTRING_LEN] = { "tx_aborted_errors", "tx_carrier_errors", "tx_fifo_errors", "tx_heartbeat_errors", "tx_window_errors", /* device-specific stats */ + "tx_boundary", "WC", "irq", "MSI", "read_dma_bw_MBs", "write_dma_bw_MBs", "read_write_dma_bw_MBs", "serial_number", "tx_pkt_start", "tx_pkt_done", "tx_req", "tx_done", "rx_small_cnt", "rx_big_cnt", @@ -1326,6 +1327,10 @@ myri10ge_get_ethtool_stats(struct net_device *netdev, for (i = 0; i < MYRI10GE_NET_STATS_LEN; i++) data[i] = ((unsigned long *)&mgp->stats)[i]; + data[i++] = (unsigned int)mgp->tx.boundary; + data[i++] = (unsigned int)(mgp->mtrr >= 0); + data[i++] = (unsigned int)mgp->pdev->irq; + data[i++] = (unsigned int)mgp->msi_enabled; data[i++] = (unsigned int)mgp->read_dma; data[i++] = (unsigned int)mgp->write_dma; data[i++] = (unsigned int)mgp->read_write_dma; -- cgit v1.2.3 From 20ed7c094dfe33b0e15e8c60f60012b9278631d3 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 5 Jul 2006 14:28:34 -0400 Subject: [netdrvr] 3c59x: snip changelog from source code Driver source code is not the preferred place to store change history. Acked-by: Andrew Morton Signed-off-by: Jeff Garzik --- drivers/net/3c59x.c | 166 ---------------------------------------------------- 1 file changed, 166 deletions(-) diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 2819de79442c..80e8ca013e44 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -17,172 +17,6 @@ 410 Severn Ave., Suite 210 Annapolis MD 21403 - Linux Kernel Additions: - - 0.99H+lk0.9 - David S. Miller - softnet, PCI DMA updates - 0.99H+lk1.0 - Jeff Garzik - Remove compatibility defines for kernel versions < 2.2.x. - Update for new 2.3.x module interface - LK1.1.2 (March 19, 2000) - * New PCI interface (jgarzik) - - LK1.1.3 25 April 2000, Andrew Morton - - Merged with 3c575_cb.c - - Don't set RxComplete in boomerang interrupt enable reg - - spinlock in vortex_timer to protect mdio functions - - disable local interrupts around call to vortex_interrupt in - vortex_tx_timeout() (So vortex_interrupt can use spin_lock()) - - Select window 3 in vortex_timer()'s write to Wn3_MAC_Ctrl - - In vortex_start_xmit(), move the lock to _after_ we've altered - vp->cur_tx and vp->tx_full. This defeats the race between - vortex_start_xmit() and vortex_interrupt which was identified - by Bogdan Costescu. - - Merged back support for six new cards from various sources - - Set vortex_have_pci if pci_module_init returns zero (fixes cardbus - insertion oops) - - Tell it that 3c905C has NWAY for 100bT autoneg - - Fix handling of SetStatusEnd in 'Too much work..' code, as - per 2.3.99's 3c575_cb (Dave Hinds). - - Split ISR into two for vortex & boomerang - - Fix MOD_INC/DEC races - - Handle resource allocation failures. - - Fix 3CCFE575CT LED polarity - - Make tx_interrupt_mitigation the default - - LK1.1.4 25 April 2000, Andrew Morton - - Add extra TxReset to vortex_up() to fix 575_cb hotplug initialisation probs. - - Put vortex_info_tbl into __devinitdata - - In the vortex_error StatsFull HACK, disable stats in vp->intr_enable as well - as in the hardware. - - Increased the loop counter in issue_and_wait from 2,000 to 4,000. - - LK1.1.5 28 April 2000, andrewm - - Added powerpc defines (John Daniel said these work...) - - Some extra diagnostics - - In vortex_error(), reset the Tx on maxCollisions. Otherwise most - chips usually get a Tx timeout. - - Added extra_reset module parm - - Replaced some inline timer manip with mod_timer - (Franois romieu ) - - In vortex_up(), don't make Wn3_config initialisation dependent upon has_nway - (this came across from 3c575_cb). - - LK1.1.6 06 Jun 2000, andrewm - - Backed out the PPC defines. - - Use del_timer_sync(), mod_timer(). - - Fix wrapped ulong comparison in boomerang_rx() - - Add IS_TORNADO, use it to suppress 3c905C checksum error msg - (Donald Becker, I Lee Hetherington ) - - Replace union wn3_config with BFINS/BFEXT manipulation for - sparc64 (Pete Zaitcev, Peter Jones) - - In vortex_error, do_tx_reset and vortex_tx_timeout(Vortex): - do a netif_wake_queue() to better recover from errors. (Anders Pedersen, - Donald Becker) - - Print a warning on out-of-memory (rate limited to 1 per 10 secs) - - Added two more Cardbus 575 NICs: 5b57 and 6564 (Paul Wagland) - - LK1.1.7 2 Jul 2000 andrewm - - Better handling of shared IRQs - - Reset the transmitter on a Tx reclaim error - - Fixed crash under OOM during vortex_open() (Mark Hemment) - - Fix Rx cessation problem during OOM (help from Mark Hemment) - - The spinlocks around the mdio access were blocking interrupts for 300uS. - Fix all this to use spin_lock_bh() within mdio_read/write - - Only write to TxFreeThreshold if it's a boomerang - other NICs don't - have one. - - Added 802.3x MAC-layer flow control support - - LK1.1.8 13 Aug 2000 andrewm - - Ignore request_region() return value - already reserved if Cardbus. - - Merged some additional Cardbus flags from Don's 0.99Qk - - Some fixes for 3c556 (Fred Maciel) - - Fix for EISA initialisation (Jan Rekorajski) - - Renamed MII_XCVR_PWR and EEPROM_230 to align with 3c575_cb and D. Becker's drivers - - Fixed MII_XCVR_PWR for 3CCFE575CT - - Added INVERT_LED_PWR, used it. - - Backed out the extra_reset stuff - - LK1.1.9 12 Sep 2000 andrewm - - Backed out the tx_reset_resume flags. It was a no-op. - - In vortex_error, don't reset the Tx on txReclaim errors - - In vortex_error, don't reset the Tx on maxCollisions errors. - Hence backed out all the DownListPtr logic here. - - In vortex_error, give Tornado cards a partial TxReset on - maxCollisions (David Hinds). Defined MAX_COLLISION_RESET for this. - - Redid some driver flags and device names based on pcmcia_cs-3.1.20. - - Fixed a bug where, if vp->tx_full is set when the interface - is downed, it remains set when the interface is upped. Bad - things happen. - - LK1.1.10 17 Sep 2000 andrewm - - Added EEPROM_8BIT for 3c555 (Fred Maciel) - - Added experimental support for the 3c556B Laptop Hurricane (Louis Gerbarg) - - Add HAS_NWAY to "3c900 Cyclone 10Mbps TPO" - - LK1.1.11 13 Nov 2000 andrewm - - Dump MOD_INC/DEC_USE_COUNT, use SET_MODULE_OWNER - - LK1.1.12 1 Jan 2001 andrewm (2.4.0-pre1) - - Call pci_enable_device before we request our IRQ (Tobias Ringstrom) - - Add 3c590 PCI latency timer hack to vortex_probe1 (from 0.99Ra) - - Added extended issue_and_wait for the 3c905CX. - - Look for an MII on PHY index 24 first (3c905CX oddity). - - Add HAS_NWAY to 3cSOHO100-TX (Brett Frankenberger) - - Don't free skbs we don't own on oom path in vortex_open(). - - LK1.1.13 27 Jan 2001 - - Added explicit `medialock' flag so we can truly - lock the media type down with `options'. - - "check ioremap return and some tidbits" (Arnaldo Carvalho de Melo ) - - Added and used EEPROM_NORESET for 3c556B PM resumes. - - Fixed leakage of vp->rx_ring. - - Break out separate HAS_HWCKSM device capability flag. - - Kill vp->tx_full (ANK) - - Merge zerocopy fragment handling (ANK?) - - LK1.1.14 15 Feb 2001 - - Enable WOL. Can be turned on with `enable_wol' module option. - - EISA and PCI initialisation fixes (jgarzik, Manfred Spraul) - - If a device's internalconfig register reports it has NWAY, - use it, even if autoselect is enabled. - - LK1.1.15 6 June 2001 akpm - - Prevent double counting of received bytes (Lars Christensen) - - Add ethtool support (jgarzik) - - Add module parm descriptions (Andrzej M. Krzysztofowicz) - - Implemented alloc_etherdev() API - - Special-case the 'Tx error 82' message. - - LK1.1.16 18 July 2001 akpm - - Make NETIF_F_SG dependent upon nr_free_highpages(), not on CONFIG_HIGHMEM - - Lessen verbosity of bootup messages - - Fix WOL - use new PM API functions. - - Use netif_running() instead of vp->open in suspend/resume. - - Don't reset the interface logic on open/close/rmmod. It upsets - autonegotiation, and hence DHCP (from 0.99T). - - Back out EEPROM_NORESET flag because of the above (we do it for all - NICs). - - Correct 3c982 identification string - - Rename wait_for_completion() to issue_and_wait() to avoid completion.h - clash. - - LK1.1.17 18Dec01 akpm - - PCI ID 9805 is a Python-T, not a dual-port Cyclone. Apparently. - And it has NWAY. - - Mask our advertised modes (vp->advertising) with our capabilities - (MII reg5) when deciding which duplex mode to use. - - Add `global_options' as default for options[]. Ditto global_enable_wol, - global_full_duplex. - - LK1.1.18 01Jul02 akpm - - Fix for undocumented transceiver power-up bit on some 3c566B's - (Donald Becker, Rahul Karnik) - - - See http://www.zip.com.au/~akpm/linux/#3c59x-2.3 for more details. - - Also see Documentation/networking/vortex.txt - - LK1.1.19 10Nov02 Marc Zyngier - - EISA sysfs integration. */ /* -- cgit v1.2.3 From bce305f4fe779f29d99d414685243f5da0803254 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Wed, 5 Jul 2006 15:00:40 +0200 Subject: [PATCH] 8139too deadlock fix > stack backtrace: > [] rtl8139_start_xmit+0xd9/0xff [8139too] > [] netpoll_send_skb+0x98/0xea This seems to be a real deadlock... So netpoll_send_skb takes the _xmit_lock, which is all nitty gritty but then rtl8139_start_xmit comes around while that lock is taken, and does spin_unlock_irq(&tp->lock); which.. enables interrupts and softirqs; this is quite bad because the xmit lock is taken in softirq context for the watchdog like this: [] _spin_lock+0x23/0x32 [] dev_watchdog+0x14/0xb1 [] run_timer_softirq+0xf2/0x14a [] __do_softirq+0x55/0xb0 [] do_softirq+0x58/0xbd Which would deadlock now that the spin_unlock_irq() has enabled irqs/softirqs while the _xmit_lock is still held. The patch below turns this into a irqsave/irqrestore pair so that interrupts don't get enabled unconditionally. Signed-off-by: Arjan van de Ven Signed-off-by: Ingo Molnar Signed-off-by: Jeff Garzik --- drivers/net/8139too.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index cd9718512d1c..e4f4eaff7679 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -1709,6 +1709,7 @@ static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) void __iomem *ioaddr = tp->mmio_addr; unsigned int entry; unsigned int len = skb->len; + unsigned long flags; /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % NUM_TX_DESC; @@ -1725,7 +1726,7 @@ static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) return 0; } - spin_lock_irq(&tp->lock); + spin_lock_irqsave(&tp->lock, flags); RTL_W32_F (TxStatus0 + (entry * sizeof (u32)), tp->tx_flag | max(len, (unsigned int)ETH_ZLEN)); @@ -1736,7 +1737,7 @@ static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) netif_stop_queue (dev); - spin_unlock_irq(&tp->lock); + spin_unlock_irqrestore(&tp->lock, flags); if (netif_msg_tx_queued(tp)) printk (KERN_DEBUG "%s: Queued Tx packet size %u to slot %d.\n", -- cgit v1.2.3 From a93620b860434a19820072e656f4933e101ea6d4 Mon Sep 17 00:00:00 2001 From: Martin Hicks Date: Wed, 28 Jun 2006 12:17:47 -0400 Subject: [PATCH] sata_vsc: data_xfer should use mmio Hi, sata_vsc is an MMIO device, and should use the correct data_xfer function. This problem was introduced by: commit a6b2c5d4754dc539a560fdf0d3fb78a14174394a Author: Alan Cox Date: Mon May 22 16:59:59 2006 +0100 [PATCH] PATCH: libata. Add ->data_xfer method Signed-off-by: Martin Hicks Signed-off-by: Jeff Garzik --- drivers/scsi/sata_vsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c index 916fe6fba756..ad37871594f5 100644 --- a/drivers/scsi/sata_vsc.c +++ b/drivers/scsi/sata_vsc.c @@ -297,7 +297,7 @@ static const struct ata_port_operations vsc_sata_ops = { .bmdma_status = ata_bmdma_status, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, - .data_xfer = ata_pio_data_xfer, + .data_xfer = ata_mmio_data_xfer, .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, -- cgit v1.2.3 From e6d902a3bfd53da375588e498251f4f4f6cd9650 Mon Sep 17 00:00:00 2001 From: Brian King Date: Wed, 28 Jun 2006 08:30:31 -0500 Subject: [PATCH] libata: Conditionally set host->max_cmd_len In preparation for SAS attached SATA devices, which will not have a libata scsi_host, only setup host->max_cmd_len if ap->host exists. Signed-off-by: Brian King Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 1c960ac1617f..fd0e6cc0619f 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1325,6 +1325,19 @@ static void ata_dev_config_ncq(struct ata_device *dev, snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth); } +static void ata_set_port_max_cmd_len(struct ata_port *ap) +{ + int i; + + if (ap->host) { + ap->host->max_cmd_len = 0; + for (i = 0; i < ATA_MAX_DEVICES; i++) + ap->host->max_cmd_len = max_t(unsigned int, + ap->host->max_cmd_len, + ap->device[i].cdb_len); + } +} + /** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure @@ -1344,7 +1357,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info) struct ata_port *ap = dev->ap; const u16 *id = dev->id; unsigned int xfer_mask; - int i, rc; + int rc; if (!ata_dev_enabled(dev) && ata_msg_info(ap)) { ata_dev_printk(dev, KERN_INFO, @@ -1474,11 +1487,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info) cdb_intr_string); } - ap->host->max_cmd_len = 0; - for (i = 0; i < ATA_MAX_DEVICES; i++) - ap->host->max_cmd_len = max_t(unsigned int, - ap->host->max_cmd_len, - ap->device[i].cdb_len); + ata_set_port_max_cmd_len(ap); /* limit bridge transfers to udma5, 200 sectors */ if (ata_dev_knobble(dev)) { -- cgit v1.2.3 From b51e9e5db0e36239f786692f1cac6e435ed30c66 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 29 Jun 2006 01:29:30 +0900 Subject: [PATCH] libata: add ap->pflags and move core dynamic flags to it ap->flags is way too clamped. Separate out core dynamic flags to ap->pflags. ATA_FLAG_DISABLED is a dynamic flag but left alone as it's referenced by a lot of LLDs and it's gonna be removed once all LLDs are converted to new EH. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 2 +- drivers/scsi/libata-core.c | 24 +++++++++++----------- drivers/scsi/libata-eh.c | 50 +++++++++++++++++++++++----------------------- drivers/scsi/libata-scsi.c | 2 +- drivers/scsi/sata_sil.c | 2 +- include/linux/libata.h | 30 +++++++++++++++++----------- 6 files changed, 58 insertions(+), 52 deletions(-) diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 15f6cd4279b7..7447ba0374e8 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -1052,7 +1052,7 @@ static void ahci_thaw(struct ata_port *ap) static void ahci_error_handler(struct ata_port *ap) { - if (!(ap->flags & ATA_FLAG_FROZEN)) { + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ ahci_stop_engine(ap); ahci_start_engine(ap); diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index fd0e6cc0619f..ccab1d7ceada 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -907,7 +907,7 @@ void ata_port_queue_task(struct ata_port *ap, void (*fn)(void *), void *data, { int rc; - if (ap->flags & ATA_FLAG_FLUSH_PORT_TASK) + if (ap->pflags & ATA_PFLAG_FLUSH_PORT_TASK) return; PREPARE_WORK(&ap->port_task, fn, data); @@ -938,7 +938,7 @@ void ata_port_flush_task(struct ata_port *ap) DPRINTK("ENTER\n"); spin_lock_irqsave(ap->lock, flags); - ap->flags |= ATA_FLAG_FLUSH_PORT_TASK; + ap->pflags |= ATA_PFLAG_FLUSH_PORT_TASK; spin_unlock_irqrestore(ap->lock, flags); DPRINTK("flush #1\n"); @@ -957,7 +957,7 @@ void ata_port_flush_task(struct ata_port *ap) } spin_lock_irqsave(ap->lock, flags); - ap->flags &= ~ATA_FLAG_FLUSH_PORT_TASK; + ap->pflags &= ~ATA_PFLAG_FLUSH_PORT_TASK; spin_unlock_irqrestore(ap->lock, flags); if (ata_msg_ctl(ap)) @@ -1009,7 +1009,7 @@ unsigned ata_exec_internal(struct ata_device *dev, spin_lock_irqsave(ap->lock, flags); /* no internal command while frozen */ - if (ap->flags & ATA_FLAG_FROZEN) { + if (ap->pflags & ATA_PFLAG_FROZEN) { spin_unlock_irqrestore(ap->lock, flags); return AC_ERR_SYSTEM; } @@ -2641,7 +2641,7 @@ int ata_std_prereset(struct ata_port *ap) /* if SATA, resume phy */ if (ap->cbl == ATA_CBL_SATA) { - if (ap->flags & ATA_FLAG_LOADING) + if (ap->pflags & ATA_PFLAG_LOADING) timing = sata_deb_timing_boot; else timing = sata_deb_timing_eh; @@ -4294,7 +4294,7 @@ static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) unsigned int i; /* no command while frozen */ - if (unlikely(ap->flags & ATA_FLAG_FROZEN)) + if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) return NULL; /* the last tag is reserved for internal command. */ @@ -4416,7 +4416,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc) * taken care of. */ if (ap->ops->error_handler) { - WARN_ON(ap->flags & ATA_FLAG_FROZEN); + WARN_ON(ap->pflags & ATA_PFLAG_FROZEN); if (unlikely(qc->err_mask)) qc->flags |= ATA_QCFLAG_FAILED; @@ -5051,13 +5051,13 @@ int ata_device_resume(struct ata_device *dev) { struct ata_port *ap = dev->ap; - if (ap->flags & ATA_FLAG_SUSPENDED) { + if (ap->pflags & ATA_PFLAG_SUSPENDED) { struct ata_device *failed_dev; ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 200000); - ap->flags &= ~ATA_FLAG_SUSPENDED; + ap->pflags &= ~ATA_PFLAG_SUSPENDED; while (ata_set_mode(ap, &failed_dev)) ata_dev_disable(failed_dev); } @@ -5088,7 +5088,7 @@ int ata_device_suspend(struct ata_device *dev, pm_message_t state) if (state.event != PM_EVENT_FREEZE) ata_standby_drive(dev); - ap->flags |= ATA_FLAG_SUSPENDED; + ap->pflags |= ATA_PFLAG_SUSPENDED; return 0; } @@ -5459,7 +5459,7 @@ int ata_device_add(const struct ata_probe_ent *ent) ap->eh_info.probe_mask = (1 << ATA_MAX_DEVICES) - 1; ap->eh_info.action |= ATA_EH_SOFTRESET; - ap->flags |= ATA_FLAG_LOADING; + ap->pflags |= ATA_PFLAG_LOADING; ata_port_schedule_eh(ap); spin_unlock_irqrestore(ap->lock, flags); @@ -5527,7 +5527,7 @@ void ata_port_detach(struct ata_port *ap) /* tell EH we're leaving & flush EH */ spin_lock_irqsave(ap->lock, flags); - ap->flags |= ATA_FLAG_UNLOADING; + ap->pflags |= ATA_PFLAG_UNLOADING; spin_unlock_irqrestore(ap->lock, flags); ata_port_wait_eh(ap); diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index bf5a72aca8a4..f2f29a8bc38e 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c @@ -270,13 +270,13 @@ void ata_scsi_error(struct Scsi_Host *host) ap->eh_context.i = ap->eh_info; memset(&ap->eh_info, 0, sizeof(ap->eh_info)); - ap->flags |= ATA_FLAG_EH_IN_PROGRESS; - ap->flags &= ~ATA_FLAG_EH_PENDING; + ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS; + ap->pflags &= ~ATA_PFLAG_EH_PENDING; spin_unlock_irqrestore(ap_lock, flags); /* invoke EH. if unloading, just finish failed qcs */ - if (!(ap->flags & ATA_FLAG_UNLOADING)) + if (!(ap->pflags & ATA_PFLAG_UNLOADING)) ap->ops->error_handler(ap); else ata_eh_finish(ap); @@ -287,7 +287,7 @@ void ata_scsi_error(struct Scsi_Host *host) */ spin_lock_irqsave(ap_lock, flags); - if (ap->flags & ATA_FLAG_EH_PENDING) { + if (ap->pflags & ATA_PFLAG_EH_PENDING) { if (--repeat_cnt) { ata_port_printk(ap, KERN_INFO, "EH pending after completion, " @@ -323,19 +323,19 @@ void ata_scsi_error(struct Scsi_Host *host) /* clean up */ spin_lock_irqsave(ap_lock, flags); - if (ap->flags & ATA_FLAG_LOADING) { - ap->flags &= ~ATA_FLAG_LOADING; + if (ap->pflags & ATA_PFLAG_LOADING) { + ap->pflags &= ~ATA_PFLAG_LOADING; } else { - if (ap->flags & ATA_FLAG_SCSI_HOTPLUG) + if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG) queue_work(ata_aux_wq, &ap->hotplug_task); - if (ap->flags & ATA_FLAG_RECOVERED) + if (ap->pflags & ATA_PFLAG_RECOVERED) ata_port_printk(ap, KERN_INFO, "EH complete\n"); } - ap->flags &= ~(ATA_FLAG_SCSI_HOTPLUG | ATA_FLAG_RECOVERED); + ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED); /* tell wait_eh that we're done */ - ap->flags &= ~ATA_FLAG_EH_IN_PROGRESS; + ap->pflags &= ~ATA_PFLAG_EH_IN_PROGRESS; wake_up_all(&ap->eh_wait_q); spin_unlock_irqrestore(ap_lock, flags); @@ -360,7 +360,7 @@ void ata_port_wait_eh(struct ata_port *ap) retry: spin_lock_irqsave(ap->lock, flags); - while (ap->flags & (ATA_FLAG_EH_PENDING | ATA_FLAG_EH_IN_PROGRESS)) { + while (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) { prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); spin_unlock_irqrestore(ap->lock, flags); schedule(); @@ -489,7 +489,7 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc) WARN_ON(!ap->ops->error_handler); qc->flags |= ATA_QCFLAG_FAILED; - qc->ap->flags |= ATA_FLAG_EH_PENDING; + qc->ap->pflags |= ATA_PFLAG_EH_PENDING; /* The following will fail if timeout has already expired. * ata_scsi_error() takes care of such scmds on EH entry. @@ -513,7 +513,7 @@ void ata_port_schedule_eh(struct ata_port *ap) { WARN_ON(!ap->ops->error_handler); - ap->flags |= ATA_FLAG_EH_PENDING; + ap->pflags |= ATA_PFLAG_EH_PENDING; scsi_schedule_eh(ap->host); DPRINTK("port EH scheduled\n"); @@ -578,7 +578,7 @@ static void __ata_port_freeze(struct ata_port *ap) if (ap->ops->freeze) ap->ops->freeze(ap); - ap->flags |= ATA_FLAG_FROZEN; + ap->pflags |= ATA_PFLAG_FROZEN; DPRINTK("ata%u port frozen\n", ap->id); } @@ -646,7 +646,7 @@ void ata_eh_thaw_port(struct ata_port *ap) spin_lock_irqsave(ap->lock, flags); - ap->flags &= ~ATA_FLAG_FROZEN; + ap->pflags &= ~ATA_PFLAG_FROZEN; if (ap->ops->thaw) ap->ops->thaw(ap); @@ -731,7 +731,7 @@ static void ata_eh_detach_dev(struct ata_device *dev) if (ata_scsi_offline_dev(dev)) { dev->flags |= ATA_DFLAG_DETACHED; - ap->flags |= ATA_FLAG_SCSI_HOTPLUG; + ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; } /* clear per-dev EH actions */ @@ -761,7 +761,7 @@ static void ata_eh_about_to_do(struct ata_port *ap, struct ata_device *dev, spin_lock_irqsave(ap->lock, flags); ata_eh_clear_action(dev, &ap->eh_info, action); - ap->flags |= ATA_FLAG_RECOVERED; + ap->pflags |= ATA_PFLAG_RECOVERED; spin_unlock_irqrestore(ap->lock, flags); } @@ -1027,7 +1027,7 @@ static void ata_eh_analyze_ncq_error(struct ata_port *ap) int tag, rc; /* if frozen, we can't do much */ - if (ap->flags & ATA_FLAG_FROZEN) + if (ap->pflags & ATA_PFLAG_FROZEN) return; /* is it NCQ device error? */ @@ -1327,7 +1327,7 @@ static void ata_eh_autopsy(struct ata_port *ap) } /* enforce default EH actions */ - if (ap->flags & ATA_FLAG_FROZEN || + if (ap->pflags & ATA_PFLAG_FROZEN || all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT)) action |= ATA_EH_SOFTRESET; else if (all_err_mask) @@ -1385,7 +1385,7 @@ static void ata_eh_report(struct ata_port *ap) return; frozen = ""; - if (ap->flags & ATA_FLAG_FROZEN) + if (ap->pflags & ATA_PFLAG_FROZEN) frozen = " frozen"; if (ehc->i.dev) { @@ -1465,7 +1465,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, struct ata_eh_context *ehc = &ap->eh_context; unsigned int *classes = ehc->classes; int tries = ATA_EH_RESET_TRIES; - int verbose = !(ap->flags & ATA_FLAG_LOADING); + int verbose = !(ap->pflags & ATA_PFLAG_LOADING); unsigned int action; ata_reset_fn_t reset; int i, did_followup_srst, rc; @@ -1636,7 +1636,7 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, } spin_lock_irqsave(ap->lock, flags); - ap->flags |= ATA_FLAG_SCSI_HOTPLUG; + ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; spin_unlock_irqrestore(ap->lock, flags); } } @@ -1673,7 +1673,7 @@ static int ata_eh_skip_recovery(struct ata_port *ap) struct ata_eh_context *ehc = &ap->eh_context; int i; - if (ap->flags & ATA_FLAG_FROZEN || ata_port_nr_enabled(ap)) + if (ap->pflags & ATA_PFLAG_FROZEN || ata_port_nr_enabled(ap)) return 0; /* skip if class codes for all vacant slots are ATA_DEV_NONE */ @@ -1744,7 +1744,7 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, rc = 0; /* if UNLOADING, finish immediately */ - if (ap->flags & ATA_FLAG_UNLOADING) + if (ap->pflags & ATA_PFLAG_UNLOADING) goto out; /* skip EH if possible. */ @@ -1908,7 +1908,7 @@ void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) { - if (!(ap->flags & ATA_FLAG_LOADING)) { + if (!(ap->pflags & ATA_PFLAG_LOADING)) { ata_eh_autopsy(ap); ata_eh_report(ap); } diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 2915bca691e8..153452e77264 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -2930,7 +2930,7 @@ void ata_scsi_hotplug(void *data) struct ata_port *ap = data; int i; - if (ap->flags & ATA_FLAG_UNLOADING) { + if (ap->pflags & ATA_PFLAG_UNLOADING) { DPRINTK("ENTER/EXIT - unloading\n"); return; } diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index 7aabb45c35e5..70dbfb8a7d51 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -370,7 +370,7 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2) * during hardreset makes controllers with broken SIEN * repeat probing needlessly. */ - if (!(ap->flags & ATA_FLAG_FROZEN)) { + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { ata_ehi_hotplugged(&ap->eh_info); ap->eh_info.serror |= serror; } diff --git a/include/linux/libata.h b/include/linux/libata.h index f4284bf89758..b5d247d780f8 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -160,22 +160,27 @@ enum { ATA_FLAG_HRST_TO_RESUME = (1 << 11), /* hardreset to resume phy */ ATA_FLAG_SKIP_D2H_BSY = (1 << 12), /* can't wait for the first D2H * Register FIS clearing BSY */ - ATA_FLAG_DEBUGMSG = (1 << 13), - ATA_FLAG_FLUSH_PORT_TASK = (1 << 14), /* flush port task */ - ATA_FLAG_EH_PENDING = (1 << 15), /* EH pending */ - ATA_FLAG_EH_IN_PROGRESS = (1 << 16), /* EH in progress */ - ATA_FLAG_FROZEN = (1 << 17), /* port is frozen */ - ATA_FLAG_RECOVERED = (1 << 18), /* recovery action performed */ - ATA_FLAG_LOADING = (1 << 19), /* boot/loading probe */ - ATA_FLAG_UNLOADING = (1 << 20), /* module is unloading */ - ATA_FLAG_SCSI_HOTPLUG = (1 << 21), /* SCSI hotplug scheduled */ + /* The following flag belongs to ap->pflags but is kept in + * ap->flags because it's referenced in many LLDs and will be + * removed in not-too-distant future. + */ + ATA_FLAG_DISABLED = (1 << 23), /* port is disabled, ignore it */ + + /* bits 24:31 of ap->flags are reserved for LLD specific flags */ - ATA_FLAG_DISABLED = (1 << 22), /* port is disabled, ignore it */ - ATA_FLAG_SUSPENDED = (1 << 23), /* port is suspended (power) */ + /* struct ata_port pflags */ + ATA_PFLAG_EH_PENDING = (1 << 0), /* EH pending */ + ATA_PFLAG_EH_IN_PROGRESS = (1 << 1), /* EH in progress */ + ATA_PFLAG_FROZEN = (1 << 2), /* port is frozen */ + ATA_PFLAG_RECOVERED = (1 << 3), /* recovery action performed */ + ATA_PFLAG_LOADING = (1 << 4), /* boot/loading probe */ + ATA_PFLAG_UNLOADING = (1 << 5), /* module is unloading */ + ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), /* SCSI hotplug scheduled */ - /* bits 24:31 of ap->flags are reserved for LLDD specific flags */ + ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */ + ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ /* struct ata_queued_cmd flags */ ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */ @@ -486,6 +491,7 @@ struct ata_port { const struct ata_port_operations *ops; spinlock_t *lock; unsigned long flags; /* ATA_FLAG_xxx */ + unsigned int pflags; /* ATA_PFLAG_xxx */ unsigned int id; /* unique id req'd by scsi midlyr */ unsigned int port_no; /* unique port #; from zero */ unsigned int hard_port_no; /* hardware port #; from zero */ -- cgit v1.2.3 From 0662c58b3265f52f708a6d59476bc7862b01f9c0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 02:54:58 +0900 Subject: [PATCH] libata: fix ehc->i.action setting in ata_eh_autopsy() ata_eh_autopsy() used to directly assign determined action mask to ehc->i.action thus overriding actions set by some of nested analyze functions. This patch makes ata_eh_autopsy() add action masks just as it's done in other places. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-eh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index f2f29a8bc38e..4a670db9aa05 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c @@ -1346,7 +1346,7 @@ static void ata_eh_autopsy(struct ata_port *ap) /* record autopsy result */ ehc->i.dev = failed_dev; - ehc->i.action = action; + ehc->i.action |= action; DPRINTK("EXIT\n"); } -- cgit v1.2.3 From e30349d27e093f32ef517b5416d9dce1998d4676 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 03:02:15 +0900 Subject: [PATCH] libata: replace ap_lock w/ ap->lock in ata_scsi_error() ap_lock was used because &ap->host_set->lock was too long and used a lot. Now that &ap->host_set->lock is replaced with ap->lock, there's no reason to keep ap_lock. [ed. note: that's not entirely true. ap_lock is a local variable, caching the results of a de-ref. In theory, if the compiler is smart enough, this patch is cosmetic. However, since this is not a fast path (it is the error path), this patch is nonetheless acceptable, even though it _may_ introduce a performance regression.] Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-eh.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 4a670db9aa05..d19666c376ad 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c @@ -190,7 +190,6 @@ enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) void ata_scsi_error(struct Scsi_Host *host) { struct ata_port *ap = ata_shost_to_port(host); - spinlock_t *ap_lock = ap->lock; int i, repeat_cnt = ATA_EH_MAX_REPEAT; unsigned long flags; @@ -217,7 +216,7 @@ void ata_scsi_error(struct Scsi_Host *host) struct scsi_cmnd *scmd, *tmp; int nr_timedout = 0; - spin_lock_irqsave(ap_lock, flags); + spin_lock_irqsave(ap->lock, flags); list_for_each_entry_safe(scmd, tmp, &host->eh_cmd_q, eh_entry) { struct ata_queued_cmd *qc; @@ -256,15 +255,15 @@ void ata_scsi_error(struct Scsi_Host *host) if (nr_timedout) __ata_port_freeze(ap); - spin_unlock_irqrestore(ap_lock, flags); + spin_unlock_irqrestore(ap->lock, flags); } else - spin_unlock_wait(ap_lock); + spin_unlock_wait(ap->lock); repeat: /* invoke error handler */ if (ap->ops->error_handler) { /* fetch & clear EH info */ - spin_lock_irqsave(ap_lock, flags); + spin_lock_irqsave(ap->lock, flags); memset(&ap->eh_context, 0, sizeof(ap->eh_context)); ap->eh_context.i = ap->eh_info; @@ -273,7 +272,7 @@ void ata_scsi_error(struct Scsi_Host *host) ap->pflags |= ATA_PFLAG_EH_IN_PROGRESS; ap->pflags &= ~ATA_PFLAG_EH_PENDING; - spin_unlock_irqrestore(ap_lock, flags); + spin_unlock_irqrestore(ap->lock, flags); /* invoke EH. if unloading, just finish failed qcs */ if (!(ap->pflags & ATA_PFLAG_UNLOADING)) @@ -285,14 +284,14 @@ void ata_scsi_error(struct Scsi_Host *host) * recovered the port but before this point. Repeat * EH in such case. */ - spin_lock_irqsave(ap_lock, flags); + spin_lock_irqsave(ap->lock, flags); if (ap->pflags & ATA_PFLAG_EH_PENDING) { if (--repeat_cnt) { ata_port_printk(ap, KERN_INFO, "EH pending after completion, " "repeating EH (cnt=%d)\n", repeat_cnt); - spin_unlock_irqrestore(ap_lock, flags); + spin_unlock_irqrestore(ap->lock, flags); goto repeat; } ata_port_printk(ap, KERN_ERR, "EH pending after %d " @@ -302,14 +301,14 @@ void ata_scsi_error(struct Scsi_Host *host) /* this run is complete, make sure EH info is clear */ memset(&ap->eh_info, 0, sizeof(ap->eh_info)); - /* Clear host_eh_scheduled while holding ap_lock such + /* Clear host_eh_scheduled while holding ap->lock such * that if exception occurs after this point but * before EH completion, SCSI midlayer will * re-initiate EH. */ host->host_eh_scheduled = 0; - spin_unlock_irqrestore(ap_lock, flags); + spin_unlock_irqrestore(ap->lock, flags); } else { WARN_ON(ata_qc_from_tag(ap, ap->active_tag) == NULL); ap->ops->eng_timeout(ap); @@ -321,7 +320,7 @@ void ata_scsi_error(struct Scsi_Host *host) scsi_eh_flush_done_q(&ap->eh_done_q); /* clean up */ - spin_lock_irqsave(ap_lock, flags); + spin_lock_irqsave(ap->lock, flags); if (ap->pflags & ATA_PFLAG_LOADING) { ap->pflags &= ~ATA_PFLAG_LOADING; @@ -338,7 +337,7 @@ void ata_scsi_error(struct Scsi_Host *host) ap->pflags &= ~ATA_PFLAG_EH_IN_PROGRESS; wake_up_all(&ap->eh_wait_q); - spin_unlock_irqrestore(ap_lock, flags); + spin_unlock_irqrestore(ap->lock, flags); DPRINTK("EXIT\n"); } -- cgit v1.2.3 From 28324304350e23db24d679c55de3f06a5b1e40aa Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:26 +0900 Subject: [PATCH] libata: implement ATA_EHI_RESUME_LINK Implement ATA_EHI_RESUME_LINK, which indicates that the link needs to be resumed. This used to be implied by ATA_EHI_HOTPLUGGED. However, hotplug isn't the only event which requires link resume and separating this out allows other places to request link resume. This differentiation also allows better debounce timing selection. This patch converts user scan to use ATA_EHI_RESUME_LINK. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 15 ++++++++------- drivers/scsi/libata-scsi.c | 1 + include/linux/libata.h | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index ccab1d7ceada..90db054fa994 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -2627,13 +2627,14 @@ int ata_std_prereset(struct ata_port *ap) const unsigned long *timing; int rc; - /* hotplug? */ - if (ehc->i.flags & ATA_EHI_HOTPLUGGED) { - if (ap->flags & ATA_FLAG_HRST_TO_RESUME) - ehc->i.action |= ATA_EH_HARDRESET; - if (ap->flags & ATA_FLAG_SKIP_D2H_BSY) - ata_wait_spinup(ap); - } + /* handle link resume & hotplug spinup */ + if ((ehc->i.flags & ATA_EHI_RESUME_LINK) && + (ap->flags & ATA_FLAG_HRST_TO_RESUME)) + ehc->i.action |= ATA_EH_HARDRESET; + + if ((ehc->i.flags & ATA_EHI_HOTPLUGGED) && + (ap->flags & ATA_FLAG_SKIP_D2H_BSY)) + ata_wait_spinup(ap); /* if we're about to do hardreset, nothing more to do */ if (ehc->i.action & ATA_EH_HARDRESET) diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 153452e77264..ba3e515f2b9d 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -3011,6 +3011,7 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, if (dev) { ap->eh_info.probe_mask |= 1 << dev->devno; ap->eh_info.action |= ATA_EH_SOFTRESET; + ap->eh_info.flags |= ATA_EHI_RESUME_LINK; } else rc = -EINVAL; } diff --git a/include/linux/libata.h b/include/linux/libata.h index b5d247d780f8..4d4ed2c8fec7 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -259,6 +259,7 @@ enum { /* ata_eh_info->flags */ ATA_EHI_HOTPLUGGED = (1 << 0), /* could have been hotplugged */ + ATA_EHI_RESUME_LINK = (1 << 1), /* need to resume link */ ATA_EHI_DID_RESET = (1 << 16), /* already reset this port */ @@ -836,7 +837,7 @@ static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi) if (ehi->flags & ATA_EHI_HOTPLUGGED) return; - ehi->flags |= ATA_EHI_HOTPLUGGED; + ehi->flags |= ATA_EHI_HOTPLUGGED | ATA_EHI_RESUME_LINK; ehi->hotplug_timestamp = jiffies; ehi->err_mask |= AC_ERR_ATA_BUS; -- cgit v1.2.3 From e9c839142d698086d3fe33a0daafde55ddd00c4e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:26 +0900 Subject: [PATCH] libata: clean up debounce parameters and improve parameter selection The names of predefined debounce timing parameters didn't exactly match their usages. Rename to more generic names and implement param selection helper sata_ehc_deb_timing() which uses EHI_HOTPLUGGED to select params. Combined with the previous EHI_RESUME_LINK differentiation, this makes parameter selection accurate. e.g. user scan resumes link but normal deb param is used instead of hotplug param. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 25 +++++++++++-------------- drivers/scsi/sata_sil24.c | 2 +- include/linux/libata.h | 15 ++++++++++++--- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 90db054fa994..73174452d1c1 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -61,9 +61,9 @@ #include "libata.h" /* debounce timing parameters in msecs { interval, duration, timeout } */ -const unsigned long sata_deb_timing_boot[] = { 5, 100, 2000 }; -const unsigned long sata_deb_timing_eh[] = { 25, 500, 2000 }; -const unsigned long sata_deb_timing_before_fsrst[] = { 100, 2000, 5000 }; +const unsigned long sata_deb_timing_normal[] = { 5, 100, 2000 }; +const unsigned long sata_deb_timing_hotplug[] = { 25, 500, 2000 }; +const unsigned long sata_deb_timing_long[] = { 100, 2000, 5000 }; static unsigned int ata_dev_init_params(struct ata_device *dev, u16 heads, u16 sectors); @@ -2588,7 +2588,7 @@ static void ata_wait_spinup(struct ata_port *ap) /* first, debounce phy if SATA */ if (ap->cbl == ATA_CBL_SATA) { - rc = sata_phy_debounce(ap, sata_deb_timing_eh); + rc = sata_phy_debounce(ap, sata_deb_timing_hotplug); /* if debounced successfully and offline, no need to wait */ if ((rc == 0 || rc == -EOPNOTSUPP) && ata_port_offline(ap)) @@ -2624,7 +2624,7 @@ static void ata_wait_spinup(struct ata_port *ap) int ata_std_prereset(struct ata_port *ap) { struct ata_eh_context *ehc = &ap->eh_context; - const unsigned long *timing; + const unsigned long *timing = sata_ehc_deb_timing(ehc); int rc; /* handle link resume & hotplug spinup */ @@ -2642,11 +2642,6 @@ int ata_std_prereset(struct ata_port *ap) /* if SATA, resume phy */ if (ap->cbl == ATA_CBL_SATA) { - if (ap->pflags & ATA_PFLAG_LOADING) - timing = sata_deb_timing_boot; - else - timing = sata_deb_timing_eh; - rc = sata_phy_resume(ap, timing); if (rc && rc != -EOPNOTSUPP) { /* phy resume failed */ @@ -2734,6 +2729,8 @@ int ata_std_softreset(struct ata_port *ap, unsigned int *classes) */ int sata_std_hardreset(struct ata_port *ap, unsigned int *class) { + struct ata_eh_context *ehc = &ap->eh_context; + const unsigned long *timing = sata_ehc_deb_timing(ehc); u32 scontrol; int rc; @@ -2771,7 +2768,7 @@ int sata_std_hardreset(struct ata_port *ap, unsigned int *class) msleep(1); /* bring phy back */ - sata_phy_resume(ap, sata_deb_timing_eh); + sata_phy_resume(ap, timing); /* TODO: phy layer with polling, timeouts, etc. */ if (ata_port_offline(ap)) { @@ -5852,9 +5849,9 @@ u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, * Do not depend on ABI/API stability. */ -EXPORT_SYMBOL_GPL(sata_deb_timing_boot); -EXPORT_SYMBOL_GPL(sata_deb_timing_eh); -EXPORT_SYMBOL_GPL(sata_deb_timing_before_fsrst); +EXPORT_SYMBOL_GPL(sata_deb_timing_normal); +EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); +EXPORT_SYMBOL_GPL(sata_deb_timing_long); EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_std_ports); EXPORT_SYMBOL_GPL(ata_device_add); diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c index 07a1c6a8a414..04ae1ef25484 100644 --- a/drivers/scsi/sata_sil24.c +++ b/drivers/scsi/sata_sil24.c @@ -607,7 +607,7 @@ static int sil24_hardreset(struct ata_port *ap, unsigned int *class) /* SStatus oscillates between zero and valid status after * DEV_RST, debounce it. */ - rc = sata_phy_debounce(ap, sata_deb_timing_before_fsrst); + rc = sata_phy_debounce(ap, sata_deb_timing_long); if (rc) { reason = "PHY debouncing failed"; goto err; diff --git a/include/linux/libata.h b/include/linux/libata.h index 4d4ed2c8fec7..2f7bbfc0c41b 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -629,9 +629,18 @@ struct ata_timing { #define FIT(v,vmin,vmax) max_t(short,min_t(short,v,vmax),vmin) -extern const unsigned long sata_deb_timing_boot[]; -extern const unsigned long sata_deb_timing_eh[]; -extern const unsigned long sata_deb_timing_before_fsrst[]; +extern const unsigned long sata_deb_timing_normal[]; +extern const unsigned long sata_deb_timing_hotplug[]; +extern const unsigned long sata_deb_timing_long[]; + +static inline const unsigned long * +sata_ehc_deb_timing(struct ata_eh_context *ehc) +{ + if (ehc->i.flags & ATA_EHI_HOTPLUGGED) + return sata_deb_timing_hotplug; + else + return sata_deb_timing_normal; +} extern void ata_port_probe(struct ata_port *); extern void __sata_phy_reset(struct ata_port *ap); -- cgit v1.2.3 From 1cdaf534f829b8759ba30f97d5e8dceb2ab77ba4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:26 +0900 Subject: [PATCH] libata: implement ATA_EHI_NO_AUTOPSY and QUIET Implement ATA_EHI_NO_AUTOPSY and QUIET. These used to be implied by ATA_PFLAG_LOADING, but new power management and PMP support need to use these separately. e.g. Suspend/resume operations shouldn't print full EH messages and resume shouldn't be recorded as an error. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 6 ++++-- drivers/scsi/libata-eh.c | 31 +++++++++++++++++-------------- include/linux/libata.h | 2 ++ 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 73174452d1c1..f368536f8e91 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5447,6 +5447,7 @@ int ata_device_add(const struct ata_probe_ent *ent) } if (ap->ops->error_handler) { + struct ata_eh_info *ehi = &ap->eh_info; unsigned long flags; ata_port_probe(ap); @@ -5454,8 +5455,9 @@ int ata_device_add(const struct ata_probe_ent *ent) /* kick EH for boot probing */ spin_lock_irqsave(ap->lock, flags); - ap->eh_info.probe_mask = (1 << ATA_MAX_DEVICES) - 1; - ap->eh_info.action |= ATA_EH_SOFTRESET; + ehi->probe_mask = (1 << ATA_MAX_DEVICES) - 1; + ehi->action |= ATA_EH_SOFTRESET; + ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; ap->pflags |= ATA_PFLAG_LOADING; ata_port_schedule_eh(ap); diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index d19666c376ad..1e9e73d13485 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c @@ -322,14 +322,13 @@ void ata_scsi_error(struct Scsi_Host *host) /* clean up */ spin_lock_irqsave(ap->lock, flags); - if (ap->pflags & ATA_PFLAG_LOADING) { + if (ap->pflags & ATA_PFLAG_LOADING) ap->pflags &= ~ATA_PFLAG_LOADING; - } else { - if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG) - queue_work(ata_aux_wq, &ap->hotplug_task); - if (ap->pflags & ATA_PFLAG_RECOVERED) - ata_port_printk(ap, KERN_INFO, "EH complete\n"); - } + else if (ap->pflags & ATA_PFLAG_SCSI_HOTPLUG) + queue_work(ata_aux_wq, &ap->hotplug_task); + + if (ap->pflags & ATA_PFLAG_RECOVERED) + ata_port_printk(ap, KERN_INFO, "EH complete\n"); ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED); @@ -759,8 +758,12 @@ static void ata_eh_about_to_do(struct ata_port *ap, struct ata_device *dev, unsigned long flags; spin_lock_irqsave(ap->lock, flags); + ata_eh_clear_action(dev, &ap->eh_info, action); - ap->pflags |= ATA_PFLAG_RECOVERED; + + if (!(ap->eh_context.i.flags & ATA_EHI_QUIET)) + ap->pflags |= ATA_PFLAG_RECOVERED; + spin_unlock_irqrestore(ap->lock, flags); } @@ -1274,6 +1277,9 @@ static void ata_eh_autopsy(struct ata_port *ap) DPRINTK("ENTER\n"); + if (ehc->i.flags & ATA_EHI_NO_AUTOPSY) + return; + /* obtain and analyze SError */ rc = sata_scr_read(ap, SCR_ERROR, &serror); if (rc == 0) { @@ -1464,7 +1470,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, struct ata_eh_context *ehc = &ap->eh_context; unsigned int *classes = ehc->classes; int tries = ATA_EH_RESET_TRIES; - int verbose = !(ap->pflags & ATA_PFLAG_LOADING); + int verbose = !(ehc->i.flags & ATA_EHI_QUIET); unsigned int action; ata_reset_fn_t reset; int i, did_followup_srst, rc; @@ -1907,11 +1913,8 @@ void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset) { - if (!(ap->pflags & ATA_PFLAG_LOADING)) { - ata_eh_autopsy(ap); - ata_eh_report(ap); - } - + ata_eh_autopsy(ap); + ata_eh_report(ap); ata_eh_recover(ap, prereset, softreset, hardreset, postreset); ata_eh_finish(ap); } diff --git a/include/linux/libata.h b/include/linux/libata.h index 2f7bbfc0c41b..36938ae1254d 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -260,6 +260,8 @@ enum { /* ata_eh_info->flags */ ATA_EHI_HOTPLUGGED = (1 << 0), /* could have been hotplugged */ ATA_EHI_RESUME_LINK = (1 << 1), /* need to resume link */ + ATA_EHI_NO_AUTOPSY = (1 << 2), /* no autopsy */ + ATA_EHI_QUIET = (1 << 3), /* be quiet */ ATA_EHI_DID_RESET = (1 << 16), /* already reset this port */ -- cgit v1.2.3 From c0b6c0377c32fe3f6a2cf1e018db6da8a3b78379 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:26 +0900 Subject: [PATCH] libata: separate out __ata_ehi_hotplugged() Separate out __ata_ehi_hotplugged() from ata_ehi_hotplugged(). The underscored version doesn't set AC_ERR_ATA_BUS. This will be used for resume which is a hotplug event but not an ATA bus error. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- include/linux/libata.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/linux/libata.h b/include/linux/libata.h index 36938ae1254d..2aa1398bbd52 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -843,7 +843,7 @@ extern void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, (ehi)->desc_len = 0; \ } while (0) -static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi) +static inline void __ata_ehi_hotplugged(struct ata_eh_info *ehi) { if (ehi->flags & ATA_EHI_HOTPLUGGED) return; @@ -851,11 +851,16 @@ static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi) ehi->flags |= ATA_EHI_HOTPLUGGED | ATA_EHI_RESUME_LINK; ehi->hotplug_timestamp = jiffies; - ehi->err_mask |= AC_ERR_ATA_BUS; ehi->action |= ATA_EH_SOFTRESET; ehi->probe_mask |= (1 << ATA_MAX_DEVICES) - 1; } +static inline void ata_ehi_hotplugged(struct ata_eh_info *ehi) +{ + __ata_ehi_hotplugged(ehi); + ehi->err_mask |= AC_ERR_ATA_BUS; +} + /* * qc helpers */ -- cgit v1.2.3 From 02670bf379267f55a43aa57f6895689697e90eb3 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:26 +0900 Subject: [PATCH] libata: implement PM EH actions Implement two PM per-dev EH actions - ATA_EH_SUSPEND and ATA_EH_RESUME. Each action puts the target device into suspended mode and resumes from it respectively. Once a device is put to suspended mode, no EH operations other than RESUME is allowed on the device. The device will stay suspended till it gets resumed and thus reset and revalidated. To implement this, a new device state helper - ata_dev_ready() - is implemented and used in EH action implementations to make them operate only on attached & running devices. If all possible devices on a port are suspended, reset is skipped too. This prevents spurious events including hotplug events from disrupting suspended devices. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 5 +- drivers/scsi/libata-eh.c | 185 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/libata.h | 12 ++- 3 files changed, 198 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index f368536f8e91..ad5cac79627c 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -2146,7 +2146,7 @@ int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) * return error code and failing device on failure. */ for (i = 0; i < ATA_MAX_DEVICES; i++) { - if (ata_dev_enabled(&ap->device[i])) { + if (ata_dev_ready(&ap->device[i])) { ap->ops->set_mode(ap); break; } @@ -2212,7 +2212,8 @@ int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) for (i = 0; i < ATA_MAX_DEVICES; i++) { dev = &ap->device[i]; - if (!ata_dev_enabled(dev)) + /* don't udpate suspended devices' xfer mode */ + if (!ata_dev_ready(dev)) continue; rc = ata_dev_set_mode(dev); diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 1e9e73d13485..b9df49a36214 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c @@ -1610,7 +1610,7 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, dev = &ap->device[i]; action = ata_eh_dev_action(dev); - if (action & ATA_EH_REVALIDATE && ata_dev_enabled(dev)) { + if (action & ATA_EH_REVALIDATE && ata_dev_ready(dev)) { if (ata_port_offline(ap)) { rc = -EIO; break; @@ -1653,6 +1653,164 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, return rc; } +/** + * ata_eh_suspend - handle suspend EH action + * @ap: target host port + * @r_failed_dev: result parameter to indicate failing device + * + * Handle suspend EH action. Disk devices are spinned down and + * other types of devices are just marked suspended. Once + * suspended, no EH action to the device is allowed until it is + * resumed. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise + */ +static int ata_eh_suspend(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + struct ata_device *dev; + int i, rc = 0; + + DPRINTK("ENTER\n"); + + for (i = 0; i < ATA_MAX_DEVICES; i++) { + unsigned long flags; + unsigned int action, err_mask; + + dev = &ap->device[i]; + action = ata_eh_dev_action(dev); + + if (!ata_dev_enabled(dev) || !(action & ATA_EH_SUSPEND)) + continue; + + WARN_ON(dev->flags & ATA_DFLAG_SUSPENDED); + + ata_eh_about_to_do(ap, dev, ATA_EH_SUSPEND); + + if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) { + /* flush cache */ + rc = ata_flush_cache(dev); + if (rc) + break; + + /* spin down */ + err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1); + if (err_mask) { + ata_dev_printk(dev, KERN_ERR, "failed to " + "spin down (err_mask=0x%x)\n", + err_mask); + rc = -EIO; + break; + } + } + + spin_lock_irqsave(ap->lock, flags); + dev->flags |= ATA_DFLAG_SUSPENDED; + spin_unlock_irqrestore(ap->lock, flags); + + ata_eh_done(ap, dev, ATA_EH_SUSPEND); + } + + if (rc) + *r_failed_dev = dev; + + DPRINTK("EXIT\n"); + return 0; +} + +/** + * ata_eh_prep_resume - prep for resume EH action + * @ap: target host port + * + * Clear SUSPENDED in preparation for scheduled resume actions. + * This allows other parts of EH to access the devices being + * resumed. + * + * LOCKING: + * Kernel thread context (may sleep). + */ +static void ata_eh_prep_resume(struct ata_port *ap) +{ + struct ata_device *dev; + unsigned long flags; + int i; + + DPRINTK("ENTER\n"); + + for (i = 0; i < ATA_MAX_DEVICES; i++) { + unsigned int action; + + dev = &ap->device[i]; + action = ata_eh_dev_action(dev); + + if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME)) + continue; + + spin_lock_irqsave(ap->lock, flags); + dev->flags &= ~ATA_DFLAG_SUSPENDED; + spin_unlock_irqrestore(ap->lock, flags); + } + + DPRINTK("EXIT\n"); +} + +/** + * ata_eh_resume - handle resume EH action + * @ap: target host port + * @r_failed_dev: result parameter to indicate failing device + * + * Handle resume EH action. Target devices are already reset and + * revalidated. Spinning up is the only operation left. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise + */ +static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + struct ata_device *dev; + int i, rc = 0; + + DPRINTK("ENTER\n"); + + for (i = 0; i < ATA_MAX_DEVICES; i++) { + unsigned int action, err_mask; + + dev = &ap->device[i]; + action = ata_eh_dev_action(dev); + + if (!ata_dev_enabled(dev) || !(action & ATA_EH_RESUME)) + continue; + + ata_eh_about_to_do(ap, dev, ATA_EH_RESUME); + + if (dev->class == ATA_DEV_ATA && !(action & ATA_EH_PM_FREEZE)) { + err_mask = ata_do_simple_cmd(dev, + ATA_CMD_IDLEIMMEDIATE); + if (err_mask) { + ata_dev_printk(dev, KERN_ERR, "failed to " + "spin up (err_mask=0x%x)\n", + err_mask); + rc = -EIO; + break; + } + } + + ata_eh_done(ap, dev, ATA_EH_RESUME); + } + + if (rc) + *r_failed_dev = dev; + + DPRINTK("EXIT\n"); + return 0; +} + static int ata_port_nr_enabled(struct ata_port *ap) { int i, cnt = 0; @@ -1678,6 +1836,18 @@ static int ata_eh_skip_recovery(struct ata_port *ap) struct ata_eh_context *ehc = &ap->eh_context; int i; + /* skip if all possible devices are suspended */ + for (i = 0; i < ata_port_max_devices(ap); i++) { + struct ata_device *dev = &ap->device[i]; + + if (ata_dev_absent(dev) || ata_dev_ready(dev)) + break; + } + + if (i == ata_port_max_devices(ap)) + return 1; + + /* always thaw frozen port and recover failed devices */ if (ap->pflags & ATA_PFLAG_FROZEN || ata_port_nr_enabled(ap)) return 0; @@ -1752,6 +1922,9 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, if (ap->pflags & ATA_PFLAG_UNLOADING) goto out; + /* prep for resume */ + ata_eh_prep_resume(ap); + /* skip EH if possible. */ if (ata_eh_skip_recovery(ap)) ehc->i.action = 0; @@ -1779,6 +1952,11 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, if (rc) goto dev_fail; + /* resume devices */ + rc = ata_eh_resume(ap, &dev); + if (rc) + goto dev_fail; + /* configure transfer mode if the port has been reset */ if (ehc->i.flags & ATA_EHI_DID_RESET) { rc = ata_set_mode(ap, &dev); @@ -1788,6 +1966,11 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, } } + /* suspend devices */ + rc = ata_eh_suspend(ap, &dev); + if (rc) + goto dev_fail; + goto out; dev_fail: diff --git a/include/linux/libata.h b/include/linux/libata.h index 2aa1398bbd52..363c7501843a 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -131,6 +131,7 @@ enum { ATA_DFLAG_CFG_MASK = (1 << 8) - 1, ATA_DFLAG_PIO = (1 << 8), /* device currently in PIO mode */ + ATA_DFLAG_SUSPENDED = (1 << 9), /* device suspended */ ATA_DFLAG_INIT_MASK = (1 << 16) - 1, ATA_DFLAG_DETACH = (1 << 16), @@ -253,9 +254,13 @@ enum { ATA_EH_REVALIDATE = (1 << 0), ATA_EH_SOFTRESET = (1 << 1), ATA_EH_HARDRESET = (1 << 2), + ATA_EH_SUSPEND = (1 << 3), + ATA_EH_RESUME = (1 << 4), + ATA_EH_PM_FREEZE = (1 << 5), ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET, - ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE, + ATA_EH_PERDEV_MASK = ATA_EH_REVALIDATE | ATA_EH_SUSPEND | + ATA_EH_RESUME | ATA_EH_PM_FREEZE, /* ata_eh_info->flags */ ATA_EHI_HOTPLUGGED = (1 << 0), /* could have been hotplugged */ @@ -944,6 +949,11 @@ static inline unsigned int ata_dev_absent(const struct ata_device *dev) return ata_class_absent(dev->class); } +static inline unsigned int ata_dev_ready(const struct ata_device *dev) +{ + return ata_dev_enabled(dev) && !(dev->flags & ATA_DFLAG_SUSPENDED); +} + /* * port helpers */ -- cgit v1.2.3 From d6f26d1f1f1128a896f38a7f8426daed0a1205a2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:26 +0900 Subject: [PATCH] libata: reimplement per-dev PM Reimplement per-dev PM. The original implementation directly put the device into suspended mode and didn't synchronize w/ EH operations including hotplug. This patch reimplements ata_scsi_device_suspend() and ata_scsi_device_resume() such that they request EH to perform the respective operations. Both functions synchronize with hotplug such that it doesn't operate on detached devices. Suspend waits for completion but resume just issues request and returns. This allows parallel wake up of devices and thus speeds up system resume. Due to sdev detach synchronization, it's not feasible to separate out EH requesting from sdev handling; thus, ata_device_suspend/resume() are removed and everything is implemented in the respective libata-scsi functions. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 84 ------------------------------- drivers/scsi/libata-scsi.c | 121 ++++++++++++++++++++++++++++++++++++++++++--- include/linux/libata.h | 2 - 3 files changed, 115 insertions(+), 92 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index ad5cac79627c..51dbc5221934 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5009,88 +5009,6 @@ int ata_flush_cache(struct ata_device *dev) return 0; } -static int ata_standby_drive(struct ata_device *dev) -{ - unsigned int err_mask; - - err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1); - if (err_mask) { - ata_dev_printk(dev, KERN_ERR, "failed to standby drive " - "(err_mask=0x%x)\n", err_mask); - return -EIO; - } - - return 0; -} - -static int ata_start_drive(struct ata_device *dev) -{ - unsigned int err_mask; - - err_mask = ata_do_simple_cmd(dev, ATA_CMD_IDLEIMMEDIATE); - if (err_mask) { - ata_dev_printk(dev, KERN_ERR, "failed to start drive " - "(err_mask=0x%x)\n", err_mask); - return -EIO; - } - - return 0; -} - -/** - * ata_device_resume - wakeup a previously suspended devices - * @dev: the device to resume - * - * Kick the drive back into action, by sending it an idle immediate - * command and making sure its transfer mode matches between drive - * and host. - * - */ -int ata_device_resume(struct ata_device *dev) -{ - struct ata_port *ap = dev->ap; - - if (ap->pflags & ATA_PFLAG_SUSPENDED) { - struct ata_device *failed_dev; - - ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); - ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 200000); - - ap->pflags &= ~ATA_PFLAG_SUSPENDED; - while (ata_set_mode(ap, &failed_dev)) - ata_dev_disable(failed_dev); - } - if (!ata_dev_enabled(dev)) - return 0; - if (dev->class == ATA_DEV_ATA) - ata_start_drive(dev); - - return 0; -} - -/** - * ata_device_suspend - prepare a device for suspend - * @dev: the device to suspend - * @state: target power management state - * - * Flush the cache on the drive, if appropriate, then issue a - * standbynow command. - */ -int ata_device_suspend(struct ata_device *dev, pm_message_t state) -{ - struct ata_port *ap = dev->ap; - - if (!ata_dev_enabled(dev)) - return 0; - if (dev->class == ATA_DEV_ATA) - ata_flush_cache(dev); - - if (state.event != PM_EVENT_FREEZE) - ata_standby_drive(dev); - ap->pflags |= ATA_PFLAG_SUSPENDED; - return 0; -} - /** * ata_port_start - Set port up for dma. * @ap: Port to initialize @@ -5946,8 +5864,6 @@ EXPORT_SYMBOL_GPL(ata_pci_default_filter); EXPORT_SYMBOL_GPL(ata_pci_clear_simplex); #endif /* CONFIG_PCI */ -EXPORT_SYMBOL_GPL(ata_device_suspend); -EXPORT_SYMBOL_GPL(ata_device_resume); EXPORT_SYMBOL_GPL(ata_scsi_device_suspend); EXPORT_SYMBOL_GPL(ata_scsi_device_resume); diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index ba3e515f2b9d..7ced41ecde86 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -397,20 +397,129 @@ void ata_dump_status(unsigned id, struct ata_taskfile *tf) } } -int ata_scsi_device_resume(struct scsi_device *sdev) +/** + * ata_scsi_device_suspend - suspend ATA device associated with sdev + * @sdev: the SCSI device to suspend + * @state: target power management state + * + * Request suspend EH action on the ATA device associated with + * @sdev and wait for the operation to complete. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state) { struct ata_port *ap = ata_shost_to_port(sdev->host); - struct ata_device *dev = __ata_scsi_find_dev(ap, sdev); + struct ata_device *dev = ata_scsi_find_dev(ap, sdev); + unsigned long flags; + unsigned int action; + int rc = 0; + + if (!dev) + goto out; + + spin_lock_irqsave(ap->lock, flags); + + /* wait for the previous resume to complete */ + while (dev->flags & ATA_DFLAG_SUSPENDED) { + spin_unlock_irqrestore(ap->lock, flags); + ata_port_wait_eh(ap); + spin_lock_irqsave(ap->lock, flags); + } + + /* if @sdev is already detached, nothing to do */ + if (sdev->sdev_state == SDEV_OFFLINE || + sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) + goto out_unlock; + + /* request suspend */ + action = ATA_EH_SUSPEND; + if (state.event != PM_EVENT_SUSPEND) + action |= ATA_EH_PM_FREEZE; + ap->eh_info.dev_action[dev->devno] |= action; + ap->eh_info.flags |= ATA_EHI_QUIET; + ata_port_schedule_eh(ap); + + spin_unlock_irqrestore(ap->lock, flags); + + /* wait for EH to do the job */ + ata_port_wait_eh(ap); + + spin_lock_irqsave(ap->lock, flags); + + /* If @sdev is still attached but the associated ATA device + * isn't suspended, the operation failed. + */ + if (sdev->sdev_state != SDEV_OFFLINE && + sdev->sdev_state != SDEV_CANCEL && sdev->sdev_state != SDEV_DEL && + !(dev->flags & ATA_DFLAG_SUSPENDED)) + rc = -EIO; - return ata_device_resume(dev); + out_unlock: + spin_unlock_irqrestore(ap->lock, flags); + out: + if (rc == 0) + sdev->sdev_gendev.power.power_state = state; + return rc; } -int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state) +/** + * ata_scsi_device_resume - resume ATA device associated with sdev + * @sdev: the SCSI device to resume + * + * Request resume EH action on the ATA device associated with + * @sdev and return immediately. This enables parallel + * wakeup/spinup of devices. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * 0. + */ +int ata_scsi_device_resume(struct scsi_device *sdev) { struct ata_port *ap = ata_shost_to_port(sdev->host); - struct ata_device *dev = __ata_scsi_find_dev(ap, sdev); + struct ata_device *dev = ata_scsi_find_dev(ap, sdev); + struct ata_eh_info *ehi = &ap->eh_info; + unsigned long flags; + unsigned int action; + + if (!dev) + goto out; + + spin_lock_irqsave(ap->lock, flags); + + /* if @sdev is already detached, nothing to do */ + if (sdev->sdev_state == SDEV_OFFLINE || + sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) + goto out_unlock; - return ata_device_suspend(dev, state); + /* request resume */ + action = ATA_EH_RESUME; + if (sdev->sdev_gendev.power.power_state.event == PM_EVENT_SUSPEND) + __ata_ehi_hotplugged(ehi); + else + action |= ATA_EH_PM_FREEZE | ATA_EH_SOFTRESET; + ehi->dev_action[dev->devno] |= action; + + /* We don't want autopsy and verbose EH messages. Disable + * those if we're the only device on this link. + */ + if (ata_port_max_devices(ap) == 1) + ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; + + ata_port_schedule_eh(ap); + + out_unlock: + spin_unlock_irqrestore(ap->lock, flags); + out: + sdev->sdev_gendev.power.power_state = PMSG_ON; + return 0; } /** diff --git a/include/linux/libata.h b/include/linux/libata.h index 363c7501843a..5ac262608199 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -687,8 +687,6 @@ extern int ata_port_online(struct ata_port *ap); extern int ata_port_offline(struct ata_port *ap); extern int ata_scsi_device_resume(struct scsi_device *); extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t state); -extern int ata_device_resume(struct ata_device *); -extern int ata_device_suspend(struct ata_device *, pm_message_t state); extern int ata_ratelimit(void); extern unsigned int ata_busy_sleep(struct ata_port *ap, unsigned long timeout_pat, -- cgit v1.2.3 From 500530f652f9e5dabe7571b018dec47742ce0f16 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:27 +0900 Subject: [PATCH] libata: reimplement controller-wide PM Reimplement controller-wide PM. ata_host_set_suspend/resume() are defined to suspend and resume a host_set. While suspended, EHs for all ports in the host_set are pegged using ATA_FLAG_SUSPENDED and frozen. Because SCSI device hotplug is done asynchronously against the rest of libata EH and the same mutex is used when adding new device, suspend cannot wait for hotplug to complete. So, if SCSI device hotplug is in progress, suspend fails with -EBUSY. In most cases, host_set resume is followed by device resume. As each resume operation requires a reset, a single host_set-wide resume operation may result in multiple resets. To avoid this, resume waits upto 1 second giving PM to request resume for devices. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 165 +++++++++++++++++++++++++++++++++++++++++++-- drivers/scsi/libata-eh.c | 128 ++++++++++++++++++++++++++++++++++- include/linux/libata.h | 12 ++++ 3 files changed, 298 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 51dbc5221934..fa65b990f8b0 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -5009,6 +5009,122 @@ int ata_flush_cache(struct ata_device *dev) return 0; } +static int ata_host_set_request_pm(struct ata_host_set *host_set, + pm_message_t mesg, unsigned int action, + unsigned int ehi_flags, int wait) +{ + unsigned long flags; + int i, rc; + + for (i = 0; i < host_set->n_ports; i++) { + struct ata_port *ap = host_set->ports[i]; + + /* Previous resume operation might still be in + * progress. Wait for PM_PENDING to clear. + */ + if (ap->pflags & ATA_PFLAG_PM_PENDING) { + ata_port_wait_eh(ap); + WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); + } + + /* request PM ops to EH */ + spin_lock_irqsave(ap->lock, flags); + + ap->pm_mesg = mesg; + if (wait) { + rc = 0; + ap->pm_result = &rc; + } + + ap->pflags |= ATA_PFLAG_PM_PENDING; + ap->eh_info.action |= action; + ap->eh_info.flags |= ehi_flags; + + ata_port_schedule_eh(ap); + + spin_unlock_irqrestore(ap->lock, flags); + + /* wait and check result */ + if (wait) { + ata_port_wait_eh(ap); + WARN_ON(ap->pflags & ATA_PFLAG_PM_PENDING); + if (rc) + return rc; + } + } + + return 0; +} + +/** + * ata_host_set_suspend - suspend host_set + * @host_set: host_set to suspend + * @mesg: PM message + * + * Suspend @host_set. Actual operation is performed by EH. This + * function requests EH to perform PM operations and waits for EH + * to finish. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * 0 on success, -errno on failure. + */ +int ata_host_set_suspend(struct ata_host_set *host_set, pm_message_t mesg) +{ + int i, j, rc; + + rc = ata_host_set_request_pm(host_set, mesg, 0, ATA_EHI_QUIET, 1); + if (rc) + goto fail; + + /* EH is quiescent now. Fail if we have any ready device. + * This happens if hotplug occurs between completion of device + * suspension and here. + */ + for (i = 0; i < host_set->n_ports; i++) { + struct ata_port *ap = host_set->ports[i]; + + for (j = 0; j < ATA_MAX_DEVICES; j++) { + struct ata_device *dev = &ap->device[j]; + + if (ata_dev_ready(dev)) { + ata_port_printk(ap, KERN_WARNING, + "suspend failed, device %d " + "still active\n", dev->devno); + rc = -EBUSY; + goto fail; + } + } + } + + host_set->dev->power.power_state = mesg; + return 0; + + fail: + ata_host_set_resume(host_set); + return rc; +} + +/** + * ata_host_set_resume - resume host_set + * @host_set: host_set to resume + * + * Resume @host_set. Actual operation is performed by EH. This + * function requests EH to perform PM operations and returns. + * Note that all resume operations are performed parallely. + * + * LOCKING: + * Kernel thread context (may sleep). + */ +void ata_host_set_resume(struct ata_host_set *host_set) +{ + ata_host_set_request_pm(host_set, PMSG_ON, ATA_EH_SOFTRESET, + ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); + host_set->dev->power.power_state = PMSG_ON; +} + /** * ata_port_start - Set port up for dma. * @ap: Port to initialize @@ -5651,20 +5767,55 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits) return (tmp == bits->val) ? 1 : 0; } -int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state) +void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state) { pci_save_state(pdev); - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); - return 0; + + if (state.event == PM_EVENT_SUSPEND) { + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + } } -int ata_pci_device_resume(struct pci_dev *pdev) +void ata_pci_device_do_resume(struct pci_dev *pdev) { pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); pci_enable_device(pdev); pci_set_master(pdev); +} + +int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); + int rc = 0; + + rc = ata_host_set_suspend(host_set, state); + if (rc) + return rc; + + if (host_set->next) { + rc = ata_host_set_suspend(host_set->next, state); + if (rc) { + ata_host_set_resume(host_set); + return rc; + } + } + + ata_pci_device_do_suspend(pdev, state); + + return 0; +} + +int ata_pci_device_resume(struct pci_dev *pdev) +{ + struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); + + ata_pci_device_do_resume(pdev); + ata_host_set_resume(host_set); + if (host_set->next) + ata_host_set_resume(host_set->next); + return 0; } #endif /* CONFIG_PCI */ @@ -5844,6 +5995,8 @@ EXPORT_SYMBOL_GPL(sata_scr_write); EXPORT_SYMBOL_GPL(sata_scr_write_flush); EXPORT_SYMBOL_GPL(ata_port_online); EXPORT_SYMBOL_GPL(ata_port_offline); +EXPORT_SYMBOL_GPL(ata_host_set_suspend); +EXPORT_SYMBOL_GPL(ata_host_set_resume); EXPORT_SYMBOL_GPL(ata_id_string); EXPORT_SYMBOL_GPL(ata_id_c_string); EXPORT_SYMBOL_GPL(ata_scsi_simulate); @@ -5858,6 +6011,8 @@ EXPORT_SYMBOL_GPL(ata_pci_host_stop); EXPORT_SYMBOL_GPL(ata_pci_init_native_mode); EXPORT_SYMBOL_GPL(ata_pci_init_one); EXPORT_SYMBOL_GPL(ata_pci_remove_one); +EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend); +EXPORT_SYMBOL_GPL(ata_pci_device_do_resume); EXPORT_SYMBOL_GPL(ata_pci_device_suspend); EXPORT_SYMBOL_GPL(ata_pci_device_resume); EXPORT_SYMBOL_GPL(ata_pci_default_filter); diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index b9df49a36214..4b6aa30f4d68 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c @@ -47,6 +47,8 @@ static void __ata_port_freeze(struct ata_port *ap); static void ata_eh_finish(struct ata_port *ap); +static void ata_eh_handle_port_suspend(struct ata_port *ap); +static void ata_eh_handle_port_resume(struct ata_port *ap); static void ata_ering_record(struct ata_ering *ering, int is_io, unsigned int err_mask) @@ -262,6 +264,9 @@ void ata_scsi_error(struct Scsi_Host *host) repeat: /* invoke error handler */ if (ap->ops->error_handler) { + /* process port resume request */ + ata_eh_handle_port_resume(ap); + /* fetch & clear EH info */ spin_lock_irqsave(ap->lock, flags); @@ -274,12 +279,15 @@ void ata_scsi_error(struct Scsi_Host *host) spin_unlock_irqrestore(ap->lock, flags); - /* invoke EH. if unloading, just finish failed qcs */ - if (!(ap->pflags & ATA_PFLAG_UNLOADING)) + /* invoke EH, skip if unloading or suspended */ + if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED))) ap->ops->error_handler(ap); else ata_eh_finish(ap); + /* process port suspend request */ + ata_eh_handle_port_suspend(ap); + /* Exception might have happend after ->error_handler * recovered the port but before this point. Repeat * EH in such case. @@ -2101,3 +2109,119 @@ void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, ata_eh_recover(ap, prereset, softreset, hardreset, postreset); ata_eh_finish(ap); } + +/** + * ata_eh_handle_port_suspend - perform port suspend operation + * @ap: port to suspend + * + * Suspend @ap. + * + * LOCKING: + * Kernel thread context (may sleep). + */ +static void ata_eh_handle_port_suspend(struct ata_port *ap) +{ + unsigned long flags; + int rc = 0; + + /* are we suspending? */ + spin_lock_irqsave(ap->lock, flags); + if (!(ap->pflags & ATA_PFLAG_PM_PENDING) || + ap->pm_mesg.event == PM_EVENT_ON) { + spin_unlock_irqrestore(ap->lock, flags); + return; + } + spin_unlock_irqrestore(ap->lock, flags); + + WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); + + /* suspend */ + ata_eh_freeze_port(ap); + + if (ap->ops->port_suspend) + rc = ap->ops->port_suspend(ap, ap->pm_mesg); + + /* report result */ + spin_lock_irqsave(ap->lock, flags); + + ap->pflags &= ~ATA_PFLAG_PM_PENDING; + if (rc == 0) + ap->pflags |= ATA_PFLAG_SUSPENDED; + else + ata_port_schedule_eh(ap); + + if (ap->pm_result) { + *ap->pm_result = rc; + ap->pm_result = NULL; + } + + spin_unlock_irqrestore(ap->lock, flags); + + return; +} + +/** + * ata_eh_handle_port_resume - perform port resume operation + * @ap: port to resume + * + * Resume @ap. + * + * This function also waits upto one second until all devices + * hanging off this port requests resume EH action. This is to + * prevent invoking EH and thus reset multiple times on resume. + * + * On DPM resume, where some of devices might not be resumed + * together, this may delay port resume upto one second, but such + * DPM resumes are rare and 1 sec delay isn't too bad. + * + * LOCKING: + * Kernel thread context (may sleep). + */ +static void ata_eh_handle_port_resume(struct ata_port *ap) +{ + unsigned long timeout; + unsigned long flags; + int i, rc = 0; + + /* are we resuming? */ + spin_lock_irqsave(ap->lock, flags); + if (!(ap->pflags & ATA_PFLAG_PM_PENDING) || + ap->pm_mesg.event != PM_EVENT_ON) { + spin_unlock_irqrestore(ap->lock, flags); + return; + } + spin_unlock_irqrestore(ap->lock, flags); + + /* spurious? */ + if (!(ap->pflags & ATA_PFLAG_SUSPENDED)) + goto done; + + if (ap->ops->port_resume) + rc = ap->ops->port_resume(ap); + + /* give devices time to request EH */ + timeout = jiffies + HZ; /* 1s max */ + while (1) { + for (i = 0; i < ATA_MAX_DEVICES; i++) { + struct ata_device *dev = &ap->device[i]; + unsigned int action = ata_eh_dev_action(dev); + + if ((dev->flags & ATA_DFLAG_SUSPENDED) && + !(action & ATA_EH_RESUME)) + break; + } + + if (i == ATA_MAX_DEVICES || time_after(jiffies, timeout)) + break; + msleep(10); + } + + done: + spin_lock_irqsave(ap->lock, flags); + ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED); + if (ap->pm_result) { + *ap->pm_result = rc; + ap->pm_result = NULL; + } + spin_unlock_irqrestore(ap->lock, flags); +} diff --git a/include/linux/libata.h b/include/linux/libata.h index 5ac262608199..6cc497a2b6da 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -182,6 +182,7 @@ enum { ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */ ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */ + ATA_PFLAG_PM_PENDING = (1 << 18), /* PM operation pending */ /* struct ata_queued_cmd flags */ ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */ @@ -549,6 +550,9 @@ struct ata_port { struct list_head eh_done_q; wait_queue_head_t eh_wait_q; + pm_message_t pm_mesg; + int *pm_result; + void *private_data; u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */ @@ -603,6 +607,9 @@ struct ata_port_operations { void (*scr_write) (struct ata_port *ap, unsigned int sc_reg, u32 val); + int (*port_suspend) (struct ata_port *ap, pm_message_t mesg); + int (*port_resume) (struct ata_port *ap); + int (*port_start) (struct ata_port *ap); void (*port_stop) (struct ata_port *ap); @@ -667,6 +674,8 @@ extern void ata_std_ports(struct ata_ioports *ioaddr); extern int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, unsigned int n_ports); extern void ata_pci_remove_one (struct pci_dev *pdev); +extern void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t state); +extern void ata_pci_device_do_resume(struct pci_dev *pdev); extern int ata_pci_device_suspend(struct pci_dev *pdev, pm_message_t state); extern int ata_pci_device_resume(struct pci_dev *pdev); extern int ata_pci_clear_simplex(struct pci_dev *pdev); @@ -687,6 +696,9 @@ extern int ata_port_online(struct ata_port *ap); extern int ata_port_offline(struct ata_port *ap); extern int ata_scsi_device_resume(struct scsi_device *); extern int ata_scsi_device_suspend(struct scsi_device *, pm_message_t state); +extern int ata_host_set_suspend(struct ata_host_set *host_set, + pm_message_t mesg); +extern void ata_host_set_resume(struct ata_host_set *host_set); extern int ata_ratelimit(void); extern unsigned int ata_busy_sleep(struct ata_port *ap, unsigned long timeout_pat, -- cgit v1.2.3 From 3d8ec91352099b32a400f1952112dc076da28106 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:27 +0900 Subject: [PATCH] sata_sil: separate out sil_init_controller() Separate out controller initialization from sil_init_one() into sil_init_controller(). This will be used by resume. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/sata_sil.c | 86 +++++++++++++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index 70dbfb8a7d51..6033f796397f 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -561,6 +561,52 @@ static void sil_dev_config(struct ata_port *ap, struct ata_device *dev) } } +static void sil_init_controller(struct pci_dev *pdev, + int n_ports, unsigned long host_flags, + void __iomem *mmio_base) +{ + u8 cls; + u32 tmp; + int i; + + /* Initialize FIFO PCI bus arbitration */ + cls = sil_get_device_cache_line(pdev); + if (cls) { + cls >>= 3; + cls++; /* cls = (line_size/8)+1 */ + for (i = 0; i < n_ports; i++) + writew(cls << 8 | cls, + mmio_base + sil_port[i].fifo_cfg); + } else + dev_printk(KERN_WARNING, &pdev->dev, + "cache line size not set. Driver may not function\n"); + + /* Apply R_ERR on DMA activate FIS errata workaround */ + if (host_flags & SIL_FLAG_RERR_ON_DMA_ACT) { + int cnt; + + for (i = 0, cnt = 0; i < n_ports; i++) { + tmp = readl(mmio_base + sil_port[i].sfis_cfg); + if ((tmp & 0x3) != 0x01) + continue; + if (!cnt) + dev_printk(KERN_INFO, &pdev->dev, + "Applying R_ERR on DMA activate " + "FIS errata fix\n"); + writel(tmp & ~0x3, mmio_base + sil_port[i].sfis_cfg); + cnt++; + } + } + + if (n_ports == 4) { + /* flip the magic "make 4 ports work" bit */ + tmp = readl(mmio_base + sil_port[2].bmdma); + if ((tmp & SIL_INTR_STEERING) == 0) + writel(tmp | SIL_INTR_STEERING, + mmio_base + sil_port[2].bmdma); + } +} + static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; @@ -570,8 +616,6 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) int rc; unsigned int i; int pci_dev_busy = 0; - u32 tmp; - u8 cls; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); @@ -630,42 +674,8 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) ata_std_ports(&probe_ent->port[i]); } - /* Initialize FIFO PCI bus arbitration */ - cls = sil_get_device_cache_line(pdev); - if (cls) { - cls >>= 3; - cls++; /* cls = (line_size/8)+1 */ - for (i = 0; i < probe_ent->n_ports; i++) - writew(cls << 8 | cls, - mmio_base + sil_port[i].fifo_cfg); - } else - dev_printk(KERN_WARNING, &pdev->dev, - "cache line size not set. Driver may not function\n"); - - /* Apply R_ERR on DMA activate FIS errata workaround */ - if (probe_ent->host_flags & SIL_FLAG_RERR_ON_DMA_ACT) { - int cnt; - - for (i = 0, cnt = 0; i < probe_ent->n_ports; i++) { - tmp = readl(mmio_base + sil_port[i].sfis_cfg); - if ((tmp & 0x3) != 0x01) - continue; - if (!cnt) - dev_printk(KERN_INFO, &pdev->dev, - "Applying R_ERR on DMA activate " - "FIS errata fix\n"); - writel(tmp & ~0x3, mmio_base + sil_port[i].sfis_cfg); - cnt++; - } - } - - if (ent->driver_data == sil_3114) { - /* flip the magic "make 4 ports work" bit */ - tmp = readl(mmio_base + sil_port[2].bmdma); - if ((tmp & SIL_INTR_STEERING) == 0) - writel(tmp | SIL_INTR_STEERING, - mmio_base + sil_port[2].bmdma); - } + sil_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags, + mmio_base); pci_set_master(pdev); -- cgit v1.2.3 From afb5a7cb84b1ea8b6045945e3d288303e6b71336 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:27 +0900 Subject: [PATCH] sata_sil: add suspend/sleep support Add suspend/sleep support. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/sata_sil.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index 6033f796397f..d0a85073ebf7 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -109,6 +109,7 @@ enum { }; static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static int sil_pci_device_resume(struct pci_dev *pdev); static void sil_dev_config(struct ata_port *ap, struct ata_device *dev); static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg); static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); @@ -160,6 +161,8 @@ static struct pci_driver sil_pci_driver = { .id_table = sil_pci_tbl, .probe = sil_init_one, .remove = ata_pci_remove_one, + .suspend = ata_pci_device_suspend, + .resume = sil_pci_device_resume, }; static struct scsi_host_template sil_sht = { @@ -178,6 +181,8 @@ static struct scsi_host_template sil_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, + .suspend = ata_scsi_device_suspend, + .resume = ata_scsi_device_resume, }; static const struct ata_port_operations sil_ops = { @@ -695,6 +700,18 @@ err_out: return rc; } +static int sil_pci_device_resume(struct pci_dev *pdev) +{ + struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); + + ata_pci_device_do_resume(pdev); + sil_init_controller(pdev, host_set->n_ports, host_set->ports[0]->flags, + host_set->mmio_base); + ata_host_set_resume(host_set); + + return 0; +} + static int __init sil_init(void) { return pci_module_init(&sil_pci_driver); -- cgit v1.2.3 From 2a41a6108d93a62910d1c36913d83a79b550b40a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:27 +0900 Subject: [PATCH] sata_sil24: separate out sil24_init_controller() Separate out controller initialization from sil24_init_one() into sil24_init_controller(). This will be used by resume. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/sata_sil24.c | 107 +++++++++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 45 deletions(-) diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c index 04ae1ef25484..1850c2e3aa21 100644 --- a/drivers/scsi/sata_sil24.c +++ b/drivers/scsi/sata_sil24.c @@ -988,6 +988,64 @@ static void sil24_host_stop(struct ata_host_set *host_set) kfree(hpriv); } +static void sil24_init_controller(struct pci_dev *pdev, int n_ports, + unsigned long host_flags, + void __iomem *host_base, + void __iomem *port_base) +{ + u32 tmp; + int i; + + /* GPIO off */ + writel(0, host_base + HOST_FLASH_CMD); + + /* clear global reset & mask interrupts during initialization */ + writel(0, host_base + HOST_CTRL); + + /* init ports */ + for (i = 0; i < n_ports; i++) { + void __iomem *port = port_base + i * PORT_REGS_SIZE; + + /* Initial PHY setting */ + writel(0x20c, port + PORT_PHY_CFG); + + /* Clear port RST */ + tmp = readl(port + PORT_CTRL_STAT); + if (tmp & PORT_CS_PORT_RST) { + writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR); + tmp = ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_PORT_RST, + PORT_CS_PORT_RST, 10, 100); + if (tmp & PORT_CS_PORT_RST) + dev_printk(KERN_ERR, &pdev->dev, + "failed to clear port RST\n"); + } + + /* Configure IRQ WoC */ + if (host_flags & SIL24_FLAG_PCIX_IRQ_WOC) + writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); + else + writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); + + /* Zero error counters. */ + writel(0x8000, port + PORT_DECODE_ERR_THRESH); + writel(0x8000, port + PORT_CRC_ERR_THRESH); + writel(0x8000, port + PORT_HSHK_ERR_THRESH); + writel(0x0000, port + PORT_DECODE_ERR_CNT); + writel(0x0000, port + PORT_CRC_ERR_CNT); + writel(0x0000, port + PORT_HSHK_ERR_CNT); + + /* Always use 64bit activation */ + writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); + + /* Clear port multiplier enable and resume bits */ + writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR); + } + + /* Turn on interrupts */ + writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL); +} + static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; @@ -1076,9 +1134,6 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } - /* GPIO off */ - writel(0, host_base + HOST_FLASH_CMD); - /* Apply workaround for completion IRQ loss on PCI-X errata */ if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) { tmp = readl(host_base + HOST_CTRL); @@ -1090,56 +1145,18 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) probe_ent->host_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC; } - /* clear global reset & mask interrupts during initialization */ - writel(0, host_base + HOST_CTRL); - for (i = 0; i < probe_ent->n_ports; i++) { - void __iomem *port = port_base + i * PORT_REGS_SIZE; - unsigned long portu = (unsigned long)port; + unsigned long portu = + (unsigned long)port_base + i * PORT_REGS_SIZE; probe_ent->port[i].cmd_addr = portu; probe_ent->port[i].scr_addr = portu + PORT_SCONTROL; ata_std_ports(&probe_ent->port[i]); - - /* Initial PHY setting */ - writel(0x20c, port + PORT_PHY_CFG); - - /* Clear port RST */ - tmp = readl(port + PORT_CTRL_STAT); - if (tmp & PORT_CS_PORT_RST) { - writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR); - tmp = ata_wait_register(port + PORT_CTRL_STAT, - PORT_CS_PORT_RST, - PORT_CS_PORT_RST, 10, 100); - if (tmp & PORT_CS_PORT_RST) - dev_printk(KERN_ERR, &pdev->dev, - "failed to clear port RST\n"); - } - - /* Configure IRQ WoC */ - if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) - writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); - else - writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); - - /* Zero error counters. */ - writel(0x8000, port + PORT_DECODE_ERR_THRESH); - writel(0x8000, port + PORT_CRC_ERR_THRESH); - writel(0x8000, port + PORT_HSHK_ERR_THRESH); - writel(0x0000, port + PORT_DECODE_ERR_CNT); - writel(0x0000, port + PORT_CRC_ERR_CNT); - writel(0x0000, port + PORT_HSHK_ERR_CNT); - - /* Always use 64bit activation */ - writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); - - /* Clear port multiplier enable and resume bits */ - writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR); } - /* Turn on interrupts */ - writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL); + sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->host_flags, + host_base, port_base); pci_set_master(pdev); -- cgit v1.2.3 From d2298dca9a2ec8a8ff23e251d8aaab00e9d229f9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 3 Jul 2006 16:07:27 +0900 Subject: [PATCH] sata_sil24: add suspend/sleep support Add suspend/sleep support. Signed-off-by: Tejun Heo Signed-off-by: Jeff Garzik --- drivers/scsi/sata_sil24.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c index 1850c2e3aa21..2e0f4a4076af 100644 --- a/drivers/scsi/sata_sil24.c +++ b/drivers/scsi/sata_sil24.c @@ -92,6 +92,7 @@ enum { HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */ HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */ HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */ + HOST_CTRL_GLOBAL_RST = (1 << 31), /* global reset */ /* * Port registers @@ -338,6 +339,7 @@ static int sil24_port_start(struct ata_port *ap); static void sil24_port_stop(struct ata_port *ap); static void sil24_host_stop(struct ata_host_set *host_set); static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); +static int sil24_pci_device_resume(struct pci_dev *pdev); static const struct pci_device_id sil24_pci_tbl[] = { { 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 }, @@ -353,6 +355,8 @@ static struct pci_driver sil24_pci_driver = { .id_table = sil24_pci_tbl, .probe = sil24_init_one, .remove = ata_pci_remove_one, /* safe? */ + .suspend = ata_pci_device_suspend, + .resume = sil24_pci_device_resume, }; static struct scsi_host_template sil24_sht = { @@ -372,6 +376,8 @@ static struct scsi_host_template sil24_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, + .suspend = ata_scsi_device_suspend, + .resume = ata_scsi_device_resume, }; static const struct ata_port_operations sil24_ops = { @@ -1179,6 +1185,25 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return rc; } +static int sil24_pci_device_resume(struct pci_dev *pdev) +{ + struct ata_host_set *host_set = dev_get_drvdata(&pdev->dev); + struct sil24_host_priv *hpriv = host_set->private_data; + + ata_pci_device_do_resume(pdev); + + if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) + writel(HOST_CTRL_GLOBAL_RST, hpriv->host_base + HOST_CTRL); + + sil24_init_controller(pdev, host_set->n_ports, + host_set->ports[0]->flags, + hpriv->host_base, hpriv->port_base); + + ata_host_set_resume(host_set); + + return 0; +} + static int __init sil24_init(void) { return pci_module_init(&sil24_pci_driver); -- cgit v1.2.3 From 5afc81427f79193ed55edd7184ba2b6ce119a649 Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Tue, 27 Jun 2006 14:51:25 +0200 Subject: [PATCH] libata-core.c: restore configuration boot messages in ata_dev_configure(), v2 This one looks better, IMHO. This restores the default libata configuration messages printed during booting. Signed-off-by: Signed-off-by: Jeff Garzik --- drivers/scsi/libata-core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index fa65b990f8b0..386e5f21e191 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1417,7 +1417,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info) ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc)); /* print device info to dmesg */ - if (ata_msg_info(ap)) + if (ata_msg_drv(ap) && print_info) ata_dev_printk(dev, KERN_INFO, "ATA-%d, " "max %s, %Lu sectors: %s %s\n", ata_id_major_version(id), @@ -1440,7 +1440,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info) } /* print device info to dmesg */ - if (ata_msg_info(ap)) + if (ata_msg_drv(ap) && print_info) ata_dev_printk(dev, KERN_INFO, "ATA-%d, " "max %s, %Lu sectors: CHS %u/%u/%u\n", ata_id_major_version(id), @@ -1452,7 +1452,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info) if (dev->id[59] & 0x100) { dev->multi_count = dev->id[59] & 0xff; - if (ata_msg_info(ap)) + if (ata_msg_drv(ap) && print_info) ata_dev_printk(dev, KERN_INFO, "ata%u: dev %u multi count %u\n", ap->id, dev->devno, dev->multi_count); @@ -1481,7 +1481,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info) } /* print device info to dmesg */ - if (ata_msg_info(ap)) + if (ata_msg_drv(ap) && print_info) ata_dev_printk(dev, KERN_INFO, "ATAPI, max %s%s\n", ata_mode_string(xfer_mask), cdb_intr_string); @@ -1491,7 +1491,7 @@ int ata_dev_configure(struct ata_device *dev, int print_info) /* limit bridge transfers to udma5, 200 sectors */ if (ata_dev_knobble(dev)) { - if (ata_msg_info(ap)) + if (ata_msg_drv(ap) && print_info) ata_dev_printk(dev, KERN_INFO, "applying bridge limits\n"); dev->udma_mask &= ATA_UDMA5; -- cgit v1.2.3 From 9545b5781cfa822ea1528bc65965dbace0c39a5d Mon Sep 17 00:00:00 2001 From: root Date: Wed, 5 Jul 2006 22:58:20 -0400 Subject: [PATCH] ahci: Ensure that we don't grab both functions When we force the chip into dual fn mode so we get PATA and AHCI we must be sure we don't then do anything dumb like try and grab both with the AHCI driver. Signed-off-by: Alan Cox Signed-off-by: Jeff Garzik --- drivers/scsi/ahci.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 7447ba0374e8..77e7202a0eba 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -1323,6 +1323,17 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* JMicron-specific fixup: make sure we're in AHCI mode */ + /* This is protected from races with ata_jmicron by the pci probe + locking */ + if (pdev->vendor == PCI_VENDOR_ID_JMICRON) { + /* AHCI enable, AHCI on function 0 */ + pci_write_config_byte(pdev, 0x41, 0xa1); + /* Function 1 is the PATA controller */ + if (PCI_FUNC(pdev->devfn)) + return -ENODEV; + } + rc = pci_enable_device(pdev); if (rc) return rc; @@ -1378,10 +1389,6 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (have_msi) hpriv->flags |= AHCI_FLAG_MSI; - /* JMicron-specific fixup: make sure we're in AHCI mode */ - if (pdev->vendor == 0x197b) - pci_write_config_byte(pdev, 0x41, 0xa1); - /* initialize adapter */ rc = ahci_host_init(probe_ent); if (rc) -- cgit v1.2.3 From 309bade002e9226781c2d7a015340d0089e399b5 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 5 Jul 2006 23:02:48 -0400 Subject: [PCI] Add JMicron PCI ID constants They will be used in several IDE/libata files. Signed-off-by: Jeff Garzik --- include/linux/pci_ids.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 685081c01342..c09396d2c77b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2019,6 +2019,13 @@ #define PCI_VENDOR_ID_TDI 0x192E #define PCI_DEVICE_ID_TDI_EHCI 0x0101 +#define PCI_VENDOR_ID_JMICRON 0x197B +#define PCI_DEVICE_ID_JMICRON_JMB360 0x2360 +#define PCI_DEVICE_ID_JMICRON_JMB361 0x2361 +#define PCI_DEVICE_ID_JMICRON_JMB363 0x2363 +#define PCI_DEVICE_ID_JMICRON_JMB365 0x2365 +#define PCI_DEVICE_ID_JMICRON_JMB366 0x2366 +#define PCI_DEVICE_ID_JMICRON_JMB368 0x2368 #define PCI_VENDOR_ID_TEKRAM 0x1de1 #define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29 -- cgit v1.2.3 From 7233589d77fdb593b482a8b7ee867e901f54b593 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 5 Jul 2006 20:18:39 -0700 Subject: [SPARC64]: Fix sparc64 build errors when CONFIG_PCI=n. Signed-off-by: Randy Dunlap Signed-off-by: David S. Miller --- arch/sparc64/kernel/prom.c | 2 ++ arch/sparc64/kernel/time.c | 5 ++++- include/asm-sparc64/dma-mapping.h | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c index fa484d4f241e..99daeee4209d 100644 --- a/arch/sparc64/kernel/prom.c +++ b/arch/sparc64/kernel/prom.c @@ -1032,7 +1032,9 @@ static void sun4v_vdev_irq_trans_init(struct device_node *dp) static void irq_trans_init(struct device_node *dp) { const char *model; +#ifdef CONFIG_PCI int i; +#endif model = of_get_property(dp, "model", NULL); if (!model) diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index 8dcbfbffacc9..b43de647ba73 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -788,12 +788,15 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id if (!regs) return -ENOMEM; +#ifdef CONFIG_PCI if (!strcmp(model, "ds1287") || !strcmp(model, "m5819") || !strcmp(model, "m5819p") || !strcmp(model, "m5823")) { ds1287_regs = (unsigned long) regs; - } else if (model[5] == '0' && model[6] == '2') { + } else +#endif + if (model[5] == '0' && model[6] == '2') { mstk48t02_regs = regs; } else if(model[5] == '0' && model[6] == '8') { mstk48t08_regs = regs; diff --git a/include/asm-sparc64/dma-mapping.h b/include/asm-sparc64/dma-mapping.h index 0f5b89c9323b..27c46fbeebd6 100644 --- a/include/asm-sparc64/dma-mapping.h +++ b/include/asm-sparc64/dma-mapping.h @@ -160,6 +160,20 @@ static inline void dma_free_coherent(struct device *dev, size_t size, BUG(); } +static inline void +dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + +static inline void +dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, size_t size, + enum dma_data_direction direction) +{ + BUG(); +} + #endif /* PCI */ -- cgit v1.2.3 From 37e64e5ae1a9554762b6ec494871adcf48be20cf Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Wed, 5 Jul 2006 20:42:58 -0700 Subject: [SPARC64]: Fix stack overflow checking in modular non-SMP kernels. The sparc64 kernel's EXPORT_SYMBOL(_mcount) is inside an #ifdef CONFIG_SMP. This breaks modules in non-SMP kernels built with stack overflow checking (CONFIG_STACK_DEBUG=y), as modules_install reports: WARNING: /lib/modules/2.6.17/kernel/drivers/ide/ide-cd.ko needs unknown symbol _mcount Trivially fixed by moving EXPORT_SYMBOL(_mcount) outside of the #ifdef CONFIG_SMP. Signed-off-by: Mikael Pettersson Signed-off-by: David S. Miller --- arch/sparc64/kernel/sparc64_ksyms.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 4173de425f09..237524d87cab 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -124,11 +124,6 @@ EXPORT_SYMBOL(__write_lock); EXPORT_SYMBOL(__write_unlock); EXPORT_SYMBOL(__write_trylock); -#if defined(CONFIG_MCOUNT) -extern void _mcount(void); -EXPORT_SYMBOL(_mcount); -#endif - /* CPU online map and active count. */ EXPORT_SYMBOL(cpu_online_map); EXPORT_SYMBOL(phys_cpu_present_map); @@ -136,6 +131,11 @@ EXPORT_SYMBOL(phys_cpu_present_map); EXPORT_SYMBOL(smp_call_function); #endif /* CONFIG_SMP */ +#if defined(CONFIG_MCOUNT) +extern void _mcount(void); +EXPORT_SYMBOL(_mcount); +#endif + EXPORT_SYMBOL(sparc64_get_clock_tick); /* semaphores */ -- cgit v1.2.3 From 26dab8930b408d5e5eb9ef496d68364dc955e249 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 5 Jul 2006 20:45:06 -0700 Subject: [PKT_SCHED]: Fix illegal memory dereferences when dumping actions The TCA_ACT_KIND attribute is used without checking its availability when dumping actions therefore leading to a value of 0x4 being dereferenced. The use of strcmp() in tc_lookup_action_n() isn't safe when fed with string from an attribute without enforcing proper NUL termination. Both bugs can be triggered with malformed netlink message and don't require any privileges. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/sched/act_api.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 5b9397b33238..f9d1d78e17f8 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -776,7 +776,7 @@ replay: return ret; } -static char * +static struct rtattr * find_dump_kind(struct nlmsghdr *n) { struct rtattr *tb1, *tb2[TCA_ACT_MAX+1]; @@ -804,7 +804,7 @@ find_dump_kind(struct nlmsghdr *n) return NULL; kind = tb2[TCA_ACT_KIND-1]; - return (char *) RTA_DATA(kind); + return kind; } static int @@ -817,16 +817,15 @@ tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) struct tc_action a; int ret = 0; struct tcamsg *t = (struct tcamsg *) NLMSG_DATA(cb->nlh); - char *kind = find_dump_kind(cb->nlh); + struct rtattr *kind = find_dump_kind(cb->nlh); if (kind == NULL) { printk("tc_dump_action: action bad kind\n"); return 0; } - a_o = tc_lookup_action_n(kind); + a_o = tc_lookup_action(kind); if (a_o == NULL) { - printk("failed to find %s\n", kind); return 0; } @@ -834,7 +833,7 @@ tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) a.ops = a_o; if (a_o->walk == NULL) { - printk("tc_dump_action: %s !capable of dumping table\n", kind); + printk("tc_dump_action: %s !capable of dumping table\n", a_o->kind); goto rtattr_failure; } -- cgit v1.2.3 From d152b4e1e9a18f332ecd9e66492d706edc083345 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 5 Jul 2006 20:45:57 -0700 Subject: [PKT_SCHED]: Return ENOENT if action module is unavailable Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/sched/act_api.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f9d1d78e17f8..9b2e3975be0b 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -305,6 +305,7 @@ struct tc_action *tcf_action_init_1(struct rtattr *rta, struct rtattr *est, goto err_mod; } #endif + *err = -ENOENT; goto err_out; } -- cgit v1.2.3 From 4fe683f50d3fc8e36d4749277631dfc711393aa0 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Wed, 5 Jul 2006 20:47:28 -0700 Subject: [PKT_SCHED]: Fix error handling while dumping actions "return -err" and blindly inheriting the error code in the netlink failure exception handler causes errors codes to be returned as positive value therefore making them being ignored by the caller. May lead to sending out incomplete netlink messages. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/sched/act_api.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 9b2e3975be0b..599423cc9d0d 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -250,15 +250,17 @@ tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref) RTA_PUT(skb, a->order, 0, NULL); err = tcf_action_dump_1(skb, a, bind, ref); if (err < 0) - goto rtattr_failure; + goto errout; r->rta_len = skb->tail - (u8*)r; } return 0; rtattr_failure: + err = -EINVAL; +errout: skb_trim(skb, b - skb->data); - return -err; + return err; } struct tc_action *tcf_action_init_1(struct rtattr *rta, struct rtattr *est, -- cgit v1.2.3 From 120bda20c6f64b32e8bfbdd7b34feafaa5f5332e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 5 Jul 2006 21:09:49 -0700 Subject: Linux 2.6.18-rc1 It's all good. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 82f76a96cfe6..7c010f3325a9 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 -SUBLEVEL = 17 -EXTRAVERSION = +SUBLEVEL = 18 +EXTRAVERSION = -rc1 NAME=Crazed Snow-Weasel # *DOCUMENTATION* -- cgit v1.2.3 From e9c8862f19958846dd0c7b39d0f6216aad6c7bee Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Jul 2006 00:20:47 -0400 Subject: Input: remove accept method from input_dev This method used to enforce exclusive access to iforce devices, but presenlty there are no known users of this method. Signed-off-by: Dmitry Torokhov --- drivers/input/evdev.c | 4 ---- drivers/input/input.c | 9 --------- include/linux/input.h | 2 -- 3 files changed, 15 deletions(-) diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index a29d5ceb00cf..2426a5d8537d 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -127,14 +127,10 @@ static int evdev_open(struct inode * inode, struct file * file) { struct evdev_list *list; int i = iminor(inode) - EVDEV_MINOR_BASE; - int accept_err; if (i >= EVDEV_MINORS || !evdev_table[i] || !evdev_table[i]->exist) return -ENODEV; - if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file))) - return accept_err; - if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL))) return -ENOMEM; diff --git a/drivers/input/input.c b/drivers/input/input.c index a90486f5e491..c2e4d9bdcabf 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -197,15 +197,6 @@ static void input_repeat_key(unsigned long data) mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD])); } -int input_accept_process(struct input_handle *handle, struct file *file) -{ - if (handle->dev->accept) - return handle->dev->accept(handle->dev, file); - - return 0; -} -EXPORT_SYMBOL(input_accept_process); - int input_grab_device(struct input_handle *handle) { if (handle->dev->grab) diff --git a/include/linux/input.h b/include/linux/input.h index 56f1e0e1e598..6e3afad26fa9 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -893,7 +893,6 @@ struct input_dev { int (*open)(struct input_dev *dev); void (*close)(struct input_dev *dev); - int (*accept)(struct input_dev *dev, struct file *file); int (*flush)(struct input_dev *dev, struct file *file); int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect); @@ -1030,7 +1029,6 @@ void input_release_device(struct input_handle *); int input_open_device(struct input_handle *); void input_close_device(struct input_handle *); -int input_accept_process(struct input_handle *handle, struct file *file); int input_flush_device(struct input_handle* handle, struct file* file); void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); -- cgit v1.2.3 From c7e8dc6ee6d59bf72f5478fa6355a27750e6c7d2 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Jul 2006 00:21:03 -0400 Subject: Input: add start() method to input handlers The new start() method is called immediately after connect() and also when "grabbed" device is released by its owner. This will allow input handlers to re-synchronize state of once-grabbed device with the rest of devices. Signed-off-by: Dmitry Torokhov --- drivers/char/keyboard.c | 39 ++++++++++++++++++++------------------- drivers/input/input.c | 12 ++++++++++-- include/linux/input.h | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 056ebe84b81d..38de44b87506 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -988,7 +988,7 @@ static inline unsigned char getleds(void) * interrupt routines for this thing allows us to easily mask * this when we don't want any of the above to happen. * This allows for easy and efficient race-condition prevention - * for kbd_refresh_leds => input_event(dev, EV_LED, ...) => ... + * for kbd_start => input_event(dev, EV_LED, ...) => ... */ static void kbd_bh(unsigned long dummy) @@ -1011,23 +1011,6 @@ static void kbd_bh(unsigned long dummy) DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); -/* - * This allows a newly plugged keyboard to pick the LED state. - */ -static void kbd_refresh_leds(struct input_handle *handle) -{ - unsigned char leds = ledstate; - - tasklet_disable(&keyboard_tasklet); - if (leds != 0xff) { - input_event(handle->dev, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_event(handle->dev, EV_LED, LED_NUML, !!(leds & 0x02)); - input_event(handle->dev, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_sync(handle->dev); - } - tasklet_enable(&keyboard_tasklet); -} - #if defined(CONFIG_X86) || defined(CONFIG_IA64) || defined(CONFIG_ALPHA) ||\ defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) ||\ defined(CONFIG_PARISC) || defined(CONFIG_SUPERH) ||\ @@ -1307,7 +1290,6 @@ static struct input_handle *kbd_connect(struct input_handler *handler, handle->name = "kbd"; input_open_device(handle); - kbd_refresh_leds(handle); return handle; } @@ -1318,6 +1300,24 @@ static void kbd_disconnect(struct input_handle *handle) kfree(handle); } +/* + * Start keyboard handler on the new keyboard by refreshing LED state to + * match the rest of the system. + */ +static void kbd_start(struct input_handle *handle) +{ + unsigned char leds = ledstate; + + tasklet_disable(&keyboard_tasklet); + if (leds != 0xff) { + input_event(handle->dev, EV_LED, LED_SCROLLL, !!(leds & 0x01)); + input_event(handle->dev, EV_LED, LED_NUML, !!(leds & 0x02)); + input_event(handle->dev, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_sync(handle->dev); + } + tasklet_enable(&keyboard_tasklet); +} + static struct input_device_id kbd_ids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT, @@ -1338,6 +1338,7 @@ static struct input_handler kbd_handler = { .event = kbd_event, .connect = kbd_connect, .disconnect = kbd_disconnect, + .start = kbd_start, .name = "kbd", .id_table = kbd_ids, }; diff --git a/drivers/input/input.c b/drivers/input/input.c index c2e4d9bdcabf..7aeebb9071c2 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -209,8 +209,13 @@ EXPORT_SYMBOL(input_grab_device); void input_release_device(struct input_handle *handle) { - if (handle->dev->grab == handle) + if (handle->dev->grab == handle) { handle->dev->grab = NULL; + + list_for_each_entry(handle, &handle->dev->h_list, d_node) + if (handle->handler->start) + handle->handler->start(handle); + } } EXPORT_SYMBOL(input_release_device); @@ -954,8 +959,11 @@ int input_register_device(struct input_dev *dev) list_for_each_entry(handler, &input_handler_list, node) if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) if ((id = input_match_device(handler->id_table, dev))) - if ((handle = handler->connect(handler, dev, id))) + if ((handle = handler->connect(handler, dev, id))) { input_link_handle(handle); + if (handler->start) + handler->start(handle); + } input_wakeup_procfs_readers(); diff --git a/include/linux/input.h b/include/linux/input.h index 6e3afad26fa9..55e628e8805b 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -960,6 +960,26 @@ struct input_dev { struct input_handle; +/** + * struct input_handler - implements one of interfaces for input devices + * @private: driver-specific data + * @event: event handler + * @connect: called when attaching a handler to an input device + * @disconnect: disconnects a handler from input device + * @start: starts handler for given handle. This function is called by + * input core right after connect() method and also when a process + * that "grabbed" a device releases it + * @fops: file operations this driver implements + * @minor: beginning of range of 32 minors for devices this driver + * can provide + * @name: name of the handler, to be shown in /proc/bus/input/handlers + * @id_table: pointer to a table of input_device_ids this driver can + * handle + * @blacklist: prointer to a table of input_device_ids this driver should + * ignore even if they match @id_table + * @h_list: list of input handles associated with the handler + * @node: for placing the driver onto input_handler_list + */ struct input_handler { void *private; @@ -967,6 +987,7 @@ struct input_handler { void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id); void (*disconnect)(struct input_handle *handle); + void (*start)(struct input_handle *handle); const struct file_operations *fops; int minor; -- cgit v1.2.3 From 0e739d28762e78c746cad8edd33223550a1c6a3f Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Jul 2006 00:22:43 -0400 Subject: Input: introduce input_inject_event() function Create input_inject_event() function which is to be used by input handlers as opposed to input_event() which is reserved for drivers implementing input devices. The difference is that if device is "grabbed" by some process input_inject_event() will ignore events unless sent from the handle that is currently owns the device. Signed-off-by: Dmitry Torokhov --- drivers/char/keyboard.c | 36 ++++++++++++++++++------------------ drivers/input/evdev.c | 6 +++--- drivers/input/input.c | 27 +++++++++++++++++++++++++++ include/linux/input.h | 1 + 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 38de44b87506..1e946f585673 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -223,13 +223,13 @@ static void kd_nosound(unsigned long ignored) { struct list_head *node; - list_for_each(node,&kbd_handler.h_list) { + list_for_each(node, &kbd_handler.h_list) { struct input_handle *handle = to_handle_h(node); if (test_bit(EV_SND, handle->dev->evbit)) { if (test_bit(SND_TONE, handle->dev->sndbit)) - input_event(handle->dev, EV_SND, SND_TONE, 0); + input_inject_event(handle, EV_SND, SND_TONE, 0); if (test_bit(SND_BELL, handle->dev->sndbit)) - input_event(handle->dev, EV_SND, SND_BELL, 0); + input_inject_event(handle, EV_SND, SND_BELL, 0); } } } @@ -247,11 +247,11 @@ void kd_mksound(unsigned int hz, unsigned int ticks) struct input_handle *handle = to_handle_h(node); if (test_bit(EV_SND, handle->dev->evbit)) { if (test_bit(SND_TONE, handle->dev->sndbit)) { - input_event(handle->dev, EV_SND, SND_TONE, hz); + input_inject_event(handle, EV_SND, SND_TONE, hz); break; } if (test_bit(SND_BELL, handle->dev->sndbit)) { - input_event(handle->dev, EV_SND, SND_BELL, 1); + input_inject_event(handle, EV_SND, SND_BELL, 1); break; } } @@ -272,15 +272,15 @@ int kbd_rate(struct kbd_repeat *rep) unsigned int d = 0; unsigned int p = 0; - list_for_each(node,&kbd_handler.h_list) { + list_for_each(node, &kbd_handler.h_list) { struct input_handle *handle = to_handle_h(node); struct input_dev *dev = handle->dev; if (test_bit(EV_REP, dev->evbit)) { if (rep->delay > 0) - input_event(dev, EV_REP, REP_DELAY, rep->delay); + input_inject_event(handle, EV_REP, REP_DELAY, rep->delay); if (rep->period > 0) - input_event(dev, EV_REP, REP_PERIOD, rep->period); + input_inject_event(handle, EV_REP, REP_PERIOD, rep->period); d = dev->rep[REP_DELAY]; p = dev->rep[REP_PERIOD]; } @@ -988,7 +988,7 @@ static inline unsigned char getleds(void) * interrupt routines for this thing allows us to easily mask * this when we don't want any of the above to happen. * This allows for easy and efficient race-condition prevention - * for kbd_start => input_event(dev, EV_LED, ...) => ... + * for kbd_start => input_inject_event(dev, EV_LED, ...) => ... */ static void kbd_bh(unsigned long dummy) @@ -998,11 +998,11 @@ static void kbd_bh(unsigned long dummy) if (leds != ledstate) { list_for_each(node, &kbd_handler.h_list) { - struct input_handle * handle = to_handle_h(node); - input_event(handle->dev, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_event(handle->dev, EV_LED, LED_NUML, !!(leds & 0x02)); - input_event(handle->dev, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_sync(handle->dev); + struct input_handle *handle = to_handle_h(node); + input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); + input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); + input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); } } @@ -1310,10 +1310,10 @@ static void kbd_start(struct input_handle *handle) tasklet_disable(&keyboard_tasklet); if (leds != 0xff) { - input_event(handle->dev, EV_LED, LED_SCROLLL, !!(leds & 0x01)); - input_event(handle->dev, EV_LED, LED_NUML, !!(leds & 0x02)); - input_event(handle->dev, EV_LED, LED_CAPSL, !!(leds & 0x04)); - input_sync(handle->dev); + input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & 0x01)); + input_inject_event(handle, EV_LED, LED_NUML, !!(leds & 0x02)); + input_inject_event(handle, EV_LED, LED_CAPSL, !!(leds & 0x04)); + input_inject_event(handle, EV_SYN, SYN_REPORT, 0); } tasklet_enable(&keyboard_tasklet); } diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 2426a5d8537d..4bf48188cc91 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -256,7 +256,7 @@ static ssize_t evdev_write(struct file * file, const char __user * buffer, size_ if (evdev_event_from_user(buffer + retval, &event)) return -EFAULT; - input_event(list->evdev->handle.dev, event.type, event.code, event.value); + input_inject_event(&list->evdev->handle, event.type, event.code, event.value); retval += evdev_event_size(); } @@ -424,8 +424,8 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd, if (get_user(v, ip + 1)) return -EFAULT; - input_event(dev, EV_REP, REP_DELAY, u); - input_event(dev, EV_REP, REP_PERIOD, v); + input_inject_event(&evdev->handle, EV_REP, REP_DELAY, u); + input_inject_event(&evdev->handle, EV_REP, REP_PERIOD, v); return 0; diff --git a/drivers/input/input.c b/drivers/input/input.c index 7aeebb9071c2..e20913942927 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -35,6 +35,16 @@ static LIST_HEAD(input_handler_list); static struct input_handler *input_table[8]; +/** + * input_event() - report new input event + * @handle: device that generated the event + * @type: type of the event + * @code: event code + * @value: value of the event + * + * This function should be used by drivers implementing various input devices + * See also input_inject_event() + */ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct input_handle *handle; @@ -183,6 +193,23 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in } EXPORT_SYMBOL(input_event); +/** + * input_inject_event() - send input event from input handler + * @handle: input handle to send event through + * @type: type of the event + * @code: event code + * @value: value of the event + * + * Similar to input_event() but will ignore event if device is "grabbed" and handle + * injecting event is not the one that owns the device. + */ +void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + if (!handle->dev->grab || handle->dev->grab == handle) + input_event(handle->dev, type, code, value); +} +EXPORT_SYMBOL(input_inject_event); + static void input_repeat_key(unsigned long data) { struct input_dev *dev = (void *) data; diff --git a/include/linux/input.h b/include/linux/input.h index 55e628e8805b..b3253ab72ff7 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -1053,6 +1053,7 @@ void input_close_device(struct input_handle *); int input_flush_device(struct input_handle* handle, struct file* file); void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); +void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value); static inline void input_report_key(struct input_dev *dev, unsigned int code, int value) { -- cgit v1.2.3 From c7948989f84ee6e9c68cc643f8c6a635eb7a904b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 6 Jul 2006 00:23:38 -0400 Subject: Input: wistron - fix section reference mismatches Signed-off-by: Andrew Morton Signed-off-by: Dmitry Torokhov --- drivers/input/misc/wistron_btns.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index ccf0faeee5c1..a8efc1af36cb 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -94,7 +94,7 @@ static void call_bios(struct regs *regs) static ssize_t __init locate_wistron_bios(void __iomem *base) { - static const unsigned char __initdata signature[] = + static unsigned char __initdata signature[] = { 0x42, 0x21, 0x55, 0x30 }; ssize_t offset; @@ -259,11 +259,11 @@ static int __init dmi_matched(struct dmi_system_id *dmi) return 1; } -static struct key_entry keymap_empty[] = { +static struct key_entry keymap_empty[] __initdata = { { KE_END, 0 } }; -static struct key_entry keymap_fs_amilo_pro_v2000[] = { +static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = { { KE_KEY, 0x01, KEY_HELP }, { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, @@ -273,7 +273,7 @@ static struct key_entry keymap_fs_amilo_pro_v2000[] = { { KE_END, 0 } }; -static struct key_entry keymap_fujitsu_n3510[] = { +static struct key_entry keymap_fujitsu_n3510[] __initdata = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, { KE_KEY, 0x36, KEY_WWW }, @@ -285,7 +285,7 @@ static struct key_entry keymap_fujitsu_n3510[] = { { KE_END, 0 } }; -static struct key_entry keymap_wistron_ms2111[] = { +static struct key_entry keymap_wistron_ms2111[] __initdata = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, { KE_KEY, 0x13, KEY_PROG3 }, @@ -294,7 +294,7 @@ static struct key_entry keymap_wistron_ms2111[] = { { KE_END, 0 } }; -static struct key_entry keymap_wistron_ms2141[] = { +static struct key_entry keymap_wistron_ms2141[] __initdata = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, { KE_WIFI, 0x30, 0 }, @@ -307,7 +307,7 @@ static struct key_entry keymap_wistron_ms2141[] = { { KE_END, 0 } }; -static struct key_entry keymap_acer_aspire_1500[] = { +static struct key_entry keymap_acer_aspire_1500[] __initdata = { { KE_KEY, 0x11, KEY_PROG1 }, { KE_KEY, 0x12, KEY_PROG2 }, { KE_WIFI, 0x30, 0 }, @@ -317,7 +317,7 @@ static struct key_entry keymap_acer_aspire_1500[] = { { KE_END, 0 } }; -static struct key_entry keymap_acer_travelmate_240[] = { +static struct key_entry keymap_acer_travelmate_240[] __initdata = { { KE_KEY, 0x31, KEY_MAIL }, { KE_KEY, 0x36, KEY_WWW }, { KE_KEY, 0x11, KEY_PROG1 }, @@ -327,7 +327,7 @@ static struct key_entry keymap_acer_travelmate_240[] = { { KE_END, 0 } }; -static struct key_entry keymap_aopen_1559as[] = { +static struct key_entry keymap_aopen_1559as[] __initdata = { { KE_KEY, 0x01, KEY_HELP }, { KE_KEY, 0x06, KEY_PROG3 }, { KE_KEY, 0x11, KEY_PROG1 }, @@ -343,7 +343,7 @@ static struct key_entry keymap_aopen_1559as[] = { * a list of buttons and their key codes (reported when loading this module * with force=1) and the output of dmidecode to $MODULE_AUTHOR. */ -static struct dmi_system_id dmi_ids[] = { +static struct dmi_system_id dmi_ids[] __initdata = { { .callback = dmi_matched, .ident = "Fujitsu-Siemens Amilo Pro V2000", -- cgit v1.2.3 From fc0a75ce4835187a3f76d6b35f0644d1b168eef5 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Thu, 6 Jul 2006 09:56:30 +0200 Subject: [PATCH] blktrace: fix barrier vs sync typo Signed-off-by: Nathan Scott Signed-off-by: Jens Axboe --- include/linux/blktrace_api.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index a7e8cef73d15..d95615fc6da5 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -11,7 +11,7 @@ enum blktrace_cat { BLK_TC_READ = 1 << 0, /* reads */ BLK_TC_WRITE = 1 << 1, /* writes */ BLK_TC_BARRIER = 1 << 2, /* barrier */ - BLK_TC_SYNC = 1 << 3, /* barrier */ + BLK_TC_SYNC = 1 << 3, /* sync IO */ BLK_TC_QUEUE = 1 << 4, /* queueing/merging */ BLK_TC_REQUEUE = 1 << 5, /* requeueing */ BLK_TC_ISSUE = 1 << 6, /* issue */ -- cgit v1.2.3 From 40359ccb836866435b03a0cb57345002b587d875 Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Thu, 6 Jul 2006 10:03:28 +0200 Subject: [PATCH] blktrace: readahead support Provide the needed kernel support for distinguishing readahead from regular read requests when tracing block devices. Signed-off-by: Nathan Scott Signed-off-by: Jens Axboe --- block/blktrace.c | 5 ++++- include/linux/blktrace_api.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/block/blktrace.c b/block/blktrace.c index 92925e7d9e6c..b8c0702777ff 100644 --- a/block/blktrace.c +++ b/block/blktrace.c @@ -69,7 +69,7 @@ static u32 ddir_act[2] __read_mostly = { BLK_TC_ACT(BLK_TC_READ), BLK_TC_ACT(BLK /* * Bio action bits of interest */ -static u32 bio_act[3] __read_mostly = { 0, BLK_TC_ACT(BLK_TC_BARRIER), BLK_TC_ACT(BLK_TC_SYNC) }; +static u32 bio_act[5] __read_mostly = { 0, BLK_TC_ACT(BLK_TC_BARRIER), BLK_TC_ACT(BLK_TC_SYNC), 0, BLK_TC_ACT(BLK_TC_AHEAD) }; /* * More could be added as needed, taking care to increment the decrementer @@ -79,6 +79,8 @@ static u32 bio_act[3] __read_mostly = { 0, BLK_TC_ACT(BLK_TC_BARRIER), BLK_TC_AC (((rw) & (1 << BIO_RW_BARRIER)) >> (BIO_RW_BARRIER - 0)) #define trace_sync_bit(rw) \ (((rw) & (1 << BIO_RW_SYNC)) >> (BIO_RW_SYNC - 1)) +#define trace_ahead_bit(rw) \ + (((rw) & (1 << BIO_RW_AHEAD)) << (BIO_RW_AHEAD - 0)) /* * The worker for the various blk_add_trace*() types. Fills out a @@ -100,6 +102,7 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, what |= ddir_act[rw & WRITE]; what |= bio_act[trace_barrier_bit(rw)]; what |= bio_act[trace_sync_bit(rw)]; + what |= bio_act[trace_ahead_bit(rw)]; pid = tsk->pid; if (unlikely(act_log_check(bt, what, sector, pid))) diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index d95615fc6da5..2346a1db8561 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -19,6 +19,7 @@ enum blktrace_cat { BLK_TC_FS = 1 << 8, /* fs requests */ BLK_TC_PC = 1 << 9, /* pc requests */ BLK_TC_NOTIFY = 1 << 10, /* special message */ + BLK_TC_AHEAD = 1 << 11, /* readahead */ BLK_TC_END = 1 << 15, /* only 16-bits, reminder */ }; -- cgit v1.2.3 From 1959d21232931dfa686769a21161413f10d6652f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 6 Jul 2006 10:18:05 +0200 Subject: [PATCH] Only the first two bits in bio->bi_rw and rq->flags match Not three, as assumed. This causes the barrier bit to be needlessly set for some IO. Signed-off-by: Jens Axboe --- block/ll_rw_blk.c | 4 ++-- include/linux/blktrace_api.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index ab17c7224bb6..61d6b3c65b66 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -3491,8 +3491,8 @@ EXPORT_SYMBOL(end_request); void blk_rq_bio_prep(request_queue_t *q, struct request *rq, struct bio *bio) { - /* first three bits are identical in rq->flags and bio->bi_rw */ - rq->flags |= (bio->bi_rw & 7); + /* first two bits are identical in rq->flags and bio->bi_rw */ + rq->flags |= (bio->bi_rw & 3); rq->nr_phys_segments = bio_phys_segments(q, bio); rq->nr_hw_segments = bio_hw_segments(q, bio); diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 2346a1db8561..7520cc1ff9e2 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -148,7 +148,7 @@ static inline void blk_add_trace_rq(struct request_queue *q, struct request *rq, u32 what) { struct blk_trace *bt = q->blk_trace; - int rw = rq->flags & 0x07; + int rw = rq->flags & 0x03; if (likely(!bt)) return; -- cgit v1.2.3 From 699756199d65700e8deed59ae250439ca8684686 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Jul 2006 23:54:30 -0400 Subject: Input: fm801-gp - fix use after free Fixes Coverity #id 916 Signed-off-by: Dmitry Torokhov --- drivers/input/gameport/fm801-gp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/gameport/fm801-gp.c b/drivers/input/gameport/fm801-gp.c index 47e93daa0fa7..90de5afe03c2 100644 --- a/drivers/input/gameport/fm801-gp.c +++ b/drivers/input/gameport/fm801-gp.c @@ -106,10 +106,10 @@ static int __devinit fm801_gp_probe(struct pci_dev *pci, const struct pci_device gp->gameport = port; gp->res_port = request_region(port->io, 0x10, "FM801 GP"); if (!gp->res_port) { - kfree(gp); - gameport_free_port(port); printk(KERN_DEBUG "fm801-gp: unable to grab region 0x%x-0x%x\n", port->io, port->io + 0x0f); + gameport_free_port(port); + kfree(gp); return -EBUSY; } -- cgit v1.2.3 From 95349fe8144b7d18f04bdca1c2d3fb85789de4fb Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Jul 2006 23:54:48 -0400 Subject: Input: libps2 - warn instead of oopsing when passed bad arguments This is more user-friendly and also fixes Coverity #id 249 Signed-off-by: Dmitry Torokhov --- drivers/input/serio/libps2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index 61a6f977846f..ed202f2f251a 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -177,6 +177,11 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) return -1; } + if (send && !param) { + WARN_ON(1); + return -1; + } + mutex_lock_nested(&ps2dev->cmd_mutex, SINGLE_DEPTH_NESTING); serio_pause_rx(ps2dev->serio); -- cgit v1.2.3 From 8d64d3722c6abbb43bccd518ececc5559e1962b4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 6 Jul 2006 23:55:00 -0400 Subject: Input: iforce - check array bounds before accessing elements Fixes Coverity #id 864 Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/iforce/iforce-main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index 6d99e3c37884..dc6cfea23866 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -222,22 +222,22 @@ static int iforce_erase_effect(struct input_dev *dev, int effect_id) int err = 0; struct iforce_core_effect* core_effect; - /* Check who is trying to erase this effect */ - if (iforce->core_effects[effect_id].owner != current->pid) { - printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, iforce->core_effects[effect_id].owner); - return -EACCES; - } - if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX) return -EINVAL; - core_effect = iforce->core_effects + effect_id; + core_effect = &iforce->core_effects[effect_id]; + + /* Check who is trying to erase this effect */ + if (core_effect->owner != current->pid) { + printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, core_effect->owner); + return -EACCES; + } if (test_bit(FF_MOD1_IS_USED, core_effect->flags)) - err = release_resource(&(iforce->core_effects[effect_id].mod1_chunk)); + err = release_resource(&core_effect->mod1_chunk); if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags)) - err = release_resource(&(iforce->core_effects[effect_id].mod2_chunk)); + err = release_resource(&core_effect->mod2_chunk); /*TODO: remember to change that if more FF_MOD* bits are added */ core_effect->flags[0] = 0; -- cgit v1.2.3 From a496e25dfb25493a57bcee5d66875d6ff80a9093 Mon Sep 17 00:00:00 2001 From: Dave Jones Date: Fri, 7 Jul 2006 12:31:27 -0400 Subject: [PATCH] Fix cpufreq vs hotplug lockdep recursion. [ There's some not quite baked bits in cpufreq-git right now so sending this on as a patch instead ] On Thu, 2006-07-06 at 07:58 -0700, Tom London wrote: > After installing .2356 I get this each time I boot: > ======================================================= > [ INFO: possible circular locking dependency detected ] > ------------------------------------------------------- > S06cpuspeed/1620 is trying to acquire lock: > (dbs_mutex){--..}, at: [] mutex_lock+0x21/0x24 > > but task is already holding lock: > (cpucontrol){--..}, at: [] mutex_lock+0x21/0x24 > > which lock already depends on the new lock. > make sure the cpu hotplug recursive mutex (yuck) is taken early in the cpufreq codepaths to avoid a AB-BA deadlock. Signed-off-by: Arjan van de Ven Signed-off-by: Dave Jones Signed-off-by: Linus Torvalds --- drivers/cpufreq/cpufreq.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 1ba4039777e8..8d328186f774 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -423,6 +423,8 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy, if (cpufreq_parse_governor(str_governor, &new_policy.policy, &new_policy.governor)) return -EINVAL; + lock_cpu_hotplug(); + /* Do not use cpufreq_set_policy here or the user_policy.max will be wrongly overridden */ mutex_lock(&policy->lock); @@ -432,6 +434,8 @@ static ssize_t store_scaling_governor (struct cpufreq_policy * policy, policy->user_policy.governor = policy->governor; mutex_unlock(&policy->lock); + unlock_cpu_hotplug(); + return ret ? ret : count; } -- cgit v1.2.3 From 5a8da02ba59a9f978e2af4c5da9a029ea5f5ee3b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 7 Jul 2006 16:54:05 -0700 Subject: [NET]: Fix network device interface printk message priority The printk's in the network device interface code should all be tagged with severity. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/dev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index 066a60a75280..0096349ee38b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1727,7 +1727,7 @@ static int ing_filter(struct sk_buff *skb) if (dev->qdisc_ingress) { __u32 ttl = (__u32) G_TC_RTTL(skb->tc_verd); if (MAX_RED_LOOP < ttl++) { - printk("Redir loop detected Dropping packet (%s->%s)\n", + printk(KERN_WARNING "Redir loop detected Dropping packet (%s->%s)\n", skb->input_dev->name, skb->dev->name); return TC_ACT_SHOT; } @@ -2922,7 +2922,7 @@ int register_netdevice(struct net_device *dev) /* Fix illegal SG+CSUM combinations. */ if ((dev->features & NETIF_F_SG) && !(dev->features & NETIF_F_ALL_CSUM)) { - printk("%s: Dropping NETIF_F_SG since no checksum feature.\n", + printk(KERN_NOTICE "%s: Dropping NETIF_F_SG since no checksum feature.\n", dev->name); dev->features &= ~NETIF_F_SG; } @@ -2930,7 +2930,7 @@ int register_netdevice(struct net_device *dev) /* TSO requires that SG is present as well. */ if ((dev->features & NETIF_F_TSO) && !(dev->features & NETIF_F_SG)) { - printk("%s: Dropping NETIF_F_TSO since no SG feature.\n", + printk(KERN_NOTICE "%s: Dropping NETIF_F_TSO since no SG feature.\n", dev->name); dev->features &= ~NETIF_F_TSO; } -- cgit v1.2.3 From 8ba10ab128e88bfbe58f7164543827ef3c3a2c88 Mon Sep 17 00:00:00 2001 From: Steve French Date: Sat, 8 Jul 2006 02:17:40 +0000 Subject: [CIFS] CIFS_DEBUG2 depends on CIFS Signed-off-by: Steve French --- fs/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/Kconfig b/fs/Kconfig index 00aa3d5c5a83..7db05742a92d 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1791,6 +1791,7 @@ config CIFS_POSIX config CIFS_DEBUG2 bool "Enable additional CIFS debugging routines" + depends on CIFS help Enabling this option adds a few more debugging routines to the cifs code which slightly increases the size of -- cgit v1.2.3 From c165b0040502ef0d2b5751eef3cd96ea8210ae4c Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Sat, 8 Jul 2006 13:28:53 -0700 Subject: [TG3]: add amd8131 to "write reorder" chipsets Add the AMD 8131 bridge to the list of chipsets that reorder writes. Signed-off-by: John W. Linville Acked-by: Michael Chan Signed-off-by: David S. Miller --- drivers/net/tg3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index f645921aff8b..ce6f3be86da0 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -10078,6 +10078,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) static struct pci_device_id write_reorder_chipsets[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_8131_BRIDGE) }, { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8385_0) }, { }, -- cgit v1.2.3 From 00181fc94648b4bb30d30ef95506055105316051 Mon Sep 17 00:00:00 2001 From: Matt LaPlante Date: Sat, 8 Jul 2006 13:30:09 -0700 Subject: [ATM]: Typo in drivers/atm/Kconfig... From: Matt LaPlante Signed-off-by: Charles Williams Signed-off-by: David S. Miller --- drivers/atm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/atm/Kconfig b/drivers/atm/Kconfig index 01a9f1cb7743..cfa5af883e13 100644 --- a/drivers/atm/Kconfig +++ b/drivers/atm/Kconfig @@ -398,7 +398,7 @@ config ATM_FORE200E_USE_TASKLET default n help This defers work to be done by the interrupt handler to a - tasklet instead of hanlding everything at interrupt time. This + tasklet instead of handling everything at interrupt time. This may improve the responsive of the host. config ATM_FORE200E_TX_RETRY -- cgit v1.2.3 From 1252ecf63f77ea147bd40f5462c7d9e3d3ae2815 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Sat, 8 Jul 2006 13:30:52 -0700 Subject: [ATM]: fix possible recursive locking in skb_migrate() ok this is a real potential deadlock in a way, it takes two locks of 2 skbuffs without doing any kind of lock ordering; I think the following patch should fix it. Just sort the lock taking order by address of the skb.. it's not pretty but it's the best this can do in a minimally invasive way. Signed-off-by: Arjan van de Ven Signed-off-by: Chas Williams Signed-off-by: David S. Miller --- net/atm/ipcommon.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/net/atm/ipcommon.c b/net/atm/ipcommon.c index 4b1faca5013f..1d3de42fada0 100644 --- a/net/atm/ipcommon.c +++ b/net/atm/ipcommon.c @@ -25,22 +25,27 @@ /* * skb_migrate appends the list at "from" to "to", emptying "from" in the * process. skb_migrate is atomic with respect to all other skb operations on - * "from" and "to". Note that it locks both lists at the same time, so beware - * of potential deadlocks. + * "from" and "to". Note that it locks both lists at the same time, so to deal + * with the lock ordering, the locks are taken in address order. * * This function should live in skbuff.c or skbuff.h. */ -void skb_migrate(struct sk_buff_head *from,struct sk_buff_head *to) +void skb_migrate(struct sk_buff_head *from, struct sk_buff_head *to) { unsigned long flags; struct sk_buff *skb_from = (struct sk_buff *) from; struct sk_buff *skb_to = (struct sk_buff *) to; struct sk_buff *prev; - spin_lock_irqsave(&from->lock,flags); - spin_lock(&to->lock); + if ((unsigned long) from < (unsigned long) to) { + spin_lock_irqsave(&from->lock, flags); + spin_lock_nested(&to->lock, SINGLE_DEPTH_NESTING); + } else { + spin_lock_irqsave(&to->lock, flags); + spin_lock_nested(&from->lock, SINGLE_DEPTH_NESTING); + } prev = from->prev; from->next->prev = to->prev; prev->next = skb_to; @@ -51,7 +56,7 @@ void skb_migrate(struct sk_buff_head *from,struct sk_buff_head *to) from->prev = skb_from; from->next = skb_from; from->qlen = 0; - spin_unlock_irqrestore(&from->lock,flags); + spin_unlock_irqrestore(&from->lock, flags); } -- cgit v1.2.3 From 9c6c6795eda34e4dc38ecac912a16b6314082beb Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sat, 8 Jul 2006 13:33:28 -0700 Subject: [IRDA]: fix drivers/net/irda/ali-ircc.c:ali_ircc_init() The Coverity checker spotted, that from the changes from commit 898b1d16f8230fb912a0c2248df685735c6ceda3 the if (ret) platform_driver_unregister(&ali_ircc_driver); was dead code. This patch changes this function to what seems to have been the intention. Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller --- drivers/net/irda/ali-ircc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index bf1fca5a3fa0..e3c8cd5eca67 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -146,7 +146,7 @@ static int __init ali_ircc_init(void) { ali_chip_t *chip; chipio_t info; - int ret = -ENODEV; + int ret; int cfg, cfg_base; int reg, revision; int i = 0; @@ -160,6 +160,7 @@ static int __init ali_ircc_init(void) return ret; } + ret = -ENODEV; /* Probe for all the ALi chipsets we know about */ for (chip= chips; chip->name; chip++, i++) -- cgit v1.2.3 From 89114afd435a486deb8583e89f490fc274444d18 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 8 Jul 2006 13:34:32 -0700 Subject: [NET] gso: Add skb_is_gso This patch adds the wrapper function skb_is_gso which can be used instead of directly testing skb_shinfo(skb)->gso_size. This makes things a little nicer and allows us to change the primary key for indicating whether an skb is GSO (if we ever want to do that). Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- drivers/net/bnx2.c | 2 +- drivers/net/chelsio/sge.c | 2 +- drivers/net/e1000/e1000_main.c | 7 +++---- drivers/net/forcedeth.c | 2 +- drivers/net/ixgb/ixgb_main.c | 2 +- drivers/net/loopback.c | 2 +- drivers/net/myri10ge/myri10ge.c | 2 +- drivers/net/sky2.c | 2 +- drivers/net/typhoon.c | 4 ++-- drivers/s390/net/qeth_main.c | 2 +- include/linux/netdevice.h | 2 +- include/linux/skbuff.h | 5 +++++ net/bridge/br_forward.c | 2 +- net/bridge/br_netfilter.c | 2 +- net/ipv4/ip_output.c | 4 ++-- net/ipv4/xfrm4_output.c | 2 +- net/ipv6/ip6_output.c | 4 ++-- net/ipv6/xfrm6_output.c | 2 +- 18 files changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 64b6a72b4f6a..db73de0d2511 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -1639,7 +1639,7 @@ bnx2_tx_int(struct bnx2 *bp) skb = tx_buf->skb; #ifdef BCM_TSO /* partial BD completions possible with TSO packets */ - if (skb_shinfo(skb)->gso_size) { + if (skb_is_gso(skb)) { u16 last_idx, last_ring_idx; last_idx = sw_cons + diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c index 87f94d939ff8..61b3754f50ff 100644 --- a/drivers/net/chelsio/sge.c +++ b/drivers/net/chelsio/sge.c @@ -1417,7 +1417,7 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev) struct cpl_tx_pkt *cpl; #ifdef NETIF_F_TSO - if (skb_shinfo(skb)->gso_size) { + if (skb_is_gso(skb)) { int eth_type; struct cpl_tx_pkt_lso *hdr; diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index f77624f5f17b..1f34d06d01b0 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -2394,7 +2394,7 @@ e1000_tso(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, uint8_t ipcss, ipcso, tucss, tucso, hdr_len; int err; - if (skb_shinfo(skb)->gso_size) { + if (skb_is_gso(skb)) { if (skb_header_cloned(skb)) { err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); if (err) @@ -2519,7 +2519,7 @@ e1000_tx_map(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, * tso gets written back prematurely before the data is fully * DMA'd to the controller */ if (!skb->data_len && tx_ring->last_tx_tso && - !skb_shinfo(skb)->gso_size) { + !skb_is_gso(skb)) { tx_ring->last_tx_tso = 0; size -= 4; } @@ -2806,8 +2806,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) #ifdef NETIF_F_TSO /* Controller Erratum workaround */ - if (!skb->data_len && tx_ring->last_tx_tso && - !skb_shinfo(skb)->gso_size) + if (!skb->data_len && tx_ring->last_tx_tso && !skb_is_gso(skb)) count++; #endif diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 037d870712ff..ad81ec68f887 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -1495,7 +1495,7 @@ static int nv_start_xmit(struct sk_buff *skb, struct net_device *dev) np->tx_skbuff[nr] = skb; #ifdef NETIF_F_TSO - if (skb_shinfo(skb)->gso_size) + if (skb_is_gso(skb)) tx_flags_extra = NV_TX2_TSO | (skb_shinfo(skb)->gso_size << NV_TX2_TSO_SHIFT); else #endif diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c index b91e082483f6..7eb08d929139 100644 --- a/drivers/net/ixgb/ixgb_main.c +++ b/drivers/net/ixgb/ixgb_main.c @@ -1173,7 +1173,7 @@ ixgb_tso(struct ixgb_adapter *adapter, struct sk_buff *skb) uint16_t ipcse, tucse, mss; int err; - if(likely(skb_shinfo(skb)->gso_size)) { + if (likely(skb_is_gso(skb))) { if (skb_header_cloned(skb)) { err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); if (err) diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 43fef7de8cb9..997cbce9af6e 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -139,7 +139,7 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) #endif #ifdef LOOPBACK_TSO - if (skb_shinfo(skb)->gso_size) { + if (skb_is_gso(skb)) { BUG_ON(skb->protocol != htons(ETH_P_IP)); BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP); diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index f4c8fd373b9b..ee1de971a712 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -2116,7 +2116,7 @@ abort_linearize: } idx = (idx + 1) & tx->mask; } while (idx != last_idx); - if (skb_shinfo(skb)->gso_size) { + if (skb_is_gso(skb)) { printk(KERN_ERR "myri10ge: %s: TSO but wanted to linearize?!?!?\n", mgp->dev->name); diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 418f169a6a31..31093760aa1e 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -1159,7 +1159,7 @@ static unsigned tx_le_req(const struct sk_buff *skb) count = sizeof(dma_addr_t) / sizeof(u32); count += skb_shinfo(skb)->nr_frags * count; - if (skb_shinfo(skb)->gso_size) + if (skb_is_gso(skb)) ++count; if (skb->ip_summed == CHECKSUM_HW) diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 063816f2b11e..4103c37172f9 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -805,7 +805,7 @@ typhoon_start_tx(struct sk_buff *skb, struct net_device *dev) * If problems develop with TSO, check this first. */ numDesc = skb_shinfo(skb)->nr_frags + 1; - if(skb_tso_size(skb)) + if (skb_is_gso(skb)) numDesc++; /* When checking for free space in the ring, we need to also @@ -845,7 +845,7 @@ typhoon_start_tx(struct sk_buff *skb, struct net_device *dev) TYPHOON_TX_PF_VLAN_TAG_SHIFT); } - if(skb_tso_size(skb)) { + if (skb_is_gso(skb)) { first_txd->processFlags |= TYPHOON_TX_PF_TCP_SEGMENT; first_txd->numDesc++; diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index 8e8963f15731..329e12c1fae4 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -4457,7 +4457,7 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb) queue = card->qdio.out_qs [qeth_get_priority_queue(card, skb, ipv, cast_type)]; - if (skb_shinfo(skb)->gso_size) + if (skb_is_gso(skb)) large_send = card->options.large_send; /*are we able to do TSO ? If so ,prepare and send it from here */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 85f99f60deea..0359a6430018 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1001,7 +1001,7 @@ static inline int net_gso_ok(int features, int gso_type) static inline int skb_gso_ok(struct sk_buff *skb, int features) { - return net_gso_ok(features, skb_shinfo(skb)->gso_size ? + return net_gso_ok(features, skb_is_gso(skb) ? skb_shinfo(skb)->gso_type : 0); } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 3597b4f14389..0bf31b83578c 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1455,5 +1455,10 @@ static inline void skb_init_secmark(struct sk_buff *skb) { } #endif +static inline int skb_is_gso(const struct sk_buff *skb) +{ + return skb_shinfo(skb)->gso_size; +} + #endif /* __KERNEL__ */ #endif /* _LINUX_SKBUFF_H */ diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 8be9f2123e54..6ccd32b30809 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -35,7 +35,7 @@ static inline unsigned packet_length(const struct sk_buff *skb) int br_dev_queue_push_xmit(struct sk_buff *skb) { /* drop mtu oversized packets except gso */ - if (packet_length(skb) > skb->dev->mtu && !skb_shinfo(skb)->gso_size) + if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb)) kfree_skb(skb); else { #ifdef CONFIG_BRIDGE_NETFILTER diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 8298a5179aef..cbc8a389a0a8 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -761,7 +761,7 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb) { if (skb->protocol == htons(ETH_P_IP) && skb->len > skb->dev->mtu && - !skb_shinfo(skb)->gso_size) + !skb_is_gso(skb)) return ip_fragment(skb, br_dev_queue_push_xmit); else return br_dev_queue_push_xmit(skb); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index ca0e714613fb..7c9f9a6421b8 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -209,7 +209,7 @@ static inline int ip_finish_output(struct sk_buff *skb) return dst_output(skb); } #endif - if (skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) + if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) return ip_fragment(skb, ip_finish_output2); else return ip_finish_output2(skb); @@ -1095,7 +1095,7 @@ ssize_t ip_append_page(struct sock *sk, struct page *page, while (size > 0) { int i; - if (skb_shinfo(skb)->gso_size) + if (skb_is_gso(skb)) len = size; else { diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c index 193363e22932..d16f863cf687 100644 --- a/net/ipv4/xfrm4_output.c +++ b/net/ipv4/xfrm4_output.c @@ -134,7 +134,7 @@ static int xfrm4_output_finish(struct sk_buff *skb) } #endif - if (!skb_shinfo(skb)->gso_size) + if (!skb_is_gso(skb)) return xfrm4_output_finish2(skb); skb->protocol = htons(ETH_P_IP); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 2c5b44575af0..3bc74ce78800 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -147,7 +147,7 @@ static int ip6_output2(struct sk_buff *skb) int ip6_output(struct sk_buff *skb) { - if ((skb->len > dst_mtu(skb->dst) && !skb_shinfo(skb)->gso_size) || + if ((skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) || dst_allfrag(skb->dst)) return ip6_fragment(skb, ip6_output2); else @@ -229,7 +229,7 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl, skb->priority = sk->sk_priority; mtu = dst_mtu(dst); - if ((skb->len <= mtu) || ipfragok || skb_shinfo(skb)->gso_size) { + if ((skb->len <= mtu) || ipfragok || skb_is_gso(skb)) { IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c index 48fccb1eca08..0eea60ea9ebc 100644 --- a/net/ipv6/xfrm6_output.c +++ b/net/ipv6/xfrm6_output.c @@ -122,7 +122,7 @@ static int xfrm6_output_finish(struct sk_buff *skb) { struct sk_buff *segs; - if (!skb_shinfo(skb)->gso_size) + if (!skb_is_gso(skb)) return xfrm6_output_finish2(skb); skb->protocol = htons(ETH_P_IP); -- cgit v1.2.3 From a430a43d087545c96542ee64573237919109d370 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 8 Jul 2006 13:34:56 -0700 Subject: [NET] gso: Fix up GSO packets with broken checksums Certain subsystems in the stack (e.g., netfilter) can break the partial checksum on GSO packets. Until they're fixed, this patch allows this to work by recomputing the partial checksums through the GSO mechanism. Once they've all been converted to update the partial checksum instead of clearing it, this workaround can be removed. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/netdevice.h | 8 +++-- include/net/protocol.h | 2 ++ include/net/tcp.h | 1 + net/core/dev.c | 36 ++++++++++++++++--- net/ipv4/af_inet.c | 36 +++++++++++++++++++ net/ipv4/tcp_ipv4.c | 18 ++++++++++ net/ipv6/ipv6_sockglue.c | 89 ++++++++++++++++++++++++++++++++--------------- net/ipv6/tcp_ipv6.c | 19 ++++++++++ 8 files changed, 174 insertions(+), 35 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0359a6430018..76cc099c8580 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -549,6 +549,7 @@ struct packet_type { struct net_device *); struct sk_buff *(*gso_segment)(struct sk_buff *skb, int features); + int (*gso_send_check)(struct sk_buff *skb); void *af_packet_priv; struct list_head list; }; @@ -1001,13 +1002,14 @@ static inline int net_gso_ok(int features, int gso_type) static inline int skb_gso_ok(struct sk_buff *skb, int features) { - return net_gso_ok(features, skb_is_gso(skb) ? - skb_shinfo(skb)->gso_type : 0); + return net_gso_ok(features, skb_shinfo(skb)->gso_type); } static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) { - return !skb_gso_ok(skb, dev->features); + return skb_is_gso(skb) && + (!skb_gso_ok(skb, dev->features) || + unlikely(skb->ip_summed != CHECKSUM_HW)); } #endif /* __KERNEL__ */ diff --git a/include/net/protocol.h b/include/net/protocol.h index a225d6371cb1..c643bce64e55 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -36,6 +36,7 @@ struct net_protocol { int (*handler)(struct sk_buff *skb); void (*err_handler)(struct sk_buff *skb, u32 info); + int (*gso_send_check)(struct sk_buff *skb); struct sk_buff *(*gso_segment)(struct sk_buff *skb, int features); int no_policy; @@ -51,6 +52,7 @@ struct inet6_protocol int type, int code, int offset, __u32 info); + int (*gso_send_check)(struct sk_buff *skb); struct sk_buff *(*gso_segment)(struct sk_buff *skb, int features); diff --git a/include/net/tcp.h b/include/net/tcp.h index 3cd803b0d7a5..0720bddff1e9 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1086,6 +1086,7 @@ extern struct request_sock_ops tcp_request_sock_ops; extern int tcp_v4_destroy_sock(struct sock *sk); +extern int tcp_v4_gso_send_check(struct sk_buff *skb); extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); #ifdef CONFIG_PROC_FS diff --git a/net/core/dev.c b/net/core/dev.c index 0096349ee38b..4d2b5167d7f5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1162,9 +1162,17 @@ int skb_checksum_help(struct sk_buff *skb, int inward) unsigned int csum; int ret = 0, offset = skb->h.raw - skb->data; - if (inward) { - skb->ip_summed = CHECKSUM_NONE; - goto out; + if (inward) + goto out_set_summed; + + if (unlikely(skb_shinfo(skb)->gso_size)) { + static int warned; + + WARN_ON(!warned); + warned = 1; + + /* Let GSO fix up the checksum. */ + goto out_set_summed; } if (skb_cloned(skb)) { @@ -1181,6 +1189,8 @@ int skb_checksum_help(struct sk_buff *skb, int inward) BUG_ON(skb->csum + 2 > offset); *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum); + +out_set_summed: skb->ip_summed = CHECKSUM_NONE; out: return ret; @@ -1201,17 +1211,35 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); struct packet_type *ptype; int type = skb->protocol; + int err; BUG_ON(skb_shinfo(skb)->frag_list); - BUG_ON(skb->ip_summed != CHECKSUM_HW); skb->mac.raw = skb->data; skb->mac_len = skb->nh.raw - skb->data; __skb_pull(skb, skb->mac_len); + if (unlikely(skb->ip_summed != CHECKSUM_HW)) { + static int warned; + + WARN_ON(!warned); + warned = 1; + + if (skb_header_cloned(skb) && + (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) + return ERR_PTR(err); + } + rcu_read_lock(); list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { if (ptype->type == type && !ptype->dev && ptype->gso_segment) { + if (unlikely(skb->ip_summed != CHECKSUM_HW)) { + err = ptype->gso_send_check(skb); + segs = ERR_PTR(err); + if (err || skb_gso_ok(skb, features)) + break; + __skb_push(skb, skb->data - skb->nh.raw); + } segs = ptype->gso_segment(skb, features); break; } diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 318d4674faa1..c84a32070f8d 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1097,6 +1097,40 @@ int inet_sk_rebuild_header(struct sock *sk) EXPORT_SYMBOL(inet_sk_rebuild_header); +static int inet_gso_send_check(struct sk_buff *skb) +{ + struct iphdr *iph; + struct net_protocol *ops; + int proto; + int ihl; + int err = -EINVAL; + + if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) + goto out; + + iph = skb->nh.iph; + ihl = iph->ihl * 4; + if (ihl < sizeof(*iph)) + goto out; + + if (unlikely(!pskb_may_pull(skb, ihl))) + goto out; + + skb->h.raw = __skb_pull(skb, ihl); + iph = skb->nh.iph; + proto = iph->protocol & (MAX_INET_PROTOS - 1); + err = -EPROTONOSUPPORT; + + rcu_read_lock(); + ops = rcu_dereference(inet_protos[proto]); + if (likely(ops && ops->gso_send_check)) + err = ops->gso_send_check(skb); + rcu_read_unlock(); + +out: + return err; +} + static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) { struct sk_buff *segs = ERR_PTR(-EINVAL); @@ -1162,6 +1196,7 @@ static struct net_protocol igmp_protocol = { static struct net_protocol tcp_protocol = { .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, + .gso_send_check = tcp_v4_gso_send_check, .gso_segment = tcp_tso_segment, .no_policy = 1, }; @@ -1208,6 +1243,7 @@ static int ipv4_proc_init(void); static struct packet_type ip_packet_type = { .type = __constant_htons(ETH_P_IP), .func = ip_rcv, + .gso_send_check = inet_gso_send_check, .gso_segment = inet_gso_segment, }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5a886e6efbbe..a891133f00e4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -496,6 +496,24 @@ void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb) } } +int tcp_v4_gso_send_check(struct sk_buff *skb) +{ + struct iphdr *iph; + struct tcphdr *th; + + if (!pskb_may_pull(skb, sizeof(*th))) + return -EINVAL; + + iph = skb->nh.iph; + th = skb->h.th; + + th->check = 0; + th->check = ~tcp_v4_check(th, skb->len, iph->saddr, iph->daddr, 0); + skb->csum = offsetof(struct tcphdr, check); + skb->ip_summed = CHECKSUM_HW; + return 0; +} + /* * This routine will send an RST to the other tcp. * diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 0c17dec11c8d..43327264e69c 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -57,29 +57,11 @@ DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; -static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) +static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb, + int proto) { - struct sk_buff *segs = ERR_PTR(-EINVAL); - struct ipv6hdr *ipv6h; - struct inet6_protocol *ops; - int proto; + struct inet6_protocol *ops = NULL; - if (unlikely(skb_shinfo(skb)->gso_type & - ~(SKB_GSO_UDP | - SKB_GSO_DODGY | - SKB_GSO_TCP_ECN | - SKB_GSO_TCPV6 | - 0))) - goto out; - - if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) - goto out; - - ipv6h = skb->nh.ipv6h; - proto = ipv6h->nexthdr; - __skb_pull(skb, sizeof(*ipv6h)); - - rcu_read_lock(); for (;;) { struct ipv6_opt_hdr *opth; int len; @@ -88,30 +70,80 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) ops = rcu_dereference(inet6_protos[proto]); if (unlikely(!ops)) - goto unlock; + break; if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) break; } if (unlikely(!pskb_may_pull(skb, 8))) - goto unlock; + break; opth = (void *)skb->data; len = opth->hdrlen * 8 + 8; if (unlikely(!pskb_may_pull(skb, len))) - goto unlock; + break; proto = opth->nexthdr; __skb_pull(skb, len); } - skb->h.raw = skb->data; - if (likely(ops->gso_segment)) - segs = ops->gso_segment(skb, features); + return ops; +} + +static int ipv6_gso_send_check(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + struct inet6_protocol *ops; + int err = -EINVAL; + + if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) + goto out; -unlock: + ipv6h = skb->nh.ipv6h; + __skb_pull(skb, sizeof(*ipv6h)); + err = -EPROTONOSUPPORT; + + rcu_read_lock(); + ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); + if (likely(ops && ops->gso_send_check)) { + skb->h.raw = skb->data; + err = ops->gso_send_check(skb); + } + rcu_read_unlock(); + +out: + return err; +} + +static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + struct ipv6hdr *ipv6h; + struct inet6_protocol *ops; + + if (unlikely(skb_shinfo(skb)->gso_type & + ~(SKB_GSO_UDP | + SKB_GSO_DODGY | + SKB_GSO_TCP_ECN | + SKB_GSO_TCPV6 | + 0))) + goto out; + + if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) + goto out; + + ipv6h = skb->nh.ipv6h; + __skb_pull(skb, sizeof(*ipv6h)); + segs = ERR_PTR(-EPROTONOSUPPORT); + + rcu_read_lock(); + ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); + if (likely(ops && ops->gso_segment)) { + skb->h.raw = skb->data; + segs = ops->gso_segment(skb, features); + } rcu_read_unlock(); if (unlikely(IS_ERR(segs))) @@ -130,6 +162,7 @@ out: static struct packet_type ipv6_packet_type = { .type = __constant_htons(ETH_P_IPV6), .func = ipv6_rcv, + .gso_send_check = ipv6_gso_send_check, .gso_segment = ipv6_gso_segment, }; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5bdcb9002cf7..923989d0520d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -552,6 +552,24 @@ static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb) } } +static int tcp_v6_gso_send_check(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + struct tcphdr *th; + + if (!pskb_may_pull(skb, sizeof(*th))) + return -EINVAL; + + ipv6h = skb->nh.ipv6h; + th = skb->h.th; + + th->check = 0; + th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, + IPPROTO_TCP, 0); + skb->csum = offsetof(struct tcphdr, check); + skb->ip_summed = CHECKSUM_HW; + return 0; +} static void tcp_v6_send_reset(struct sk_buff *skb) { @@ -1603,6 +1621,7 @@ struct proto tcpv6_prot = { static struct inet6_protocol tcpv6_protocol = { .handler = tcp_v6_rcv, .err_handler = tcp_v6_err, + .gso_send_check = tcp_v6_gso_send_check, .gso_segment = tcp_tso_segment, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, }; -- cgit v1.2.3 From 26e0fd1ce2418b10713b569a195bdb679233066b Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 8 Jul 2006 13:38:55 -0700 Subject: [NET]: Fix IPv4/DECnet routing rule dumping When more rules are present than fit in a single skb, the remaining rules are incorrectly skipped. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/decnet/dn_rules.c | 3 ++- net/ipv4/fib_rules.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c index 06e785fe5757..22f321d9bf9d 100644 --- a/net/decnet/dn_rules.c +++ b/net/decnet/dn_rules.c @@ -399,9 +399,10 @@ int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); hlist_for_each_entry(r, node, &dn_fib_rules, r_hlist) { if (idx < s_idx) - continue; + goto next; if (dn_fib_fill_rule(skb, r, cb, NLM_F_MULTI) < 0) break; +next: idx++; } rcu_read_unlock(); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 6c642d11d4ca..773b12ba4e3c 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -457,13 +457,13 @@ int inet_dump_rules(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); hlist_for_each_entry(r, node, &fib_rules, hlist) { - if (idx < s_idx) - continue; + goto next; if (inet_fill_rule(skb, r, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWRULE, NLM_F_MULTI) < 0) break; +next: idx++; } rcu_read_unlock(); -- cgit v1.2.3 From e2a3d40258fe20d205f8ed592e1e2c0d5529c2e1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 8 Jul 2006 15:00:28 -0700 Subject: power: improve inline asm memory constraints Use "+m" rather than a combination of "=m" and "m" for improved clarity and consistency. Signed-off-by: Linus Torvalds --- include/asm-powerpc/atomic.h | 32 ++++++++++++++++---------------- include/asm-powerpc/bitops.h | 16 ++++++++-------- include/asm-powerpc/system.h | 16 ++++++++-------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/include/asm-powerpc/atomic.h b/include/asm-powerpc/atomic.h index bb3c0ab7e667..53283e2540b3 100644 --- a/include/asm-powerpc/atomic.h +++ b/include/asm-powerpc/atomic.h @@ -27,8 +27,8 @@ static __inline__ void atomic_add(int a, atomic_t *v) PPC405_ERR77(0,%3) " stwcx. %0,0,%3 \n\ bne- 1b" - : "=&r" (t), "=m" (v->counter) - : "r" (a), "r" (&v->counter), "m" (v->counter) + : "=&r" (t), "+m" (v->counter) + : "r" (a), "r" (&v->counter) : "cc"); } @@ -63,8 +63,8 @@ static __inline__ void atomic_sub(int a, atomic_t *v) PPC405_ERR77(0,%3) " stwcx. %0,0,%3 \n\ bne- 1b" - : "=&r" (t), "=m" (v->counter) - : "r" (a), "r" (&v->counter), "m" (v->counter) + : "=&r" (t), "+m" (v->counter) + : "r" (a), "r" (&v->counter) : "cc"); } @@ -97,8 +97,8 @@ static __inline__ void atomic_inc(atomic_t *v) PPC405_ERR77(0,%2) " stwcx. %0,0,%2 \n\ bne- 1b" - : "=&r" (t), "=m" (v->counter) - : "r" (&v->counter), "m" (v->counter) + : "=&r" (t), "+m" (v->counter) + : "r" (&v->counter) : "cc"); } @@ -141,8 +141,8 @@ static __inline__ void atomic_dec(atomic_t *v) PPC405_ERR77(0,%2)\ " stwcx. %0,0,%2\n\ bne- 1b" - : "=&r" (t), "=m" (v->counter) - : "r" (&v->counter), "m" (v->counter) + : "=&r" (t), "+m" (v->counter) + : "r" (&v->counter) : "cc"); } @@ -253,8 +253,8 @@ static __inline__ void atomic64_add(long a, atomic64_t *v) add %0,%2,%0\n\ stdcx. %0,0,%3 \n\ bne- 1b" - : "=&r" (t), "=m" (v->counter) - : "r" (a), "r" (&v->counter), "m" (v->counter) + : "=&r" (t), "+m" (v->counter) + : "r" (a), "r" (&v->counter) : "cc"); } @@ -287,8 +287,8 @@ static __inline__ void atomic64_sub(long a, atomic64_t *v) subf %0,%2,%0\n\ stdcx. %0,0,%3 \n\ bne- 1b" - : "=&r" (t), "=m" (v->counter) - : "r" (a), "r" (&v->counter), "m" (v->counter) + : "=&r" (t), "+m" (v->counter) + : "r" (a), "r" (&v->counter) : "cc"); } @@ -319,8 +319,8 @@ static __inline__ void atomic64_inc(atomic64_t *v) addic %0,%0,1\n\ stdcx. %0,0,%2 \n\ bne- 1b" - : "=&r" (t), "=m" (v->counter) - : "r" (&v->counter), "m" (v->counter) + : "=&r" (t), "+m" (v->counter) + : "r" (&v->counter) : "cc"); } @@ -361,8 +361,8 @@ static __inline__ void atomic64_dec(atomic64_t *v) addic %0,%0,-1\n\ stdcx. %0,0,%2\n\ bne- 1b" - : "=&r" (t), "=m" (v->counter) - : "r" (&v->counter), "m" (v->counter) + : "=&r" (t), "+m" (v->counter) + : "r" (&v->counter) : "cc"); } diff --git a/include/asm-powerpc/bitops.h b/include/asm-powerpc/bitops.h index 76e2f08c3c83..c341063d0804 100644 --- a/include/asm-powerpc/bitops.h +++ b/include/asm-powerpc/bitops.h @@ -65,8 +65,8 @@ static __inline__ void set_bit(int nr, volatile unsigned long *addr) PPC405_ERR77(0,%3) PPC_STLCX "%0,0,%3\n" "bne- 1b" - : "=&r"(old), "=m"(*p) - : "r"(mask), "r"(p), "m"(*p) + : "=&r" (old), "+m" (*p) + : "r" (mask), "r" (p) : "cc" ); } @@ -82,8 +82,8 @@ static __inline__ void clear_bit(int nr, volatile unsigned long *addr) PPC405_ERR77(0,%3) PPC_STLCX "%0,0,%3\n" "bne- 1b" - : "=&r"(old), "=m"(*p) - : "r"(mask), "r"(p), "m"(*p) + : "=&r" (old), "+m" (*p) + : "r" (mask), "r" (p) : "cc" ); } @@ -99,8 +99,8 @@ static __inline__ void change_bit(int nr, volatile unsigned long *addr) PPC405_ERR77(0,%3) PPC_STLCX "%0,0,%3\n" "bne- 1b" - : "=&r"(old), "=m"(*p) - : "r"(mask), "r"(p), "m"(*p) + : "=&r" (old), "+m" (*p) + : "r" (mask), "r" (p) : "cc" ); } @@ -179,8 +179,8 @@ static __inline__ void set_bits(unsigned long mask, unsigned long *addr) "or %0,%0,%2\n" PPC_STLCX "%0,0,%3\n" "bne- 1b" - : "=&r" (old), "=m" (*addr) - : "r" (mask), "r" (addr), "m" (*addr) + : "=&r" (old), "+m" (*addr) + : "r" (mask), "r" (addr) : "cc"); } diff --git a/include/asm-powerpc/system.h b/include/asm-powerpc/system.h index d075725bf444..c6569516ba35 100644 --- a/include/asm-powerpc/system.h +++ b/include/asm-powerpc/system.h @@ -220,8 +220,8 @@ __xchg_u32(volatile void *p, unsigned long val) " stwcx. %3,0,%2 \n\ bne- 1b" ISYNC_ON_SMP - : "=&r" (prev), "=m" (*(volatile unsigned int *)p) - : "r" (p), "r" (val), "m" (*(volatile unsigned int *)p) + : "=&r" (prev), "+m" (*(volatile unsigned int *)p) + : "r" (p), "r" (val) : "cc", "memory"); return prev; @@ -240,8 +240,8 @@ __xchg_u64(volatile void *p, unsigned long val) " stdcx. %3,0,%2 \n\ bne- 1b" ISYNC_ON_SMP - : "=&r" (prev), "=m" (*(volatile unsigned long *)p) - : "r" (p), "r" (val), "m" (*(volatile unsigned long *)p) + : "=&r" (prev), "+m" (*(volatile unsigned long *)p) + : "r" (p), "r" (val) : "cc", "memory"); return prev; @@ -299,8 +299,8 @@ __cmpxchg_u32(volatile unsigned int *p, unsigned long old, unsigned long new) ISYNC_ON_SMP "\n\ 2:" - : "=&r" (prev), "=m" (*p) - : "r" (p), "r" (old), "r" (new), "m" (*p) + : "=&r" (prev), "+m" (*p) + : "r" (p), "r" (old), "r" (new) : "cc", "memory"); return prev; @@ -322,8 +322,8 @@ __cmpxchg_u64(volatile unsigned long *p, unsigned long old, unsigned long new) ISYNC_ON_SMP "\n\ 2:" - : "=&r" (prev), "=m" (*p) - : "r" (p), "r" (old), "r" (new), "m" (*p) + : "=&r" (prev), "+m" (*p) + : "r" (p), "r" (old), "r" (new) : "cc", "memory"); return prev; -- cgit v1.2.3 From b862f3b099f3ea672c7438c0b282ce8201d39dfc Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 8 Jul 2006 15:24:18 -0700 Subject: i386: improve and correct inline asm memory constraints Use "+m" rather than a combination of "=m" and "m" for improved clarity and consistency. This also fixes some inlines that incorrectly didn't tell the compiler that they read the old value at all, potentially causing the compiler to generate bogus code. It appear that all of those potential bugs were hidden by the use of extra "volatile" specifiers on the data structures in question, though. Signed-off-by: Linus Torvalds --- include/asm-i386/atomic.h | 30 ++++++++++++++---------------- include/asm-i386/futex.h | 10 +++++----- include/asm-i386/local.h | 14 ++++++-------- include/asm-i386/posix_types.h | 4 ++-- include/asm-i386/rwlock.h | 4 ++-- include/asm-i386/rwsem.h | 35 +++++++++++++++++------------------ include/asm-i386/semaphore.h | 8 ++++---- include/asm-i386/spinlock.h | 14 +++++++------- 8 files changed, 57 insertions(+), 62 deletions(-) diff --git a/include/asm-i386/atomic.h b/include/asm-i386/atomic.h index 4f061fa73794..51a166242522 100644 --- a/include/asm-i386/atomic.h +++ b/include/asm-i386/atomic.h @@ -46,8 +46,8 @@ static __inline__ void atomic_add(int i, atomic_t *v) { __asm__ __volatile__( LOCK_PREFIX "addl %1,%0" - :"=m" (v->counter) - :"ir" (i), "m" (v->counter)); + :"+m" (v->counter) + :"ir" (i)); } /** @@ -61,8 +61,8 @@ static __inline__ void atomic_sub(int i, atomic_t *v) { __asm__ __volatile__( LOCK_PREFIX "subl %1,%0" - :"=m" (v->counter) - :"ir" (i), "m" (v->counter)); + :"+m" (v->counter) + :"ir" (i)); } /** @@ -80,8 +80,8 @@ static __inline__ int atomic_sub_and_test(int i, atomic_t *v) __asm__ __volatile__( LOCK_PREFIX "subl %2,%0; sete %1" - :"=m" (v->counter), "=qm" (c) - :"ir" (i), "m" (v->counter) : "memory"); + :"+m" (v->counter), "=qm" (c) + :"ir" (i) : "memory"); return c; } @@ -95,8 +95,7 @@ static __inline__ void atomic_inc(atomic_t *v) { __asm__ __volatile__( LOCK_PREFIX "incl %0" - :"=m" (v->counter) - :"m" (v->counter)); + :"+m" (v->counter)); } /** @@ -109,8 +108,7 @@ static __inline__ void atomic_dec(atomic_t *v) { __asm__ __volatile__( LOCK_PREFIX "decl %0" - :"=m" (v->counter) - :"m" (v->counter)); + :"+m" (v->counter)); } /** @@ -127,8 +125,8 @@ static __inline__ int atomic_dec_and_test(atomic_t *v) __asm__ __volatile__( LOCK_PREFIX "decl %0; sete %1" - :"=m" (v->counter), "=qm" (c) - :"m" (v->counter) : "memory"); + :"+m" (v->counter), "=qm" (c) + : : "memory"); return c != 0; } @@ -146,8 +144,8 @@ static __inline__ int atomic_inc_and_test(atomic_t *v) __asm__ __volatile__( LOCK_PREFIX "incl %0; sete %1" - :"=m" (v->counter), "=qm" (c) - :"m" (v->counter) : "memory"); + :"+m" (v->counter), "=qm" (c) + : : "memory"); return c != 0; } @@ -166,8 +164,8 @@ static __inline__ int atomic_add_negative(int i, atomic_t *v) __asm__ __volatile__( LOCK_PREFIX "addl %2,%0; sets %1" - :"=m" (v->counter), "=qm" (c) - :"ir" (i), "m" (v->counter) : "memory"); + :"+m" (v->counter), "=qm" (c) + :"ir" (i) : "memory"); return c; } diff --git a/include/asm-i386/futex.h b/include/asm-i386/futex.h index 7b8ceefd010f..946d97cfea23 100644 --- a/include/asm-i386/futex.h +++ b/include/asm-i386/futex.h @@ -20,8 +20,8 @@ .align 8\n\ .long 1b,3b\n\ .previous" \ - : "=r" (oldval), "=r" (ret), "=m" (*uaddr) \ - : "i" (-EFAULT), "m" (*uaddr), "0" (oparg), "1" (0)) + : "=r" (oldval), "=r" (ret), "+m" (*uaddr) \ + : "i" (-EFAULT), "0" (oparg), "1" (0)) #define __futex_atomic_op2(insn, ret, oldval, uaddr, oparg) \ __asm__ __volatile ( \ @@ -38,9 +38,9 @@ .align 8\n\ .long 1b,4b,2b,4b\n\ .previous" \ - : "=&a" (oldval), "=&r" (ret), "=m" (*uaddr), \ + : "=&a" (oldval), "=&r" (ret), "+m" (*uaddr), \ "=&r" (tem) \ - : "r" (oparg), "i" (-EFAULT), "m" (*uaddr), "1" (0)) + : "r" (oparg), "i" (-EFAULT), "1" (0)) static inline int futex_atomic_op_inuser (int encoded_op, int __user *uaddr) @@ -123,7 +123,7 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval) " .long 1b,3b \n" " .previous \n" - : "=a" (oldval), "=m" (*uaddr) + : "=a" (oldval), "+m" (*uaddr) : "i" (-EFAULT), "r" (newval), "0" (oldval) : "memory" ); diff --git a/include/asm-i386/local.h b/include/asm-i386/local.h index 3b4998c51d08..12060e22f7e2 100644 --- a/include/asm-i386/local.h +++ b/include/asm-i386/local.h @@ -17,32 +17,30 @@ static __inline__ void local_inc(local_t *v) { __asm__ __volatile__( "incl %0" - :"=m" (v->counter) - :"m" (v->counter)); + :"+m" (v->counter)); } static __inline__ void local_dec(local_t *v) { __asm__ __volatile__( "decl %0" - :"=m" (v->counter) - :"m" (v->counter)); + :"+m" (v->counter)); } static __inline__ void local_add(long i, local_t *v) { __asm__ __volatile__( "addl %1,%0" - :"=m" (v->counter) - :"ir" (i), "m" (v->counter)); + :"+m" (v->counter) + :"ir" (i)); } static __inline__ void local_sub(long i, local_t *v) { __asm__ __volatile__( "subl %1,%0" - :"=m" (v->counter) - :"ir" (i), "m" (v->counter)); + :"+m" (v->counter) + :"ir" (i)); } /* On x86, these are no better than the atomic variants. */ diff --git a/include/asm-i386/posix_types.h b/include/asm-i386/posix_types.h index 4e47ed059ad6..133e31e7dfde 100644 --- a/include/asm-i386/posix_types.h +++ b/include/asm-i386/posix_types.h @@ -51,12 +51,12 @@ typedef struct { #undef __FD_SET #define __FD_SET(fd,fdsetp) \ __asm__ __volatile__("btsl %1,%0": \ - "=m" (*(__kernel_fd_set *) (fdsetp)):"r" ((int) (fd))) + "+m" (*(__kernel_fd_set *) (fdsetp)):"r" ((int) (fd))) #undef __FD_CLR #define __FD_CLR(fd,fdsetp) \ __asm__ __volatile__("btrl %1,%0": \ - "=m" (*(__kernel_fd_set *) (fdsetp)):"r" ((int) (fd))) + "+m" (*(__kernel_fd_set *) (fdsetp)):"r" ((int) (fd))) #undef __FD_ISSET #define __FD_ISSET(fd,fdsetp) (__extension__ ({ \ diff --git a/include/asm-i386/rwlock.h b/include/asm-i386/rwlock.h index 94f00195d543..96b0bef2ea56 100644 --- a/include/asm-i386/rwlock.h +++ b/include/asm-i386/rwlock.h @@ -37,7 +37,7 @@ "popl %%eax\n\t" \ "1:\n", \ "subl $1,%0\n\t", \ - "=m" (*(volatile int *)rw) : : "memory") + "+m" (*(volatile int *)rw) : : "memory") #define __build_read_lock(rw, helper) do { \ if (__builtin_constant_p(rw)) \ @@ -63,7 +63,7 @@ "popl %%eax\n\t" \ "1:\n", \ "subl $" RW_LOCK_BIAS_STR ",%0\n\t", \ - "=m" (*(volatile int *)rw) : : "memory") + "+m" (*(volatile int *)rw) : : "memory") #define __build_write_lock(rw, helper) do { \ if (__builtin_constant_p(rw)) \ diff --git a/include/asm-i386/rwsem.h b/include/asm-i386/rwsem.h index 2f07601562e7..43113f5608eb 100644 --- a/include/asm-i386/rwsem.h +++ b/include/asm-i386/rwsem.h @@ -111,8 +111,8 @@ LOCK_PREFIX " incl (%%eax)\n\t" /* adds 0x00000001, returns the old value " jmp 1b\n" LOCK_SECTION_END "# ending down_read\n\t" - : "=m"(sem->count) - : "a"(sem), "m"(sem->count) + : "+m" (sem->count) + : "a" (sem) : "memory", "cc"); } @@ -133,8 +133,8 @@ LOCK_PREFIX " cmpxchgl %2,%0\n\t" " jnz 1b\n\t" "2:\n\t" "# ending __down_read_trylock\n\t" - : "+m"(sem->count), "=&a"(result), "=&r"(tmp) - : "i"(RWSEM_ACTIVE_READ_BIAS) + : "+m" (sem->count), "=&a" (result), "=&r" (tmp) + : "i" (RWSEM_ACTIVE_READ_BIAS) : "memory", "cc"); return result>=0 ? 1 : 0; } @@ -161,8 +161,8 @@ LOCK_PREFIX " xadd %%edx,(%%eax)\n\t" /* subtract 0x0000ffff, returns the " jmp 1b\n" LOCK_SECTION_END "# ending down_write" - : "=m"(sem->count), "=d"(tmp) - : "a"(sem), "1"(tmp), "m"(sem->count) + : "+m" (sem->count), "=d" (tmp) + : "a" (sem), "1" (tmp) : "memory", "cc"); } @@ -205,8 +205,8 @@ LOCK_PREFIX " xadd %%edx,(%%eax)\n\t" /* subtracts 1, returns the old valu " jmp 1b\n" LOCK_SECTION_END "# ending __up_read\n" - : "=m"(sem->count), "=d"(tmp) - : "a"(sem), "1"(tmp), "m"(sem->count) + : "+m" (sem->count), "=d" (tmp) + : "a" (sem), "1" (tmp) : "memory", "cc"); } @@ -231,8 +231,8 @@ LOCK_PREFIX " xaddl %%edx,(%%eax)\n\t" /* tries to transition 0xffff0001 -> " jmp 1b\n" LOCK_SECTION_END "# ending __up_write\n" - : "=m"(sem->count) - : "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS), "m"(sem->count) + : "+m" (sem->count) + : "a" (sem), "i" (-RWSEM_ACTIVE_WRITE_BIAS) : "memory", "cc", "edx"); } @@ -256,8 +256,8 @@ LOCK_PREFIX " addl %2,(%%eax)\n\t" /* transitions 0xZZZZ0001 -> 0xYYYY0001 " jmp 1b\n" LOCK_SECTION_END "# ending __downgrade_write\n" - : "=m"(sem->count) - : "a"(sem), "i"(-RWSEM_WAITING_BIAS), "m"(sem->count) + : "+m" (sem->count) + : "a" (sem), "i" (-RWSEM_WAITING_BIAS) : "memory", "cc"); } @@ -268,8 +268,8 @@ static inline void rwsem_atomic_add(int delta, struct rw_semaphore *sem) { __asm__ __volatile__( LOCK_PREFIX "addl %1,%0" - : "=m"(sem->count) - : "ir"(delta), "m"(sem->count)); + : "+m" (sem->count) + : "ir" (delta)); } /* @@ -280,10 +280,9 @@ static inline int rwsem_atomic_update(int delta, struct rw_semaphore *sem) int tmp = delta; __asm__ __volatile__( -LOCK_PREFIX "xadd %0,(%2)" - : "+r"(tmp), "=m"(sem->count) - : "r"(sem), "m"(sem->count) - : "memory"); +LOCK_PREFIX "xadd %0,%1" + : "+r" (tmp), "+m" (sem->count) + : : "memory"); return tmp+delta; } diff --git a/include/asm-i386/semaphore.h b/include/asm-i386/semaphore.h index f7a0f310c524..d51e800acf29 100644 --- a/include/asm-i386/semaphore.h +++ b/include/asm-i386/semaphore.h @@ -107,7 +107,7 @@ static inline void down(struct semaphore * sem) "call __down_failed\n\t" "jmp 1b\n" LOCK_SECTION_END - :"=m" (sem->count) + :"+m" (sem->count) : :"memory","ax"); } @@ -132,7 +132,7 @@ static inline int down_interruptible(struct semaphore * sem) "call __down_failed_interruptible\n\t" "jmp 1b\n" LOCK_SECTION_END - :"=a" (result), "=m" (sem->count) + :"=a" (result), "+m" (sem->count) : :"memory"); return result; @@ -157,7 +157,7 @@ static inline int down_trylock(struct semaphore * sem) "call __down_failed_trylock\n\t" "jmp 1b\n" LOCK_SECTION_END - :"=a" (result), "=m" (sem->count) + :"=a" (result), "+m" (sem->count) : :"memory"); return result; @@ -182,7 +182,7 @@ static inline void up(struct semaphore * sem) "jmp 1b\n" LOCK_SECTION_END ".subsection 0\n" - :"=m" (sem->count) + :"+m" (sem->count) : :"memory","ax"); } diff --git a/include/asm-i386/spinlock.h b/include/asm-i386/spinlock.h index 87c40f830653..d816c62a7a1d 100644 --- a/include/asm-i386/spinlock.h +++ b/include/asm-i386/spinlock.h @@ -65,7 +65,7 @@ static inline void __raw_spin_lock(raw_spinlock_t *lock) alternative_smp( __raw_spin_lock_string, __raw_spin_lock_string_up, - "=m" (lock->slock) : : "memory"); + "+m" (lock->slock) : : "memory"); } /* @@ -79,7 +79,7 @@ static inline void __raw_spin_lock_flags(raw_spinlock_t *lock, unsigned long fla alternative_smp( __raw_spin_lock_string_flags, __raw_spin_lock_string_up, - "=m" (lock->slock) : "r" (flags) : "memory"); + "+m" (lock->slock) : "r" (flags) : "memory"); } #endif @@ -88,7 +88,7 @@ static inline int __raw_spin_trylock(raw_spinlock_t *lock) char oldval; __asm__ __volatile__( "xchgb %b0,%1" - :"=q" (oldval), "=m" (lock->slock) + :"=q" (oldval), "+m" (lock->slock) :"0" (0) : "memory"); return oldval > 0; } @@ -104,7 +104,7 @@ static inline int __raw_spin_trylock(raw_spinlock_t *lock) #define __raw_spin_unlock_string \ "movb $1,%0" \ - :"=m" (lock->slock) : : "memory" + :"+m" (lock->slock) : : "memory" static inline void __raw_spin_unlock(raw_spinlock_t *lock) @@ -118,7 +118,7 @@ static inline void __raw_spin_unlock(raw_spinlock_t *lock) #define __raw_spin_unlock_string \ "xchgb %b0, %1" \ - :"=q" (oldval), "=m" (lock->slock) \ + :"=q" (oldval), "+m" (lock->slock) \ :"0" (oldval) : "memory" static inline void __raw_spin_unlock(raw_spinlock_t *lock) @@ -199,13 +199,13 @@ static inline int __raw_write_trylock(raw_rwlock_t *lock) static inline void __raw_read_unlock(raw_rwlock_t *rw) { - asm volatile(LOCK_PREFIX "incl %0" :"=m" (rw->lock) : : "memory"); + asm volatile(LOCK_PREFIX "incl %0" :"+m" (rw->lock) : : "memory"); } static inline void __raw_write_unlock(raw_rwlock_t *rw) { asm volatile(LOCK_PREFIX "addl $" RW_LOCK_BIAS_STR ", %0" - : "=m" (rw->lock) : : "memory"); + : "+m" (rw->lock) : : "memory"); } #endif /* __ASM_SPINLOCK_H */ -- cgit v1.2.3 From 24f6d2fd314f8580fcfd96391ce9689727d55572 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 30 Jun 2006 02:33:07 -0700 Subject: [SCSI] aic79xx: make ahd_done_with_status() static This patch makes a needlessly global function static. Signed-off-by: Adrian Bunk Acked-by: Hannes Reinecke Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/aic7xxx/aic79xx_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index a1e8ca758594..653818d2f802 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c @@ -7289,7 +7289,7 @@ ahd_reset_cmds_pending(struct ahd_softc *ahd) ahd->flags &= ~AHD_UPDATE_PEND_CMDS; } -void +static void ahd_done_with_status(struct ahd_softc *ahd, struct scb *scb, uint32_t status) { cam_status ostat; -- cgit v1.2.3 From c9fefeb26457b87f4a767faefcf77321bb90db52 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 2 Jul 2006 11:10:18 -0500 Subject: [SCSI] scsi_transport_sas: add unindexed ports Some SAS HBAs don't want to go to the trouble of tracking port numbers, so they'd simply like to say "add this port and give it a number". This is especially beneficial from the hotplug point of view, since tracking ports and the available number space can be a real pain. The current implementation uses an incrementing number per expander to add the port on. However, since there can never be more ports than there are phys, a later implementation will try to be more intelligent about this. Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_sas.c | 36 +++++++++++++++++++++++++++++++++++- include/scsi/scsi_transport_sas.h | 4 +++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index dd075627e605..9bb4e37a1a61 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -41,6 +41,7 @@ struct sas_host_attrs { struct mutex lock; u32 next_target_id; u32 next_expander_id; + int next_port_id; }; #define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data) @@ -146,6 +147,7 @@ static int sas_host_setup(struct transport_container *tc, struct device *dev, mutex_init(&sas_host->lock); sas_host->next_target_id = 0; sas_host->next_expander_id = 0; + sas_host->next_port_id = 0; return 0; } @@ -327,7 +329,7 @@ sas_phy_protocol_attr(identify.target_port_protocols, sas_phy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n", unsigned long long); sas_phy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); -//sas_phy_simple_attr(port_identifier, port_identifier, "%d\n", u8); +//sas_phy_simple_attr(port_identifier, port_identifier, "%d\n", int); sas_phy_linkspeed_attr(negotiated_linkrate); sas_phy_linkspeed_attr(minimum_linkrate_hw); sas_phy_linkspeed_attr(minimum_linkrate); @@ -590,6 +592,38 @@ struct sas_port *sas_port_alloc(struct device *parent, int port_id) } EXPORT_SYMBOL(sas_port_alloc); +/** sas_port_alloc_num - allocate and initialize a SAS port structure + * + * @parent: parent device + * + * Allocates a SAS port structure and a number to go with it. This + * interface is really for adapters where the port number has no + * meansing, so the sas class should manage them. It will be added to + * the device tree below the device specified by @parent which must be + * either a Scsi_Host or a sas_expander_device. + * + * Returns %NULL on error + */ +struct sas_port *sas_port_alloc_num(struct device *parent) +{ + int index; + struct Scsi_Host *shost = dev_to_shost(parent); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + + /* FIXME: use idr for this eventually */ + mutex_lock(&sas_host->lock); + if (scsi_is_sas_expander_device(parent)) { + struct sas_rphy *rphy = dev_to_rphy(parent); + struct sas_expander_device *exp = rphy_to_expander_device(rphy); + + index = exp->next_port_id++; + } else + index = sas_host->next_port_id++; + mutex_unlock(&sas_host->lock); + return sas_port_alloc(parent, index); +} +EXPORT_SYMBOL(sas_port_alloc_num); + /** * sas_port_add - add a SAS port to the device hierarchy * diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index e3c503cd175e..f5772ff61d0d 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -106,6 +106,7 @@ struct sas_end_device { struct sas_expander_device { int level; + int next_port_id; #define SAS_EXPANDER_VENDOR_ID_LEN 8 char vendor_id[SAS_EXPANDER_VENDOR_ID_LEN+1]; @@ -127,7 +128,7 @@ struct sas_expander_device { struct sas_port { struct device dev; - u8 port_identifier; + int port_identifier; int num_phys; /* the other end of the link */ @@ -168,6 +169,7 @@ extern void sas_rphy_delete(struct sas_rphy *); extern int scsi_is_sas_rphy(const struct device *); struct sas_port *sas_port_alloc(struct device *, int); +struct sas_port *sas_port_alloc_num(struct device *); int sas_port_add(struct sas_port *); void sas_port_free(struct sas_port *); void sas_port_delete(struct sas_port *); -- cgit v1.2.3 From a507c6a2d91c52615bd3bb5a8ff439d9e8530939 Mon Sep 17 00:00:00 2001 From: Douglas Gilbert Date: Sun, 2 Jul 2006 13:48:10 -0400 Subject: [SCSI] update additional sense codes and some opcode names Following on from my post titled: "additional sense codes need update" see the attachment against lk 2.6.17 . ChangeLog: - update additional sense codes table to agree with SPC-4 revision 5a (14 June 2006) - adjust some of the opcode names Signed-off-by: Douglas Gilbert Signed-off-by: James Bottomley --- drivers/scsi/constants.c | 126 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index dddd2acce76f..61f6024b61ba 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -5,6 +5,7 @@ * Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002) * by D. Gilbert and aeb (20020609) * Additions for SPC-3 T10/1416-D Rev 21 22 Sept 2004, D. Gilbert 20041025 + * Update to SPC-4 T10/1713-D Rev 5a, 14 June 2006, D. Gilbert 20060702 */ #include @@ -36,55 +37,56 @@ static const char * cdb_byte0_names[] = { /* 00-03 */ "Test Unit Ready", "Rezero Unit/Rewind", NULL, "Request Sense", /* 04-07 */ "Format Unit/Medium", "Read Block Limits", NULL, "Reasssign Blocks", -/* 08-0d */ "Read (6)", NULL, "Write (6)", "Seek (6)", NULL, NULL, +/* 08-0d */ "Read(6)", NULL, "Write(6)", "Seek(6)", NULL, NULL, /* 0e-12 */ NULL, "Read Reverse", "Write Filemarks", "Space", "Inquiry", -/* 13-16 */ "Verify (6)", "Recover Buffered Data", "Mode Select (6)", - "Reserve (6)", -/* 17-1a */ "Release (6)", "Copy", "Erase", "Mode Sense (6)", +/* 13-16 */ "Verify(6)", "Recover Buffered Data", "Mode Select(6)", + "Reserve(6)", +/* 17-1a */ "Release(6)", "Copy", "Erase", "Mode Sense(6)", /* 1b-1d */ "Start/Stop Unit", "Receive Diagnostic", "Send Diagnostic", /* 1e-1f */ "Prevent/Allow Medium Removal", NULL, /* 20-22 */ NULL, NULL, NULL, /* 23-28 */ "Read Format Capacities", "Set Window", - "Read Capacity (10)", NULL, NULL, "Read (10)", -/* 29-2d */ "Read Generation", "Write (10)", "Seek (10)", "Erase (10)", - "Read updated block", -/* 2e-31 */ "Write Verify (10)", "Verify (10)", "Search High", "Search Equal", + "Read Capacity(10)", NULL, NULL, "Read(10)", +/* 29-2d */ "Read Generation", "Write(10)", "Seek(10)", "Erase(10)", + "Read updated block", +/* 2e-31 */ "Write Verify(10)", "Verify(10)", "Search High", "Search Equal", /* 32-34 */ "Search Low", "Set Limits", "Prefetch/Read Position", -/* 35-37 */ "Synchronize Cache (10)", "Lock/Unlock Cache (10)", +/* 35-37 */ "Synchronize Cache(10)", "Lock/Unlock Cache(10)", "Read Defect Data(10)", /* 38-3c */ "Medium Scan", "Compare", "Copy Verify", "Write Buffer", "Read Buffer", -/* 3d-3f */ "Update Block", "Read Long (10)", "Write Long (10)", -/* 40-41 */ "Change Definition", "Write Same (10)", +/* 3d-3f */ "Update Block", "Read Long(10)", "Write Long(10)", +/* 40-41 */ "Change Definition", "Write Same(10)", /* 42-48 */ "Read sub-channel", "Read TOC/PMA/ATIP", "Read density support", - "Play audio (10)", "Get configuration", "Play audio msf", + "Play audio(10)", "Get configuration", "Play audio msf", "Play audio track/index", -/* 49-4f */ "Play track relative (10)", "Get event status notification", +/* 49-4f */ "Play track relative(10)", "Get event status notification", "Pause/resume", "Log Select", "Log Sense", "Stop play/scan", NULL, /* 50-55 */ "Xdwrite", "Xpwrite, Read disk info", "Xdread, Read track info", - "Reserve track", "Send OPC info", "Mode Select (10)", -/* 56-5b */ "Reserve (10)", "Release (10)", "Repair track", "Read master cue", - "Mode Sense (10)", "Close track/session", + "Reserve track", "Send OPC info", "Mode Select(10)", +/* 56-5b */ "Reserve(10)", "Release(10)", "Repair track", "Read master cue", + "Mode Sense(10)", "Close track/session", /* 5c-5f */ "Read buffer capacity", "Send cue sheet", "Persistent reserve in", "Persistent reserve out", /* 60-67 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 68-6f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 70-77 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 78-7f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Variable length", -/* 80-84 */ "Xdwrite (16)", "Rebuild (16)", "Regenerate (16)", "Extended copy", +/* 80-84 */ "Xdwrite(16)", "Rebuild(16)", "Regenerate(16)", "Extended copy", "Receive copy results", -/* 85-89 */ "Memory Export In (16)", "Access control in", "Access control out", - "Read (16)", "Memory Export Out (16)", -/* 8a-8f */ "Write (16)", NULL, "Read attributes", "Write attributes", - "Write and verify (16)", "Verify (16)", -/* 90-94 */ "Pre-fetch (16)", "Synchronize cache (16)", - "Lock/unlock cache (16)", "Write same (16)", NULL, +/* 85-89 */ "ATA command pass through(16)", "Access control in", + "Access control out", "Read(16)", "Memory Export Out(16)", +/* 8a-8f */ "Write(16)", NULL, "Read attributes", "Write attributes", + "Write and verify(16)", "Verify(16)", +/* 90-94 */ "Pre-fetch(16)", "Synchronize cache(16)", + "Lock/unlock cache(16)", "Write same(16)", NULL, /* 95-99 */ NULL, NULL, NULL, NULL, NULL, -/* 9a-9f */ NULL, NULL, NULL, NULL, "Service action in (16)", - "Service action out (16)", -/* a0-a5 */ "Report luns", "Blank", "Send event", "Maintenance in", - "Maintenance out", "Move medium/play audio(12)", +/* 9a-9f */ NULL, NULL, NULL, NULL, "Service action in(16)", + "Service action out(16)", +/* a0-a5 */ "Report luns", "ATA command pass through(12)/Blank", + "Security protocol in", "Maintenance in", "Maintenance out", + "Move medium/play audio(12)", /* a6-a9 */ "Exchange medium", "Move medium attached", "Read(12)", "Play track relative(12)", /* aa-ae */ "Write(12)", NULL, "Erase(12), Get Performance", @@ -92,12 +94,12 @@ static const char * cdb_byte0_names[] = { /* af-b1 */ "Verify(12)", "Search data high(12)", "Search data equal(12)", /* b2-b4 */ "Search data low(12)", "Set limits(12)", "Read element status attached", -/* b5-b6 */ "Request volume element address", "Send volume tag, set streaming", +/* b5-b6 */ "Security protocol out", "Send volume tag, set streaming", /* b7-b9 */ "Read defect data(12)", "Read element status", "Read CD msf", /* ba-bc */ "Redundancy group (in), Scan", - "Redundancy group (out), Set cd-rom speed", "Spare in, Play cd", -/* bd-bf */ "Spare out, Mechanism status", "Volume set in, Read cd", - "Volume set out, Send DVD structure", + "Redundancy group (out), Set cd-rom speed", "Spare (in), Play cd", +/* bd-bf */ "Spare (out), Mechanism status", "Volume set (in), Read cd", + "Volume set (out), Send DVD structure", }; struct value_name_pair { @@ -112,6 +114,7 @@ static const struct value_name_pair maint_in_arr[] = { {0xc, "Report supported operation codes"}, {0xd, "Report supported task management functions"}, {0xe, "Report priority"}, + {0xf, "Report timestamp"}, }; #define MAINT_IN_SZ ARRAY_SIZE(maint_in_arr) @@ -120,6 +123,7 @@ static const struct value_name_pair maint_out_arr[] = { {0xa, "Set target port groups"}, {0xb, "Change aliases"}, {0xe, "Set priority"}, + {0xe, "Set timestamp"}, }; #define MAINT_OUT_SZ ARRAY_SIZE(maint_out_arr) @@ -427,6 +431,7 @@ static struct error_info additional[] = {0x001A, "Rewind operation in progress"}, {0x001B, "Set capacity operation in progress"}, {0x001C, "Verify operation in progress"}, + {0x001D, "ATA pass through information available"}, {0x0100, "No index/sector signal"}, @@ -438,7 +443,7 @@ static struct error_info additional[] = {0x0400, "Logical unit not ready, cause not reportable"}, {0x0401, "Logical unit is in process of becoming ready"}, - {0x0402, "Logical unit not ready, initializing cmd. required"}, + {0x0402, "Logical unit not ready, initializing command required"}, {0x0403, "Logical unit not ready, manual intervention required"}, {0x0404, "Logical unit not ready, format in progress"}, {0x0405, "Logical unit not ready, rebuild in progress"}, @@ -478,6 +483,9 @@ static struct error_info additional[] = {0x0B00, "Warning"}, {0x0B01, "Warning - specified temperature exceeded"}, {0x0B02, "Warning - enclosure degraded"}, + {0x0B03, "Warning - background self-test failed"}, + {0x0B04, "Warning - background pre-scan detected medium error"}, + {0x0B05, "Warning - background medium scan detected medium error"}, {0x0C00, "Write error"}, {0x0C01, "Write error - recovered with auto reallocation"}, @@ -493,6 +501,7 @@ static struct error_info additional[] = {0x0C0B, "Auxiliary memory write error"}, {0x0C0C, "Write error - unexpected unsolicited data"}, {0x0C0D, "Write error - not enough unsolicited data"}, + {0x0C0F, "Defects in error window"}, {0x0D00, "Error detected by third party temporary initiator"}, {0x0D01, "Third party device failure"}, @@ -504,11 +513,12 @@ static struct error_info additional[] = {0x0E00, "Invalid information unit"}, {0x0E01, "Information unit too short"}, {0x0E02, "Information unit too long"}, + {0x0E03, "Invalid field in command information unit"}, {0x1000, "Id CRC or ECC error"}, - {0x1001, "Data block guard check failed"}, - {0x1002, "Data block application tag check failed"}, - {0x1003, "Data block reference tag check failed"}, + {0x1001, "Logical block guard check failed"}, + {0x1002, "Logical block application tag check failed"}, + {0x1003, "Logical block reference tag check failed"}, {0x1100, "Unrecovered read error"}, {0x1101, "Read retries exhausted"}, @@ -530,6 +540,7 @@ static struct error_info additional[] = {0x1111, "Read error - loss of streaming"}, {0x1112, "Auxiliary memory read error"}, {0x1113, "Read error - failed retransmission request"}, + {0x1114, "Read error - lba marked bad by application client"}, {0x1200, "Address mark not found for id field"}, @@ -610,11 +621,14 @@ static struct error_info additional[] = {0x2100, "Logical block address out of range"}, {0x2101, "Invalid element address"}, {0x2102, "Invalid address for write"}, + {0x2103, "Invalid write crossing layer jump"}, {0x2200, "Illegal function (use 20 00, 24 00, or 26 00)"}, {0x2400, "Invalid field in cdb"}, {0x2401, "CDB decryption error"}, + {0x2402, "Obsolete"}, + {0x2403, "Obsolete"}, {0x2404, "Security audit value frozen"}, {0x2405, "Security working key frozen"}, {0x2406, "Nonce not unique"}, @@ -637,7 +651,10 @@ static struct error_info additional[] = {0x260C, "Invalid operation for copy source or destination"}, {0x260D, "Copy segment granularity violation"}, {0x260E, "Invalid parameter while port is enabled"}, - {0x260F, "Invalid data-out buffer integrity"}, + {0x260F, "Invalid data-out buffer integrity check value"}, + {0x2610, "Data decryption key fail limit reached"}, + {0x2611, "Incomplete key-associated data set"}, + {0x2612, "Vendor specific key reference not found"}, {0x2700, "Write protected"}, {0x2701, "Hardware write protected"}, @@ -649,6 +666,7 @@ static struct error_info additional[] = {0x2800, "Not ready to ready change, medium may have changed"}, {0x2801, "Import or export element accessed"}, + {0x2802, "Format-layer may have changed"}, {0x2900, "Power on, reset, or bus device reset occurred"}, {0x2901, "Power on occurred"}, @@ -669,6 +687,11 @@ static struct error_info additional[] = {0x2A07, "Implicit asymmetric access state transition failed"}, {0x2A08, "Priority changed"}, {0x2A09, "Capacity data has changed"}, + {0x2A10, "Timestamp changed"}, + {0x2A11, "Data encryption parameters changed by another i_t nexus"}, + {0x2A12, "Data encryption parameters changed by vendor specific " + "event"}, + {0x2A13, "Data encryption key instance counter has changed"}, {0x2B00, "Copy cannot execute since host cannot disconnect"}, @@ -690,6 +713,7 @@ static struct error_info additional[] = {0x2E00, "Insufficient time for operation"}, {0x2F00, "Commands cleared by another initiator"}, + {0x2F01, "Commands cleared by power loss notification"}, {0x3000, "Incompatible medium installed"}, {0x3001, "Cannot read medium - unknown format"}, @@ -702,7 +726,8 @@ static struct error_info additional[] = {0x3008, "Cannot write - application code mismatch"}, {0x3009, "Current session not fixated for append"}, {0x300A, "Cleaning request rejected"}, - {0x300C, "WORM medium, overwrite attempted"}, + {0x300C, "WORM medium - overwrite attempted"}, + {0x300D, "WORM medium - integrity check"}, {0x3010, "Medium not formatted"}, {0x3100, "Medium format corrupted"}, @@ -790,6 +815,9 @@ static struct error_info additional[] = {0x3F0F, "Echo buffer overwritten"}, {0x3F10, "Medium loadable"}, {0x3F11, "Medium auxiliary memory accessible"}, + {0x3F12, "iSCSI IP address added"}, + {0x3F13, "iSCSI IP address removed"}, + {0x3F14, "iSCSI IP address changed"}, /* * {0x40NN, "Ram failure"}, * {0x40NN, "Diagnostic failure on component nn"}, @@ -799,6 +827,7 @@ static struct error_info additional[] = {0x4300, "Message error"}, {0x4400, "Internal target failure"}, + {0x4471, "ATA device failed set features"}, {0x4500, "Select or reselect failure"}, @@ -807,9 +836,10 @@ static struct error_info additional[] = {0x4700, "Scsi parity error"}, {0x4701, "Data phase CRC error detected"}, {0x4702, "Scsi parity error detected during st data phase"}, - {0x4703, "Information unit CRC error detected"}, + {0x4703, "Information unit iuCRC error detected"}, {0x4704, "Asynchronous information protection error detected"}, {0x4705, "Protocol service CRC error"}, + {0x4706, "Phy test function in progress"}, {0x477f, "Some commands cleared by iSCSI Protocol event"}, {0x4800, "Initiator detected error message received"}, @@ -844,6 +874,8 @@ static struct error_info additional[] = {0x5300, "Media load or eject failed"}, {0x5301, "Unload tape failure"}, {0x5302, "Medium removal prevented"}, + {0x5303, "Medium removal prevented by data transfer element"}, + {0x5304, "Medium thread or unthread failure"}, {0x5400, "Scsi to host system interface failure"}, @@ -855,6 +887,7 @@ static struct error_info additional[] = {0x5505, "Insufficient access control resources"}, {0x5506, "Auxiliary memory out of space"}, {0x5507, "Quota error"}, + {0x5508, "Maximum number of supplemental decryption keys exceeded"}, {0x5700, "Unable to recover table-of-contents"}, @@ -1004,6 +1037,7 @@ static struct error_info additional[] = {0x6708, "Assign failure occurred"}, {0x6709, "Multiply assigned logical unit"}, {0x670A, "Set target port groups command failed"}, + {0x670B, "ATA device feature not enabled"}, {0x6800, "Logical unit not configured"}, @@ -1030,6 +1064,8 @@ static struct error_info additional[] = {0x6F03, "Read of scrambled sector without authentication"}, {0x6F04, "Media region code is mismatched to logical unit region"}, {0x6F05, "Drive region must be permanent/region reset count error"}, + {0x6F06, "Insufficient block count for binding nonce recording"}, + {0x6F07, "Conflict in binding nonce recording"}, /* * {0x70NN, "Decompression exception short algorithm id of nn"}, */ @@ -1041,6 +1077,8 @@ static struct error_info additional[] = {0x7203, "Session fixation error - incomplete track in session"}, {0x7204, "Empty or partially written reserved track"}, {0x7205, "No more track reservations allowed"}, + {0x7206, "RMZ extension is not allowed"}, + {0x7207, "No more test zone extensions are allowed"}, {0x7300, "Cd control error"}, {0x7301, "Power calibration area almost full"}, @@ -1049,6 +1087,18 @@ static struct error_info additional[] = {0x7304, "Program memory area update failure"}, {0x7305, "Program memory area is full"}, {0x7306, "RMA/PMA is almost full"}, + {0x7310, "Current power calibration area almost full"}, + {0x7311, "Current power calibration area is full"}, + {0x7317, "RDZ is full"}, + + {0x7400, "Security error"}, + {0x7401, "Unable to decrypt data"}, + {0x7402, "Unencrypted data encountered while decrypting"}, + {0x7403, "Incorrect data encryption key"}, + {0x7404, "Cryptographic integrity validation failed"}, + {0x7405, "Error decrypting data"}, + {0x7471, "Logical unit access not authorized"}, + {0, NULL} }; -- cgit v1.2.3 From dc22f16d4e46cee699c3835cc6e2a8005023517f Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Thu, 6 Jul 2006 11:23:14 -0600 Subject: [SCSI] mptsas: use unnumbered port API and remove driver porttracking This allows us to be rid of the machinery in mptsas for creating and tracking port numbers. Since mptsas is merely inventing the numbers, the SAS transport class may as well do it instead. Signed-off-by: Eric Moore Signed-off-by: James Bottomley --- drivers/message/fusion/mptbase.h | 1 - drivers/message/fusion/mptsas.c | 38 ++++++++++++++++---------------------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h index a5ce10b67d02..0b555e2309f4 100644 --- a/drivers/message/fusion/mptbase.h +++ b/drivers/message/fusion/mptbase.h @@ -644,7 +644,6 @@ typedef struct _MPT_ADAPTER struct work_struct fc_rescan_work; char fc_rescan_work_q_name[KOBJ_NAME_LEN]; struct workqueue_struct *fc_rescan_work_q; - u8 port_serial_number; } MPT_ADAPTER; /* diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index f7bd8b11ed3b..0fb7d5881f4f 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -144,7 +144,6 @@ struct mptsas_devinfo { * Specific details on ports, wide/narrow */ struct mptsas_portinfo_details{ - u8 port_id; /* port number provided to transport */ u16 num_phys; /* number of phys belong to this port */ u64 phy_bitmask; /* TODO, extend support for 255 phys */ struct sas_rphy *rphy; /* transport layer rphy object */ @@ -350,10 +349,10 @@ mptsas_port_delete(struct mptsas_portinfo_details * port_details) port_info = port_details->port_info; phy_info = port_info->phy_info; - dsaswideprintk((KERN_DEBUG "%s: [%p]: port=%02d num_phys=%02d " + dsaswideprintk((KERN_DEBUG "%s: [%p]: num_phys=%02d " "bitmask=0x%016llX\n", - __FUNCTION__, port_details, port_details->port_id, - port_details->num_phys, port_details->phy_bitmask)); + __FUNCTION__, port_details, port_details->num_phys, + port_details->phy_bitmask)); for (i = 0; i < port_info->num_phys; i++, phy_info++) { if(phy_info->port_details != port_details) @@ -462,9 +461,8 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) * phy be removed by firmware events. */ dsaswideprintk((KERN_DEBUG - "%s: [%p]: port=%d deleting phy = %d\n", - __FUNCTION__, port_details, - port_details->port_id, i)); + "%s: [%p]: deleting phy = %d\n", + __FUNCTION__, port_details, i)); port_details->num_phys--; port_details->phy_bitmask &= ~ (1 << phy_info->phy_id); memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo)); @@ -493,7 +491,6 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) goto out; port_details->num_phys = 1; port_details->port_info = port_info; - port_details->port_id = ioc->port_serial_number++; if (phy_info->phy_id < 64 ) port_details->phy_bitmask |= (1 << phy_info->phy_id); @@ -525,12 +522,8 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) mptsas_get_port(phy_info_cmp); port_details->starget = mptsas_get_starget(phy_info_cmp); - port_details->port_id = - phy_info_cmp->port_details->port_id; port_details->num_phys = phy_info_cmp->port_details->num_phys; -// port_info->port_serial_number--; - ioc->port_serial_number--; if (!phy_info_cmp->port_details->num_phys) kfree(phy_info_cmp->port_details); } else @@ -554,11 +547,11 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info) if (!port_details) continue; dsaswideprintk((KERN_DEBUG - "%s: [%p]: phy_id=%02d port_id=%02d num_phys=%02d " + "%s: [%p]: phy_id=%02d num_phys=%02d " "bitmask=0x%016llX\n", __FUNCTION__, - port_details, i, port_details->port_id, - port_details->num_phys, port_details->phy_bitmask)); + port_details, i, port_details->num_phys, + port_details->phy_bitmask)); dsaswideprintk((KERN_DEBUG"\t\tport = %p rphy=%p\n", port_details->port, port_details->rphy)); } @@ -1608,11 +1601,7 @@ static int mptsas_probe_one_phy(struct device *dev, if (phy_info->sas_port_add_phy) { if (!port) { - port = sas_port_alloc(dev, - phy_info->port_details->port_id); - dsaswideprintk((KERN_DEBUG - "sas_port_alloc: port=%p dev=%p port_id=%d\n", - port, dev, phy_info->port_details->port_id)); + port = sas_port_alloc_num(dev); if (!port) { error = -ENOMEM; goto out; @@ -1625,6 +1614,9 @@ static int mptsas_probe_one_phy(struct device *dev, goto out; } mptsas_set_port(phy_info, port); + dsaswideprintk((KERN_DEBUG + "sas_port_alloc: port=%p dev=%p port_id=%d\n", + port, dev, port->port_identifier)); } dsaswideprintk((KERN_DEBUG "sas_port_add_phy: phy_id=%d\n", phy_info->phy_id)); @@ -1939,7 +1931,8 @@ mptsas_delete_expander_phys(MPT_ADAPTER *ioc) expander_sas_address) continue; #ifdef MPT_DEBUG_SAS_WIDE - dev_printk(KERN_DEBUG, &port->dev, "delete\n"); + dev_printk(KERN_DEBUG, &port->dev, + "delete port (%d)\n", port->port_identifier); #endif sas_port_delete(port); mptsas_port_delete(phy_info->port_details); @@ -2185,7 +2178,8 @@ mptsas_hotplug_work(void *arg) ioc->name, ds, ev->channel, ev->id, phy_info->phy_id); #ifdef MPT_DEBUG_SAS_WIDE - dev_printk(KERN_DEBUG, &port->dev, "delete\n"); + dev_printk(KERN_DEBUG, &port->dev, + "delete port (%d)\n", port->port_identifier); #endif sas_port_delete(port); mptsas_port_delete(phy_info->port_details); -- cgit v1.2.3 From 5a0e326dfa6d87a1ac6929a7d6d327e8803b7aef Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:49:16 -0400 Subject: [SCSI] lpfc 8.1.7: Use mod_timer instead of add_timer in lpfc_els_timeout_handler Use mod_timer instead of add_timer in lpfc_els_timeout_handler This patch was formerly posted by Mark Haverkamp. http://marc.theaimsgroup.com/?l=linux-scsi&m=114246089015681&w=2 Signed-off-by: Mark Haverkamp Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_els.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 4126fd87956f..cac9e870838a 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -3282,10 +3282,9 @@ lpfc_els_timeout_handler(struct lpfc_hba *phba) } else lpfc_sli_release_iocbq(phba, piocb); } - if (phba->sli.ring[LPFC_ELS_RING].txcmplq_cnt) { - phba->els_tmofunc.expires = jiffies + HZ * timeout; - add_timer(&phba->els_tmofunc); - } + if (phba->sli.ring[LPFC_ELS_RING].txcmplq_cnt) + mod_timer(&phba->els_tmofunc, jiffies + HZ * timeout); + spin_unlock_irq(phba->host->host_lock); } -- cgit v1.2.3 From e17da18e2f78845899467d6e580146ef673f5330 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:49:25 -0400 Subject: [SCSI] lpfc 8.1.7: Standardize the driver on a single define for the maximum supported targets Standardize the driver on a single define for the maximum supported targets. Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc.h | 9 +++++---- drivers/scsi/lpfc/lpfc_hbadisc.c | 4 ++-- drivers/scsi/lpfc/lpfc_scsi.c | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index f81691fcf177..d44f9aac6b8f 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -21,10 +21,12 @@ struct lpfc_sli2_slim; -#define LPFC_MAX_TARGET 256 /* max targets supported */ -#define LPFC_MAX_DISC_THREADS 64 /* max outstanding discovery els req */ -#define LPFC_MAX_NS_RETRY 3 /* max NameServer retries */ +#define LPFC_MAX_TARGET 256 /* max number of targets supported */ +#define LPFC_MAX_DISC_THREADS 64 /* max outstanding discovery els + requests */ +#define LPFC_MAX_NS_RETRY 3 /* Number of retry attempts to contact + the NameServer before giving up. */ #define LPFC_DFT_HBA_Q_DEPTH 2048 /* max cmds per hba */ #define LPFC_LC_HBA_Q_DEPTH 1024 /* max cmds per low cost hba */ #define LPFC_LP101_HBA_Q_DEPTH 128 /* max cmds per low cost hba */ @@ -41,7 +43,6 @@ struct lpfc_sli2_slim; (( (u64)(high)<<16 ) << 16)|( (u64)(low)))) /* Provide maximum configuration definitions. */ #define LPFC_DRVR_TIMEOUT 16 /* driver iocb timeout value in sec */ -#define MAX_FCP_TARGET 256 /* max num of FCP targets supported */ #define FC_MAX_ADPTMSG 64 #define MAX_HBAEVT 32 diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index adb086009ae0..4d6cf990c4fc 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1084,7 +1084,7 @@ lpfc_register_remote_port(struct lpfc_hba * phba, fc_remote_port_rolechg(rport, rport_ids.roles); if ((rport->scsi_target_id != -1) && - (rport->scsi_target_id < MAX_FCP_TARGET)) { + (rport->scsi_target_id < LPFC_MAX_TARGET)) { ndlp->nlp_sid = rport->scsi_target_id; } @@ -1313,7 +1313,7 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) if ((rport_add == mapped) && ((!nlp->rport) || (nlp->rport->scsi_target_id == -1) || - (nlp->rport->scsi_target_id >= MAX_FCP_TARGET))) { + (nlp->rport->scsi_target_id >= LPFC_MAX_TARGET))) { nlp->nlp_state = NLP_STE_UNMAPPED_NODE; spin_lock_irq(phba->host->host_lock); nlp->nlp_flag |= NLP_TGT_NO_SCSIID; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index aea1ee472f3d..578468810416 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -1078,7 +1078,7 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) * targets known to the driver. Should any target reset * fail, this routine returns failure to the midlayer. */ - for (i = 0; i < MAX_FCP_TARGET; i++) { + for (i = 0; i < LPFC_MAX_TARGET; i++) { /* Search the mapped list for this target ID */ match = 0; list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { -- cgit v1.2.3 From 9f49d3b05f092b89e774be20c654ceb0c64a7d19 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:49:34 -0400 Subject: [SCSI] lpfc 8.1.7: Fix memory leak and cleanup code related to per ring lookup array Fix memory leak and cleanup code related to per ring lookup array. Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_mem.c | 5 +++++ drivers/scsi/lpfc/lpfc_sli.c | 25 ------------------------- drivers/scsi/lpfc/lpfc_sli.h | 2 -- 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index 07017658ac56..066292d3995a 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -133,6 +133,11 @@ lpfc_mem_free(struct lpfc_hba * phba) pci_pool_destroy(phba->lpfc_scsi_dma_buf_pool); pci_pool_destroy(phba->lpfc_mbuf_pool); + + /* Free the iocb lookup array */ + kfree(psli->iocbq_lookup); + psli->iocbq_lookup = NULL; + } void * diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index bb69a7a1ec59..8193a384a9fb 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -191,35 +191,12 @@ static int lpfc_sli_ringtxcmpl_put(struct lpfc_hba * phba, struct lpfc_sli_ring * pring, struct lpfc_iocbq * piocb) { - uint16_t iotag; - list_add_tail(&piocb->list, &pring->txcmplq); pring->txcmplq_cnt++; if (unlikely(pring->ringno == LPFC_ELS_RING)) mod_timer(&phba->els_tmofunc, jiffies + HZ * (phba->fc_ratov << 1)); - if (pring->fast_lookup) { - /* Setup fast lookup based on iotag for completion */ - iotag = piocb->iocb.ulpIoTag; - if (iotag && (iotag < pring->fast_iotag)) - *(pring->fast_lookup + iotag) = piocb; - else { - - /* Cmd ring put: iotag greater then - configured max wd0 */ - lpfc_printf_log(phba, - KERN_ERR, - LOG_SLI, - "%d:0316 Cmd ring %d put: iotag x%x " - "greater then configured max x%x " - "wd0 x%x\n", - phba->brd_no, - pring->ringno, iotag, - pring->fast_iotag, - *(((uint32_t *)(&piocb->iocb)) + 7)); - } - } return (0); } @@ -2659,8 +2636,6 @@ lpfc_sli_hba_down(struct lpfc_hba * phba) INIT_LIST_HEAD(&(pring->txq)); - kfree(pring->fast_lookup); - pring->fast_lookup = NULL; } spin_unlock_irqrestore(phba->host->host_lock, flags); diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index a52d6c6cf083..d8ef0d2894d4 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -135,8 +135,6 @@ struct lpfc_sli_ring { uint32_t fast_iotag; /* max fastlookup based iotag */ uint32_t iotag_ctr; /* keeps track of the next iotag to use */ uint32_t iotag_max; /* max iotag value to use */ - struct lpfc_iocbq ** fast_lookup; /* array of IOCB ptrs indexed by - iotag */ struct list_head txq; uint16_t txq_cnt; /* current length of queue */ uint16_t txq_max; /* max length */ -- cgit v1.2.3 From d0e56dad5c10e2a34a830e02dfd3aca20e63e387 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:49:42 -0400 Subject: [SCSI] lpfc 8.1.7: Fixed infinite retry of REG_LOGIN mailbox failed due to MBXERR_RPI_FULL Fixed infinite retry of REG_LOGIN mailbox failed due to MBXERR_RPI_FULL Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_nportdisc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 27d60ad897cd..bd0b0e293d63 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -1110,6 +1110,17 @@ lpfc_cmpl_reglogin_reglogin_issue(struct lpfc_hba * phba, phba->brd_no, did, mb->mbxStatus, phba->hba_state); + /* + * If RegLogin failed due to lack of HBA resources do not + * retry discovery. + */ + if (mb->mbxStatus == MBXERR_RPI_FULL) { + ndlp->nlp_prev_state = NLP_STE_UNUSED_NODE; + ndlp->nlp_state = NLP_STE_UNUSED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); + return ndlp->nlp_state; + } + /* Put ndlp in npr list set plogi timer for 1 sec */ mod_timer(&ndlp->nlp_delayfunc, jiffies + HZ * 1); spin_lock_irq(phba->host->host_lock); -- cgit v1.2.3 From 4db621e0f3817e14c79513b42c0479d44d8467de Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:49:49 -0400 Subject: [SCSI] lpfc 8.1.7: Issue DOWN_LINK prior to INIT_LINK to work around link failure issue Issue DOWN_LINK prior to INIT_LINK to work around link failure issue Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_attr.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index b62a72dfab29..511de17a1f83 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -219,9 +219,19 @@ lpfc_issue_lip(struct Scsi_Host *host) return -ENOMEM; memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t)); - lpfc_init_link(phba, pmboxq, phba->cfg_topology, phba->cfg_link_speed); + pmboxq->mb.mbxCommand = MBX_DOWN_LINK; + pmboxq->mb.mbxOwner = OWN_HOST; + mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, phba->fc_ratov * 2); + if ((mbxstatus == MBX_SUCCESS) && (pmboxq->mb.mbxStatus == 0)) { + memset((void *)pmboxq, 0, sizeof (LPFC_MBOXQ_t)); + lpfc_init_link(phba, pmboxq, phba->cfg_topology, + phba->cfg_link_speed); + mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, + phba->fc_ratov * 2); + } + if (mbxstatus == MBX_TIMEOUT) pmboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl; else -- cgit v1.2.3 From 688a88635f9d0d9251d35198e931eaac8816abef Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:49:56 -0400 Subject: [SCSI] lpfc 8.1.7: Fix txcmplq related panics on heavy IO while downloading firmware Fix txcmplq related panics on heavy IO while downloading firmware Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_init.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 81755a3f7c68..2944eda1e907 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1339,7 +1339,8 @@ lpfc_offline(struct lpfc_hba * phba) struct lpfc_sli_ring *pring; struct lpfc_sli *psli; unsigned long iflag; - int i = 0; + int i; + int cnt = 0; if (!phba) return 0; @@ -1348,17 +1349,26 @@ lpfc_offline(struct lpfc_hba * phba) return 0; psli = &phba->sli; - pring = &psli->ring[psli->fcp_ring]; lpfc_linkdown(phba); - /* The linkdown event takes 30 seconds to timeout. */ - while (pring->txcmplq_cnt) { - mdelay(10); - if (i++ > 3000) - break; + for (i = 0; i < psli->num_rings; i++) { + pring = &psli->ring[i]; + /* The linkdown event takes 30 seconds to timeout. */ + while (pring->txcmplq_cnt) { + mdelay(10); + if (cnt++ > 3000) { + lpfc_printf_log(phba, + KERN_WARNING, LOG_INIT, + "%d:0466 Outstanding IO when " + "bringing Adapter offline\n", + phba->brd_no); + break; + } + } } + /* stop all timers associated with this hba */ lpfc_stop_timer(phba); phba->work_hba_events = 0; -- cgit v1.2.3 From 9279565046f39f3ab338818c9923a23b9f25be5f Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:02 -0400 Subject: [SCSI] lpfc 8.1.7: Correct bogus nodev_tmo message on NPort that changes its NPort Id Correct bogus nodev_tmo message on NPort that changes its NPort Id Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_els.c | 58 ++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index cac9e870838a..b89f6cb641e6 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -648,33 +648,32 @@ lpfc_more_plogi(struct lpfc_hba * phba) } static struct lpfc_nodelist * -lpfc_plogi_confirm_nport(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, +lpfc_plogi_confirm_nport(struct lpfc_hba * phba, struct lpfc_dmabuf *prsp, struct lpfc_nodelist *ndlp) { struct lpfc_nodelist *new_ndlp; - struct lpfc_dmabuf *pcmd, *prsp; uint32_t *lp; struct serv_parm *sp; uint8_t name[sizeof (struct lpfc_name)]; uint32_t rc; - pcmd = (struct lpfc_dmabuf *) cmdiocb->context2; - prsp = (struct lpfc_dmabuf *) pcmd->list.next; lp = (uint32_t *) prsp->virt; sp = (struct serv_parm *) ((uint8_t *) lp + sizeof (uint32_t)); + memset(name, 0, sizeof (struct lpfc_name)); /* Now we to find out if the NPort we are logging into, matches the WWPN * we have for that ndlp. If not, we have some work to do. */ new_ndlp = lpfc_findnode_wwpn(phba, NLP_SEARCH_ALL, &sp->portName); - memset(name, 0, sizeof (struct lpfc_name)); - rc = memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)); - if (!rc || (new_ndlp == ndlp)) { + if (new_ndlp == ndlp) return ndlp; - } if (!new_ndlp) { + rc = + memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)); + if (!rc) + return ndlp; new_ndlp = mempool_alloc(phba->nlp_mem_pool, GFP_ATOMIC); if (!new_ndlp) return ndlp; @@ -683,17 +682,21 @@ lpfc_plogi_confirm_nport(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, } lpfc_unreg_rpi(phba, new_ndlp); - new_ndlp->nlp_prev_state = ndlp->nlp_state; new_ndlp->nlp_DID = ndlp->nlp_DID; - new_ndlp->nlp_state = NLP_STE_PLOGI_ISSUE; - lpfc_nlp_list(phba, new_ndlp, NLP_PLOGI_LIST); + new_ndlp->nlp_prev_state = ndlp->nlp_prev_state; + new_ndlp->nlp_state = ndlp->nlp_state; + lpfc_nlp_list(phba, new_ndlp, ndlp->nlp_flag & NLP_LIST_MASK); /* Move this back to NPR list */ - lpfc_unreg_rpi(phba, ndlp); - ndlp->nlp_DID = 0; /* Two ndlps cannot have the same did */ - ndlp->nlp_state = NLP_STE_NPR_NODE; - lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); - + if (memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)) == 0) { + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); + } + else { + lpfc_unreg_rpi(phba, ndlp); + ndlp->nlp_DID = 0; /* Two ndlps cannot have the same did */ + ndlp->nlp_state = NLP_STE_NPR_NODE; + lpfc_nlp_list(phba, ndlp, NLP_NPR_LIST); + } return new_ndlp; } @@ -703,6 +706,7 @@ lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, { IOCB_t *irsp; struct lpfc_nodelist *ndlp; + struct lpfc_dmabuf *prsp; int disc, rc, did, type; @@ -769,7 +773,10 @@ lpfc_cmpl_els_plogi(struct lpfc_hba * phba, struct lpfc_iocbq * cmdiocb, } } else { /* Good status, call state machine */ - ndlp = lpfc_plogi_confirm_nport(phba, cmdiocb, ndlp); + prsp = list_entry(((struct lpfc_dmabuf *) + cmdiocb->context2)->list.next, + struct lpfc_dmabuf, list); + ndlp = lpfc_plogi_confirm_nport(phba, prsp, ndlp); rc = lpfc_disc_state_machine(phba, ndlp, cmdiocb, NLP_EVT_CMPL_PLOGI); } @@ -3441,6 +3448,8 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, if ((did & Fabric_DID_MASK) == Fabric_DID_MASK) { ndlp->nlp_type |= NLP_FABRIC; } + ndlp->nlp_state = NLP_STE_UNUSED_NODE; + lpfc_nlp_list(phba, ndlp, NLP_UNUSED_LIST); } phba->fc_stat.elsRcvFrame++; @@ -3462,13 +3471,14 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, rjt_err = 1; break; } + ndlp = lpfc_plogi_confirm_nport(phba, mp, ndlp); lpfc_disc_state_machine(phba, ndlp, elsiocb, NLP_EVT_RCV_PLOGI); break; case ELS_CMD_FLOGI: phba->fc_stat.elsRcvFLOGI++; lpfc_els_rcv_flogi(phba, elsiocb, ndlp, newnode); if (newnode) { - mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } break; case ELS_CMD_LOGO: @@ -3491,7 +3501,7 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, phba->fc_stat.elsRcvRSCN++; lpfc_els_rcv_rscn(phba, elsiocb, ndlp, newnode); if (newnode) { - mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } break; case ELS_CMD_ADISC: @@ -3534,28 +3544,28 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, phba->fc_stat.elsRcvLIRR++; lpfc_els_rcv_lirr(phba, elsiocb, ndlp); if (newnode) { - mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } break; case ELS_CMD_RPS: phba->fc_stat.elsRcvRPS++; lpfc_els_rcv_rps(phba, elsiocb, ndlp); if (newnode) { - mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } break; case ELS_CMD_RPL: phba->fc_stat.elsRcvRPL++; lpfc_els_rcv_rpl(phba, elsiocb, ndlp); if (newnode) { - mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } break; case ELS_CMD_RNID: phba->fc_stat.elsRcvRNID++; lpfc_els_rcv_rnid(phba, elsiocb, ndlp); if (newnode) { - mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } break; default: @@ -3567,7 +3577,7 @@ lpfc_els_unsol_event(struct lpfc_hba * phba, "%d:0115 Unknown ELS command x%x received from " "NPORT x%x\n", phba->brd_no, cmd, did); if (newnode) { - mempool_free( ndlp, phba->nlp_mem_pool); + lpfc_nlp_list(phba, ndlp, NLP_NO_LIST); } break; } -- cgit v1.2.3 From bcf4dbfaf38502d3919e0601c06afac7d5e42685 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:08 -0400 Subject: [SCSI] lpfc 8.1.7: Consolidate dma buf cleanup into a separate function Consolidate dma buf cleanup into a separate function Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_scsi.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 578468810416..d45616e70c67 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -153,22 +153,6 @@ static void lpfc_release_scsi_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb) { unsigned long iflag = 0; - /* - * There are only two special cases to consider. (1) the scsi command - * requested scatter-gather usage or (2) the scsi command allocated - * a request buffer, but did not request use_sg. There is a third - * case, but it does not require resource deallocation. - */ - if ((psb->seg_cnt > 0) && (psb->pCmd->use_sg)) { - dma_unmap_sg(&phba->pcidev->dev, psb->pCmd->request_buffer, - psb->seg_cnt, psb->pCmd->sc_data_direction); - } else { - if ((psb->nonsg_phys) && (psb->pCmd->request_bufflen)) { - dma_unmap_single(&phba->pcidev->dev, psb->nonsg_phys, - psb->pCmd->request_bufflen, - psb->pCmd->sc_data_direction); - } - } spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag); psb->pCmd = NULL; @@ -281,6 +265,27 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd) return 0; } +static void +lpfc_scsi_unprep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * psb) +{ + /* + * There are only two special cases to consider. (1) the scsi command + * requested scatter-gather usage or (2) the scsi command allocated + * a request buffer, but did not request use_sg. There is a third + * case, but it does not require resource deallocation. + */ + if ((psb->seg_cnt > 0) && (psb->pCmd->use_sg)) { + dma_unmap_sg(&phba->pcidev->dev, psb->pCmd->request_buffer, + psb->seg_cnt, psb->pCmd->sc_data_direction); + } else { + if ((psb->nonsg_phys) && (psb->pCmd->request_bufflen)) { + dma_unmap_single(&phba->pcidev->dev, psb->nonsg_phys, + psb->pCmd->request_bufflen, + psb->pCmd->sc_data_direction); + } + } +} + static void lpfc_handle_fcp_err(struct lpfc_scsi_buf *lpfc_cmd) { @@ -454,6 +459,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, cmd->scsi_done(cmd); if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { + lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); lpfc_release_scsi_buf(phba, lpfc_cmd); return; } @@ -511,6 +517,7 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } } + lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); lpfc_release_scsi_buf(phba, lpfc_cmd); } @@ -822,6 +829,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) return 0; out_host_busy_free_buf: + lpfc_scsi_unprep_dma_buf(phba, lpfc_cmd); lpfc_release_scsi_buf(phba, lpfc_cmd); out_host_busy: return SCSI_MLQUEUE_HOST_BUSY; -- cgit v1.2.3 From 420b630d6e9ff41dc3e2a2a2808b67907951e094 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:16 -0400 Subject: [SCSI] lpfc 8.1.7: Fix panic in lpfc_sli_validate_fcp_iocb Fix panic in lpfc_sli_validate_fcp_iocb due to access of scsi_cmnd after returning it to the midlayer Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_scsi.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index d45616e70c67..a760a44173df 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -616,6 +616,7 @@ lpfc_scsi_prep_cmnd(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd, static int lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd, + unsigned int lun, uint8_t task_mgmt_cmd) { struct lpfc_sli *psli; @@ -634,8 +635,7 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba, piocb = &piocbq->iocb; fcp_cmnd = lpfc_cmd->fcp_cmnd; - int_to_scsilun(lpfc_cmd->pCmd->device->lun, - &lpfc_cmd->fcp_cmnd->fcp_lun); + int_to_scsilun(lun, &lpfc_cmd->fcp_cmnd->fcp_lun); fcp_cmnd->fcpCntl2 = task_mgmt_cmd; piocb->ulpCommand = CMD_FCP_ICMND64_CR; @@ -662,14 +662,16 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba, static int lpfc_scsi_tgt_reset(struct lpfc_scsi_buf * lpfc_cmd, struct lpfc_hba * phba, - unsigned tgt_id, struct lpfc_rport_data *rdata) + unsigned tgt_id, unsigned int lun, + struct lpfc_rport_data *rdata) { struct lpfc_iocbq *iocbq; struct lpfc_iocbq *iocbqrsp; int ret; lpfc_cmd->rdata = rdata; - ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_TARGET_RESET); + ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, lun, + FCP_TARGET_RESET); if (!ret) return FAILED; @@ -977,12 +979,12 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) if (lpfc_cmd == NULL) goto out; - lpfc_cmd->pCmd = cmnd; lpfc_cmd->timeout = 60; lpfc_cmd->scsi_hba = phba; lpfc_cmd->rdata = rdata; - ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, FCP_LUN_RESET); + ret = lpfc_scsi_prep_task_mgmt_cmd(phba, lpfc_cmd, cmnd->device->lun, + FCP_LUN_RESET); if (!ret) goto out_free_scsi_buf; @@ -1009,7 +1011,6 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) cmd_status = iocbqrsp->iocb.ulpStatus; lpfc_sli_release_iocbq(phba, iocbqrsp); - lpfc_release_scsi_buf(phba, lpfc_cmd); /* * All outstanding txcmplq I/Os should have been aborted by the device. @@ -1048,6 +1049,8 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd) } out_free_scsi_buf: + lpfc_release_scsi_buf(phba, lpfc_cmd); + lpfc_printf_log(phba, KERN_ERR, LOG_FCP, "%d:0713 SCSI layer issued LUN reset (%d, %d) " "Data: x%x x%x x%x\n", @@ -1078,7 +1081,6 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) /* The lpfc_cmd storage is reused. Set all loop invariants. */ lpfc_cmd->timeout = 60; - lpfc_cmd->pCmd = cmnd; lpfc_cmd->scsi_hba = phba; /* @@ -1098,8 +1100,8 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd) if (!match) continue; - ret = lpfc_scsi_tgt_reset(lpfc_cmd, phba, - i, ndlp->rport->dd_data); + ret = lpfc_scsi_tgt_reset(lpfc_cmd, phba, i, cmnd->device->lun, + ndlp->rport->dd_data); if (ret != SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_FCP, "%d:0713 Bus Reset on target %d failed\n", -- cgit v1.2.3 From 40496f073ffe75c59414a7236f95ecbc49562596 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:22 -0400 Subject: [SCSI] lpfc 8.1.7: Adding new issue_reset sysfs attribute Adding new issue_reset sysfs attribute Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_attr.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 511de17a1f83..cdae011b2813 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -243,6 +243,47 @@ lpfc_issue_lip(struct Scsi_Host *host) return 0; } +static int +lpfc_selective_reset(struct lpfc_hba *phba) +{ + struct completion online_compl; + int status = 0; + + init_completion(&online_compl); + lpfc_workq_post_event(phba, &status, &online_compl, + LPFC_EVT_OFFLINE); + wait_for_completion(&online_compl); + + if (status != 0) + return -EIO; + + init_completion(&online_compl); + lpfc_workq_post_event(phba, &status, &online_compl, + LPFC_EVT_ONLINE); + wait_for_completion(&online_compl); + + if (status != 0) + return -EIO; + + return 0; +} + +static ssize_t +lpfc_issue_reset(struct class_device *cdev, const char *buf, size_t count) +{ + struct Scsi_Host *host = class_to_shost(cdev); + struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; + int status = -EINVAL; + + if (strncmp(buf, "selective", sizeof("selective") - 1) == 0) + status = lpfc_selective_reset(phba); + + if (status == 0) + return strlen(buf); + else + return status; +} + static ssize_t lpfc_nport_evt_cnt_show(struct class_device *cdev, char *buf) { @@ -546,6 +587,7 @@ static CLASS_DEVICE_ATTR(board_online, S_IRUGO | S_IWUSR, lpfc_board_online_show, lpfc_board_online_store); static CLASS_DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR, lpfc_board_mode_show, lpfc_board_mode_store); +static CLASS_DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); static int lpfc_poll = 0; module_param(lpfc_poll, int, 0); @@ -751,6 +793,7 @@ struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_management_version, &class_device_attr_board_online, &class_device_attr_board_mode, + &class_device_attr_issue_reset, &class_device_attr_lpfc_poll, &class_device_attr_lpfc_poll_tmo, NULL, -- cgit v1.2.3 From 5e0b43385559bfe78c1f80d71ec203effe244d9a Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:29 -0400 Subject: [SCSI] lpfc 8.1.7: Remove depricated sysfs attribute board_online Remove depricated sysfs attribute board_online, as it's replaced by the new issue_reset attribute Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_attr.c | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index cdae011b2813..48379c606124 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -292,45 +292,6 @@ lpfc_nport_evt_cnt_show(struct class_device *cdev, char *buf) return snprintf(buf, PAGE_SIZE, "%d\n", phba->nport_event_cnt); } -static ssize_t -lpfc_board_online_show(struct class_device *cdev, char *buf) -{ - struct Scsi_Host *host = class_to_shost(cdev); - struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; - - if (phba->fc_flag & FC_OFFLINE_MODE) - return snprintf(buf, PAGE_SIZE, "0\n"); - else - return snprintf(buf, PAGE_SIZE, "1\n"); -} - -static ssize_t -lpfc_board_online_store(struct class_device *cdev, const char *buf, - size_t count) -{ - struct Scsi_Host *host = class_to_shost(cdev); - struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata; - struct completion online_compl; - int val=0, status=0; - - if (sscanf(buf, "%d", &val) != 1) - return -EINVAL; - - init_completion(&online_compl); - - if (val) - lpfc_workq_post_event(phba, &status, &online_compl, - LPFC_EVT_ONLINE); - else - lpfc_workq_post_event(phba, &status, &online_compl, - LPFC_EVT_OFFLINE); - wait_for_completion(&online_compl); - if (!status) - return strlen(buf); - else - return -EIO; -} - static ssize_t lpfc_board_mode_show(struct class_device *cdev, char *buf) { @@ -583,8 +544,6 @@ static CLASS_DEVICE_ATTR(lpfc_drvr_version, S_IRUGO, lpfc_drvr_version_show, NULL); static CLASS_DEVICE_ATTR(management_version, S_IRUGO, management_version_show, NULL); -static CLASS_DEVICE_ATTR(board_online, S_IRUGO | S_IWUSR, - lpfc_board_online_show, lpfc_board_online_store); static CLASS_DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR, lpfc_board_mode_show, lpfc_board_mode_store); static CLASS_DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset); @@ -791,7 +750,6 @@ struct class_device_attribute *lpfc_host_attrs[] = { &class_device_attr_lpfc_max_luns, &class_device_attr_nport_evt_cnt, &class_device_attr_management_version, - &class_device_attr_board_online, &class_device_attr_board_mode, &class_device_attr_issue_reset, &class_device_attr_lpfc_poll, -- cgit v1.2.3 From 953969ddf5b049361ed1e8471cc43dc4134d2a6f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 9 Jul 2006 08:47:46 -0700 Subject: Revert "ACPI: dock driver" This reverts commit a5e1b94008f2a96abf4a0c0371a55a56b320c13e. Adrian Bunk points out that it has build errors, and apparently no maintenance. Throw it out. Signed-off-by: Linus Torvalds --- drivers/acpi/Kconfig | 7 - drivers/acpi/Makefile | 1 - drivers/acpi/dock.c | 739 -------------------------------------------- drivers/acpi/scan.c | 23 -- include/acpi/acpi_bus.h | 2 +- include/acpi/acpi_drivers.h | 17 - 6 files changed, 1 insertion(+), 788 deletions(-) delete mode 100644 drivers/acpi/dock.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index fef7bab12244..290c767dd77f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -133,13 +133,6 @@ config ACPI_FAN This driver adds support for ACPI fan devices, allowing user-mode applications to perform basic fan control (on, off, status). -config ACPI_DOCK - tristate "Dock" - depends on !ACPI_IBM_DOCK - default y - help - This driver adds support for ACPI controlled docking stations - config ACPI_PROCESSOR tristate "Processor" default y diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index bce7ca27b429..bb5b80a80b18 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -42,7 +42,6 @@ obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_EC) += ec.o obj-$(CONFIG_ACPI_FAN) += fan.o -obj-$(CONFIG_ACPI_DOCK) += dock.o obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c deleted file mode 100644 index 510a94524298..000000000000 --- a/drivers/acpi/dock.c +++ /dev/null @@ -1,739 +0,0 @@ -/* - * dock.c - ACPI dock station driver - * - * Copyright (C) 2006 Kristen Carlson Accardi - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include -#include -#include -#include - -#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver" - -ACPI_MODULE_NAME("dock") -MODULE_AUTHOR("Kristen Carlson Accardi"); -MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME); -MODULE_LICENSE("GPL"); - -static struct atomic_notifier_head dock_notifier_list; - -struct dock_station { - acpi_handle handle; - unsigned long last_dock_time; - u32 flags; - spinlock_t dd_lock; - spinlock_t hp_lock; - struct list_head dependent_devices; - struct list_head hotplug_devices; -}; - -struct dock_dependent_device { - struct list_head list; - struct list_head hotplug_list; - acpi_handle handle; - acpi_notify_handler handler; - void *context; -}; - -#define DOCK_DOCKING 0x00000001 -#define DOCK_EVENT KOBJ_DOCK -#define UNDOCK_EVENT KOBJ_UNDOCK - -static struct dock_station *dock_station; - -/***************************************************************************** - * Dock Dependent device functions * - *****************************************************************************/ -/** - * alloc_dock_dependent_device - allocate and init a dependent device - * @handle: the acpi_handle of the dependent device - * - * Allocate memory for a dependent device structure for a device referenced - * by the acpi handle - */ -static struct dock_dependent_device * -alloc_dock_dependent_device(acpi_handle handle) -{ - struct dock_dependent_device *dd; - - dd = kzalloc(sizeof(*dd), GFP_KERNEL); - if (dd) { - dd->handle = handle; - INIT_LIST_HEAD(&dd->list); - INIT_LIST_HEAD(&dd->hotplug_list); - } - return dd; -} - -/** - * add_dock_dependent_device - associate a device with the dock station - * @ds: The dock station - * @dd: The dependent device - * - * Add the dependent device to the dock's dependent device list. - */ -static void -add_dock_dependent_device(struct dock_station *ds, - struct dock_dependent_device *dd) -{ - spin_lock(&ds->dd_lock); - list_add_tail(&dd->list, &ds->dependent_devices); - spin_unlock(&ds->dd_lock); -} - -/** - * dock_add_hotplug_device - associate a hotplug handler with the dock station - * @ds: The dock station - * @dd: The dependent device struct - * - * Add the dependent device to the dock's hotplug device list - */ -static void -dock_add_hotplug_device(struct dock_station *ds, - struct dock_dependent_device *dd) -{ - spin_lock(&ds->hp_lock); - list_add_tail(&dd->hotplug_list, &ds->hotplug_devices); - spin_unlock(&ds->hp_lock); -} - -/** - * dock_del_hotplug_device - remove a hotplug handler from the dock station - * @ds: The dock station - * @dd: the dependent device struct - * - * Delete the dependent device from the dock's hotplug device list - */ -static void -dock_del_hotplug_device(struct dock_station *ds, - struct dock_dependent_device *dd) -{ - spin_lock(&ds->hp_lock); - list_del(&dd->hotplug_list); - spin_unlock(&ds->hp_lock); -} - -/** - * find_dock_dependent_device - get a device dependent on this dock - * @ds: the dock station - * @handle: the acpi_handle of the device we want - * - * iterate over the dependent device list for this dock. If the - * dependent device matches the handle, return. - */ -static struct dock_dependent_device * -find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) -{ - struct dock_dependent_device *dd; - - spin_lock(&ds->dd_lock); - list_for_each_entry(dd, &ds->dependent_devices, list) { - if (handle == dd->handle) { - spin_unlock(&ds->dd_lock); - return dd; - } - } - spin_unlock(&ds->dd_lock); - return NULL; -} - -/***************************************************************************** - * Dock functions * - *****************************************************************************/ -/** - * is_dock - see if a device is a dock station - * @handle: acpi handle of the device - * - * If an acpi object has a _DCK method, then it is by definition a dock - * station, so return true. - */ -static int is_dock(acpi_handle handle) -{ - acpi_status status; - acpi_handle tmp; - - status = acpi_get_handle(handle, "_DCK", &tmp); - if (ACPI_FAILURE(status)) - return 0; - return 1; -} - -/** - * is_dock_device - see if a device is on a dock station - * @handle: acpi handle of the device - * - * If this device is either the dock station itself, - * or is a device dependent on the dock station, then it - * is a dock device - */ -int is_dock_device(acpi_handle handle) -{ - if (!dock_station) - return 0; - - if (is_dock(handle) || find_dock_dependent_device(dock_station, handle)) - return 1; - - return 0; -} - -EXPORT_SYMBOL_GPL(is_dock_device); - -/** - * dock_present - see if the dock station is present. - * @ds: the dock station - * - * execute the _STA method. note that present does not - * imply that we are docked. - */ -static int dock_present(struct dock_station *ds) -{ - unsigned long sta; - acpi_status status; - - if (ds) { - status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta); - if (ACPI_SUCCESS(status) && sta) - return 1; - } - return 0; -} - - - -/** - * dock_create_acpi_device - add new devices to acpi - * @handle - handle of the device to add - * - * This function will create a new acpi_device for the given - * handle if one does not exist already. This should cause - * acpi to scan for drivers for the given devices, and call - * matching driver's add routine. - * - * Returns a pointer to the acpi_device corresponding to the handle. - */ -static struct acpi_device * dock_create_acpi_device(acpi_handle handle) -{ - struct acpi_device *device = NULL; - struct acpi_device *parent_device; - acpi_handle parent; - int ret; - - if (acpi_bus_get_device(handle, &device)) { - /* - * no device created for this object, - * so we should create one. - */ - acpi_get_parent(handle, &parent); - if (acpi_bus_get_device(parent, &parent_device)) - parent_device = NULL; - - ret = acpi_bus_add(&device, parent_device, handle, - ACPI_BUS_TYPE_DEVICE); - if (ret) { - pr_debug("error adding bus, %x\n", - -ret); - return NULL; - } - } - return device; -} - -/** - * dock_remove_acpi_device - remove the acpi_device struct from acpi - * @handle - the handle of the device to remove - * - * Tell acpi to remove the acpi_device. This should cause any loaded - * driver to have it's remove routine called. - */ -static void dock_remove_acpi_device(acpi_handle handle) -{ - struct acpi_device *device; - int ret; - - if (!acpi_bus_get_device(handle, &device)) { - ret = acpi_bus_trim(device, 1); - if (ret) - pr_debug("error removing bus, %x\n", -ret); - } -} - - -/** - * hotplug_dock_devices - insert or remove devices on the dock station - * @ds: the dock station - * @event: either bus check or eject request - * - * Some devices on the dock station need to have drivers called - * to perform hotplug operations after a dock event has occurred. - * Traverse the list of dock devices that have registered a - * hotplug handler, and call the handler. - */ -static void hotplug_dock_devices(struct dock_station *ds, u32 event) -{ - struct dock_dependent_device *dd; - - spin_lock(&ds->hp_lock); - - /* - * First call driver specific hotplug functions - */ - list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) { - if (dd->handler) - dd->handler(dd->handle, event, dd->context); - } - - /* - * Now make sure that an acpi_device is created for each - * dependent device, or removed if this is an eject request. - * This will cause acpi_drivers to be stopped/started if they - * exist - */ - list_for_each_entry(dd, &ds->dependent_devices, list) { - if (event == ACPI_NOTIFY_EJECT_REQUEST) - dock_remove_acpi_device(dd->handle); - else - dock_create_acpi_device(dd->handle); - } - spin_unlock(&ds->hp_lock); -} - -static void dock_event(struct dock_station *ds, u32 event, int num) -{ - struct acpi_device *device; - - device = dock_create_acpi_device(ds->handle); - if (device) - kobject_uevent(&device->kobj, num); -} - -/** - * eject_dock - respond to a dock eject request - * @ds: the dock station - * - * This is called after _DCK is called, to execute the dock station's - * _EJ0 method. - */ -static void eject_dock(struct dock_station *ds) -{ - struct acpi_object_list arg_list; - union acpi_object arg; - acpi_status status; - acpi_handle tmp; - - /* all dock devices should have _EJ0, but check anyway */ - status = acpi_get_handle(ds->handle, "_EJ0", &tmp); - if (ACPI_FAILURE(status)) { - pr_debug("No _EJ0 support for dock device\n"); - return; - } - - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = 1; - - if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0", - &arg_list, NULL))) - pr_debug("Failed to evaluate _EJ0!\n"); -} - -/** - * handle_dock - handle a dock event - * @ds: the dock station - * @dock: to dock, or undock - that is the question - * - * Execute the _DCK method in response to an acpi event - */ -static void handle_dock(struct dock_station *ds, int dock) -{ - acpi_status status; - struct acpi_object_list arg_list; - union acpi_object arg; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *obj; - - acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer); - obj = name_buffer.pointer; - - printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking"); - - /* _DCK method has one argument */ - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = dock; - status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); - if (ACPI_FAILURE(status)) - pr_debug("%s: failed to execute _DCK\n", obj->string.pointer); - kfree(buffer.pointer); - kfree(name_buffer.pointer); -} - -static inline void dock(struct dock_station *ds) -{ - handle_dock(ds, 1); -} - -static inline void undock(struct dock_station *ds) -{ - handle_dock(ds, 0); -} - -static inline void begin_dock(struct dock_station *ds) -{ - ds->flags |= DOCK_DOCKING; -} - -static inline void complete_dock(struct dock_station *ds) -{ - ds->flags &= ~(DOCK_DOCKING); - ds->last_dock_time = jiffies; -} - -/** - * dock_in_progress - see if we are in the middle of handling a dock event - * @ds: the dock station - * - * Sometimes while docking, false dock events can be sent to the driver - * because good connections aren't made or some other reason. Ignore these - * if we are in the middle of doing something. - */ -static int dock_in_progress(struct dock_station *ds) -{ - if ((ds->flags & DOCK_DOCKING) || - time_before(jiffies, (ds->last_dock_time + HZ))) - return 1; - return 0; -} - -/** - * register_dock_notifier - add yourself to the dock notifier list - * @nb: the callers notifier block - * - * If a driver wishes to be notified about dock events, they can - * use this function to put a notifier block on the dock notifier list. - * this notifier call chain will be called after a dock event, but - * before hotplugging any new devices. - */ -int register_dock_notifier(struct notifier_block *nb) -{ - return atomic_notifier_chain_register(&dock_notifier_list, nb); -} - -EXPORT_SYMBOL_GPL(register_dock_notifier); - -/** - * unregister_dock_notifier - remove yourself from the dock notifier list - * @nb: the callers notifier block - */ -void unregister_dock_notifier(struct notifier_block *nb) -{ - atomic_notifier_chain_unregister(&dock_notifier_list, nb); -} - -EXPORT_SYMBOL_GPL(unregister_dock_notifier); - -/** - * register_hotplug_dock_device - register a hotplug function - * @handle: the handle of the device - * @handler: the acpi_notifier_handler to call after docking - * @context: device specific data - * - * If a driver would like to perform a hotplug operation after a dock - * event, they can register an acpi_notifiy_handler to be called by - * the dock driver after _DCK is executed. - */ -int -register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler, - void *context) -{ - struct dock_dependent_device *dd; - - if (!dock_station) - return -ENODEV; - - /* - * make sure this handle is for a device dependent on the dock, - * this would include the dock station itself - */ - dd = find_dock_dependent_device(dock_station, handle); - if (dd) { - dd->handler = handler; - dd->context = context; - dock_add_hotplug_device(dock_station, dd); - return 0; - } - - return -EINVAL; -} - -EXPORT_SYMBOL_GPL(register_hotplug_dock_device); - -/** - * unregister_hotplug_dock_device - remove yourself from the hotplug list - * @handle: the acpi handle of the device - */ -void unregister_hotplug_dock_device(acpi_handle handle) -{ - struct dock_dependent_device *dd; - - if (!dock_station) - return; - - dd = find_dock_dependent_device(dock_station, handle); - if (dd) - dock_del_hotplug_device(dock_station, dd); -} - -EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); - -/** - * dock_notify - act upon an acpi dock notification - * @handle: the dock station handle - * @event: the acpi event - * @data: our driver data struct - * - * If we are notified to dock, then check to see if the dock is - * present and then dock. Notify all drivers of the dock event, - * and then hotplug and devices that may need hotplugging. For undock - * check to make sure the dock device is still present, then undock - * and hotremove all the devices that may need removing. - */ -static void dock_notify(acpi_handle handle, u32 event, void *data) -{ - struct dock_station *ds = (struct dock_station *)data; - - switch (event) { - case ACPI_NOTIFY_BUS_CHECK: - if (!dock_in_progress(ds) && dock_present(ds)) { - begin_dock(ds); - dock(ds); - if (!dock_present(ds)) { - printk(KERN_ERR PREFIX "Unable to dock!\n"); - break; - } - atomic_notifier_call_chain(&dock_notifier_list, - event, NULL); - hotplug_dock_devices(ds, event); - complete_dock(ds); - dock_event(ds, event, DOCK_EVENT); - } - break; - case ACPI_NOTIFY_DEVICE_CHECK: - /* - * According to acpi spec 3.0a, if a DEVICE_CHECK notification - * is sent and _DCK is present, it is assumed to mean an - * undock request. This notify routine will only be called - * for objects defining _DCK, so we will fall through to eject - * request here. However, we will pass an eject request through - * to the driver who wish to hotplug. - */ - case ACPI_NOTIFY_EJECT_REQUEST: - if (!dock_in_progress(ds) && dock_present(ds)) { - /* - * here we need to generate the undock - * event prior to actually doing the undock - * so that the device struct still exists. - */ - dock_event(ds, event, UNDOCK_EVENT); - hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); - undock(ds); - eject_dock(ds); - if (dock_present(ds)) - printk(KERN_ERR PREFIX "Unable to undock!\n"); - } - break; - default: - printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); - } -} - -/** - * find_dock_devices - find devices on the dock station - * @handle: the handle of the device we are examining - * @lvl: unused - * @context: the dock station private data - * @rv: unused - * - * This function is called by acpi_walk_namespace. It will - * check to see if an object has an _EJD method. If it does, then it - * will see if it is dependent on the dock station. - */ -static acpi_status -find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - acpi_status status; - acpi_handle tmp; - struct dock_station *ds = (struct dock_station *)context; - struct dock_dependent_device *dd; - - status = acpi_bus_get_ejd(handle, &tmp); - if (ACPI_FAILURE(status)) - return AE_OK; - - if (tmp == ds->handle) { - dd = alloc_dock_dependent_device(handle); - if (dd) - add_dock_dependent_device(ds, dd); - } - - return AE_OK; -} - -/** - * dock_add - add a new dock station - * @handle: the dock station handle - * - * allocated and initialize a new dock station device. Find all devices - * that are on the dock station, and register for dock event notifications. - */ -static int dock_add(acpi_handle handle) -{ - int ret; - acpi_status status; - struct dock_dependent_device *dd; - - /* allocate & initialize the dock_station private data */ - dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); - if (!dock_station) - return -ENOMEM; - dock_station->handle = handle; - dock_station->last_dock_time = jiffies - HZ; - INIT_LIST_HEAD(&dock_station->dependent_devices); - INIT_LIST_HEAD(&dock_station->hotplug_devices); - spin_lock_init(&dock_station->dd_lock); - spin_lock_init(&dock_station->hp_lock); - - /* Find dependent devices */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock_devices, dock_station, - NULL); - - /* add the dock station as a device dependent on itself */ - dd = alloc_dock_dependent_device(handle); - if (!dd) { - kfree(dock_station); - return -ENOMEM; - } - add_dock_dependent_device(dock_station, dd); - - /* register for dock events */ - status = acpi_install_notify_handler(dock_station->handle, - ACPI_SYSTEM_NOTIFY, - dock_notify, dock_station); - - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Error installing notify handler\n"); - ret = -ENODEV; - goto dock_add_err; - } - - printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME); - - return 0; - -dock_add_err: - kfree(dock_station); - kfree(dd); - return ret; -} - -/** - * dock_remove - free up resources related to the dock station - */ -static int dock_remove(void) -{ - struct dock_dependent_device *dd, *tmp; - acpi_status status; - - if (!dock_station) - return 0; - - /* remove dependent devices */ - list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices, - list) - kfree(dd); - - /* remove dock notify handler */ - status = acpi_remove_notify_handler(dock_station->handle, - ACPI_SYSTEM_NOTIFY, - dock_notify); - if (ACPI_FAILURE(status)) - printk(KERN_ERR "Error removing notify handler\n"); - - /* free dock station memory */ - kfree(dock_station); - return 0; -} - -/** - * find_dock - look for a dock station - * @handle: acpi handle of a device - * @lvl: unused - * @context: counter of dock stations found - * @rv: unused - * - * This is called by acpi_walk_namespace to look for dock stations. - */ -static acpi_status -find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - int *count = (int *)context; - acpi_status status = AE_OK; - - if (is_dock(handle)) { - if (dock_add(handle) >= 0) { - (*count)++; - status = AE_CTRL_TERMINATE; - } - } - return status; -} - -static int __init dock_init(void) -{ - int num = 0; - - dock_station = NULL; - - /* look for a dock station */ - acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_UINT32_MAX, find_dock, &num, NULL); - - if (!num) - return -ENODEV; - - return 0; -} - -static void __exit dock_exit(void) -{ - dock_remove(); -} - -postcore_initcall(dock_init); -module_exit(dock_exit); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5fcb50c7b778..cac4fcdcfc8d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -663,29 +663,6 @@ static int acpi_bus_find_driver(struct acpi_device *device) Device Enumeration -------------------------------------------------------------------------- */ -acpi_status -acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) -{ - acpi_status status; - acpi_handle tmp; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - union acpi_object *obj; - - status = acpi_get_handle(handle, "_EJD", &tmp); - if (ACPI_FAILURE(status)) - return status; - - status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer); - if (ACPI_SUCCESS(status)) { - obj = buffer.pointer; - status = acpi_get_handle(NULL, obj->string.pointer, ejd); - kfree(buffer.pointer); - } - return status; -} -EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); - - static int acpi_bus_get_flags(struct acpi_device *device) { acpi_status status = AE_OK; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index f338e40bd544..a2b3e390a503 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -334,7 +334,7 @@ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type); int acpi_bus_trim(struct acpi_device *start, int rmdevice); int acpi_bus_start(struct acpi_device *device); -acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd); + int acpi_match_ids(struct acpi_device *device, char *ids); int acpi_create_dir(struct acpi_device *); void acpi_remove_dir(struct acpi_device *); diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 6a5bdcefec64..b425f9bb6d43 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -110,21 +110,4 @@ int acpi_processor_set_thermal_limit(acpi_handle handle, int type); extern int acpi_specific_hotkey_enabled; -/*-------------------------------------------------------------------------- - Dock Station - -------------------------------------------------------------------------- */ -#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE) -extern int is_dock_device(acpi_handle handle); -extern int register_dock_notifier(struct notifier_block *nb); -extern void unregister_dock_notifier(struct notifier_block *nb); -extern int register_hotplug_dock_device(acpi_handle handle, - acpi_notify_handler handler, void *context); -extern void unregister_hotplug_dock_device(acpi_handle handle); -#else -#define is_dock_device(h) (0) -#define register_dock_notifier(nb) (-ENODEV) -#define unregister_dock_notifier(nb) do { } while(0) -#define register_hotplug_dock_device(h1, h2, c) (-ENODEV) -#define unregister_hotplug_dock_device(h) do { } while(0) -#endif #endif /*__ACPI_DRIVERS_H__*/ -- cgit v1.2.3 From ce8b3ce55bb0bd9318954efab0f714b088178978 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:36 -0400 Subject: [SCSI] lpfc 8.1.7: Correct the wait in attachment that delays for topology discovery Correct the wait in attachment that delays for topology discovery Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_init.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 2944eda1e907..2a81df054022 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -405,19 +405,26 @@ lpfc_config_port_post(struct lpfc_hba * phba) } /* MBOX buffer will be freed in mbox compl */ - i = 0; + return (0); +} + +static int +lpfc_discovery_wait(struct lpfc_hba *phba) +{ + int i = 0; + while ((phba->hba_state != LPFC_HBA_READY) || (phba->num_disc_nodes) || (phba->fc_prli_sent) || ((phba->fc_map_cnt == 0) && (i<2)) || - (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE)) { + (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE)) { /* Check every second for 30 retries. */ i++; if (i > 30) { - break; + return -ETIMEDOUT; } if ((i >= 15) && (phba->hba_state <= LPFC_LINK_DOWN)) { /* The link is down. Set linkdown timeout */ - break; + return -ETIMEDOUT; } /* Delay for 1 second to give discovery time to complete. */ @@ -425,12 +432,7 @@ lpfc_config_port_post(struct lpfc_hba * phba) } - /* Since num_disc_nodes keys off of PLOGI, delay a bit to let - * any potential PRLIs to flush thru the SLI sub-system. - */ - msleep(50); - - return (0); + return 0; } /************************************************************************/ @@ -1649,6 +1651,8 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) goto out_free_irq; } + lpfc_discovery_wait(phba); + if (phba->cfg_poll & DISABLE_FCP_RING_INT) { spin_lock_irq(phba->host->host_lock); lpfc_poll_start_timer(phba); -- cgit v1.2.3 From b4c026520ff0a4cb838a941bb0ed8996075e3d8c Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:43 -0400 Subject: [SCSI] lpfc 8.1.7: Add lpfc_sli_flush_mbox_queue() function Add lpfc_sli_flush_mbox_queue() function and use it in lpfc_offline() call to avoid deadlock on thread block. Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_crtn.h | 1 + drivers/scsi/lpfc/lpfc_init.c | 1 + drivers/scsi/lpfc/lpfc_sli.c | 20 +++++++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index ee22173fce43..517e9e4dd461 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -147,6 +147,7 @@ int lpfc_sli_hba_setup(struct lpfc_hba *); int lpfc_sli_hba_down(struct lpfc_hba *); int lpfc_sli_issue_mbox(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t); int lpfc_sli_handle_mb_event(struct lpfc_hba *); +int lpfc_sli_flush_mbox_queue(struct lpfc_hba *); int lpfc_sli_handle_slow_ring_event(struct lpfc_hba *, struct lpfc_sli_ring *, uint32_t); void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *); diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 2a81df054022..ee591c107e1c 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1353,6 +1353,7 @@ lpfc_offline(struct lpfc_hba * phba) psli = &phba->sli; lpfc_linkdown(phba); + lpfc_sli_flush_mbox_queue(phba); for (i = 0; i < psli->num_rings; i++) { pring = &psli->ring[i]; diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 8193a384a9fb..c441e37eb053 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -578,7 +578,7 @@ lpfc_sli_handle_mb_event(struct lpfc_hba * phba) /* Stray Mailbox Interrupt, mbxCommand mbxStatus */ lpfc_printf_log(phba, - KERN_ERR, + KERN_WARNING, LOG_MBOX | LOG_SLI, "%d:0304 Stray Mailbox Interrupt " "mbxCommand x%x mbxStatus x%x\n", @@ -3085,6 +3085,24 @@ lpfc_sli_issue_mbox_wait(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmboxq, return retval; } +int +lpfc_sli_flush_mbox_queue(struct lpfc_hba * phba) +{ + int i = 0; + + while (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE && !phba->stopped) { + if (i++ > LPFC_MBOX_TMO * 1000) + return 1; + + if (lpfc_sli_handle_mb_event(phba) == 0) + i = 0; + + msleep(1); + } + + return (phba->sli.sli_flag & LPFC_SLI_MBOX_ACTIVE) ? 1 : 0; +} + irqreturn_t lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs) { -- cgit v1.2.3 From 65a29c166fe331574880a375559405ac802b027a Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:50 -0400 Subject: [SCSI] lpfc 8.1.7: Misc Fixes Misc Fixes: - Fix some sparse warnings - casts of address space - Fix handling of the adapter registration string. Each invocation was byteswapping, so every other adapter init attempt failed. - Correct comments and default value for the lpfc_max_luns parameter Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_attr.c | 10 +++++----- drivers/scsi/lpfc/lpfc_init.c | 10 +++++++--- drivers/scsi/lpfc/lpfc_sli.c | 10 +++++----- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 48379c606124..5c68cdd8736f 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -706,12 +706,12 @@ LPFC_ATTR(discovery_threads, 32, 1, 64, "Maximum number of ELS commands " "during discovery"); /* -# lpfc_max_luns: maximum number of LUNs per target driver will support -# Value range is [1,32768]. Default value is 256. -# NOTE: The SCSI layer will scan each target for this many luns +# lpfc_max_luns: maximum allowed LUN. +# Value range is [0,65535]. Default value is 255. +# NOTE: The SCSI layer might probe all allowed LUN on some old targets. */ -LPFC_ATTR_R(max_luns, 256, 1, 32768, - "Maximum number of LUNs per target driver will support"); +LPFC_ATTR_R(max_luns, 255, 0, 65535, + "Maximum allowed LUN"); /* # lpfc_poll_tmo: .Milliseconds driver will wait between polling FCP ring. diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index ee591c107e1c..ef47b824cbed 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -71,6 +71,7 @@ lpfc_config_port_prep(struct lpfc_hba * phba) uint16_t offset = 0; static char licensed[56] = "key unlock for use with gnu public licensed code only\0"; + static int init_key = 1; pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmb) { @@ -82,10 +83,13 @@ lpfc_config_port_prep(struct lpfc_hba * phba) phba->hba_state = LPFC_INIT_MBX_CMDS; if (lpfc_is_LC_HBA(phba->pcidev->device)) { - uint32_t *ptext = (uint32_t *) licensed; + if (init_key) { + uint32_t *ptext = (uint32_t *) licensed; - for (i = 0; i < 56; i += sizeof (uint32_t), ptext++) - *ptext = cpu_to_be32(*ptext); + for (i = 0; i < 56; i += sizeof (uint32_t), ptext++) + *ptext = cpu_to_be32(*ptext); + init_key = 0; + } lpfc_read_nv(phba, pmb); memset((char*)mb->un.varRDnvp.rsvd3, 0, diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index c441e37eb053..350a625fa224 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1547,8 +1547,8 @@ lpfc_sli_brdready(struct lpfc_hba * phba, uint32_t mask) void lpfc_reset_barrier(struct lpfc_hba * phba) { - uint32_t * resp_buf; - uint32_t * mbox_buf; + uint32_t __iomem *resp_buf; + uint32_t __iomem *mbox_buf; volatile uint32_t mbox; uint32_t hc_copy; int i; @@ -1564,7 +1564,7 @@ void lpfc_reset_barrier(struct lpfc_hba * phba) * Tell the other part of the chip to suspend temporarily all * its DMA activity. */ - resp_buf = (uint32_t *)phba->MBslimaddr; + resp_buf = phba->MBslimaddr; /* Disable the error attention */ hc_copy = readl(phba->HCregaddr); @@ -1582,7 +1582,7 @@ void lpfc_reset_barrier(struct lpfc_hba * phba) ((MAILBOX_t *)&mbox)->mbxOwner = OWN_CHIP; writel(BARRIER_TEST_PATTERN, (resp_buf + 1)); - mbox_buf = (uint32_t *)phba->MBslimaddr; + mbox_buf = phba->MBslimaddr; writel(mbox, mbox_buf); for (i = 0; @@ -1782,7 +1782,7 @@ lpfc_sli_brdrestart(struct lpfc_hba * phba) skip_post = 0; word0 = 0; /* This is really setting up word1 */ } - to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t); + to_slim = phba->MBslimaddr + sizeof (uint32_t); writel(*(uint32_t *) mb, to_slim); readl(to_slim); /* flush */ -- cgit v1.2.3 From 035bff20bff2b6779ed3ad0a24a51a1a96828bc8 Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 6 Jul 2006 15:50:58 -0400 Subject: [SCSI] lpfc 8.1.7: Change version number to 8.1.7 Change version number to 8.1.7 Signed-off-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 6b737568b831..10e89c6ae823 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.1.6" +#define LPFC_DRIVER_VERSION "8.1.7" #define LPFC_DRIVER_NAME "lpfc" -- cgit v1.2.3 From 4e73ea7b02a8d2759b20ab52f13f40d5f5421044 Mon Sep 17 00:00:00 2001 From: Luben Tuikov Date: Fri, 7 Jul 2006 00:02:18 -0700 Subject: [SCSI] st.c: Improve sense output Convert this: st0: Error with sense data: <6>st: Current: sense key: Illegal Request Additional sense: Invalid field in cdb To this: st0: Current: sense key: Illegal Request Additional sense: Invalid field in cdb Signed-off-by: Luben Tuikov Acked-by: Kai Makisara Signed-off-by: Andrew Morton Signed-off-by: James Bottomley --- drivers/scsi/st.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 756ceb93ddc8..7f669b600677 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -368,7 +368,7 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt) SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2], SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]); if (cmdstatp->have_sense) - __scsi_print_sense("st", SRpnt->sense, SCSI_SENSE_BUFFERSIZE); + __scsi_print_sense(name, SRpnt->sense, SCSI_SENSE_BUFFERSIZE); } ) /* end DEB */ if (!debugging) { /* Abnormal conditions for tape */ if (!cmdstatp->have_sense) @@ -384,9 +384,8 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt) scode != VOLUME_OVERFLOW && SRpnt->cmd[0] != MODE_SENSE && SRpnt->cmd[0] != TEST_UNIT_READY) { - printk(KERN_WARNING "%s: Error with sense data: ", name); - __scsi_print_sense("st", SRpnt->sense, - SCSI_SENSE_BUFFERSIZE); + + __scsi_print_sense(name, SRpnt->sense, SCSI_SENSE_BUFFERSIZE); } } -- cgit v1.2.3 From 5e13cdfa5ba94724d6ab11de26b3ec3c94b88b00 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 8 Jul 2006 20:39:30 +0200 Subject: [SCSI] aha152x: stop poking at saved scsi_cmnd members Stop poking into the old_ & co scsi_cmnd fields that should only be used in the EH code. Untested, but this is required to move ahead with the EH fixes. Signed-off-by: Christoph Hellwig Signed-off-by: James Bottomley --- drivers/scsi/aha152x.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index 36e63f82d9f8..cff3d389b010 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -551,6 +551,11 @@ struct aha152x_hostdata { struct aha152x_scdata { Scsi_Cmnd *next; /* next sc in queue */ struct semaphore *sem; /* semaphore to block on */ + unsigned char cmd_len; + unsigned char cmnd[MAX_COMMAND_SIZE]; + unsigned short use_sg; + unsigned request_bufflen; + void *request_buffer; }; @@ -1006,11 +1011,20 @@ static int aha152x_internal_queue(Scsi_Cmnd *SCpnt, struct semaphore *sem, int p return FAILED; } } else { + struct aha152x_scdata *sc; + SCpnt->host_scribble = kmalloc(sizeof(struct aha152x_scdata), GFP_ATOMIC); if(SCpnt->host_scribble==0) { printk(ERR_LEAD "allocation failed\n", CMDINFO(SCpnt)); return FAILED; } + + sc = SCDATA(SCpnt); + memcpy(sc->cmnd, SCpnt->cmnd, sizeof(sc->cmnd)); + sc->request_buffer = SCpnt->request_buffer; + sc->request_bufflen = SCpnt->request_bufflen; + sc->use_sg = SCpnt->use_sg; + sc->cmd_len = SCpnt->cmd_len; } SCNEXT(SCpnt) = NULL; @@ -1565,6 +1579,9 @@ static void busfree_run(struct Scsi_Host *shpnt) #endif if(DONE_SC->SCp.phase & check_condition) { + struct scsi_cmnd *cmd = HOSTDATA(shpnt)->done_SC; + struct aha152x_scdata *sc = SCDATA(cmd); + #if 0 if(HOSTDATA(shpnt)->debug & debug_eh) { printk(ERR_LEAD "received sense: ", CMDINFO(DONE_SC)); @@ -1573,13 +1590,13 @@ static void busfree_run(struct Scsi_Host *shpnt) #endif /* restore old command */ - memcpy((void *) DONE_SC->cmnd, (void *) DONE_SC->data_cmnd, sizeof(DONE_SC->data_cmnd)); - DONE_SC->request_buffer = DONE_SC->buffer; - DONE_SC->request_bufflen = DONE_SC->bufflen; - DONE_SC->use_sg = DONE_SC->old_use_sg; - DONE_SC->cmd_len = DONE_SC->old_cmd_len; + memcpy(cmd->cmnd, sc->cmnd, sizeof(sc->cmnd)); + cmd->request_buffer = sc->request_buffer; + cmd->request_bufflen = sc->request_bufflen; + cmd->use_sg = sc->use_sg; + cmd->cmd_len = sc->cmd_len; - DONE_SC->SCp.Status = 0x02; + cmd->SCp.Status = 0x02; HOSTDATA(shpnt)->commands--; if (!HOSTDATA(shpnt)->commands) -- cgit v1.2.3 From d14164316d48a4319d8da9dc24be8663b2472bae Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sat, 8 Jul 2006 14:02:26 -0600 Subject: [SCSI] aic7[9x]xx: Remove last vestiges of reverse_scan Remove last vestiges of the reverse_scan paramater from aic7xxx and aic79xx. Signed-off-by: Matthew Wilcox Signed-off-by: James Bottomley --- drivers/scsi/aic7xxx/aic79xx_osm.c | 21 --------------------- drivers/scsi/aic7xxx/aic7xxx_osm.c | 1 - 2 files changed, 22 deletions(-) diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index b244c7124179..998999c0a972 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -242,25 +242,6 @@ ahd_print_path(struct ahd_softc *ahd, struct scb *scb) */ static uint32_t aic79xx_no_reset; -/* - * Certain PCI motherboards will scan PCI devices from highest to lowest, - * others scan from lowest to highest, and they tend to do all kinds of - * strange things when they come into contact with PCI bridge chips. The - * net result of all this is that the PCI card that is actually used to boot - * the machine is very hard to detect. Most motherboards go from lowest - * PCI slot number to highest, and the first SCSI controller found is the - * one you boot from. The only exceptions to this are when a controller - * has its BIOS disabled. So, we by default sort all of our SCSI controllers - * from lowest PCI slot number to highest PCI slot number. We also force - * all controllers with their BIOS disabled to the end of the list. This - * works on *almost* all computers. Where it doesn't work, we have this - * option. Setting this option to non-0 will reverse the order of the sort - * to highest first, then lowest, but will still leave cards with their BIOS - * disabled at the very end. That should fix everyone up unless there are - * really strange cirumstances. - */ -static uint32_t aic79xx_reverse_scan; - /* * Should we force EXTENDED translation on a controller. * 0 == Use whatever is in the SEEPROM or default to off @@ -350,7 +331,6 @@ MODULE_PARM_DESC(aic79xx, " periodically to prevent tag starvation.\n" " This may be required by some older disk\n" " or drives/RAID arrays.\n" -" reverse_scan Sort PCI devices highest Bus/Slot to lowest\n" " tag_info: Set per-target tag depth\n" " global_tag_depth: Global tag depth for all targets on all buses\n" " slewrate:Set the signal slew rate (0-15).\n" @@ -1031,7 +1011,6 @@ aic79xx_setup(char *s) #ifdef AHD_DEBUG { "debug", &ahd_debug }, #endif - { "reverse_scan", &aic79xx_reverse_scan }, { "periodic_otag", &aic79xx_periodic_otag }, { "pci_parity", &aic79xx_pci_parity }, { "seltime", &aic79xx_seltime }, diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index debf3e2a0798..aa4be8a31415 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -353,7 +353,6 @@ MODULE_PARM_DESC(aic7xxx, " periodically to prevent tag starvation.\n" " This may be required by some older disk\n" " drives or RAID arrays.\n" -" reverse_scan Sort PCI devices highest Bus/Slot to lowest\n" " tag_info: Set per-target tag depth\n" " global_tag_depth: Global tag depth for every target\n" " on every bus\n" -- cgit v1.2.3 From 6c51fe10475eda9f5c535f337411e1ed0e6915b9 Mon Sep 17 00:00:00 2001 From: Dave C Boutcher Date: Thu, 6 Jul 2006 22:07:47 -0500 Subject: [SCSI] ibmvscsi: allocate lpevents for ibmvscsi on iseries Allocate the correct number of lp events when running ibmvscsi on legacy iseries Signed-off-by: Dave Boutcher Signed-off-by: James Bottomley --- drivers/scsi/ibmvscsi/iseries_vscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/ibmvscsi/iseries_vscsi.c b/drivers/scsi/ibmvscsi/iseries_vscsi.c index 7eed0b098171..6aeb5f003c3c 100644 --- a/drivers/scsi/ibmvscsi/iseries_vscsi.c +++ b/drivers/scsi/ibmvscsi/iseries_vscsi.c @@ -81,7 +81,7 @@ int ibmvscsi_init_crq_queue(struct crq_queue *queue, int rc; single_host_data = hostdata; - rc = viopath_open(viopath_hostLp, viomajorsubtype_scsi, 0); + rc = viopath_open(viopath_hostLp, viomajorsubtype_scsi, max_requests); if (rc < 0) { printk("viopath_open failed with rc %d in open_event_path\n", rc); -- cgit v1.2.3 From ae0fda0cdf009731a33f031f86df8566f1977ffe Mon Sep 17 00:00:00 2001 From: Dave C Boutcher Date: Thu, 6 Jul 2006 22:08:49 -0500 Subject: [SCSI] ibmvscsi: handle inactive SCSI target during probe Without this patch we register an interrupt with request_irq, but then return a bad return code from the module probe. Signed-off-by: James Bottomley --- drivers/scsi/ibmvscsi/rpa_vscsi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 242b8873b333..ed22b96580c6 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -238,6 +238,7 @@ int ibmvscsi_init_crq_queue(struct crq_queue *queue, if (rc == 2) { /* Adapter is good, but other end is not ready */ printk(KERN_WARNING "ibmvscsi: Partner adapter not ready\n"); + retrc = 0; } else if (rc != 0) { printk(KERN_WARNING "ibmvscsi: Error %d opening adapter\n", rc); goto reg_crq_failed; -- cgit v1.2.3 From 631c228cd09bd5b93090fa60bd9803ec14aa0586 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 8 Jul 2006 20:42:15 +0200 Subject: [SCSI] hide EH backup data outside the scsi_cmnd Currently struct scsi_cmnd has various fields that are used to backup original data after the corresponding fields have been overridden for EH commands. This means drivers can easily get at it and misuse it. Due to the old_ naming this doesn't happen for most of them, but two that have different names have been used wrong a lot (see previous patch). Another downside is that they unessecarily bloat the scsi_cmnd size. This patch moves them onstack in scsi_send_eh_cmnd to fix those two issues aswell as allowing future EH fixes like moving the EH command submissions to use SG lists like everything else. Signed-off-by: Christoph Hellwig Signed-off-by: James Bottomley --- drivers/scsi/aha152x.c | 14 ++-- drivers/scsi/scsi.c | 11 +-- drivers/scsi/scsi_error.c | 210 ++++++++++++++++++++-------------------------- drivers/scsi/scsi_lib.c | 88 ++----------------- drivers/scsi/scsi_priv.h | 1 - drivers/scsi/sd.c | 3 +- drivers/scsi/sr.c | 5 +- include/scsi/scsi_cmnd.h | 9 -- 8 files changed, 108 insertions(+), 233 deletions(-) diff --git a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c index cff3d389b010..f974869ea323 100644 --- a/drivers/scsi/aha152x.c +++ b/drivers/scsi/aha152x.c @@ -1179,6 +1179,10 @@ static int aha152x_device_reset(Scsi_Cmnd * SCpnt) DECLARE_MUTEX_LOCKED(sem); struct timer_list timer; int ret, issued, disconnected; + unsigned char old_cmd_len = SCpnt->cmd_len; + unsigned short old_use_sg = SCpnt->use_sg; + void *old_buffer = SCpnt->request_buffer; + unsigned old_bufflen = SCpnt->request_bufflen; unsigned long flags; #if defined(AHA152X_DEBUG) @@ -1212,11 +1216,11 @@ static int aha152x_device_reset(Scsi_Cmnd * SCpnt) add_timer(&timer); down(&sem); del_timer(&timer); - - SCpnt->cmd_len = SCpnt->old_cmd_len; - SCpnt->use_sg = SCpnt->old_use_sg; - SCpnt->request_buffer = SCpnt->buffer; - SCpnt->request_bufflen = SCpnt->bufflen; + + SCpnt->cmd_len = old_cmd_len; + SCpnt->use_sg = old_use_sg; + SCpnt->request_buffer = old_buffer; + SCpnt->request_bufflen = old_bufflen; DO_LOCK(flags); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 2ab7df0dcfe8..b332caddd5b3 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -346,7 +346,7 @@ void scsi_log_send(struct scsi_cmnd *cmd) if (level > 3) { printk(KERN_INFO "buffer = 0x%p, bufflen = %d," " done = 0x%p, queuecommand 0x%p\n", - cmd->buffer, cmd->bufflen, + cmd->request_buffer, cmd->request_bufflen, cmd->done, sdev->host->hostt->queuecommand); @@ -661,11 +661,6 @@ void __scsi_done(struct scsi_cmnd *cmd) */ int scsi_retry_command(struct scsi_cmnd *cmd) { - /* - * Restore the SCSI command state. - */ - scsi_setup_cmd_retry(cmd); - /* * Zero the sense information from the last time we tried * this command. @@ -711,10 +706,6 @@ void scsi_finish_command(struct scsi_cmnd *cmd) "Notifying upper driver of completion " "(result %x)\n", cmd->result)); - /* - * We can get here with use_sg=0, causing a panic in the upper level - */ - cmd->use_sg = cmd->old_use_sg; cmd->done(cmd); } EXPORT_SYMBOL(scsi_finish_command); diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 6683d596234a..6a5b731bd5ba 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -460,19 +460,67 @@ static void scsi_eh_done(struct scsi_cmnd *scmd) * Return value: * SUCCESS or FAILED or NEEDS_RETRY **/ -static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) +static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout, int copy_sense) { struct scsi_device *sdev = scmd->device; struct Scsi_Host *shost = sdev->host; + int old_result = scmd->result; DECLARE_COMPLETION(done); unsigned long timeleft; unsigned long flags; + unsigned char old_cmnd[MAX_COMMAND_SIZE]; + enum dma_data_direction old_data_direction; + unsigned short old_use_sg; + unsigned char old_cmd_len; + unsigned old_bufflen; + void *old_buffer; int rtn; + /* + * We need saved copies of a number of fields - this is because + * error handling may need to overwrite these with different values + * to run different commands, and once error handling is complete, + * we will need to restore these values prior to running the actual + * command. + */ + old_buffer = scmd->request_buffer; + old_bufflen = scmd->request_bufflen; + memcpy(old_cmnd, scmd->cmnd, sizeof(scmd->cmnd)); + old_data_direction = scmd->sc_data_direction; + old_cmd_len = scmd->cmd_len; + old_use_sg = scmd->use_sg; + + if (copy_sense) { + int gfp_mask = GFP_ATOMIC; + + if (shost->hostt->unchecked_isa_dma) + gfp_mask |= __GFP_DMA; + + scmd->sc_data_direction = DMA_FROM_DEVICE; + scmd->request_bufflen = 252; + scmd->request_buffer = kzalloc(scmd->request_bufflen, gfp_mask); + if (!scmd->request_buffer) + return FAILED; + } else { + scmd->request_buffer = NULL; + scmd->request_bufflen = 0; + scmd->sc_data_direction = DMA_NONE; + } + + scmd->underflow = 0; + scmd->use_sg = 0; + scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); + if (sdev->scsi_level <= SCSI_2) scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) | (sdev->lun << 5 & 0xe0); + /* + * Zero the sense buffer. The scsi spec mandates that any + * untransferred sense data should be interpreted as being zero. + */ + memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); + shost->eh_action = &done; spin_lock_irqsave(shost->host_lock, flags); @@ -522,6 +570,29 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) rtn = FAILED; } + + /* + * Last chance to have valid sense data. + */ + if (copy_sense) { + if (!SCSI_SENSE_VALID(scmd)) { + memcpy(scmd->sense_buffer, scmd->request_buffer, + sizeof(scmd->sense_buffer)); + } + kfree(scmd->request_buffer); + } + + + /* + * Restore original data + */ + scmd->request_buffer = old_buffer; + scmd->request_bufflen = old_bufflen; + memcpy(scmd->cmnd, old_cmnd, sizeof(scmd->cmnd)); + scmd->sc_data_direction = old_data_direction; + scmd->cmd_len = old_cmd_len; + scmd->use_sg = old_use_sg; + scmd->result = old_result; return rtn; } @@ -537,56 +608,10 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) static int scsi_request_sense(struct scsi_cmnd *scmd) { static unsigned char generic_sense[6] = - {REQUEST_SENSE, 0, 0, 0, 252, 0}; - unsigned char *scsi_result; - int saved_result; - int rtn; + {REQUEST_SENSE, 0, 0, 0, 252, 0}; memcpy(scmd->cmnd, generic_sense, sizeof(generic_sense)); - - scsi_result = kmalloc(252, GFP_ATOMIC | ((scmd->device->host->hostt->unchecked_isa_dma) ? __GFP_DMA : 0)); - - - if (unlikely(!scsi_result)) { - printk(KERN_ERR "%s: cannot allocate scsi_result.\n", - __FUNCTION__); - return FAILED; - } - - /* - * zero the sense buffer. some host adapters automatically always - * request sense, so it is not a good idea that - * scmd->request_buffer and scmd->sense_buffer point to the same - * address (db). 0 is not a valid sense code. - */ - memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); - memset(scsi_result, 0, 252); - - saved_result = scmd->result; - scmd->request_buffer = scsi_result; - scmd->request_bufflen = 252; - scmd->use_sg = 0; - scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); - scmd->sc_data_direction = DMA_FROM_DEVICE; - scmd->underflow = 0; - - rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT); - - /* last chance to have valid sense data */ - if(!SCSI_SENSE_VALID(scmd)) { - memcpy(scmd->sense_buffer, scmd->request_buffer, - sizeof(scmd->sense_buffer)); - } - - kfree(scsi_result); - - /* - * when we eventually call scsi_finish, we really wish to complete - * the original request, so let's restore the original data. (db) - */ - scsi_setup_cmd_retry(scmd); - scmd->result = saved_result; - return rtn; + return scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT, 1); } /** @@ -605,12 +630,6 @@ void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, struct list_head *done_q) { scmd->device->host->host_failed--; scmd->eh_eflags = 0; - - /* - * set this back so that the upper level can correctly free up - * things. - */ - scsi_setup_cmd_retry(scmd); list_move_tail(&scmd->eh_entry, done_q); } EXPORT_SYMBOL(scsi_eh_finish_cmd); @@ -715,47 +734,26 @@ static int scsi_eh_tur(struct scsi_cmnd *scmd) { static unsigned char tur_command[6] = {TEST_UNIT_READY, 0, 0, 0, 0, 0}; int retry_cnt = 1, rtn; - int saved_result; retry_tur: memcpy(scmd->cmnd, tur_command, sizeof(tur_command)); - /* - * zero the sense buffer. the scsi spec mandates that any - * untransferred sense data should be interpreted as being zero. - */ - memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); - - saved_result = scmd->result; - scmd->request_buffer = NULL; - scmd->request_bufflen = 0; - scmd->use_sg = 0; - scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); - scmd->underflow = 0; - scmd->sc_data_direction = DMA_NONE; - rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT); + rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT, 0); - /* - * when we eventually call scsi_finish, we really wish to complete - * the original request, so let's restore the original data. (db) - */ - scsi_setup_cmd_retry(scmd); - scmd->result = saved_result; - - /* - * hey, we are done. let's look to see what happened. - */ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n", __FUNCTION__, scmd, rtn)); - if (rtn == SUCCESS) - return 0; - else if (rtn == NEEDS_RETRY) { + + switch (rtn) { + case NEEDS_RETRY: if (retry_cnt--) goto retry_tur; + /*FALLTHRU*/ + case SUCCESS: return 0; + default: + return 1; } - return 1; } /** @@ -837,44 +835,16 @@ static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd) static int scsi_eh_try_stu(struct scsi_cmnd *scmd) { static unsigned char stu_command[6] = {START_STOP, 0, 0, 0, 1, 0}; - int rtn; - int saved_result; - if (!scmd->device->allow_restart) - return 1; - - memcpy(scmd->cmnd, stu_command, sizeof(stu_command)); - - /* - * zero the sense buffer. the scsi spec mandates that any - * untransferred sense data should be interpreted as being zero. - */ - memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer)); - - saved_result = scmd->result; - scmd->request_buffer = NULL; - scmd->request_bufflen = 0; - scmd->use_sg = 0; - scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); - scmd->underflow = 0; - scmd->sc_data_direction = DMA_NONE; + if (scmd->device->allow_restart) { + int rtn; - rtn = scsi_send_eh_cmnd(scmd, START_UNIT_TIMEOUT); - - /* - * when we eventually call scsi_finish, we really wish to complete - * the original request, so let's restore the original data. (db) - */ - scsi_setup_cmd_retry(scmd); - scmd->result = saved_result; + memcpy(scmd->cmnd, stu_command, sizeof(stu_command)); + rtn = scsi_send_eh_cmnd(scmd, START_UNIT_TIMEOUT, 0); + if (rtn == SUCCESS) + return 0; + } - /* - * hey, we are done. let's look to see what happened. - */ - SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n", - __FUNCTION__, scmd, rtn)); - if (rtn == SUCCESS) - return 0; return 1; } @@ -1684,8 +1654,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag) scmd->scsi_done = scsi_reset_provider_done_command; scmd->done = NULL; - scmd->buffer = NULL; - scmd->bufflen = 0; scmd->request_buffer = NULL; scmd->request_bufflen = 0; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 08af9aae7df3..077c1c691210 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -436,60 +436,16 @@ EXPORT_SYMBOL_GPL(scsi_execute_async); * * Arguments: cmd - command that is ready to be queued. * - * Returns: Nothing - * * Notes: This function has the job of initializing a number of * fields related to error handling. Typically this will * be called once for each command, as required. */ -static int scsi_init_cmd_errh(struct scsi_cmnd *cmd) +static void scsi_init_cmd_errh(struct scsi_cmnd *cmd) { cmd->serial_number = 0; - memset(cmd->sense_buffer, 0, sizeof cmd->sense_buffer); - if (cmd->cmd_len == 0) cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]); - - /* - * We need saved copies of a number of fields - this is because - * error handling may need to overwrite these with different values - * to run different commands, and once error handling is complete, - * we will need to restore these values prior to running the actual - * command. - */ - cmd->old_use_sg = cmd->use_sg; - cmd->old_cmd_len = cmd->cmd_len; - cmd->sc_old_data_direction = cmd->sc_data_direction; - cmd->old_underflow = cmd->underflow; - memcpy(cmd->data_cmnd, cmd->cmnd, sizeof(cmd->cmnd)); - cmd->buffer = cmd->request_buffer; - cmd->bufflen = cmd->request_bufflen; - - return 1; -} - -/* - * Function: scsi_setup_cmd_retry() - * - * Purpose: Restore the command state for a retry - * - * Arguments: cmd - command to be restored - * - * Returns: Nothing - * - * Notes: Immediately prior to retrying a command, we need - * to restore certain fields that we saved above. - */ -void scsi_setup_cmd_retry(struct scsi_cmnd *cmd) -{ - memcpy(cmd->cmnd, cmd->data_cmnd, sizeof(cmd->data_cmnd)); - cmd->request_buffer = cmd->buffer; - cmd->request_bufflen = cmd->bufflen; - cmd->use_sg = cmd->old_use_sg; - cmd->cmd_len = cmd->old_cmd_len; - cmd->sc_data_direction = cmd->sc_old_data_direction; - cmd->underflow = cmd->old_underflow; } void scsi_device_unbusy(struct scsi_device *sdev) @@ -807,22 +763,13 @@ static void scsi_free_sgtable(struct scatterlist *sgl, int index) */ static void scsi_release_buffers(struct scsi_cmnd *cmd) { - struct request *req = cmd->request; - - /* - * Free up any indirection buffers we allocated for DMA purposes. - */ if (cmd->use_sg) scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); - else if (cmd->request_buffer != req->buffer) - kfree(cmd->request_buffer); /* * Zero these out. They now point to freed memory, and it is * dangerous to hang onto the pointers. */ - cmd->buffer = NULL; - cmd->bufflen = 0; cmd->request_buffer = NULL; cmd->request_bufflen = 0; } @@ -858,7 +805,7 @@ static void scsi_release_buffers(struct scsi_cmnd *cmd) void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) { int result = cmd->result; - int this_count = cmd->bufflen; + int this_count = cmd->request_bufflen; request_queue_t *q = cmd->device->request_queue; struct request *req = cmd->request; int clear_errors = 1; @@ -866,28 +813,14 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) int sense_valid = 0; int sense_deferred = 0; - /* - * Free up any indirection buffers we allocated for DMA purposes. - * For the case of a READ, we need to copy the data out of the - * bounce buffer and into the real buffer. - */ - if (cmd->use_sg) - scsi_free_sgtable(cmd->buffer, cmd->sglist_len); - else if (cmd->buffer != req->buffer) { - if (rq_data_dir(req) == READ) { - unsigned long flags; - char *to = bio_kmap_irq(req->bio, &flags); - memcpy(to, cmd->buffer, cmd->bufflen); - bio_kunmap_irq(to, &flags); - } - kfree(cmd->buffer); - } + scsi_release_buffers(cmd); if (result) { sense_valid = scsi_command_normalize_sense(cmd, &sshdr); if (sense_valid) sense_deferred = scsi_sense_is_deferred(&sshdr); } + if (blk_pc_request(req)) { /* SG_IO ioctl from block level */ req->errors = result; if (result) { @@ -907,15 +840,6 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) req->data_len = cmd->resid; } - /* - * Zero these out. They now point to freed memory, and it is - * dangerous to hang onto the pointers. - */ - cmd->buffer = NULL; - cmd->bufflen = 0; - cmd->request_buffer = NULL; - cmd->request_bufflen = 0; - /* * Next deal with any sectors which we were able to correctly * handle. @@ -1012,7 +936,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) if (!(req->flags & REQ_QUIET)) { scmd_printk(KERN_INFO, cmd, "Volume overflow, CDB: "); - __scsi_print_command(cmd->data_cmnd); + __scsi_print_command(cmd->cmnd); scsi_print_sense("", cmd); } /* See SSC3rXX or current. */ @@ -1143,7 +1067,7 @@ static void scsi_blk_pc_done(struct scsi_cmnd *cmd) * successfully. Since this is a REQ_BLOCK_PC command the * caller should check the request's errors value */ - scsi_io_completion(cmd, cmd->bufflen); + scsi_io_completion(cmd, cmd->request_bufflen); } static void scsi_setup_blk_pc_cmnd(struct scsi_cmnd *cmd) diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index e2fbe9a9d5a9..ae24c85aaeea 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -57,7 +57,6 @@ extern int scsi_eh_scmd_add(struct scsi_cmnd *, int); /* scsi_lib.c */ extern int scsi_maybe_unblock_host(struct scsi_device *sdev); -extern void scsi_setup_cmd_retry(struct scsi_cmnd *cmd); extern void scsi_device_unbusy(struct scsi_device *sdev); extern int scsi_queue_insert(struct scsi_cmnd *cmd, int reason); extern void scsi_next_command(struct scsi_cmnd *cmd); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3225d31449e1..98bd3aab9739 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -502,8 +502,7 @@ static int sd_init_command(struct scsi_cmnd * SCpnt) SCpnt->cmnd[4] = (unsigned char) this_count; SCpnt->cmnd[5] = 0; } - SCpnt->request_bufflen = SCpnt->bufflen = - this_count * sdp->sector_size; + SCpnt->request_bufflen = this_count * sdp->sector_size; /* * We shouldn't disconnect in the middle of a sector, so with a dumb diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index fd94408577e5..fae6e95a6298 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -360,7 +360,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) "mismatch count %d, bytes %d\n", size, SCpnt->request_bufflen); if (SCpnt->request_bufflen > size) - SCpnt->request_bufflen = SCpnt->bufflen = size; + SCpnt->request_bufflen = size; } } @@ -387,8 +387,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) if (this_count > 0xffff) { this_count = 0xffff; - SCpnt->request_bufflen = SCpnt->bufflen = - this_count * s_size; + SCpnt->request_bufflen = this_count * s_size; } SCpnt->cmnd[2] = (unsigned char) (block >> 24) & 0xff; diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 371f70d9aa92..58e6444eebee 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -58,9 +58,7 @@ struct scsi_cmnd { int timeout_per_command; unsigned char cmd_len; - unsigned char old_cmd_len; enum dma_data_direction sc_data_direction; - enum dma_data_direction sc_old_data_direction; /* These elements define the operation we are about to perform */ #define MAX_COMMAND_SIZE 16 @@ -71,18 +69,11 @@ struct scsi_cmnd { void *request_buffer; /* Actual requested buffer */ /* These elements define the operation we ultimately want to perform */ - unsigned char data_cmnd[MAX_COMMAND_SIZE]; - unsigned short old_use_sg; /* We save use_sg here when requesting - * sense info */ unsigned short use_sg; /* Number of pieces of scatter-gather */ unsigned short sglist_len; /* size of malloc'd scatter-gather list */ - unsigned bufflen; /* Size of data buffer */ - void *buffer; /* Data buffer */ unsigned underflow; /* Return error if less than this amount is transferred */ - unsigned old_underflow; /* save underflow here when reusing the - * command for error handling */ unsigned transfersize; /* How much we are guaranteed to transfer with each SCSI transfer -- cgit v1.2.3 From ebbaeab18b1c520054ea70e512ac0db7456ede01 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sun, 9 Jul 2006 11:36:23 -0700 Subject: [PKT_SCHED]: act_api: Fix module leak while flushing actions Module reference needs to be given back if message header construction fails. Signed-off-by: Thomas Graf Signed-off-by: David S. Miller --- net/sched/act_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 599423cc9d0d..0972247a839c 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -602,8 +602,8 @@ static int tca_action_flush(struct rtattr *rta, struct nlmsghdr *n, u32 pid) return err; rtattr_failure: - module_put(a->ops->owner); nlmsg_failure: + module_put(a->ops->owner); err_out: kfree_skb(skb); kfree(a); -- cgit v1.2.3 From 24781734643ea2e9fd864f58000e47793e2dcb04 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 9 Jul 2006 12:13:18 -0700 Subject: [ATM] net/atm/clip.c: fix PROC_FS=n compile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following compile error with CONFIG_PROC_FS=n by reverting commit dcdb02752ff13a64433c36f2937a58d93ae7a19e: <-- snip --> ... CC net/atm/clip.o net/atm/clip.c: In function ‘atm_clip_init’: net/atm/clip.c:975: error: ‘atm_proc_root’ undeclared (first use in this function) net/atm/clip.c:975: error: (Each undeclared identifier is reported only once net/atm/clip.c:975: error: for each function it appears in.) net/atm/clip.c:977: error: ‘arp_seq_fops’ undeclared (first use in this function) make[2]: *** [net/atm/clip.o] Error 1 <-- snip --> Signed-off-by: Adrian Bunk Signed-off-by: David S. Miller --- net/atm/clip.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/net/atm/clip.c b/net/atm/clip.c index 121bf6f49148..2e62105d91bd 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -962,7 +962,6 @@ static struct file_operations arp_seq_fops = { static int __init atm_clip_init(void) { - struct proc_dir_entry *p; neigh_table_init_no_netlink(&clip_tbl); clip_tbl_hook = &clip_tbl; @@ -972,9 +971,15 @@ static int __init atm_clip_init(void) setup_timer(&idle_timer, idle_timer_check, 0); - p = create_proc_entry("arp", S_IRUGO, atm_proc_root); - if (p) - p->proc_fops = &arp_seq_fops; +#ifdef CONFIG_PROC_FS + { + struct proc_dir_entry *p; + + p = create_proc_entry("arp", S_IRUGO, atm_proc_root); + if (p) + p->proc_fops = &arp_seq_fops; + } +#endif return 0; } -- cgit v1.2.3 From 1b30dd359ebec22d035e8b145751319f63772ca1 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sun, 9 Jul 2006 12:14:22 -0700 Subject: [AX.25]: Use kzalloc Replace kzalloc instead of kmalloc + memset. Signed-off-by: Ralf Baechle Signed-off-by: David S. Miller --- net/ax25/af_ax25.c | 3 +-- net/ax25/ax25_dev.c | 4 +--- net/netrom/af_netrom.c | 4 +--- net/rose/af_rose.c | 3 +-- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 10a3c0aa8398..f12be2acf9bc 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -486,10 +486,9 @@ ax25_cb *ax25_create_cb(void) { ax25_cb *ax25; - if ((ax25 = kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL) + if ((ax25 = kzalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL) return NULL; - memset(ax25, 0x00, sizeof(*ax25)); atomic_set(&ax25->refcount, 1); skb_queue_head_init(&ax25->write_queue); diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c index 47e6e790bd67..b787678220ff 100644 --- a/net/ax25/ax25_dev.c +++ b/net/ax25/ax25_dev.c @@ -55,15 +55,13 @@ void ax25_dev_device_up(struct net_device *dev) { ax25_dev *ax25_dev; - if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) { + if ((ax25_dev = kzalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) { printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n"); return; } ax25_unregister_sysctl(); - memset(ax25_dev, 0x00, sizeof(*ax25_dev)); - dev->ax25_ptr = ax25_dev; ax25_dev->dev = dev; dev_hold(dev); diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index 389a4119e1b4..ecc796878f38 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -1382,14 +1382,12 @@ static int __init nr_proto_init(void) return -1; } - dev_nr = kmalloc(nr_ndevs * sizeof(struct net_device *), GFP_KERNEL); + dev_nr = kzalloc(nr_ndevs * sizeof(struct net_device *), GFP_KERNEL); if (dev_nr == NULL) { printk(KERN_ERR "NET/ROM: nr_proto_init - unable to allocate device array\n"); return -1; } - memset(dev_nr, 0x00, nr_ndevs * sizeof(struct net_device *)); - for (i = 0; i < nr_ndevs; i++) { char name[IFNAMSIZ]; struct net_device *dev; diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index d0a67bb31363..c115295ab431 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -1490,14 +1490,13 @@ static int __init rose_proto_init(void) rose_callsign = null_ax25_address; - dev_rose = kmalloc(rose_ndevs * sizeof(struct net_device *), GFP_KERNEL); + dev_rose = kzalloc(rose_ndevs * sizeof(struct net_device *), GFP_KERNEL); if (dev_rose == NULL) { printk(KERN_ERR "ROSE: rose_proto_init - unable to allocate device structure\n"); rc = -ENOMEM; goto out_proto_unregister; } - memset(dev_rose, 0x00, rose_ndevs * sizeof(struct net_device*)); for (i = 0; i < rose_ndevs; i++) { struct net_device *dev; char name[IFNAMSIZ]; -- cgit v1.2.3 From f6dd9221dddb3550e60d32aee688588ec208312c Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 7 Jul 2006 20:44:38 -0400 Subject: ACPI: ACPICA 20060707 Added the ACPI_PACKED_POINTERS_NOT_SUPPORTED macro to support C compilers that do not allow the initialization of address pointers within packed structures - even though the hardware itself may support misaligned transfers. Some of the debug data structures are packed by default to minimize size. Added an error message for the case where acpi_os_get_thread_id() returns zero. A non-zero value is required by the core ACPICA code to ensure the proper operation of AML mutexes and recursive control methods. The DSDT is now the only ACPI table that determines whether the AML interpreter is in 32-bit or 64-bit mode. Not really a functional change, but the hooks for per-table 32/64 switching have been removed from the code. A clarification to the ACPI specification is forthcoming in ACPI 3.0B. Fixed a possible leak of an Owner ID in the error path of tbinstal.c acpi_tb_init_table_descriptor() and migrated all table OwnerID deletion to a single place in acpi_tb_uninstall_table() to correct possible leaks when using the acpi_tb_delete_tables_by_type() interface (with assistance from Lance Ortiz.) Fixed a problem with Serialized control methods where the semaphore associated with the method could be over-signaled after multiple method invocations. Fixed two issues with the locking of the internal namespace data structure. Both the Unload() operator and acpi_unload_table() interface now lock the namespace during the namespace deletion associated with the table unload (with assistance from Linn Crosetto.) Fixed problem reports (Valery Podrezov) integrated: - Eliminate unnecessary memory allocation for CreateXxxxField http://bugzilla.kernel.org/show_bug.cgi?id=5426 Fixed problem reports (Fiodor Suietov) integrated: - Incomplete cleanup branches in AcpiTbGetTableRsdt (BZ 369) - On Address Space handler deletion, needless deactivation call (BZ 374) - AcpiRemoveAddressSpaceHandler: validate Device handle parameter (BZ 375) - Possible memory leak, Notify sub-objects of Processor, Power, ThermalZone (BZ 376) - AcpiRemoveAddressSpaceHandler: validate Handler parameter (BZ 378) - Minimum Length of RSDT should be validated (BZ 379) - AcpiRemoveNotifyHandler: return AE_NOT_EXIST if Processor Obj has no Handler (BZ (380) - AcpiUnloadTable: return AE_NOT_EXIST if no table of specified type loaded (BZ 381) Signed-off-by: Bob Moore Signed-off-by: Len Brown --- drivers/acpi/dispatcher/dsinit.c | 10 --------- drivers/acpi/dispatcher/dsmethod.c | 23 ++++---------------- drivers/acpi/dispatcher/dswexec.c | 4 ++-- drivers/acpi/events/evregion.c | 44 ++++++++++++++++++++++---------------- drivers/acpi/events/evxface.c | 44 ++++++++++++++++++++++---------------- drivers/acpi/events/evxfregn.c | 13 ++++++++++- drivers/acpi/executer/exconfig.c | 1 - drivers/acpi/executer/exconvrt.c | 3 +++ drivers/acpi/executer/exsystem.c | 8 +++---- drivers/acpi/namespace/nsalloc.c | 13 ++++++++++- drivers/acpi/tables/tbget.c | 12 ++++++++++- drivers/acpi/tables/tbinstal.c | 21 +++++++++++++++--- drivers/acpi/tables/tbrsdt.c | 27 +++++++++++++++++++---- drivers/acpi/tables/tbxface.c | 32 ++++++++++++++++----------- drivers/acpi/utilities/utdelete.c | 13 +++++++---- drivers/acpi/utilities/utmisc.c | 25 ++++++++++++++++------ drivers/acpi/utilities/utstate.c | 7 ++++++ include/acpi/acconfig.h | 2 +- include/acpi/acinterp.h | 10 ++++++--- include/acpi/aclocal.h | 2 +- include/acpi/acresrc.h | 8 +++++-- 21 files changed, 209 insertions(+), 113 deletions(-) diff --git a/drivers/acpi/dispatcher/dsinit.c b/drivers/acpi/dispatcher/dsinit.c index daf51b5b5875..1888c055d10f 100644 --- a/drivers/acpi/dispatcher/dsinit.c +++ b/drivers/acpi/dispatcher/dsinit.c @@ -116,16 +116,6 @@ acpi_ds_init_one_object(acpi_handle obj_handle, case ACPI_TYPE_METHOD: - /* - * Set the execution data width (32 or 64) based upon the - * revision number of the parent ACPI table. - * TBD: This is really for possible future support of integer width - * on a per-table basis. Currently, we just use a global for the width. - */ - if (info->table_desc->pointer->revision == 1) { - node->flags |= ANOBJ_DATA_WIDTH_32; - } - info->method_count++; break; diff --git a/drivers/acpi/dispatcher/dsmethod.c b/drivers/acpi/dispatcher/dsmethod.c index a39a33f4847a..cf888add3191 100644 --- a/drivers/acpi/dispatcher/dsmethod.c +++ b/drivers/acpi/dispatcher/dsmethod.c @@ -134,7 +134,7 @@ acpi_ds_create_method_mutex(union acpi_operand_object *method_desc) union acpi_operand_object *mutex_desc; acpi_status status; - ACPI_FUNCTION_NAME(ds_create_method_mutex); + ACPI_FUNCTION_TRACE(ds_create_method_mutex); /* Create the new mutex object */ @@ -493,7 +493,7 @@ acpi_ds_restart_control_method(struct acpi_walk_state *walk_state, ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "****Restart [%4.4s] Op %p ReturnValueFromCallee %p\n", - (char *)&walk_state->method_node->name, + acpi_ut_get_node_name(walk_state->method_node), walk_state->method_call_op, return_desc)); ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, @@ -610,6 +610,7 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, acpi_os_release_mutex(method_desc->method.mutex->mutex. os_mutex); + method_desc->method.mutex->mutex.owner_thread = NULL; } } @@ -620,27 +621,11 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, */ method_node = walk_state->method_node; - /* Lock namespace for possible update */ - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return_VOID; - } - - /* - * Delete any namespace entries created immediately underneath - * the method - */ - if (method_node && method_node->child) { - acpi_ns_delete_namespace_subtree(method_node); - } - /* - * Delete any namespace entries created anywhere else within + * Delete any namespace objects created anywhere within * the namespace by the execution of this method */ acpi_ns_delete_namespace_by_owner(method_desc->method.owner_id); - status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); } /* Decrement the thread count on the method */ diff --git a/drivers/acpi/dispatcher/dswexec.c b/drivers/acpi/dispatcher/dswexec.c index b1ded62d0df1..d7a616c3104e 100644 --- a/drivers/acpi/dispatcher/dswexec.c +++ b/drivers/acpi/dispatcher/dswexec.c @@ -313,10 +313,10 @@ acpi_ds_exec_begin_op(struct acpi_walk_state *walk_state, case AML_CLASS_EXECUTE: case AML_CLASS_CREATE: /* - * Most operators with arguments. + * Most operators with arguments (except create_xxx_field operators) * Start a new result/operand state */ - if (walk_state->opcode != AML_CREATE_FIELD_OP) { + if (walk_state->op_info->object_type != ACPI_TYPE_BUFFER_FIELD) { status = acpi_ds_result_stack_push(walk_state); } break; diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c index 094a17e4c86d..21caae04fe85 100644 --- a/drivers/acpi/events/evregion.c +++ b/drivers/acpi/events/evregion.c @@ -528,34 +528,40 @@ acpi_ev_detach_region(union acpi_operand_object *region_obj, } } - /* Call the setup handler with the deactivate notification */ + /* + * If the region has been activated, call the setup handler + * with the deactivate notification + */ + if (region_obj->region.flags & AOPOBJ_SETUP_COMPLETE) { + region_setup = handler_obj->address_space.setup; + status = + region_setup(region_obj, + ACPI_REGION_DEACTIVATE, + handler_obj->address_space. + context, region_context); - region_setup = handler_obj->address_space.setup; - status = - region_setup(region_obj, ACPI_REGION_DEACTIVATE, - handler_obj->address_space.context, - region_context); + /* Init routine may fail, Just ignore errors */ - /* Init routine may fail, Just ignore errors */ + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "from region handler - deactivate, [%s]", + acpi_ut_get_region_name + (region_obj->region. + space_id))); + } - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "from region init, [%s]", - acpi_ut_get_region_name - (region_obj->region.space_id))); + region_obj->region.flags &= + ~(AOPOBJ_SETUP_COMPLETE); } - region_obj->region.flags &= ~(AOPOBJ_SETUP_COMPLETE); - /* * Remove handler reference in the region * - * NOTE: this doesn't mean that the region goes away - * The region is just inaccessible as indicated to - * the _REG method + * NOTE: this doesn't mean that the region goes away, the region + * is just inaccessible as indicated to the _REG method * - * If the region is on the handler's list - * this better be the region's handler + * If the region is on the handler's list, this must be the + * region's handler */ region_obj->region.handler = NULL; acpi_ut_remove_reference(handler_obj); diff --git a/drivers/acpi/events/evxface.c b/drivers/acpi/events/evxface.c index 4f948df17ab9..923fd2b46955 100644 --- a/drivers/acpi/events/evxface.c +++ b/drivers/acpi/events/evxface.c @@ -428,7 +428,7 @@ acpi_remove_notify_handler(acpi_handle device, node = acpi_ns_map_handle_to_node(device); if (!node) { status = AE_BAD_PARAMETER; - goto unlock; + goto unlock_and_exit; } /* Root Object */ @@ -442,7 +442,7 @@ acpi_remove_notify_handler(acpi_handle device, ((handler_type & ACPI_DEVICE_NOTIFY) && !acpi_gbl_device_notify.handler)) { status = AE_NOT_EXIST; - goto unlock; + goto unlock_and_exit; } /* Make sure all deferred tasks are completed */ @@ -474,7 +474,7 @@ acpi_remove_notify_handler(acpi_handle device, if (!acpi_ev_is_notify_object(node)) { status = AE_TYPE; - goto unlock; + goto unlock_and_exit; } /* Check for an existing internal object */ @@ -482,17 +482,21 @@ acpi_remove_notify_handler(acpi_handle device, obj_desc = acpi_ns_get_attached_object(node); if (!obj_desc) { status = AE_NOT_EXIST; - goto unlock; + goto unlock_and_exit; } /* Object exists - make sure there's an existing handler */ if (handler_type & ACPI_SYSTEM_NOTIFY) { notify_obj = obj_desc->common_notify.system_notify; - if ((!notify_obj) || - (notify_obj->notify.handler != handler)) { + if (!notify_obj) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + if (notify_obj->notify.handler != handler) { status = AE_BAD_PARAMETER; - goto unlock; + goto unlock_and_exit; } /* Make sure all deferred tasks are completed */ @@ -510,10 +514,14 @@ acpi_remove_notify_handler(acpi_handle device, if (handler_type & ACPI_DEVICE_NOTIFY) { notify_obj = obj_desc->common_notify.device_notify; - if ((!notify_obj) || - (notify_obj->notify.handler != handler)) { + if (!notify_obj) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + if (notify_obj->notify.handler != handler) { status = AE_BAD_PARAMETER; - goto unlock; + goto unlock_and_exit; } /* Make sure all deferred tasks are completed */ @@ -530,9 +538,9 @@ acpi_remove_notify_handler(acpi_handle device, } } -unlock: + unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); -exit: + exit: if (ACPI_FAILURE(status)) ACPI_EXCEPTION((AE_INFO, status, "Removing notify handler")); return_ACPI_STATUS(status); @@ -586,7 +594,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); if (!gpe_event_info) { status = AE_BAD_PARAMETER; - goto unlock; + goto unlock_and_exit; } /* Make sure that there isn't a handler there already */ @@ -594,7 +602,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_HANDLER) { status = AE_ALREADY_EXISTS; - goto unlock; + goto unlock_and_exit; } /* Allocate and init handler object */ @@ -602,7 +610,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, handler = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_handler_info)); if (!handler) { status = AE_NO_MEMORY; - goto unlock; + goto unlock_and_exit; } handler->address = address; @@ -613,7 +621,7 @@ acpi_install_gpe_handler(acpi_handle gpe_device, status = acpi_ev_disable_gpe(gpe_event_info); if (ACPI_FAILURE(status)) { - goto unlock; + goto unlock_and_exit; } /* Install the handler */ @@ -628,9 +636,9 @@ acpi_install_gpe_handler(acpi_handle gpe_device, acpi_os_release_lock(acpi_gbl_gpe_lock, flags); -unlock: + unlock_and_exit: (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); -exit: + exit: if (ACPI_FAILURE(status)) ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler failed")); diff --git a/drivers/acpi/events/evxfregn.c b/drivers/acpi/events/evxfregn.c index e8b86a0baad0..83b12a9afa32 100644 --- a/drivers/acpi/events/evxfregn.c +++ b/drivers/acpi/events/evxfregn.c @@ -155,7 +155,11 @@ acpi_remove_address_space_handler(acpi_handle device, /* Convert and validate the device handle */ node = acpi_ns_map_handle_to_node(device); - if (!node) { + if (!node || + ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_PROCESSOR) && + (node->type != ACPI_TYPE_THERMAL) && + (node != acpi_gbl_root_node))) { status = AE_BAD_PARAMETER; goto unlock_and_exit; } @@ -178,6 +182,13 @@ acpi_remove_address_space_handler(acpi_handle device, if (handler_obj->address_space.space_id == space_id) { + /* Handler must be the same as the installed handler */ + + if (handler_obj->address_space.handler != handler) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + /* Matched space_id, first dereference this in the Regions */ ACPI_DEBUG_PRINT((ACPI_DB_OPREGION, diff --git a/drivers/acpi/executer/exconfig.c b/drivers/acpi/executer/exconfig.c index 83fed079a276..c8341fa5fe01 100644 --- a/drivers/acpi/executer/exconfig.c +++ b/drivers/acpi/executer/exconfig.c @@ -502,7 +502,6 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle) * (Offset contains the table_id) */ acpi_ns_delete_namespace_by_owner(table_info->owner_id); - acpi_ut_release_owner_id(&table_info->owner_id); /* Delete the table itself */ diff --git a/drivers/acpi/executer/exconvrt.c b/drivers/acpi/executer/exconvrt.c index b732e399b1ef..544e81a6a438 100644 --- a/drivers/acpi/executer/exconvrt.c +++ b/drivers/acpi/executer/exconvrt.c @@ -170,6 +170,9 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, return_ACPI_STATUS(AE_NO_MEMORY); } + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(result))); + /* Save the Result */ return_desc->integer.value = result; diff --git a/drivers/acpi/executer/exsystem.c b/drivers/acpi/executer/exsystem.c index 6b5d1e6ce94b..28aef3e69ecc 100644 --- a/drivers/acpi/executer/exsystem.c +++ b/drivers/acpi/executer/exsystem.c @@ -60,7 +60,7 @@ ACPI_MODULE_NAME("exsystem") * * DESCRIPTION: Implements a semaphore wait with a check to see if the * semaphore is available immediately. If it is not, the - * interpreter is released. + * interpreter is released before waiting. * ******************************************************************************/ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) @@ -110,9 +110,9 @@ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) * * RETURN: Status * - * DESCRIPTION: Implements a semaphore wait with a check to see if the - * semaphore is available immediately. If it is not, the - * interpreter is released. + * DESCRIPTION: Implements a mutex wait with a check to see if the + * mutex is available immediately. If it is not, the + * interpreter is released before waiting. * ******************************************************************************/ diff --git a/drivers/acpi/namespace/nsalloc.c b/drivers/acpi/namespace/nsalloc.c index dc3f0739a46b..55b407aae266 100644 --- a/drivers/acpi/namespace/nsalloc.c +++ b/drivers/acpi/namespace/nsalloc.c @@ -386,14 +386,17 @@ void acpi_ns_delete_namespace_subtree(struct acpi_namespace_node *parent_node) * specific ID. Used to delete entire ACPI tables. All * reference counts are updated. * + * MUTEX: Locks namespace during deletion walk. + * ******************************************************************************/ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) { struct acpi_namespace_node *child_node; struct acpi_namespace_node *deletion_node; - u32 level; struct acpi_namespace_node *parent_node; + u32 level; + acpi_status status; ACPI_FUNCTION_TRACE_U32(ns_delete_namespace_by_owner, owner_id); @@ -401,6 +404,13 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) return_VOID; } + /* Lock namespace for possible update */ + + status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE(status)) { + return_VOID; + } + deletion_node = NULL; parent_node = acpi_gbl_root_node; child_node = NULL; @@ -469,5 +479,6 @@ void acpi_ns_delete_namespace_by_owner(acpi_owner_id owner_id) } } + (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); return_VOID; } diff --git a/drivers/acpi/tables/tbget.c b/drivers/acpi/tables/tbget.c index 99eacceff563..7856db759af0 100644 --- a/drivers/acpi/tables/tbget.c +++ b/drivers/acpi/tables/tbget.c @@ -320,6 +320,16 @@ acpi_tb_get_this_table(struct acpi_pointer *address, ACPI_FUNCTION_TRACE(tb_get_this_table); + /* Validate minimum length */ + + if (header->length < sizeof(struct acpi_table_header)) { + ACPI_ERROR((AE_INFO, + "Table length (%X) is smaller than minimum (%X)", + header->length, sizeof(struct acpi_table_header))); + + return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); + } + /* * Flags contains the current processor mode (Virtual or Physical * addressing) The pointer_type is either Logical or Physical @@ -356,7 +366,7 @@ acpi_tb_get_this_table(struct acpi_pointer *address, */ status = acpi_os_map_memory(address->pointer.physical, (acpi_size) header->length, - (void *)&full_table); + ACPI_CAST_PTR(void, &full_table)); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, "Could not map memory for table [%4.4s] at %8.8X%8.8X for length %X", diff --git a/drivers/acpi/tables/tbinstal.c b/drivers/acpi/tables/tbinstal.c index 7ca2df75bb11..1668a232fb67 100644 --- a/drivers/acpi/tables/tbinstal.c +++ b/drivers/acpi/tables/tbinstal.c @@ -256,7 +256,7 @@ acpi_tb_init_table_descriptor(acpi_table_type table_type, status = acpi_ut_allocate_owner_id(&table_desc->owner_id); if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + goto error_exit1; } /* Install the table into the global data structure */ @@ -274,8 +274,8 @@ acpi_tb_init_table_descriptor(acpi_table_type table_type, * at this location, so return an error. */ if (list_head->next) { - ACPI_FREE(table_desc); - return_ACPI_STATUS(AE_ALREADY_EXISTS); + status = AE_ALREADY_EXISTS; + goto error_exit2; } table_desc->next = list_head->next; @@ -335,6 +335,17 @@ acpi_tb_init_table_descriptor(acpi_table_type table_type, table_info->owner_id = table_desc->owner_id; table_info->installed_desc = table_desc; return_ACPI_STATUS(AE_OK); + + /* Error exit with cleanup */ + + error_exit2: + + acpi_ut_release_owner_id(&table_desc->owner_id); + + error_exit1: + + ACPI_FREE(table_desc); + return_ACPI_STATUS(status); } /******************************************************************************* @@ -525,6 +536,10 @@ struct acpi_table_desc *acpi_tb_uninstall_table(struct acpi_table_desc acpi_tb_delete_single_table(table_desc); + /* Free the owner ID associated with this table */ + + acpi_ut_release_owner_id(&table_desc->owner_id); + /* Free the table descriptor */ next_desc = table_desc->next; diff --git a/drivers/acpi/tables/tbrsdt.c b/drivers/acpi/tables/tbrsdt.c index abcb08c2592a..0ad3dbb9ebca 100644 --- a/drivers/acpi/tables/tbrsdt.c +++ b/drivers/acpi/tables/tbrsdt.c @@ -183,6 +183,17 @@ acpi_status acpi_tb_validate_rsdt(struct acpi_table_header *table_ptr) ACPI_FUNCTION_ENTRY(); + /* Validate minimum length */ + + if (table_ptr->length < sizeof(struct acpi_table_header)) { + ACPI_ERROR((AE_INFO, + "RSDT/XSDT length (%X) is smaller than minimum (%X)", + table_ptr->length, + sizeof(struct acpi_table_header))); + + return (AE_INVALID_TABLE_LENGTH); + } + /* Search for appropriate signature, RSDT or XSDT */ if (acpi_gbl_root_table_type == ACPI_TABLE_TYPE_RSDT) { @@ -210,7 +221,7 @@ acpi_status acpi_tb_validate_rsdt(struct acpi_table_header *table_ptr) ACPI_ERROR((AE_INFO, "Looking for XSDT")); } - ACPI_DUMP_BUFFER((char *)table_ptr, 48); + ACPI_DUMP_BUFFER(ACPI_CAST_PTR(char, table_ptr), 48); return (AE_BAD_SIGNATURE); } @@ -258,7 +269,7 @@ acpi_status acpi_tb_get_table_rsdt(void) status = acpi_tb_validate_rsdt(table_info.pointer); if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + goto error_cleanup; } /* Get the number of tables defined in the RSDT or XSDT */ @@ -270,14 +281,14 @@ acpi_status acpi_tb_get_table_rsdt(void) status = acpi_tb_convert_to_xsdt(&table_info); if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + goto error_cleanup; } /* Save the table pointers and allocation info */ status = acpi_tb_init_table_descriptor(ACPI_TABLE_ID_XSDT, &table_info); if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + goto error_cleanup; } acpi_gbl_XSDT = @@ -285,4 +296,12 @@ acpi_status acpi_tb_get_table_rsdt(void) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "XSDT located at %p\n", acpi_gbl_XSDT)); return_ACPI_STATUS(status); + + error_cleanup: + + /* Free table allocated by acpi_tb_get_table */ + + acpi_tb_delete_single_table(&table_info); + + return_ACPI_STATUS(status); } diff --git a/drivers/acpi/tables/tbxface.c b/drivers/acpi/tables/tbxface.c index 4e91f2984815..7767987be15a 100644 --- a/drivers/acpi/tables/tbxface.c +++ b/drivers/acpi/tables/tbxface.c @@ -134,8 +134,8 @@ ACPI_EXPORT_SYMBOL(acpi_load_tables) * RETURN: Status * * DESCRIPTION: This function is called to load a table from the caller's - * buffer. The buffer must contain an entire ACPI Table including - * a valid header. The header fields will be verified, and if it + * buffer. The buffer must contain an entire ACPI Table including + * a valid header. The header fields will be verified, and if it * is determined that the table is invalid, the call will fail. * ******************************************************************************/ @@ -245,15 +245,18 @@ acpi_status acpi_unload_table(acpi_table_type table_type) /* Find all tables of the requested type */ table_desc = acpi_gbl_table_lists[table_type].next; + if (!table_desc) { + return_ACPI_STATUS(AE_NOT_EXIST); + } + while (table_desc) { /* - * Delete all namespace entries owned by this table. Note that these - * entries can appear anywhere in the namespace by virtue of the AML - * "Scope" operator. Thus, we need to track ownership by an ID, not + * Delete all namespace objects owned by this table. Note that these + * objects can appear anywhere in the namespace by virtue of the AML + * "Scope" operator. Thus, we need to track ownership by an ID, not * simply a position within the hierarchy */ acpi_ns_delete_namespace_by_owner(table_desc->owner_id); - acpi_ut_release_owner_id(&table_desc->owner_id); table_desc = table_desc->next; } @@ -275,12 +278,12 @@ ACPI_EXPORT_SYMBOL(acpi_unload_table) * see acpi_gbl_acpi_table_flag * out_table_header - pointer to the struct acpi_table_header if successful * - * DESCRIPTION: This function is called to get an ACPI table header. The caller + * DESCRIPTION: This function is called to get an ACPI table header. The caller * supplies an pointer to a data area sufficient to contain an ACPI * struct acpi_table_header structure. * * The header contains a length field that can be used to determine - * the size of the buffer needed to contain the entire table. This + * the size of the buffer needed to contain the entire table. This * function is not valid for the RSD PTR table since it does not * have a standard header and is fixed length. * @@ -322,7 +325,8 @@ acpi_get_table_header(acpi_table_type table_type, /* Copy the header to the caller's buffer */ - ACPI_MEMCPY((void *)out_table_header, (void *)tbl_ptr, + ACPI_MEMCPY(ACPI_CAST_PTR(void, out_table_header), + ACPI_CAST_PTR(void, tbl_ptr), sizeof(struct acpi_table_header)); return_ACPI_STATUS(status); @@ -344,10 +348,10 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_header) * * RETURN: Status * - * DESCRIPTION: This function is called to get an ACPI table. The caller + * DESCRIPTION: This function is called to get an ACPI table. The caller * supplies an out_buffer large enough to contain the entire ACPI - * table. The caller should call the acpi_get_table_header function - * first to determine the buffer size needed. Upon completion + * table. The caller should call the acpi_get_table_header function + * first to determine the buffer size needed. Upon completion * the out_buffer->Length field will indicate the number of bytes * copied into the out_buffer->buf_ptr buffer. This table will be * a complete table including the header. @@ -417,7 +421,9 @@ acpi_get_table(acpi_table_type table_type, /* Copy the table to the buffer */ - ACPI_MEMCPY((void *)ret_buffer->pointer, (void *)tbl_ptr, table_length); + ACPI_MEMCPY(ACPI_CAST_PTR(void, ret_buffer->pointer), + ACPI_CAST_PTR(void, tbl_ptr), table_length); + return_ACPI_STATUS(AE_OK); } diff --git a/drivers/acpi/utilities/utdelete.c b/drivers/acpi/utilities/utdelete.c index 38ebe1c54330..9d3f1149ba21 100644 --- a/drivers/acpi/utilities/utdelete.c +++ b/drivers/acpi/utilities/utdelete.c @@ -447,11 +447,16 @@ acpi_ut_update_object_reference(union acpi_operand_object *object, u16 action) */ switch (ACPI_GET_OBJECT_TYPE(object)) { case ACPI_TYPE_DEVICE: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_POWER: + case ACPI_TYPE_THERMAL: - acpi_ut_update_ref_count(object->device.system_notify, - action); - acpi_ut_update_ref_count(object->device.device_notify, - action); + /* Update the notify objects for these types (if present) */ + + acpi_ut_update_ref_count(object->common_notify. + system_notify, action); + acpi_ut_update_ref_count(object->common_notify. + device_notify, action); break; case ACPI_TYPE_PACKAGE: diff --git a/drivers/acpi/utilities/utmisc.c b/drivers/acpi/utilities/utmisc.c index 33268310c738..6d8a8211be90 100644 --- a/drivers/acpi/utilities/utmisc.c +++ b/drivers/acpi/utilities/utmisc.c @@ -65,7 +65,7 @@ ACPI_MODULE_NAME("utmisc") u8 acpi_ut_is_aml_table(struct acpi_table_header *table) { - /* Ignore tables that contain AML */ + /* These are the only tables that contain executable AML */ if (ACPI_COMPARE_NAME(table->signature, DSDT_SIG) || ACPI_COMPARE_NAME(table->signature, PSDT_SIG) || @@ -419,10 +419,15 @@ void acpi_ut_set_integer_width(u8 revision) { if (revision <= 1) { + + /* 32-bit case */ + acpi_gbl_integer_bit_width = 32; acpi_gbl_integer_nybble_width = 8; acpi_gbl_integer_byte_width = 4; } else { + /* 64-bit case (ACPI 2.0+) */ + acpi_gbl_integer_bit_width = 64; acpi_gbl_integer_nybble_width = 16; acpi_gbl_integer_byte_width = 8; @@ -502,6 +507,7 @@ acpi_ut_display_init_pathname(u8 type, * FUNCTION: acpi_ut_valid_acpi_char * * PARAMETERS: Char - The character to be examined + * Position - Byte position (0-3) * * RETURN: TRUE if the character is valid, FALSE otherwise * @@ -609,7 +615,9 @@ acpi_name acpi_ut_repair_name(acpi_name name) * * RETURN: Status and Converted value * - * DESCRIPTION: Convert a string into an unsigned value. + * DESCRIPTION: Convert a string into an unsigned value. Performs either a + * 32-bit or 64-bit conversion, depending on the current mode + * of the interpreter. * NOTE: Does not support Octal strings, not needed. * ******************************************************************************/ @@ -627,7 +635,7 @@ acpi_ut_strtoul64(char *string, u32 base, acpi_integer * ret_integer) u8 sign_of0x = 0; u8 term = 0; - ACPI_FUNCTION_TRACE(ut_stroul64); + ACPI_FUNCTION_TRACE_STR(ut_stroul64, string); switch (base) { case ACPI_ANY_BASE: @@ -675,11 +683,13 @@ acpi_ut_strtoul64(char *string, u32 base, acpi_integer * ret_integer) } } + /* + * Perform a 32-bit or 64-bit conversion, depending upon the current + * execution mode of the interpreter + */ dividend = (mode32) ? ACPI_UINT32_MAX : ACPI_UINT64_MAX; - /* At least one character in the string here */ - - /* Main loop: convert the string to a 64-bit integer */ + /* Main loop: convert the string to a 32- or 64-bit integer */ while (*string) { if (ACPI_IS_DIGIT(*string)) { @@ -754,6 +764,9 @@ acpi_ut_strtoul64(char *string, u32 base, acpi_integer * ret_integer) all_done: + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Converted value: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64(return_value))); + *ret_integer = return_value; return_ACPI_STATUS(AE_OK); diff --git a/drivers/acpi/utilities/utstate.c b/drivers/acpi/utilities/utstate.c index 0f5c5bb5deff..eaa13d05c859 100644 --- a/drivers/acpi/utilities/utstate.c +++ b/drivers/acpi/utilities/utstate.c @@ -199,6 +199,13 @@ struct acpi_thread_state *acpi_ut_create_thread_state(void) state->common.descriptor_type = ACPI_DESC_TYPE_STATE_THREAD; state->thread.thread_id = acpi_os_get_thread_id(); + /* Check for invalid thread ID - zero is very bad, it will break things */ + + if (!state->thread.thread_id) { + ACPI_ERROR((AE_INFO, "Invalid zero ID from AcpiOsGetThreadId")); + state->thread.thread_id = (acpi_thread_id) 1; + } + return_PTR((struct acpi_thread_state *)state); } diff --git a/include/acpi/acconfig.h b/include/acpi/acconfig.h index b492857fe721..9e6c23c360b2 100644 --- a/include/acpi/acconfig.h +++ b/include/acpi/acconfig.h @@ -63,7 +63,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20060623 +#define ACPI_CA_VERSION 0x20060707 /* * OS name, used for the _OS object. The _OS object is essentially obsolete, diff --git a/include/acpi/acinterp.h b/include/acpi/acinterp.h index 216339a8f1f6..91586d0d5bb5 100644 --- a/include/acpi/acinterp.h +++ b/include/acpi/acinterp.h @@ -53,10 +53,14 @@ #define ACPI_EXD_TABLE_SIZE(name) (sizeof(name) / sizeof (struct acpi_exdump_info)) /* - * If possible, pack the following structure to byte alignment, since we - * don't care about performance for debug output + * If possible, pack the following structures to byte alignment, since we + * don't care about performance for debug output. Two cases where we cannot + * pack the structures: + * + * 1) Hardware does not support misaligned memory transfers + * 2) Compiler does not support pointers within packed structures */ -#ifndef ACPI_MISALIGNMENT_NOT_SUPPORTED +#if (!defined(ACPI_MISALIGNMENT_NOT_SUPPORTED) && !defined(ACPI_PACKED_POINTERS_NOT_SUPPORTED)) #pragma pack(1) #endif diff --git a/include/acpi/aclocal.h b/include/acpi/aclocal.h index 56b802486161..7010fea26b48 100644 --- a/include/acpi/aclocal.h +++ b/include/acpi/aclocal.h @@ -204,7 +204,7 @@ struct acpi_namespace_node { /* Namespace Node flags */ #define ANOBJ_END_OF_PEER_LIST 0x01 /* End-of-list, Peer field points to parent */ -#define ANOBJ_DATA_WIDTH_32 0x02 /* Parent table uses 32-bit math */ +#define ANOBJ_RESERVED 0x02 /* Available for future use */ #define ANOBJ_METHOD_ARG 0x04 /* Node is a method argument */ #define ANOBJ_METHOD_LOCAL 0x08 /* Node is a method local */ #define ANOBJ_SUBTREE_HAS_INI 0x10 /* Used to optimize device initialization */ diff --git a/include/acpi/acresrc.h b/include/acpi/acresrc.h index ad11fc13fbef..80a3b33571b4 100644 --- a/include/acpi/acresrc.h +++ b/include/acpi/acresrc.h @@ -50,9 +50,13 @@ /* * If possible, pack the following structures to byte alignment, since we - * don't care about performance for debug output + * don't care about performance for debug output. Two cases where we cannot + * pack the structures: + * + * 1) Hardware does not support misaligned memory transfers + * 2) Compiler does not support pointers within packed structures */ -#ifndef ACPI_MISALIGNMENT_NOT_SUPPORTED +#if (!defined(ACPI_MISALIGNMENT_NOT_SUPPORTED) && !defined(ACPI_PACKED_POINTERS_NOT_SUPPORTED)) #pragma pack(1) #endif -- cgit v1.2.3 From ab8aa06a5c0b75974fb1949365cbb20a15cedf14 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 7 Jul 2006 20:11:07 -0400 Subject: ACPI: acpi_os_get_thread_id() returns current Linux mutexes and the debug code that that reference acpi_os_get_thread_id() are happy with 0. But the AML mutexes in exmutex.c expect a unique non-zero number for each thread - as they track this thread_id to permit the mutex re-entrancy defined by the ACPI spec. http://bugzilla.kernel.org/show_bug.cgi?id=6687 Signed-off-by: Len Brown --- drivers/acpi/executer/exmutex.c | 4 ++-- drivers/acpi/utilities/utdebug.c | 4 ++-- drivers/acpi/utilities/utmutex.c | 8 ++++---- include/acpi/aclocal.h | 2 +- include/acpi/platform/aclinux.h | 5 +++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/executer/exmutex.c b/drivers/acpi/executer/exmutex.c index d8ac2877cf05..3a39c2e8e104 100644 --- a/drivers/acpi/executer/exmutex.c +++ b/drivers/acpi/executer/exmutex.c @@ -267,9 +267,9 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, && (obj_desc->mutex.os_mutex != ACPI_GLOBAL_LOCK)) { ACPI_ERROR((AE_INFO, "Thread %X cannot release Mutex [%4.4s] acquired by thread %X", - walk_state->thread->thread_id, + (u32) walk_state->thread->thread_id, acpi_ut_get_node_name(obj_desc->mutex.node), - obj_desc->mutex.owner_thread->thread_id)); + (u32) obj_desc->mutex.owner_thread->thread_id)); return_ACPI_STATUS(AE_AML_NOT_OWNER); } diff --git a/drivers/acpi/utilities/utdebug.c b/drivers/acpi/utilities/utdebug.c index 5ec1cfcc611d..bb1eaf9aa653 100644 --- a/drivers/acpi/utilities/utdebug.c +++ b/drivers/acpi/utilities/utdebug.c @@ -47,7 +47,7 @@ ACPI_MODULE_NAME("utdebug") #ifdef ACPI_DEBUG_OUTPUT -static u32 acpi_gbl_prev_thread_id = 0xFFFFFFFF; +static acpi_thread_id acpi_gbl_prev_thread_id; static char *acpi_gbl_fn_entry_str = "----Entry"; static char *acpi_gbl_fn_exit_str = "----Exit-"; @@ -181,7 +181,7 @@ acpi_ut_debug_print(u32 requested_debug_level, if (ACPI_LV_THREADS & acpi_dbg_level) { acpi_os_printf ("\n**** Context Switch from TID %X to TID %X ****\n\n", - acpi_gbl_prev_thread_id, thread_id); + (u32) acpi_gbl_prev_thread_id, (u32) thread_id); } acpi_gbl_prev_thread_id = thread_id; diff --git a/drivers/acpi/utilities/utmutex.c b/drivers/acpi/utilities/utmutex.c index dfc8f30ca892..c39062a047cd 100644 --- a/drivers/acpi/utilities/utmutex.c +++ b/drivers/acpi/utilities/utmutex.c @@ -244,14 +244,14 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %X attempting to acquire Mutex [%s]\n", - this_thread_id, acpi_ut_get_mutex_name(mutex_id))); + (u32) this_thread_id, acpi_ut_get_mutex_name(mutex_id))); status = acpi_os_acquire_mutex(acpi_gbl_mutex_info[mutex_id].mutex, ACPI_WAIT_FOREVER); if (ACPI_SUCCESS(status)) { ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, "Thread %X acquired Mutex [%s]\n", - this_thread_id, + (u32) this_thread_id, acpi_ut_get_mutex_name(mutex_id))); acpi_gbl_mutex_info[mutex_id].use_count++; @@ -259,7 +259,7 @@ acpi_status acpi_ut_acquire_mutex(acpi_mutex_handle mutex_id) } else { ACPI_EXCEPTION((AE_INFO, status, "Thread %X could not acquire Mutex [%X]", - this_thread_id, mutex_id)); + (u32) this_thread_id, mutex_id)); } return (status); @@ -285,7 +285,7 @@ acpi_status acpi_ut_release_mutex(acpi_mutex_handle mutex_id) this_thread_id = acpi_os_get_thread_id(); ACPI_DEBUG_PRINT((ACPI_DB_MUTEX, - "Thread %X releasing Mutex [%s]\n", this_thread_id, + "Thread %X releasing Mutex [%s]\n", (u32) this_thread_id, acpi_ut_get_mutex_name(mutex_id))); if (mutex_id > ACPI_MAX_MUTEX) { diff --git a/include/acpi/aclocal.h b/include/acpi/aclocal.h index 56b802486161..fbafee6e56d3 100644 --- a/include/acpi/aclocal.h +++ b/include/acpi/aclocal.h @@ -127,7 +127,7 @@ typedef u8 acpi_owner_id; /* This Thread ID means that the mutex is not in use (unlocked) */ -#define ACPI_MUTEX_NOT_ACQUIRED (u32) -1 +#define ACPI_MUTEX_NOT_ACQUIRED (acpi_thread_id) 0 /* Table for the global mutexes */ diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 3f853cabbd41..1cb51bf96ece 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -59,6 +59,7 @@ #include #include #include +#include /* Host-dependent types and defines */ @@ -100,8 +101,8 @@ #define acpi_cpu_flags unsigned long -#define acpi_thread_id u32 +#define acpi_thread_id struct task_struct * -static inline acpi_thread_id acpi_os_get_thread_id(void) { return 0; } +static inline acpi_thread_id acpi_os_get_thread_id(void) { return current; } #endif /* __ACLINUX_H__ */ -- cgit v1.2.3 From d608ab9917fbe9926704c07b27df72ac78133870 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Mon, 26 Jun 2006 18:18:11 +0200 Subject: [SERIAL] dz: Fix compilation error Fix the following compilation error in the dz serial driver that got introduced with the "kernel console should send CRLF not LFCR" change. CC drivers/serial/dz.o drivers/serial/dz.c: In function 'dz_console_putchar': drivers/serial/dz.c:679: error: 'uport' undeclared (first use in this function) drivers/serial/dz.c:679: error: (Each undeclared identifier is reported only once drivers/serial/dz.c:679: error: for each function it appears in.) Signed-off-by: Martin Michlmayr Signed-off-by: Russell King --- drivers/serial/dz.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c index d119c8296a78..8a98aae80e22 100644 --- a/drivers/serial/dz.c +++ b/drivers/serial/dz.c @@ -673,7 +673,7 @@ static void dz_reset(struct dz_port *dport) } #ifdef CONFIG_SERIAL_DZ_CONSOLE -static void dz_console_putchar(struct uart_port *port, int ch) +static void dz_console_putchar(struct uart_port *uport, int ch) { struct dz_port *dport = (struct dz_port *)uport; unsigned long flags; -- cgit v1.2.3 From c65b15cfd6b8b74c6f2b3635bf47ee661d351ef3 Mon Sep 17 00:00:00 2001 From: Julien BLACHE Date: Sun, 9 Jul 2006 20:40:17 +0200 Subject: [SERIAL] IP22: fix serial console hangs The patch below fixes serial console hangs as seen on IP22 machines. Typically, while booting, the machine hangs for ~1 minute displaying "INIT: ", then the same thing happens again when init enters in the designated runlevel and finally the getty process on ttyS0 hangs indefinitely (though strace'ing it helps). strace (-e raw=ioctl, otherwise the ioctl() translation is utterly bogus) reveals that getty hangs on ioctl() 0x540f which happens to be TCSETSW (I saw it hang on another console ioctl() but couldn't reproduce that one). A diff between ip22zilog and sunzilog revealed the following differences: 1. the channel A flag being set on up.port.flags instead of up.flags 2. the channel A flag being set on what is marked as being channel B 3. sunzilog has a call to uart_update_timeout(port, termios->c_cflag, baud); at the end of sunzilog_set_termios(), which ip22zilog lacks (on purpose ?) The patch below addresses point 1 and fixes the serial console hangs just fine. However point 2 should be investigated by someone familiar with the IP22 Zilog; it's probably OK as is but even if it is, a comment in ip22zilog.c is badly needed. Point 3 is left as an exercise for whoever feels like digging into ip22zilog :) These are the main obvious differences between ip22zilog and sunzilog. Newer versions of sunzilog (Linus's git tree as of today) are more close to ip22zilog as the sbus_{write,read}b have been changed into simple {write,read}b, which shrinks the diff by a fair amount. Resyncing both drivers should be doable in a few hours time now for someone familiar with the IP22 Zilog hardware. Signed-off-by: Julien BLACHE Signed-off-by: Russell King --- drivers/serial/ip22zilog.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/serial/ip22zilog.c b/drivers/serial/ip22zilog.c index 342042889f6e..5ff269fb604c 100644 --- a/drivers/serial/ip22zilog.c +++ b/drivers/serial/ip22zilog.c @@ -1143,9 +1143,8 @@ static void __init ip22zilog_prepare(void) up[(chip * 2) + 1].port.fifosize = 1; up[(chip * 2) + 1].port.ops = &ip22zilog_pops; up[(chip * 2) + 1].port.type = PORT_IP22ZILOG; - up[(chip * 2) + 1].port.flags |= IP22ZILOG_FLAG_IS_CHANNEL_A; up[(chip * 2) + 1].port.line = (chip * 2) + 1; - up[(chip * 2) + 1].flags = 0; + up[(chip * 2) + 1].flags |= IP22ZILOG_FLAG_IS_CHANNEL_A; } } -- cgit v1.2.3 From 3be91ec7388bae3cf1bfb4febcee5ab6c65f409f Mon Sep 17 00:00:00 2001 From: Zang Roy-r61911 Date: Fri, 30 Jun 2006 02:29:58 -0700 Subject: [SERIAL] 8250: add tsi108 serial support The following patch gets rid of CONFIG_TSI108_BRIDGE. I add UPIO_TSI to handle IIR and IER register in serial_in and serial_out. (1) the reason to rewrite serial_in: TSI108 rev Z1 version ERRATA. Reading the UART's Interrupt Identification Register (IIR) clears the Transmit Holding Register Empty (THRE) and Transmit buffer Empty (TEMP) interrupts even if they are not enabled in the Interrupt Enable Register (IER). This leads to loss of the interrupts. Interrupts are not cleared when reading UART registers as 32-bit word. (2) the reason to rewrite serial_out: Check for UART_IER_UUE bit in the autoconfig routine. This section of autoconfig is excluded for Tsi108/109 because bits 7 and 6 are reserved for internal use. They are R/W bits. In addition to incorrect identification, changing these bits (from 00) will make Tsi108/109 UART non-functional. Signed-off-by: Roy Zang Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/serial/8250.c | 12 ++++++++++++ drivers/serial/serial_core.c | 1 + include/linux/serial_core.h | 1 + 3 files changed, 14 insertions(+) diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 0995430e4cf1..5443fcd38355 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -299,6 +299,7 @@ static inline int map_8250_out_reg(struct uart_8250_port *up, int offset) static unsigned int serial_in(struct uart_8250_port *up, int offset) { + unsigned int tmp; offset = map_8250_in_reg(up, offset) << up->port.regshift; switch (up->port.iotype) { @@ -317,6 +318,13 @@ static unsigned int serial_in(struct uart_8250_port *up, int offset) return __raw_readl(up->port.membase + offset); #endif + case UPIO_TSI: + if (offset == UART_IIR) { + tmp = readl((u32 *)(up->port.membase + UART_RX)); + return (cpu_to_le32(tmp) >> 8) & 0xff; + } else + return readb(up->port.membase + offset); + default: return inb(up->port.iobase + offset); } @@ -346,6 +354,10 @@ serial_out(struct uart_8250_port *up, int offset, int value) __raw_writel(value, up->port.membase + offset); break; #endif + case UPIO_TSI: + if (!((offset == UART_IER) && (value & UART_IER_UUE))) + writeb(value, up->port.membase + offset); + break; default: outb(value, up->port.iobase + offset); diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index d5f636fbf29a..80ef7d482756 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -2036,6 +2036,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) case UPIO_MEM: case UPIO_MEM32: case UPIO_AU: + case UPIO_TSI: snprintf(address, sizeof(address), "MMIO 0x%lx", port->mapbase); break; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 058cba70818a..86501a3de2ac 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -227,6 +227,7 @@ struct uart_port { #define UPIO_MEM (2) #define UPIO_MEM32 (3) #define UPIO_AU (4) /* Au1x00 type IO */ +#define UPIO_TSI (5) /* Tsi108/109 type IO */ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ -- cgit v1.2.3 From 68aa2c0d4a36b43ea9c6d77134c94b4501fd2eb4 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Fri, 30 Jun 2006 02:29:59 -0700 Subject: [SERIAL] 8250: sysrq deadlock fix Fix http://bugzilla.kernel.org/show_bug.cgi?id=6716 Doing a sysrq over a serial line into an SMP machine presently deadlocks. Signed-off-by: Andrew Morton Signed-off-by: Russell King --- drivers/serial/8250.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 5443fcd38355..0ae9ced00ed4 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -2252,10 +2252,14 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) touch_nmi_watchdog(); - if (oops_in_progress) { - locked = spin_trylock_irqsave(&up->port.lock, flags); + local_irq_save(flags); + if (up->port.sysrq) { + /* serial8250_handle_port() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); } else - spin_lock_irqsave(&up->port.lock, flags); + spin_lock(&up->port.lock); /* * First save the IER then disable the interrupts @@ -2277,7 +2281,8 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) serial_out(up, UART_IER, ier); if (locked) - spin_unlock_irqrestore(&up->port.lock, flags); + spin_unlock(&up->port.lock); + local_irq_restore(flags); } static int serial8250_console_setup(struct console *co, char *options) -- cgit v1.2.3 From c8f7a62cdde461914c6457d5f4362538ed810bf4 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 9 Jul 2006 17:22:28 -0400 Subject: Revert "Revert "ACPI: dock driver"" This reverts 953969ddf5b049361ed1e8471cc43dc4134d2a6f commit. --- drivers/acpi/Kconfig | 7 + drivers/acpi/Makefile | 1 + drivers/acpi/dock.c | 739 ++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/scan.c | 23 ++ include/acpi/acpi_bus.h | 2 +- include/acpi/acpi_drivers.h | 17 + 6 files changed, 788 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/dock.c diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 290c767dd77f..fef7bab12244 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -133,6 +133,13 @@ config ACPI_FAN This driver adds support for ACPI fan devices, allowing user-mode applications to perform basic fan control (on, off, status). +config ACPI_DOCK + tristate "Dock" + depends on !ACPI_IBM_DOCK + default y + help + This driver adds support for ACPI controlled docking stations + config ACPI_PROCESSOR tristate "Processor" default y diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index bb5b80a80b18..bce7ca27b429 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_EC) += ec.o obj-$(CONFIG_ACPI_FAN) += fan.o +obj-$(CONFIG_ACPI_DOCK) += dock.o obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c new file mode 100644 index 000000000000..510a94524298 --- /dev/null +++ b/drivers/acpi/dock.c @@ -0,0 +1,739 @@ +/* + * dock.c - ACPI dock station driver + * + * Copyright (C) 2006 Kristen Carlson Accardi + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver" + +ACPI_MODULE_NAME("dock") +MODULE_AUTHOR("Kristen Carlson Accardi"); +MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +static struct atomic_notifier_head dock_notifier_list; + +struct dock_station { + acpi_handle handle; + unsigned long last_dock_time; + u32 flags; + spinlock_t dd_lock; + spinlock_t hp_lock; + struct list_head dependent_devices; + struct list_head hotplug_devices; +}; + +struct dock_dependent_device { + struct list_head list; + struct list_head hotplug_list; + acpi_handle handle; + acpi_notify_handler handler; + void *context; +}; + +#define DOCK_DOCKING 0x00000001 +#define DOCK_EVENT KOBJ_DOCK +#define UNDOCK_EVENT KOBJ_UNDOCK + +static struct dock_station *dock_station; + +/***************************************************************************** + * Dock Dependent device functions * + *****************************************************************************/ +/** + * alloc_dock_dependent_device - allocate and init a dependent device + * @handle: the acpi_handle of the dependent device + * + * Allocate memory for a dependent device structure for a device referenced + * by the acpi handle + */ +static struct dock_dependent_device * +alloc_dock_dependent_device(acpi_handle handle) +{ + struct dock_dependent_device *dd; + + dd = kzalloc(sizeof(*dd), GFP_KERNEL); + if (dd) { + dd->handle = handle; + INIT_LIST_HEAD(&dd->list); + INIT_LIST_HEAD(&dd->hotplug_list); + } + return dd; +} + +/** + * add_dock_dependent_device - associate a device with the dock station + * @ds: The dock station + * @dd: The dependent device + * + * Add the dependent device to the dock's dependent device list. + */ +static void +add_dock_dependent_device(struct dock_station *ds, + struct dock_dependent_device *dd) +{ + spin_lock(&ds->dd_lock); + list_add_tail(&dd->list, &ds->dependent_devices); + spin_unlock(&ds->dd_lock); +} + +/** + * dock_add_hotplug_device - associate a hotplug handler with the dock station + * @ds: The dock station + * @dd: The dependent device struct + * + * Add the dependent device to the dock's hotplug device list + */ +static void +dock_add_hotplug_device(struct dock_station *ds, + struct dock_dependent_device *dd) +{ + spin_lock(&ds->hp_lock); + list_add_tail(&dd->hotplug_list, &ds->hotplug_devices); + spin_unlock(&ds->hp_lock); +} + +/** + * dock_del_hotplug_device - remove a hotplug handler from the dock station + * @ds: The dock station + * @dd: the dependent device struct + * + * Delete the dependent device from the dock's hotplug device list + */ +static void +dock_del_hotplug_device(struct dock_station *ds, + struct dock_dependent_device *dd) +{ + spin_lock(&ds->hp_lock); + list_del(&dd->hotplug_list); + spin_unlock(&ds->hp_lock); +} + +/** + * find_dock_dependent_device - get a device dependent on this dock + * @ds: the dock station + * @handle: the acpi_handle of the device we want + * + * iterate over the dependent device list for this dock. If the + * dependent device matches the handle, return. + */ +static struct dock_dependent_device * +find_dock_dependent_device(struct dock_station *ds, acpi_handle handle) +{ + struct dock_dependent_device *dd; + + spin_lock(&ds->dd_lock); + list_for_each_entry(dd, &ds->dependent_devices, list) { + if (handle == dd->handle) { + spin_unlock(&ds->dd_lock); + return dd; + } + } + spin_unlock(&ds->dd_lock); + return NULL; +} + +/***************************************************************************** + * Dock functions * + *****************************************************************************/ +/** + * is_dock - see if a device is a dock station + * @handle: acpi handle of the device + * + * If an acpi object has a _DCK method, then it is by definition a dock + * station, so return true. + */ +static int is_dock(acpi_handle handle) +{ + acpi_status status; + acpi_handle tmp; + + status = acpi_get_handle(handle, "_DCK", &tmp); + if (ACPI_FAILURE(status)) + return 0; + return 1; +} + +/** + * is_dock_device - see if a device is on a dock station + * @handle: acpi handle of the device + * + * If this device is either the dock station itself, + * or is a device dependent on the dock station, then it + * is a dock device + */ +int is_dock_device(acpi_handle handle) +{ + if (!dock_station) + return 0; + + if (is_dock(handle) || find_dock_dependent_device(dock_station, handle)) + return 1; + + return 0; +} + +EXPORT_SYMBOL_GPL(is_dock_device); + +/** + * dock_present - see if the dock station is present. + * @ds: the dock station + * + * execute the _STA method. note that present does not + * imply that we are docked. + */ +static int dock_present(struct dock_station *ds) +{ + unsigned long sta; + acpi_status status; + + if (ds) { + status = acpi_evaluate_integer(ds->handle, "_STA", NULL, &sta); + if (ACPI_SUCCESS(status) && sta) + return 1; + } + return 0; +} + + + +/** + * dock_create_acpi_device - add new devices to acpi + * @handle - handle of the device to add + * + * This function will create a new acpi_device for the given + * handle if one does not exist already. This should cause + * acpi to scan for drivers for the given devices, and call + * matching driver's add routine. + * + * Returns a pointer to the acpi_device corresponding to the handle. + */ +static struct acpi_device * dock_create_acpi_device(acpi_handle handle) +{ + struct acpi_device *device = NULL; + struct acpi_device *parent_device; + acpi_handle parent; + int ret; + + if (acpi_bus_get_device(handle, &device)) { + /* + * no device created for this object, + * so we should create one. + */ + acpi_get_parent(handle, &parent); + if (acpi_bus_get_device(parent, &parent_device)) + parent_device = NULL; + + ret = acpi_bus_add(&device, parent_device, handle, + ACPI_BUS_TYPE_DEVICE); + if (ret) { + pr_debug("error adding bus, %x\n", + -ret); + return NULL; + } + } + return device; +} + +/** + * dock_remove_acpi_device - remove the acpi_device struct from acpi + * @handle - the handle of the device to remove + * + * Tell acpi to remove the acpi_device. This should cause any loaded + * driver to have it's remove routine called. + */ +static void dock_remove_acpi_device(acpi_handle handle) +{ + struct acpi_device *device; + int ret; + + if (!acpi_bus_get_device(handle, &device)) { + ret = acpi_bus_trim(device, 1); + if (ret) + pr_debug("error removing bus, %x\n", -ret); + } +} + + +/** + * hotplug_dock_devices - insert or remove devices on the dock station + * @ds: the dock station + * @event: either bus check or eject request + * + * Some devices on the dock station need to have drivers called + * to perform hotplug operations after a dock event has occurred. + * Traverse the list of dock devices that have registered a + * hotplug handler, and call the handler. + */ +static void hotplug_dock_devices(struct dock_station *ds, u32 event) +{ + struct dock_dependent_device *dd; + + spin_lock(&ds->hp_lock); + + /* + * First call driver specific hotplug functions + */ + list_for_each_entry(dd, &ds->hotplug_devices, hotplug_list) { + if (dd->handler) + dd->handler(dd->handle, event, dd->context); + } + + /* + * Now make sure that an acpi_device is created for each + * dependent device, or removed if this is an eject request. + * This will cause acpi_drivers to be stopped/started if they + * exist + */ + list_for_each_entry(dd, &ds->dependent_devices, list) { + if (event == ACPI_NOTIFY_EJECT_REQUEST) + dock_remove_acpi_device(dd->handle); + else + dock_create_acpi_device(dd->handle); + } + spin_unlock(&ds->hp_lock); +} + +static void dock_event(struct dock_station *ds, u32 event, int num) +{ + struct acpi_device *device; + + device = dock_create_acpi_device(ds->handle); + if (device) + kobject_uevent(&device->kobj, num); +} + +/** + * eject_dock - respond to a dock eject request + * @ds: the dock station + * + * This is called after _DCK is called, to execute the dock station's + * _EJ0 method. + */ +static void eject_dock(struct dock_station *ds) +{ + struct acpi_object_list arg_list; + union acpi_object arg; + acpi_status status; + acpi_handle tmp; + + /* all dock devices should have _EJ0, but check anyway */ + status = acpi_get_handle(ds->handle, "_EJ0", &tmp); + if (ACPI_FAILURE(status)) { + pr_debug("No _EJ0 support for dock device\n"); + return; + } + + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = 1; + + if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0", + &arg_list, NULL))) + pr_debug("Failed to evaluate _EJ0!\n"); +} + +/** + * handle_dock - handle a dock event + * @ds: the dock station + * @dock: to dock, or undock - that is the question + * + * Execute the _DCK method in response to an acpi event + */ +static void handle_dock(struct dock_station *ds, int dock) +{ + acpi_status status; + struct acpi_object_list arg_list; + union acpi_object arg; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer name_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + + acpi_get_name(ds->handle, ACPI_FULL_PATHNAME, &name_buffer); + obj = name_buffer.pointer; + + printk(KERN_INFO PREFIX "%s\n", dock ? "docking" : "undocking"); + + /* _DCK method has one argument */ + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = dock; + status = acpi_evaluate_object(ds->handle, "_DCK", &arg_list, &buffer); + if (ACPI_FAILURE(status)) + pr_debug("%s: failed to execute _DCK\n", obj->string.pointer); + kfree(buffer.pointer); + kfree(name_buffer.pointer); +} + +static inline void dock(struct dock_station *ds) +{ + handle_dock(ds, 1); +} + +static inline void undock(struct dock_station *ds) +{ + handle_dock(ds, 0); +} + +static inline void begin_dock(struct dock_station *ds) +{ + ds->flags |= DOCK_DOCKING; +} + +static inline void complete_dock(struct dock_station *ds) +{ + ds->flags &= ~(DOCK_DOCKING); + ds->last_dock_time = jiffies; +} + +/** + * dock_in_progress - see if we are in the middle of handling a dock event + * @ds: the dock station + * + * Sometimes while docking, false dock events can be sent to the driver + * because good connections aren't made or some other reason. Ignore these + * if we are in the middle of doing something. + */ +static int dock_in_progress(struct dock_station *ds) +{ + if ((ds->flags & DOCK_DOCKING) || + time_before(jiffies, (ds->last_dock_time + HZ))) + return 1; + return 0; +} + +/** + * register_dock_notifier - add yourself to the dock notifier list + * @nb: the callers notifier block + * + * If a driver wishes to be notified about dock events, they can + * use this function to put a notifier block on the dock notifier list. + * this notifier call chain will be called after a dock event, but + * before hotplugging any new devices. + */ +int register_dock_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&dock_notifier_list, nb); +} + +EXPORT_SYMBOL_GPL(register_dock_notifier); + +/** + * unregister_dock_notifier - remove yourself from the dock notifier list + * @nb: the callers notifier block + */ +void unregister_dock_notifier(struct notifier_block *nb) +{ + atomic_notifier_chain_unregister(&dock_notifier_list, nb); +} + +EXPORT_SYMBOL_GPL(unregister_dock_notifier); + +/** + * register_hotplug_dock_device - register a hotplug function + * @handle: the handle of the device + * @handler: the acpi_notifier_handler to call after docking + * @context: device specific data + * + * If a driver would like to perform a hotplug operation after a dock + * event, they can register an acpi_notifiy_handler to be called by + * the dock driver after _DCK is executed. + */ +int +register_hotplug_dock_device(acpi_handle handle, acpi_notify_handler handler, + void *context) +{ + struct dock_dependent_device *dd; + + if (!dock_station) + return -ENODEV; + + /* + * make sure this handle is for a device dependent on the dock, + * this would include the dock station itself + */ + dd = find_dock_dependent_device(dock_station, handle); + if (dd) { + dd->handler = handler; + dd->context = context; + dock_add_hotplug_device(dock_station, dd); + return 0; + } + + return -EINVAL; +} + +EXPORT_SYMBOL_GPL(register_hotplug_dock_device); + +/** + * unregister_hotplug_dock_device - remove yourself from the hotplug list + * @handle: the acpi handle of the device + */ +void unregister_hotplug_dock_device(acpi_handle handle) +{ + struct dock_dependent_device *dd; + + if (!dock_station) + return; + + dd = find_dock_dependent_device(dock_station, handle); + if (dd) + dock_del_hotplug_device(dock_station, dd); +} + +EXPORT_SYMBOL_GPL(unregister_hotplug_dock_device); + +/** + * dock_notify - act upon an acpi dock notification + * @handle: the dock station handle + * @event: the acpi event + * @data: our driver data struct + * + * If we are notified to dock, then check to see if the dock is + * present and then dock. Notify all drivers of the dock event, + * and then hotplug and devices that may need hotplugging. For undock + * check to make sure the dock device is still present, then undock + * and hotremove all the devices that may need removing. + */ +static void dock_notify(acpi_handle handle, u32 event, void *data) +{ + struct dock_station *ds = (struct dock_station *)data; + + switch (event) { + case ACPI_NOTIFY_BUS_CHECK: + if (!dock_in_progress(ds) && dock_present(ds)) { + begin_dock(ds); + dock(ds); + if (!dock_present(ds)) { + printk(KERN_ERR PREFIX "Unable to dock!\n"); + break; + } + atomic_notifier_call_chain(&dock_notifier_list, + event, NULL); + hotplug_dock_devices(ds, event); + complete_dock(ds); + dock_event(ds, event, DOCK_EVENT); + } + break; + case ACPI_NOTIFY_DEVICE_CHECK: + /* + * According to acpi spec 3.0a, if a DEVICE_CHECK notification + * is sent and _DCK is present, it is assumed to mean an + * undock request. This notify routine will only be called + * for objects defining _DCK, so we will fall through to eject + * request here. However, we will pass an eject request through + * to the driver who wish to hotplug. + */ + case ACPI_NOTIFY_EJECT_REQUEST: + if (!dock_in_progress(ds) && dock_present(ds)) { + /* + * here we need to generate the undock + * event prior to actually doing the undock + * so that the device struct still exists. + */ + dock_event(ds, event, UNDOCK_EVENT); + hotplug_dock_devices(ds, ACPI_NOTIFY_EJECT_REQUEST); + undock(ds); + eject_dock(ds); + if (dock_present(ds)) + printk(KERN_ERR PREFIX "Unable to undock!\n"); + } + break; + default: + printk(KERN_ERR PREFIX "Unknown dock event %d\n", event); + } +} + +/** + * find_dock_devices - find devices on the dock station + * @handle: the handle of the device we are examining + * @lvl: unused + * @context: the dock station private data + * @rv: unused + * + * This function is called by acpi_walk_namespace. It will + * check to see if an object has an _EJD method. If it does, then it + * will see if it is dependent on the dock station. + */ +static acpi_status +find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + acpi_status status; + acpi_handle tmp; + struct dock_station *ds = (struct dock_station *)context; + struct dock_dependent_device *dd; + + status = acpi_bus_get_ejd(handle, &tmp); + if (ACPI_FAILURE(status)) + return AE_OK; + + if (tmp == ds->handle) { + dd = alloc_dock_dependent_device(handle); + if (dd) + add_dock_dependent_device(ds, dd); + } + + return AE_OK; +} + +/** + * dock_add - add a new dock station + * @handle: the dock station handle + * + * allocated and initialize a new dock station device. Find all devices + * that are on the dock station, and register for dock event notifications. + */ +static int dock_add(acpi_handle handle) +{ + int ret; + acpi_status status; + struct dock_dependent_device *dd; + + /* allocate & initialize the dock_station private data */ + dock_station = kzalloc(sizeof(*dock_station), GFP_KERNEL); + if (!dock_station) + return -ENOMEM; + dock_station->handle = handle; + dock_station->last_dock_time = jiffies - HZ; + INIT_LIST_HEAD(&dock_station->dependent_devices); + INIT_LIST_HEAD(&dock_station->hotplug_devices); + spin_lock_init(&dock_station->dd_lock); + spin_lock_init(&dock_station->hp_lock); + + /* Find dependent devices */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_dock_devices, dock_station, + NULL); + + /* add the dock station as a device dependent on itself */ + dd = alloc_dock_dependent_device(handle); + if (!dd) { + kfree(dock_station); + return -ENOMEM; + } + add_dock_dependent_device(dock_station, dd); + + /* register for dock events */ + status = acpi_install_notify_handler(dock_station->handle, + ACPI_SYSTEM_NOTIFY, + dock_notify, dock_station); + + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Error installing notify handler\n"); + ret = -ENODEV; + goto dock_add_err; + } + + printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME); + + return 0; + +dock_add_err: + kfree(dock_station); + kfree(dd); + return ret; +} + +/** + * dock_remove - free up resources related to the dock station + */ +static int dock_remove(void) +{ + struct dock_dependent_device *dd, *tmp; + acpi_status status; + + if (!dock_station) + return 0; + + /* remove dependent devices */ + list_for_each_entry_safe(dd, tmp, &dock_station->dependent_devices, + list) + kfree(dd); + + /* remove dock notify handler */ + status = acpi_remove_notify_handler(dock_station->handle, + ACPI_SYSTEM_NOTIFY, + dock_notify); + if (ACPI_FAILURE(status)) + printk(KERN_ERR "Error removing notify handler\n"); + + /* free dock station memory */ + kfree(dock_station); + return 0; +} + +/** + * find_dock - look for a dock station + * @handle: acpi handle of a device + * @lvl: unused + * @context: counter of dock stations found + * @rv: unused + * + * This is called by acpi_walk_namespace to look for dock stations. + */ +static acpi_status +find_dock(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + int *count = (int *)context; + acpi_status status = AE_OK; + + if (is_dock(handle)) { + if (dock_add(handle) >= 0) { + (*count)++; + status = AE_CTRL_TERMINATE; + } + } + return status; +} + +static int __init dock_init(void) +{ + int num = 0; + + dock_station = NULL; + + /* look for a dock station */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_dock, &num, NULL); + + if (!num) + return -ENODEV; + + return 0; +} + +static void __exit dock_exit(void) +{ + dock_remove(); +} + +postcore_initcall(dock_init); +module_exit(dock_exit); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index cac4fcdcfc8d..5fcb50c7b778 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -663,6 +663,29 @@ static int acpi_bus_find_driver(struct acpi_device *device) Device Enumeration -------------------------------------------------------------------------- */ +acpi_status +acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) +{ + acpi_status status; + acpi_handle tmp; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + + status = acpi_get_handle(handle, "_EJD", &tmp); + if (ACPI_FAILURE(status)) + return status; + + status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer); + if (ACPI_SUCCESS(status)) { + obj = buffer.pointer; + status = acpi_get_handle(NULL, obj->string.pointer, ejd); + kfree(buffer.pointer); + } + return status; +} +EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); + + static int acpi_bus_get_flags(struct acpi_device *device) { acpi_status status = AE_OK; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a2b3e390a503..f338e40bd544 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -334,7 +334,7 @@ int acpi_bus_add(struct acpi_device **child, struct acpi_device *parent, acpi_handle handle, int type); int acpi_bus_trim(struct acpi_device *start, int rmdevice); int acpi_bus_start(struct acpi_device *device); - +acpi_status acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd); int acpi_match_ids(struct acpi_device *device, char *ids); int acpi_create_dir(struct acpi_device *); void acpi_remove_dir(struct acpi_device *); diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index b425f9bb6d43..6a5bdcefec64 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -110,4 +110,21 @@ int acpi_processor_set_thermal_limit(acpi_handle handle, int type); extern int acpi_specific_hotkey_enabled; +/*-------------------------------------------------------------------------- + Dock Station + -------------------------------------------------------------------------- */ +#if defined(CONFIG_ACPI_DOCK) || defined(CONFIG_ACPI_DOCK_MODULE) +extern int is_dock_device(acpi_handle handle); +extern int register_dock_notifier(struct notifier_block *nb); +extern void unregister_dock_notifier(struct notifier_block *nb); +extern int register_hotplug_dock_device(acpi_handle handle, + acpi_notify_handler handler, void *context); +extern void unregister_hotplug_dock_device(acpi_handle handle); +#else +#define is_dock_device(h) (0) +#define register_dock_notifier(nb) (-ENODEV) +#define unregister_dock_notifier(nb) do { } while(0) +#define register_hotplug_dock_device(h1, h2, c) (-ENODEV) +#define unregister_hotplug_dock_device(h) do { } while(0) +#endif #endif /*__ACPI_DRIVERS_H__*/ -- cgit v1.2.3 From b3cf257623fabd8f1ee6700a6d328cc1c5da5a1d Mon Sep 17 00:00:00 2001 From: Stephane Eranian Date: Sun, 9 Jul 2006 21:12:39 -0400 Subject: [PATCH] i386: use thread_info flags for debug regs and IO bitmaps Use thread info flags to track use of debug registers and IO bitmaps. - add TIF_DEBUG to track when debug registers are active - add TIF_IO_BITMAP to track when I/O bitmap is used - modify __switch_to() to use the new TIF flags Performance tested on Pentium II, ten runs of LMbench context switch benchmark (smaller is better:) before after avg 3.65 3.39 min 3.55 3.33 Signed-off-by: Stephane Eranian Signed-off-by: Chuck Ebbert <76306.1226@compuserve.com> Acked-by: Andi Kleen Signed-off-by: Linus Torvalds --- arch/i386/kernel/ioport.c | 1 + arch/i386/kernel/process.c | 50 ++++++++++++++++++++++++------------------ arch/i386/kernel/ptrace.c | 5 ++++- include/asm-i386/thread_info.h | 7 ++++++ 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c index 79026f026b85..498e8bc197d5 100644 --- a/arch/i386/kernel/ioport.c +++ b/arch/i386/kernel/ioport.c @@ -79,6 +79,7 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) memset(bitmap, 0xff, IO_BITMAP_BYTES); t->io_bitmap_ptr = bitmap; + set_thread_flag(TIF_IO_BITMAP); } /* diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 94e2c87edeaa..923bb292f47f 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -359,16 +359,16 @@ EXPORT_SYMBOL(kernel_thread); */ void exit_thread(void) { - struct task_struct *tsk = current; - struct thread_struct *t = &tsk->thread; - /* The process may have allocated an io port bitmap... nuke it. */ - if (unlikely(NULL != t->io_bitmap_ptr)) { + if (unlikely(test_thread_flag(TIF_IO_BITMAP))) { + struct task_struct *tsk = current; + struct thread_struct *t = &tsk->thread; int cpu = get_cpu(); struct tss_struct *tss = &per_cpu(init_tss, cpu); kfree(t->io_bitmap_ptr); t->io_bitmap_ptr = NULL; + clear_thread_flag(TIF_IO_BITMAP); /* * Careful, clear this in the TSS too: */ @@ -387,6 +387,7 @@ void flush_thread(void) memset(tsk->thread.debugreg, 0, sizeof(unsigned long)*8); memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array)); + clear_tsk_thread_flag(tsk, TIF_DEBUG); /* * Forget coprocessor state.. */ @@ -431,7 +432,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, savesegment(gs,p->thread.gs); tsk = current; - if (unlikely(NULL != tsk->thread.io_bitmap_ptr)) { + if (unlikely(test_tsk_thread_flag(tsk, TIF_IO_BITMAP))) { p->thread.io_bitmap_ptr = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL); if (!p->thread.io_bitmap_ptr) { p->thread.io_bitmap_max = 0; @@ -439,6 +440,7 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long esp, } memcpy(p->thread.io_bitmap_ptr, tsk->thread.io_bitmap_ptr, IO_BITMAP_BYTES); + set_tsk_thread_flag(p, TIF_IO_BITMAP); } /* @@ -533,10 +535,24 @@ int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs) return 1; } -static inline void -handle_io_bitmap(struct thread_struct *next, struct tss_struct *tss) +static noinline void __switch_to_xtra(struct task_struct *next_p, + struct tss_struct *tss) { - if (!next->io_bitmap_ptr) { + struct thread_struct *next; + + next = &next_p->thread; + + if (test_tsk_thread_flag(next_p, TIF_DEBUG)) { + set_debugreg(next->debugreg[0], 0); + set_debugreg(next->debugreg[1], 1); + set_debugreg(next->debugreg[2], 2); + set_debugreg(next->debugreg[3], 3); + /* no 4 and 5 */ + set_debugreg(next->debugreg[6], 6); + set_debugreg(next->debugreg[7], 7); + } + + if (!test_tsk_thread_flag(next_p, TIF_IO_BITMAP)) { /* * Disable the bitmap via an invalid offset. We still cache * the previous bitmap owner and the IO bitmap contents: @@ -544,6 +560,7 @@ handle_io_bitmap(struct thread_struct *next, struct tss_struct *tss) tss->io_bitmap_base = INVALID_IO_BITMAP_OFFSET; return; } + if (likely(next == tss->io_bitmap_owner)) { /* * Previous owner of the bitmap (hence the bitmap content) @@ -671,20 +688,11 @@ struct task_struct fastcall * __switch_to(struct task_struct *prev_p, struct tas set_iopl_mask(next->iopl); /* - * Now maybe reload the debug registers + * Now maybe handle debug registers and/or IO bitmaps */ - if (unlikely(next->debugreg[7])) { - set_debugreg(next->debugreg[0], 0); - set_debugreg(next->debugreg[1], 1); - set_debugreg(next->debugreg[2], 2); - set_debugreg(next->debugreg[3], 3); - /* no 4 and 5 */ - set_debugreg(next->debugreg[6], 6); - set_debugreg(next->debugreg[7], 7); - } - - if (unlikely(prev->io_bitmap_ptr || next->io_bitmap_ptr)) - handle_io_bitmap(next, tss); + if (unlikely((task_thread_info(next_p)->flags & _TIF_WORK_CTXSW)) + || test_tsk_thread_flag(prev_p, TIF_IO_BITMAP)) + __switch_to_xtra(next_p, tss); disable_tsc(prev_p, next_p); diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index fd7eaf7866e0..d3db03f4085d 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -468,8 +468,11 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) for(i=0; i<4; i++) if ((0x5f54 >> ((data >> (16 + 4*i)) & 0xf)) & 1) goto out_tsk; + if (data) + set_tsk_thread_flag(child, TIF_DEBUG); + else + clear_tsk_thread_flag(child, TIF_DEBUG); } - addr -= (long) &dummy->u_debugreg; addr = addr >> 2; child->thread.debugreg[addr] = data; diff --git a/include/asm-i386/thread_info.h b/include/asm-i386/thread_info.h index 2833fa2c0dd0..54d6d7aea938 100644 --- a/include/asm-i386/thread_info.h +++ b/include/asm-i386/thread_info.h @@ -140,6 +140,8 @@ static inline struct thread_info *current_thread_info(void) #define TIF_SECCOMP 8 /* secure computing */ #define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */ #define TIF_MEMDIE 16 +#define TIF_DEBUG 17 /* uses debug registers */ +#define TIF_IO_BITMAP 18 /* uses I/O bitmap */ #define _TIF_SYSCALL_TRACE (1< Date: Sun, 9 Jul 2006 22:09:57 -0400 Subject: ACPI: ACPI_DOCK Kconfig HOTPLUG_PCI_ACPI depends on ACPI_DOCK ACPI_IBM_DOCK depends on ACPI_DOCK=n ACPI_DOCK is EXPERIMENTAL, though that doesn't seem to mean much Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 4 ++-- drivers/pci/hotplug/Kconfig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index fef7bab12244..82289f1364f2 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -135,8 +135,7 @@ config ACPI_FAN config ACPI_DOCK tristate "Dock" - depends on !ACPI_IBM_DOCK - default y + depends on EXPERIMENTAL help This driver adds support for ACPI controlled docking stations @@ -214,6 +213,7 @@ config ACPI_IBM config ACPI_IBM_DOCK bool "Legacy Docking Station Support" depends on ACPI_IBM + depends on ACPI_DOCK=n default n ---help--- Allows the ibm_acpi driver to handle docking station events. diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 222a1cc4aa28..d305d212ad6c 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -77,7 +77,7 @@ config HOTPLUG_PCI_IBM config HOTPLUG_PCI_ACPI tristate "ACPI PCI Hotplug driver" - depends on ACPI && HOTPLUG_PCI + depends on ACPI_DOCK && HOTPLUG_PCI help Say Y here if you have a system that supports PCI Hotplug using ACPI. -- cgit v1.2.3 From af4f949c6b4ffa5119aad980626e5b04daca961b Mon Sep 17 00:00:00 2001 From: Len Brown Date: Sun, 9 Jul 2006 16:33:26 -0400 Subject: ACPI: "Device `[%s]' is not power manageable" make message debug only Signed-off-by: Len Brown --- drivers/acpi/bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index ea5a0496a4fd..b2977695e120 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -192,8 +192,8 @@ int acpi_bus_set_power(acpi_handle handle, int state) /* Make sure this is a valid target state */ if (!device->flags.power_manageable) { - printk(KERN_DEBUG "Device `[%s]' is not power manageable", - device->kobj.name); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device `[%s]' is not power manageable", + device->kobj.name)); return -ENODEV; } /* -- cgit v1.2.3 From e26a2b8f68dca28c734d857517788e3b40b8691d Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 6 Jul 2006 12:14:00 -0400 Subject: ACPI: delete some defaults from ACPI Kconfig No need for video to be always in No need for ACPI dock driver to be always in No need for smart battery driver to be always in Signed-off-by: Andi Kleen Signed-off-by: Len Brown --- drivers/acpi/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 290c767dd77f..89eacd1bfeeb 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -107,7 +107,6 @@ config ACPI_BUTTON config ACPI_VIDEO tristate "Video" depends on X86 - default y help This driver implement the ACPI Extensions For Display Adapters for integrated graphics devices on motherboard, as specified in @@ -350,7 +349,6 @@ config ACPI_SBS tristate "Smart Battery System (EXPERIMENTAL)" depends on X86 && I2C depends on EXPERIMENTAL - default y help This driver adds support for the Smart Battery System. Depends on I2C (Device Drivers ---> I2C support) -- cgit v1.2.3 From d75080328affb4b268da430b7074cc8139cc662a Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Tue, 4 Jul 2006 13:06:00 -0400 Subject: ACPI: add 'const' to several ACPI file_operations Signed-off-by: Arjan van de Ven Signed-off-by: Len Brown --- drivers/acpi/ac.c | 2 +- drivers/acpi/battery.c | 6 +++--- drivers/acpi/button.c | 4 ++-- drivers/acpi/ec.c | 2 +- drivers/acpi/event.c | 2 +- drivers/acpi/fan.c | 2 +- drivers/acpi/hotkey.c | 10 +++++----- drivers/acpi/power.c | 2 +- drivers/acpi/processor_core.c | 2 +- drivers/acpi/processor_idle.c | 2 +- drivers/acpi/sleep/proc.c | 6 +++--- drivers/acpi/system.c | 6 +++--- drivers/acpi/thermal.c | 10 +++++----- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 24ccf81d135f..96309b9660da 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -72,7 +72,7 @@ struct acpi_ac { unsigned long state; }; -static struct file_operations acpi_ac_fops = { +static const struct file_operations acpi_ac_fops = { .open = acpi_ac_open_fs, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 24bf4dca88cc..6e5221707d97 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -557,7 +557,7 @@ static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) return single_open(file, acpi_battery_read_alarm, PDE(inode)->data); } -static struct file_operations acpi_battery_info_ops = { +static const struct file_operations acpi_battery_info_ops = { .open = acpi_battery_info_open_fs, .read = seq_read, .llseek = seq_lseek, @@ -565,7 +565,7 @@ static struct file_operations acpi_battery_info_ops = { .owner = THIS_MODULE, }; -static struct file_operations acpi_battery_state_ops = { +static const struct file_operations acpi_battery_state_ops = { .open = acpi_battery_state_open_fs, .read = seq_read, .llseek = seq_lseek, @@ -573,7 +573,7 @@ static struct file_operations acpi_battery_state_ops = { .owner = THIS_MODULE, }; -static struct file_operations acpi_battery_alarm_ops = { +static const struct file_operations acpi_battery_alarm_ops = { .open = acpi_battery_alarm_open_fs, .read = seq_read, .write = acpi_battery_write_alarm, diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index fd1ba05eab68..5ef885e82c78 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -87,14 +87,14 @@ struct acpi_button { unsigned long pushed; }; -static struct file_operations acpi_button_info_fops = { +static const struct file_operations acpi_button_info_fops = { .open = acpi_button_info_open_fs, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -static struct file_operations acpi_button_state_fops = { +static const struct file_operations acpi_button_state_fops = { .open = acpi_button_state_open_fs, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 8c5d7df7d343..e5d796362854 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -929,7 +929,7 @@ static int acpi_ec_info_open_fs(struct inode *inode, struct file *file) return single_open(file, acpi_ec_read_info, PDE(inode)->data); } -static struct file_operations acpi_ec_info_ops = { +static const struct file_operations acpi_ec_info_ops = { .open = acpi_ec_info_open_fs, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index a901b23e95e7..959a893c8d1f 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -99,7 +99,7 @@ static unsigned int acpi_system_poll_event(struct file *file, poll_table * wait) return 0; } -static struct file_operations acpi_system_event_ops = { +static const struct file_operations acpi_system_event_ops = { .open = acpi_system_open_event, .read = acpi_system_read_event, .release = acpi_system_close_event, diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index daed2460924d..045c89477e59 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -120,7 +120,7 @@ acpi_fan_write_state(struct file *file, const char __user * buffer, return count; } -static struct file_operations acpi_fan_state_ops = { +static const struct file_operations acpi_fan_state_ops = { .open = acpi_fan_state_open_fs, .read = seq_read, .write = acpi_fan_write_state, diff --git a/drivers/acpi/hotkey.c b/drivers/acpi/hotkey.c index fd81a0f5222f..32c9d88fd196 100644 --- a/drivers/acpi/hotkey.c +++ b/drivers/acpi/hotkey.c @@ -184,7 +184,7 @@ static union acpi_hotkey *get_hotkey_by_event(struct *hotkey_list, int event); /* event based config */ -static struct file_operations hotkey_config_fops = { +static const struct file_operations hotkey_config_fops = { .open = hotkey_open_config, .read = seq_read, .write = hotkey_write_config, @@ -193,7 +193,7 @@ static struct file_operations hotkey_config_fops = { }; /* polling based config */ -static struct file_operations hotkey_poll_config_fops = { +static const struct file_operations hotkey_poll_config_fops = { .open = hotkey_poll_open_config, .read = seq_read, .write = hotkey_write_config, @@ -202,7 +202,7 @@ static struct file_operations hotkey_poll_config_fops = { }; /* hotkey driver info */ -static struct file_operations hotkey_info_fops = { +static const struct file_operations hotkey_info_fops = { .open = hotkey_info_open_fs, .read = seq_read, .llseek = seq_lseek, @@ -210,7 +210,7 @@ static struct file_operations hotkey_info_fops = { }; /* action */ -static struct file_operations hotkey_action_fops = { +static const struct file_operations hotkey_action_fops = { .open = hotkey_action_open_fs, .read = seq_read, .write = hotkey_execute_aml_method, @@ -219,7 +219,7 @@ static struct file_operations hotkey_action_fops = { }; /* polling results */ -static struct file_operations hotkey_polling_fops = { +static const struct file_operations hotkey_polling_fops = { .open = hotkey_polling_open_fs, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 5d3447f4582c..fec225d1b6b7 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -80,7 +80,7 @@ struct acpi_power_resource { static struct list_head acpi_power_resource_list; -static struct file_operations acpi_power_fops = { +static const struct file_operations acpi_power_fops = { .open = acpi_power_open_fs, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 52674323b14d..b13d64415b7a 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -102,7 +102,7 @@ static struct acpi_driver acpi_processor_driver = { #define INSTALL_NOTIFY_HANDLER 1 #define UNINSTALL_NOTIFY_HANDLER 2 -static struct file_operations acpi_processor_info_fops = { +static const struct file_operations acpi_processor_info_fops = { .open = acpi_processor_info_open_fs, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 8e9c26aae8fe..71066066d626 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1070,7 +1070,7 @@ static int acpi_processor_power_open_fs(struct inode *inode, struct file *file) PDE(inode)->data); } -static struct file_operations acpi_processor_power_fops = { +static const struct file_operations acpi_processor_power_fops = { .open = acpi_processor_power_open_fs, .read = seq_read, .llseek = seq_lseek, diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 4696a85a98b9..34962578039d 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -434,7 +434,7 @@ acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file) PDE(inode)->data); } -static struct file_operations acpi_system_wakeup_device_fops = { +static const struct file_operations acpi_system_wakeup_device_fops = { .open = acpi_system_wakeup_device_open_fs, .read = seq_read, .write = acpi_system_write_wakeup_device, @@ -443,7 +443,7 @@ static struct file_operations acpi_system_wakeup_device_fops = { }; #ifdef CONFIG_ACPI_SLEEP_PROC_SLEEP -static struct file_operations acpi_system_sleep_fops = { +static const struct file_operations acpi_system_sleep_fops = { .open = acpi_system_sleep_open_fs, .read = seq_read, .write = acpi_system_write_sleep, @@ -452,7 +452,7 @@ static struct file_operations acpi_system_sleep_fops = { }; #endif /* CONFIG_ACPI_SLEEP_PROC_SLEEP */ -static struct file_operations acpi_system_alarm_fops = { +static const struct file_operations acpi_system_alarm_fops = { .open = acpi_system_alarm_open_fs, .read = seq_read, .write = acpi_system_write_alarm, diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index c3bb7faad75e..d86dcb3c2366 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -57,7 +57,7 @@ static int acpi_system_info_open_fs(struct inode *inode, struct file *file) return single_open(file, acpi_system_read_info, PDE(inode)->data); } -static struct file_operations acpi_system_info_ops = { +static const struct file_operations acpi_system_info_ops = { .open = acpi_system_info_open_fs, .read = seq_read, .llseek = seq_lseek, @@ -67,7 +67,7 @@ static struct file_operations acpi_system_info_ops = { static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t, loff_t *); -static struct file_operations acpi_system_dsdt_ops = { +static const struct file_operations acpi_system_dsdt_ops = { .read = acpi_system_read_dsdt, }; @@ -94,7 +94,7 @@ acpi_system_read_dsdt(struct file *file, static ssize_t acpi_system_read_fadt(struct file *, char __user *, size_t, loff_t *); -static struct file_operations acpi_system_fadt_ops = { +static const struct file_operations acpi_system_fadt_ops = { .read = acpi_system_read_fadt, }; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 503c0b99db12..480a31796886 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -176,21 +176,21 @@ struct acpi_thermal { struct timer_list timer; }; -static struct file_operations acpi_thermal_state_fops = { +static const struct file_operations acpi_thermal_state_fops = { .open = acpi_thermal_state_open_fs, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -static struct file_operations acpi_thermal_temp_fops = { +static const struct file_operations acpi_thermal_temp_fops = { .open = acpi_thermal_temp_open_fs, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -static struct file_operations acpi_thermal_trip_fops = { +static const struct file_operations acpi_thermal_trip_fops = { .open = acpi_thermal_trip_open_fs, .read = seq_read, .write = acpi_thermal_write_trip_points, @@ -198,7 +198,7 @@ static struct file_operations acpi_thermal_trip_fops = { .release = single_release, }; -static struct file_operations acpi_thermal_cooling_fops = { +static const struct file_operations acpi_thermal_cooling_fops = { .open = acpi_thermal_cooling_open_fs, .read = seq_read, .write = acpi_thermal_write_cooling_mode, @@ -206,7 +206,7 @@ static struct file_operations acpi_thermal_cooling_fops = { .release = single_release, }; -static struct file_operations acpi_thermal_polling_fops = { +static const struct file_operations acpi_thermal_polling_fops = { .open = acpi_thermal_polling_open_fs, .read = seq_read, .write = acpi_thermal_write_polling, -- cgit v1.2.3 From 8970bfe706345223d39d33bfce5f8b29750ab716 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 10 Jul 2006 02:34:45 -0400 Subject: ACPI: SBS: fix initialization, sem2mutex cm_sbs_sem is being downed (via acpi_ac_init->acpi_lock_ac_dir) before it is initialised, with grave results. - Make it a mutex - Initialise it - Make it static - Clean other stuff up. Thanks to Paul Drynoff for reporting and testing. Cc: Rich Townsend Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/cm_sbs.c | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c index 574a75a166c5..a01ce6700bfe 100644 --- a/drivers/acpi/cm_sbs.c +++ b/drivers/acpi/cm_sbs.c @@ -39,50 +39,43 @@ ACPI_MODULE_NAME("cm_sbs") static struct proc_dir_entry *acpi_ac_dir; static struct proc_dir_entry *acpi_battery_dir; -static struct semaphore cm_sbs_sem; +static DEFINE_MUTEX(cm_sbs_mutex); -static int lock_ac_dir_cnt = 0; -static int lock_battery_dir_cnt = 0; +static int lock_ac_dir_cnt; +static int lock_battery_dir_cnt; struct proc_dir_entry *acpi_lock_ac_dir(void) { - - down(&cm_sbs_sem); - if (!acpi_ac_dir) { + mutex_lock(&cm_sbs_mutex); + if (!acpi_ac_dir) acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir); - } if (acpi_ac_dir) { lock_ac_dir_cnt++; } else { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Cannot create %s\n", ACPI_AC_CLASS)); } - up(&cm_sbs_sem); + mutex_unlock(&cm_sbs_mutex); return acpi_ac_dir; } - EXPORT_SYMBOL(acpi_lock_ac_dir); void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir_param) { - - down(&cm_sbs_sem); - if (acpi_ac_dir_param) { + mutex_lock(&cm_sbs_mutex); + if (acpi_ac_dir_param) lock_ac_dir_cnt--; - } if (lock_ac_dir_cnt == 0 && acpi_ac_dir_param && acpi_ac_dir) { remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir); acpi_ac_dir = 0; } - up(&cm_sbs_sem); + mutex_unlock(&cm_sbs_mutex); } - EXPORT_SYMBOL(acpi_unlock_ac_dir); struct proc_dir_entry *acpi_lock_battery_dir(void) { - - down(&cm_sbs_sem); + mutex_lock(&cm_sbs_mutex); if (!acpi_battery_dir) { acpi_battery_dir = proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir); @@ -93,39 +86,28 @@ struct proc_dir_entry *acpi_lock_battery_dir(void) ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Cannot create %s\n", ACPI_BATTERY_CLASS)); } - up(&cm_sbs_sem); + mutex_unlock(&cm_sbs_mutex); return acpi_battery_dir; } - EXPORT_SYMBOL(acpi_lock_battery_dir); void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir_param) { - - down(&cm_sbs_sem); - if (acpi_battery_dir_param) { + mutex_lock(&cm_sbs_mutex); + if (acpi_battery_dir_param) lock_battery_dir_cnt--; - } if (lock_battery_dir_cnt == 0 && acpi_battery_dir_param && acpi_battery_dir) { remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); acpi_battery_dir = 0; } - up(&cm_sbs_sem); + mutex_unlock(&cm_sbs_mutex); return; } - EXPORT_SYMBOL(acpi_unlock_battery_dir); static int __init acpi_cm_sbs_init(void) { - - if (acpi_disabled) - return 0; - - init_MUTEX(&cm_sbs_sem); - return 0; } - subsys_initcall(acpi_cm_sbs_init); -- cgit v1.2.3 From e21c1ca3f98529921c829a792dfdbfc5a5dc393b Mon Sep 17 00:00:00 2001 From: Len Brown Date: Mon, 10 Jul 2006 01:35:51 -0400 Subject: ACPI: acpi_os_allocate() fixes Replace acpi_in_resume with a more general hack to check irqs_disabled() on any kmalloc() from ACPI. While setting (system_state != SYSTEM_RUNNING) on resume seemed more general, Andrew Morton preferred this approach. http://bugzilla.kernel.org/show_bug.cgi?id=3469 Make acpi_os_allocate() into an inline function to allow /proc/slab_allocators to work. Delete some memset() that could fault on allocation failure. Signed-off-by: Len Brown --- drivers/acpi/osl.c | 30 ------------------------------ drivers/acpi/parser/psutils.c | 2 -- drivers/acpi/pci_link.c | 7 ------- drivers/acpi/utilities/utalloc.c | 2 ++ include/acpi/acmacros.h | 8 +++++++- include/acpi/platform/aclinux.h | 22 ++++++++++++++++++++++ 6 files changed, 31 insertions(+), 40 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index eedb05c6dc7b..47dfde95b8f8 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -136,16 +136,6 @@ void acpi_os_vprintf(const char *fmt, va_list args) #endif } - -extern int acpi_in_resume; -void *acpi_os_allocate(acpi_size size) -{ - if (acpi_in_resume) - return kmalloc(size, GFP_ATOMIC); - else - return kmalloc(size, GFP_KERNEL); -} - acpi_status acpi_os_get_root_pointer(u32 flags, struct acpi_pointer *addr) { if (efi_enabled) { @@ -1115,26 +1105,6 @@ acpi_status acpi_os_release_object(acpi_cache_t * cache, void *object) return (AE_OK); } -/******************************************************************************* - * - * FUNCTION: acpi_os_acquire_object - * - * PARAMETERS: Cache - Handle to cache object - * ReturnObject - Where the object is returned - * - * RETURN: Status - * - * DESCRIPTION: Return a zero-filled object. - * - ******************************************************************************/ - -void *acpi_os_acquire_object(acpi_cache_t * cache) -{ - void *object = kmem_cache_zalloc(cache, GFP_KERNEL); - WARN_ON(!object); - return object; -} - /****************************************************************************** * * FUNCTION: acpi_os_validate_interface diff --git a/drivers/acpi/parser/psutils.c b/drivers/acpi/parser/psutils.c index 182474ae8ce9..d405387b7414 100644 --- a/drivers/acpi/parser/psutils.c +++ b/drivers/acpi/parser/psutils.c @@ -139,12 +139,10 @@ union acpi_parse_object *acpi_ps_alloc_op(u16 opcode) /* The generic op (default) is by far the most common (16 to 1) */ op = acpi_os_acquire_object(acpi_gbl_ps_node_cache); - memset(op, 0, sizeof(struct acpi_parse_obj_common)); } else { /* Extended parseop */ op = acpi_os_acquire_object(acpi_gbl_ps_node_ext_cache); - memset(op, 0, sizeof(struct acpi_parse_obj_named)); } /* Initialize the Op */ diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 8197c0e40769..7f3e7e77e794 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -780,11 +780,6 @@ static int acpi_pci_link_resume(struct acpi_pci_link *link) return 0; } -/* - * FIXME: this is a workaround to avoid nasty warning. It will be removed - * after every device calls pci_disable_device in .resume. - */ -int acpi_in_resume; static int irqrouter_resume(struct sys_device *dev) { struct list_head *node = NULL; @@ -794,7 +789,6 @@ static int irqrouter_resume(struct sys_device *dev) /* Make sure SCI is enabled again (Apple firmware bug?) */ acpi_set_register(ACPI_BITREG_SCI_ENABLE, 1, ACPI_MTX_DO_NOT_LOCK); - acpi_in_resume = 1; list_for_each(node, &acpi_link.entries) { link = list_entry(node, struct acpi_pci_link, node); if (!link) { @@ -803,7 +797,6 @@ static int irqrouter_resume(struct sys_device *dev) } acpi_pci_link_resume(link); } - acpi_in_resume = 0; return 0; } diff --git a/drivers/acpi/utilities/utalloc.c b/drivers/acpi/utilities/utalloc.c index 5cff17dc78b3..f6cbc0b1bfd0 100644 --- a/drivers/acpi/utilities/utalloc.c +++ b/drivers/acpi/utilities/utalloc.c @@ -285,6 +285,7 @@ acpi_ut_initialize_buffer(struct acpi_buffer * buffer, return (status); } +#ifdef NOT_USED_BY_LINUX /******************************************************************************* * * FUNCTION: acpi_ut_allocate @@ -360,3 +361,4 @@ void *acpi_ut_allocate_zeroed(acpi_size size, return (allocation); } +#endif diff --git a/include/acpi/acmacros.h b/include/acpi/acmacros.h index f1ac6109556e..192fa095a515 100644 --- a/include/acpi/acmacros.h +++ b/include/acpi/acmacros.h @@ -724,9 +724,15 @@ /* Memory allocation */ +#ifndef ACPI_ALLOCATE #define ACPI_ALLOCATE(a) acpi_ut_allocate((acpi_size)(a),_COMPONENT,_acpi_module_name,__LINE__) +#endif +#ifndef ACPI_ALLOCATE_ZEROED #define ACPI_ALLOCATE_ZEROED(a) acpi_ut_allocate_zeroed((acpi_size)(a), _COMPONENT,_acpi_module_name,__LINE__) -#define ACPI_FREE(a) kfree(a) +#endif +#ifndef ACPI_FREE +#define ACPI_FREE(a) acpio_os_free(a) +#endif #define ACPI_MEM_TRACKING(a) #else diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index 3f853cabbd41..f0118262ac27 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -104,4 +104,26 @@ static inline acpi_thread_id acpi_os_get_thread_id(void) { return 0; } +/* + * The irqs_disabled() check is for resume from RAM. + * Interrupts are off during resume, just like they are for boot. + * However, boot has (system_state != SYSTEM_RUNNING) + * to quiet __might_sleep() in kmalloc() and resume does not. + */ +#include +static inline void *acpi_os_allocate(acpi_size size) { + return kmalloc(size, irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); +} +static inline void *acpi_os_allocate_zeroed(acpi_size size) { + return kzalloc(size, irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); +} + +static inline void *acpi_os_acquire_object(acpi_cache_t * cache) { + return kmem_cache_zalloc(cache, irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); +} + +#define ACPI_ALLOCATE(a) acpi_os_allocate(a) +#define ACPI_ALLOCATE_ZEROED(a) acpi_os_allocate_zeroed(a) +#define ACPI_FREE(a) kfree(a) + #endif /* __ACLINUX_H__ */ -- cgit v1.2.3 From aadd06e5c56b9ff5117ec77e59eada43dc46e2fc Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 10 Jul 2006 11:00:01 +0200 Subject: [PATCH] splice: fix problems with sys_tee() Several issues noticed/fixed: - We cannot reliably block in link_pipe() while holding both input and output mutexes. So do preparatory checks before locking down both mutexes and doing the link. - The ipipe->nrbufs vs i check was bad, because we could have dropped the ipipe lock in-between. This causes us to potentially look at unknown buffers if we were racing with someone else reading this pipe. Signed-off-by: Jens Axboe --- fs/splice.c | 238 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 133 insertions(+), 105 deletions(-) diff --git a/fs/splice.c b/fs/splice.c index 05fd2787be98..684bca3d3a10 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1306,6 +1306,85 @@ asmlinkage long sys_splice(int fd_in, loff_t __user *off_in, return error; } +/* + * Make sure there's data to read. Wait for input if we can, otherwise + * return an appropriate error. + */ +static int link_ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags) +{ + int ret; + + /* + * Check ->nrbufs without the inode lock first. This function + * is speculative anyways, so missing one is ok. + */ + if (pipe->nrbufs) + return 0; + + ret = 0; + mutex_lock(&pipe->inode->i_mutex); + + while (!pipe->nrbufs) { + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + if (!pipe->writers) + break; + if (!pipe->waiting_writers) { + if (flags & SPLICE_F_NONBLOCK) { + ret = -EAGAIN; + break; + } + } + pipe_wait(pipe); + } + + mutex_unlock(&pipe->inode->i_mutex); + return ret; +} + +/* + * Make sure there's writeable room. Wait for room if we can, otherwise + * return an appropriate error. + */ +static int link_opipe_prep(struct pipe_inode_info *pipe, unsigned int flags) +{ + int ret; + + /* + * Check ->nrbufs without the inode lock first. This function + * is speculative anyways, so missing one is ok. + */ + if (pipe->nrbufs < PIPE_BUFFERS) + return 0; + + ret = 0; + mutex_lock(&pipe->inode->i_mutex); + + while (pipe->nrbufs >= PIPE_BUFFERS) { + if (!pipe->readers) { + send_sig(SIGPIPE, current, 0); + ret = -EPIPE; + break; + } + if (flags & SPLICE_F_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + pipe->waiting_writers++; + pipe_wait(pipe); + pipe->waiting_writers--; + } + + mutex_unlock(&pipe->inode->i_mutex); + return ret; +} + /* * Link contents of ipipe to opipe. */ @@ -1314,9 +1393,7 @@ static int link_pipe(struct pipe_inode_info *ipipe, size_t len, unsigned int flags) { struct pipe_buffer *ibuf, *obuf; - int ret, do_wakeup, i, ipipe_first; - - ret = do_wakeup = ipipe_first = 0; + int ret = 0, i = 0, nbuf; /* * Potential ABBA deadlock, work around it by ordering lock @@ -1324,126 +1401,62 @@ static int link_pipe(struct pipe_inode_info *ipipe, * could deadlock (one doing tee from A -> B, the other from B -> A). */ if (ipipe->inode < opipe->inode) { - ipipe_first = 1; - mutex_lock(&ipipe->inode->i_mutex); - mutex_lock(&opipe->inode->i_mutex); + mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_CHILD); } else { - mutex_lock(&opipe->inode->i_mutex); - mutex_lock(&ipipe->inode->i_mutex); + mutex_lock_nested(&opipe->inode->i_mutex, I_MUTEX_PARENT); + mutex_lock_nested(&ipipe->inode->i_mutex, I_MUTEX_CHILD); } - for (i = 0;; i++) { + do { if (!opipe->readers) { send_sig(SIGPIPE, current, 0); if (!ret) ret = -EPIPE; break; } - if (ipipe->nrbufs - i) { - ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (PIPE_BUFFERS - 1)); - /* - * If we have room, fill this buffer - */ - if (opipe->nrbufs < PIPE_BUFFERS) { - int nbuf = (opipe->curbuf + opipe->nrbufs) & (PIPE_BUFFERS - 1); - - /* - * Get a reference to this pipe buffer, - * so we can copy the contents over. - */ - ibuf->ops->get(ipipe, ibuf); - - obuf = opipe->bufs + nbuf; - *obuf = *ibuf; - - /* - * Don't inherit the gift flag, we need to - * prevent multiple steals of this page. - */ - obuf->flags &= ~PIPE_BUF_FLAG_GIFT; - - if (obuf->len > len) - obuf->len = len; - - opipe->nrbufs++; - do_wakeup = 1; - ret += obuf->len; - len -= obuf->len; - - if (!len) - break; - if (opipe->nrbufs < PIPE_BUFFERS) - continue; - } - - /* - * We have input available, but no output room. - * If we already copied data, return that. If we - * need to drop the opipe lock, it must be ordered - * last to avoid deadlocks. - */ - if ((flags & SPLICE_F_NONBLOCK) || !ipipe_first) { - if (!ret) - ret = -EAGAIN; - break; - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } - if (do_wakeup) { - smp_mb(); - if (waitqueue_active(&opipe->wait)) - wake_up_interruptible(&opipe->wait); - kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN); - do_wakeup = 0; - } + /* + * If we have iterated all input buffers or ran out of + * output room, break. + */ + if (i >= ipipe->nrbufs || opipe->nrbufs >= PIPE_BUFFERS) + break; - opipe->waiting_writers++; - pipe_wait(opipe); - opipe->waiting_writers--; - continue; - } + ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (PIPE_BUFFERS - 1)); + nbuf = (opipe->curbuf + opipe->nrbufs) & (PIPE_BUFFERS - 1); /* - * No input buffers, do the usual checks for available - * writers and blocking and wait if necessary + * Get a reference to this pipe buffer, + * so we can copy the contents over. */ - if (!ipipe->writers) - break; - if (!ipipe->waiting_writers) { - if (ret) - break; - } + ibuf->ops->get(ipipe, ibuf); + + obuf = opipe->bufs + nbuf; + *obuf = *ibuf; + /* - * pipe_wait() drops the ipipe mutex. To avoid deadlocks - * with another process, we can only safely do that if - * the ipipe lock is ordered last. + * Don't inherit the gift flag, we need to + * prevent multiple steals of this page. */ - if ((flags & SPLICE_F_NONBLOCK) || ipipe_first) { - if (!ret) - ret = -EAGAIN; - break; - } - if (signal_pending(current)) { - if (!ret) - ret = -ERESTARTSYS; - break; - } + obuf->flags &= ~PIPE_BUF_FLAG_GIFT; - if (waitqueue_active(&ipipe->wait)) - wake_up_interruptible_sync(&ipipe->wait); - kill_fasync(&ipipe->fasync_writers, SIGIO, POLL_OUT); + if (obuf->len > len) + obuf->len = len; - pipe_wait(ipipe); - } + opipe->nrbufs++; + ret += obuf->len; + len -= obuf->len; + i++; + } while (len); mutex_unlock(&ipipe->inode->i_mutex); mutex_unlock(&opipe->inode->i_mutex); - if (do_wakeup) { + /* + * If we put data in the output pipe, wakeup any potential readers. + */ + if (ret > 0) { smp_mb(); if (waitqueue_active(&opipe->wait)) wake_up_interruptible(&opipe->wait); @@ -1464,14 +1477,29 @@ static long do_tee(struct file *in, struct file *out, size_t len, { struct pipe_inode_info *ipipe = in->f_dentry->d_inode->i_pipe; struct pipe_inode_info *opipe = out->f_dentry->d_inode->i_pipe; + int ret = -EINVAL; /* - * Link ipipe to the two output pipes, consuming as we go along. + * Duplicate the contents of ipipe to opipe without actually + * copying the data. */ - if (ipipe && opipe) - return link_pipe(ipipe, opipe, len, flags); + if (ipipe && opipe && ipipe != opipe) { + /* + * Keep going, unless we encounter an error. The ipipe/opipe + * ordering doesn't really matter. + */ + ret = link_ipipe_prep(ipipe, flags); + if (!ret) { + ret = link_opipe_prep(opipe, flags); + if (!ret) { + ret = link_pipe(ipipe, opipe, len, flags); + if (!ret && (flags & SPLICE_F_NONBLOCK)) + ret = -EAGAIN; + } + } + } - return -EINVAL; + return ret; } asmlinkage long sys_tee(int fdin, int fdout, size_t len, unsigned int flags) -- cgit v1.2.3 From 522536f6b24e63ac946259af65224f107dff7857 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 28 Jun 2006 19:11:21 -0500 Subject: [PATCH] bcm43xx-softmac: Fix an off-by-one condition in handle_irq_noise An assert statement near the start of handle_irq_noise in the softmac version of bcm43xx_main.c is there to protect against out of bound addressing using variable bcm->noisecalc.nr_samples. The arrays in question have a dimension of 8, thus the value must be < 8. Signed-Off-By: Larry.Finger Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index e1c5a939bca4..3889f79e7128 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -1547,7 +1547,7 @@ static void handle_irq_noise(struct bcm43xx_private *bcm) goto generate_new; /* Get the noise samples. */ - assert(bcm->noisecalc.nr_samples <= 8); + assert(bcm->noisecalc.nr_samples < 8); i = bcm->noisecalc.nr_samples; noise[0] = limit_value(noise[0], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); noise[1] = limit_value(noise[1], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); -- cgit v1.2.3 From b312d799b324e895745ffe148def234fc60d5b74 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 5 Jul 2006 15:57:39 +0100 Subject: [PATCH] zd1211rw: usb_clear_halt not allowed in IRQ context We will reimplement halt-clearing later, when we have periodic housekeeping routines in place. This will do as a temporary fix, the EPIPE case has not yet been seen. Signed-off-by: Daniel Drake Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/zd_usb.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index ce1cb2c6aa8d..72f90525bf68 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -375,10 +375,8 @@ static void int_urb_complete(struct urb *urb, struct pt_regs *pt_regs) case -ENODEV: case -ENOENT: case -ECONNRESET: - goto kfree; case -EPIPE: - usb_clear_halt(urb->dev, EP_INT_IN); - /* FALL-THROUGH */ + goto kfree; default: goto resubmit; } @@ -580,10 +578,8 @@ static void rx_urb_complete(struct urb *urb, struct pt_regs *pt_regs) case -ENODEV: case -ENOENT: case -ECONNRESET: - return; case -EPIPE: - usb_clear_halt(urb->dev, EP_DATA_IN); - /* FALL-THROUGH */ + return; default: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); goto resubmit; @@ -749,11 +745,9 @@ static void tx_urb_complete(struct urb *urb, struct pt_regs *pt_regs) case -ENODEV: case -ENOENT: case -ECONNRESET: + case -EPIPE: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); break; - case -EPIPE: - usb_clear_halt(urb->dev, EP_DATA_OUT); - /* FALL-THROUGH */ default: dev_dbg_f(urb_dev(urb), "urb %p error %d\n", urb, urb->status); goto resubmit; -- cgit v1.2.3 From 07a18684c92c0156f87ea158b5adc3022485f82a Mon Sep 17 00:00:00 2001 From: Kristen Accardi Date: Mon, 10 Jul 2006 14:19:15 -0400 Subject: ACPI: ACPI_DOCK: Initialize the atomic notifier list Signed-off-by: Kristen Carlson Accardi Signed-off-by: Andrew Morton Signed-off-by: Len Brown --- drivers/acpi/dock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 510a94524298..1c0a39d8b04e 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -627,6 +627,7 @@ static int dock_add(acpi_handle handle) INIT_LIST_HEAD(&dock_station->hotplug_devices); spin_lock_init(&dock_station->dd_lock); spin_lock_init(&dock_station->hp_lock); + ATOMIC_INIT_NOTIFIER_HEAD(&dock_notifier_list); /* Find dependent devices */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, -- cgit v1.2.3 From 135c294fa3660b979d71bbce1611f9ca721077b5 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 10 Jul 2006 04:43:48 -0700 Subject: [PATCH] don't select CONFIG_HOTPLUG It's useful to be able to turn off CONFIG_HOTPLUG for compile-coverage testing and for section-checking coverage. But a few things go and select CONFIG_HOTPLUG, making it a royal PITA to turn the thing off. It's only turnable offable if CONFIG_EMBEDDED anyway. So let's make those things depend on HOTPLUG, not select it. Cc: Greg KH Cc: Dominik Brodowski Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/base/Kconfig | 2 +- drivers/pci/hotplug/Kconfig | 3 +-- drivers/pcmcia/Kconfig | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 80502dc6ed66..0b4e22436935 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -20,7 +20,7 @@ config PREVENT_FIRMWARE_BUILD config FW_LOADER tristate "Userspace firmware loading support" - select HOTPLUG + depends on HOTPLUG ---help--- This option is provided for the case where no in-kernel-tree modules require userspace firmware loading support, but a module built outside diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 222a1cc4aa28..e94099c3e82e 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -6,8 +6,7 @@ menu "PCI Hotplug Support" config HOTPLUG_PCI tristate "Support for PCI Hotplug (EXPERIMENTAL)" - depends on PCI && EXPERIMENTAL - select HOTPLUG + depends on PCI && EXPERIMENTAL && HOTPLUG ---help--- Say Y here if you have a motherboard with a PCI Hotplug controller. This allows you to add and remove PCI cards while the machine is diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 61cb4b29f55c..35f88649d3b7 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -6,7 +6,7 @@ menu "PCCARD (PCMCIA/CardBus) support" config PCCARD tristate "PCCard (PCMCIA/CardBus) support" - select HOTPLUG + depends on HOTPLUG ---help--- Say Y here if you want to attach PCMCIA- or PC-cards to your Linux computer. These are credit-card size devices such as network cards, -- cgit v1.2.3 From 1a91023a9f172f820e292f2c5675fb9f8e2636f0 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 10 Jul 2006 04:43:49 -0700 Subject: [PATCH] x86_64: e820.c needs pgtable.h arch/x86_64/kernel/e820.c:42: error: 'MAXMEM' undeclared here (not in a function) Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/e820.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86_64/kernel/e820.c b/arch/x86_64/kernel/e820.c index b8eee4c7888b..e56c2adf57a4 100644 --- a/arch/x86_64/kernel/e820.c +++ b/arch/x86_64/kernel/e820.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From e45b3b6af09dab2a28a7c88b340d0bcdd173e068 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 10 Jul 2006 04:43:50 -0700 Subject: [PATCH] count_vm_events() fix Dopey bug. Causes hopelessly-wrong numbers from vmstat(8) and several other counters. Cc: Christoph Lameter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/vmstat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/vmstat.h b/include/linux/vmstat.h index 3e0daf54133e..d673b7b15c34 100644 --- a/include/linux/vmstat.h +++ b/include/linux/vmstat.h @@ -57,7 +57,7 @@ static inline void __count_vm_events(enum vm_event_item item, long delta) static inline void count_vm_events(enum vm_event_item item, long delta) { - get_cpu_var(vm_event_states.event[item])++; + get_cpu_var(vm_event_states.event[item]) += delta; put_cpu(); } -- cgit v1.2.3 From 0a565f7919cfb3d3df2c97d45751cbb83d858f97 Mon Sep 17 00:00:00 2001 From: Peter Williams Date: Mon, 10 Jul 2006 04:43:51 -0700 Subject: [PATCH] sched: fix bug in __migrate_task() Problem: In the function __migrate_task(), deactivate_task() followed by activate_task() is used to move the task from one run queue to another. This has two undesirable effects: 1. The task's priority is recalculated. (Nowhere else in the scheduler code is the priority recalculated for a change of CPU.) 2. The task's time stamp is set to the current time. At the very least, this makes the adjustment of the time stamp before the call to deactivate_task() redundant but I believe the problem is more serious as the time stamp now holds the time of the queue change instead of the time at which the task was woken. In addition, unless dest_rq is the same queue as "current" is on the time stamp could be inaccurate due to inter CPU drift. Solution: Replace the call to activate_task() with one to __activate_task(). Signed-off-by: Peter Williams Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/sched.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched.c b/kernel/sched.c index 4ee400f9d56b..b47819b676fa 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -4877,7 +4877,7 @@ static int __migrate_task(struct task_struct *p, int src_cpu, int dest_cpu) p->timestamp = p->timestamp - rq_src->timestamp_last_tick + rq_dest->timestamp_last_tick; deactivate_task(p, rq_src); - activate_task(p, rq_dest, 0); + __activate_task(p, rq_dest); if (TASK_PREEMPTS_CURR(p, rq_dest)) resched_task(rq_dest->curr); } -- cgit v1.2.3 From 2ed6e34f88a0d896a6f889b00693cae0fadacfd0 Mon Sep 17 00:00:00 2001 From: Andreas Mohr Date: Mon, 10 Jul 2006 04:43:52 -0700 Subject: [PATCH] small kernel/sched.c cleanup - constify and optimize stat_nam (thanks to Michael Tokarev!) - spelling and comment fixes Signed-off-by: Andreas Mohr Acked-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/sched.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/kernel/sched.c b/kernel/sched.c index b47819b676fa..d714611f1691 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -3384,7 +3384,7 @@ EXPORT_SYMBOL(schedule); #ifdef CONFIG_PREEMPT /* - * this is is the entry point to schedule() from in-kernel preemption + * this is the entry point to schedule() from in-kernel preemption * off of preempt_enable. Kernel preemptions off return from interrupt * occur there and call schedule directly. */ @@ -3427,7 +3427,7 @@ need_resched: EXPORT_SYMBOL(preempt_schedule); /* - * this is is the entry point to schedule() from kernel preemption + * this is the entry point to schedule() from kernel preemption * off of irq context. * Note, that this is called and return with irqs disabled. This will * protect us against recursive calling from irq. @@ -3439,7 +3439,7 @@ asmlinkage void __sched preempt_schedule_irq(void) struct task_struct *task = current; int saved_lock_depth; #endif - /* Catch callers which need to be fixed*/ + /* Catch callers which need to be fixed */ BUG_ON(ti->preempt_count || !irqs_disabled()); need_resched: @@ -4650,7 +4650,7 @@ static inline struct task_struct *younger_sibling(struct task_struct *p) return list_entry(p->sibling.next,struct task_struct,sibling); } -static const char *stat_nam[] = { "R", "S", "D", "T", "t", "Z", "X" }; +static const char stat_nam[] = "RSDTtZX"; static void show_task(struct task_struct *p) { @@ -4658,12 +4658,9 @@ static void show_task(struct task_struct *p) unsigned long free = 0; unsigned state; - printk("%-13.13s ", p->comm); state = p->state ? __ffs(p->state) + 1 : 0; - if (state < ARRAY_SIZE(stat_nam)) - printk(stat_nam[state]); - else - printk("?"); + printk("%-13.13s %c", p->comm, + state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?'); #if (BITS_PER_LONG == 32) if (state == TASK_RUNNING) printk(" running "); @@ -5776,7 +5773,7 @@ static unsigned long long measure_migration_cost(int cpu1, int cpu2) cache = vmalloc(max_size); if (!cache) { printk("could not vmalloc %d bytes for cache!\n", 2*max_size); - return 1000000; // return 1 msec on very small boxen + return 1000000; /* return 1 msec on very small boxen */ } while (size <= max_size) { -- cgit v1.2.3 From c312feb2931ded0582378712727b7ea017a951bd Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 10 Jul 2006 04:43:53 -0700 Subject: [PATCH] SELinux: decouple fscontext/context mount options Remove the conflict between fscontext and context mount options. If context= is specified without fscontext it will operate just as before, if both are specified we will use mount point labeling and all inodes will get the label specified by context=. The superblock will be labeled with the label of fscontext=, thus affecting operations which check the superblock security context, such as associate permissions. Signed-off-by: Eric Paris Acked-by: Stephen Smalley Signed-off-by: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/selinux/hooks.c | 69 ++++++++++++++++++++++++++++++--------- security/selinux/include/objsec.h | 3 +- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 24caaeec8894..7e101dbea4cb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -246,6 +246,7 @@ static int superblock_alloc_security(struct super_block *sb) sbsec->sb = sb; sbsec->sid = SECINITSID_UNLABELED; sbsec->def_sid = SECINITSID_FILE; + sbsec->mntpoint_sid = SECINITSID_UNLABELED; sb->s_security = sbsec; return 0; @@ -329,9 +330,26 @@ static match_table_t tokens = { #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" +static int may_context_mount_sb_relabel(u32 sid, + struct superblock_security_struct *sbsec, + struct task_security_struct *tsec) +{ + int rc; + + rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELFROM, NULL); + if (rc) + return rc; + + rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELTO, NULL); + return rc; +} + static int try_context_mount(struct super_block *sb, void *data) { char *context = NULL, *defcontext = NULL; + char *fscontext = NULL; const char *name; u32 sid; int alloc = 0, rc = 0, seen = 0; @@ -374,7 +392,7 @@ static int try_context_mount(struct super_block *sb, void *data) switch (token) { case Opt_context: - if (seen) { + if (seen & (Opt_context|Opt_defcontext)) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); goto out_free; @@ -390,13 +408,13 @@ static int try_context_mount(struct super_block *sb, void *data) break; case Opt_fscontext: - if (seen & (Opt_context|Opt_fscontext)) { + if (seen & Opt_fscontext) { rc = -EINVAL; printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); goto out_free; } - context = match_strdup(&args[0]); - if (!context) { + fscontext = match_strdup(&args[0]); + if (!fscontext) { rc = -ENOMEM; goto out_free; } @@ -441,29 +459,46 @@ static int try_context_mount(struct super_block *sb, void *data) if (!seen) goto out; - if (context) { - rc = security_context_to_sid(context, strlen(context), &sid); + /* sets the context of the superblock for the fs being mounted. */ + if (fscontext) { + rc = security_context_to_sid(fscontext, strlen(fscontext), &sid); if (rc) { printk(KERN_WARNING "SELinux: security_context_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", - context, sb->s_id, name, rc); + fscontext, sb->s_id, name, rc); goto out_free; } - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELFROM, NULL); + rc = may_context_mount_sb_relabel(sid, sbsec, tsec); if (rc) goto out_free; - rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELTO, NULL); + sbsec->sid = sid; + } + + /* + * Switch to using mount point labeling behavior. + * sets the label used on all file below the mountpoint, and will set + * the superblock context if not already set. + */ + if (context) { + rc = security_context_to_sid(context, strlen(context), &sid); + if (rc) { + printk(KERN_WARNING "SELinux: security_context_to_sid" + "(%s) failed for (dev %s, type %s) errno=%d\n", + context, sb->s_id, name, rc); + goto out_free; + } + + rc = may_context_mount_sb_relabel(sid, sbsec, tsec); if (rc) goto out_free; - sbsec->sid = sid; + if (!fscontext) + sbsec->sid = sid; + sbsec->mntpoint_sid = sid; - if (seen & Opt_context) - sbsec->behavior = SECURITY_FS_USE_MNTPOINT; + sbsec->behavior = SECURITY_FS_USE_MNTPOINT; } if (defcontext) { @@ -495,6 +530,7 @@ out_free: if (alloc) { kfree(context); kfree(defcontext); + kfree(fscontext); } out: return rc; @@ -876,8 +912,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent goto out; isec->sid = sid; break; + case SECURITY_FS_USE_MNTPOINT: + isec->sid = sbsec->mntpoint_sid; + break; default: - /* Default to the fs SID. */ + /* Default to the fs superblock SID. */ isec->sid = sbsec->sid; if (sbsec->proc) { diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index cf54a304169a..940178865fc7 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -57,8 +57,9 @@ struct file_security_struct { struct superblock_security_struct { struct super_block *sb; /* back pointer to sb object */ struct list_head list; /* list of superblock_security_struct */ - u32 sid; /* SID of file system */ + u32 sid; /* SID of file system superblock */ u32 def_sid; /* default SID for labeling */ + u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */ unsigned int behavior; /* labeling behavior */ unsigned char initialized; /* initialization flag */ unsigned char proc; /* proc fs */ -- cgit v1.2.3 From 0808925ea5684a0ce25483b30e94d4f398804978 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 10 Jul 2006 04:43:55 -0700 Subject: [PATCH] SELinux: add rootcontext= option to label root inode when mounting Introduce a new rootcontext= option to FS mounting. This option will allow you to explicitly label the root inode of an FS being mounted before that FS or inode because visible to userspace. This was found to be useful for things like stateless linux, see https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=190001 Signed-off-by: Eric Paris Acked-by: Stephen Smalley Signed-off-by: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/selinux/hooks.c | 66 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7e101dbea4cb..2e8b4dfcbc74 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -320,12 +320,14 @@ enum { Opt_context = 1, Opt_fscontext = 2, Opt_defcontext = 4, + Opt_rootcontext = 8, }; static match_table_t tokens = { {Opt_context, "context=%s"}, {Opt_fscontext, "fscontext=%s"}, {Opt_defcontext, "defcontext=%s"}, + {Opt_rootcontext, "rootcontext=%s"}, }; #define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" @@ -346,10 +348,25 @@ static int may_context_mount_sb_relabel(u32 sid, return rc; } +static int may_context_mount_inode_relabel(u32 sid, + struct superblock_security_struct *sbsec, + struct task_security_struct *tsec) +{ + int rc; + rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__RELABELFROM, NULL); + if (rc) + return rc; + + rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, + FILESYSTEM__ASSOCIATE, NULL); + return rc; +} + static int try_context_mount(struct super_block *sb, void *data) { char *context = NULL, *defcontext = NULL; - char *fscontext = NULL; + char *fscontext = NULL, *rootcontext = NULL; const char *name; u32 sid; int alloc = 0, rc = 0, seen = 0; @@ -423,6 +440,22 @@ static int try_context_mount(struct super_block *sb, void *data) seen |= Opt_fscontext; break; + case Opt_rootcontext: + if (seen & Opt_rootcontext) { + rc = -EINVAL; + printk(KERN_WARNING SEL_MOUNT_FAIL_MSG); + goto out_free; + } + rootcontext = match_strdup(&args[0]); + if (!rootcontext) { + rc = -ENOMEM; + goto out_free; + } + if (!alloc) + alloc = 1; + seen |= Opt_rootcontext; + break; + case Opt_defcontext: if (sbsec->behavior != SECURITY_FS_USE_XATTR) { rc = -EINVAL; @@ -501,6 +534,25 @@ static int try_context_mount(struct super_block *sb, void *data) sbsec->behavior = SECURITY_FS_USE_MNTPOINT; } + if (rootcontext) { + struct inode *inode = sb->s_root->d_inode; + struct inode_security_struct *isec = inode->i_security; + rc = security_context_to_sid(rootcontext, strlen(rootcontext), &sid); + if (rc) { + printk(KERN_WARNING "SELinux: security_context_to_sid" + "(%s) failed for (dev %s, type %s) errno=%d\n", + rootcontext, sb->s_id, name, rc); + goto out_free; + } + + rc = may_context_mount_inode_relabel(sid, sbsec, tsec); + if (rc) + goto out_free; + + isec->sid = sid; + isec->initialized = 1; + } + if (defcontext) { rc = security_context_to_sid(defcontext, strlen(defcontext), &sid); if (rc) { @@ -513,13 +565,7 @@ static int try_context_mount(struct super_block *sb, void *data) if (sid == sbsec->def_sid) goto out_free; - rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__RELABELFROM, NULL); - if (rc) - goto out_free; - - rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, - FILESYSTEM__ASSOCIATE, NULL); + rc = may_context_mount_inode_relabel(sid, sbsec, tsec); if (rc) goto out_free; @@ -531,6 +577,7 @@ out_free: kfree(context); kfree(defcontext); kfree(fscontext); + kfree(rootcontext); } out: return rc; @@ -1882,7 +1929,8 @@ static inline int selinux_option(char *option, int len) { return (match_prefix("context=", sizeof("context=")-1, option, len) || match_prefix("fscontext=", sizeof("fscontext=")-1, option, len) || - match_prefix("defcontext=", sizeof("defcontext=")-1, option, len)); + match_prefix("defcontext=", sizeof("defcontext=")-1, option, len) || + match_prefix("rootcontext=", sizeof("rootcontext=")-1, option, len)); } static inline void take_option(char **to, char *from, int *first, int len) -- cgit v1.2.3 From 73ce5934e2d855db436566297f12966eb507a435 Mon Sep 17 00:00:00 2001 From: Hisashi Hifumi Date: Mon, 10 Jul 2006 04:43:56 -0700 Subject: [PATCH] reiserfs: fix journaling issue regarding fsync() When write() extends a file(i_size is increased) and fsync() is called, change of inode must be written to journaling area through fsync(). But,currently the i_trans_id is not correctly updated when i_size is increased. So fsync() does not kick the journal writer. Reiserfs_file_write() already updates the transaction when blocks are allocated, but the case when i_size increases and new blocks are not added is not correctly treated. Following patch fix this bug. Signed-off-by: Hisashi Hifumi Cc: Jeff Mahoney Cc: Chris Mason Cc: Hans Reiser Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/file.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 752cea12e30f..f318b58510fd 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -860,8 +860,12 @@ static int reiserfs_submit_file_region_for_write(struct reiserfs_transaction_han // this sets the proper flags for O_SYNC to trigger a commit mark_inode_dirty(inode); reiserfs_write_unlock(inode->i_sb); - } else + } else { + reiserfs_write_lock(inode->i_sb); + reiserfs_update_inode_transaction(inode); mark_inode_dirty(inode); + reiserfs_write_unlock(inode->i_sb); + } sd_update = 1; } -- cgit v1.2.3 From 64552a50bc80fecb73617336bf197375868faf6e Mon Sep 17 00:00:00 2001 From: Horms Date: Mon, 10 Jul 2006 04:43:58 -0700 Subject: [PATCH] nfs: Update Documentation/nfsroot.txt to include dhcp, syslinux and isolinux * Document the ip command a little differently to make the interaction between defaults and autoconfiguration a little clearer (I hope) * Update autoconfiguration the current set of options, including DHCP * Update the boot methods to add syslinux and isolinux, and remove dd of=/dev/fd0 which is no longer supported by linux * Add a referance to initramfs along side initrd. Should the latter and its document be removed some time soon? * Various cleanups to put the text consistently into the thrid person * Reformated a bit to fit into 80 columns a bit more nicely * Should the bootloaders documentation be removed or split into a separate documentation, it seems somewhat out of scope Signed-off-by: Horms Cc: "H. Peter Anvin" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/nfsroot.txt | 275 +++++++++++++++++++++++++++------------------- 1 file changed, 160 insertions(+), 115 deletions(-) diff --git a/Documentation/nfsroot.txt b/Documentation/nfsroot.txt index d56dc71d9430..3cc953cb288f 100644 --- a/Documentation/nfsroot.txt +++ b/Documentation/nfsroot.txt @@ -4,15 +4,16 @@ Mounting the root filesystem via NFS (nfsroot) Written 1996 by Gero Kuhlmann Updated 1997 by Martin Mares Updated 2006 by Nico Schottelius +Updated 2006 by Horms -If you want to use a diskless system, as an X-terminal or printer -server for example, you have to put your root filesystem onto a -non-disk device. This can either be a ramdisk (see initrd.txt in -this directory for further information) or a filesystem mounted -via NFS. The following text describes on how to use NFS for the -root filesystem. For the rest of this text 'client' means the +In order to use a diskless system, such as an X-terminal or printer server +for example, it is necessary for the root filesystem to be present on a +non-disk device. This may be an initramfs (see Documentation/filesystems/ +ramfs-rootfs-initramfs.txt), a ramdisk (see Documenation/initrd.txt) or a +filesystem mounted via NFS. The following text describes on how to use NFS +for the root filesystem. For the rest of this text 'client' means the diskless system, and 'server' means the NFS server. @@ -21,11 +22,13 @@ diskless system, and 'server' means the NFS server. 1.) Enabling nfsroot capabilities ----------------------------- -In order to use nfsroot you have to select support for NFS during -kernel configuration. Note that NFS cannot be loaded as a module -in this case. The configuration script will then ask you whether -you want to use nfsroot, and if yes what kind of auto configuration -system you want to use. Selecting both BOOTP and RARP is safe. +In order to use nfsroot, NFS client support needs to be selected as +built-in during configuration. Once this has been selected, the nfsroot +option will become available, which should also be selected. + +In the networking options, kernel level autoconfiguration can be selected, +along with the types of autoconfiguration to support. Selecting all of +DHCP, BOOTP and RARP is safe. @@ -33,11 +36,10 @@ system you want to use. Selecting both BOOTP and RARP is safe. 2.) Kernel command line ------------------- -When the kernel has been loaded by a boot loader (either by loadlin, -LILO or a network boot program) it has to be told what root fs device -to use, and where to find the server and the name of the directory -on the server to mount as root. This can be established by a couple -of kernel command line parameters: +When the kernel has been loaded by a boot loader (see below) it needs to be +told what root fs device to use. And in the case of nfsroot, where to find +both the server and the name of the directory on the server to mount as root. +This can be established using the following kernel command line parameters: root=/dev/nfs @@ -49,23 +51,21 @@ root=/dev/nfs nfsroot=[:][,] - If the `nfsroot' parameter is NOT given on the command line, the default - "/tftpboot/%s" will be used. + If the `nfsroot' parameter is NOT given on the command line, + the default "/tftpboot/%s" will be used. - Specifies the IP address of the NFS server. If this field - is not given, the default address as determined by the - `ip' variable (see below) is used. One use of this - parameter is for example to allow using different servers - for RARP and NFS. Usually you can leave this blank. + Specifies the IP address of the NFS server. + The default address is determined by the `ip' parameter + (see below). This parameter allows the use of different + servers for IP autoconfiguration and NFS. - Name of the directory on the server to mount as root. If - there is a "%s" token in the string, the token will be - replaced by the ASCII-representation of the client's IP - address. + Name of the directory on the server to mount as root. + If there is a "%s" token in the string, it will be + replaced by the ASCII-representation of the client's + IP address. Standard NFS options. All options are separated by commas. - If the options field is not given, the following defaults - will be used: + The following defaults are used: port = as given by server portmap daemon rsize = 1024 wsize = 1024 @@ -81,129 +81,174 @@ nfsroot=[:][,] ip=:::::: This parameter tells the kernel how to configure IP addresses of devices - and also how to set up the IP routing table. It was originally called `nfsaddrs', - but now the boot-time IP configuration works independently of NFS, so it - was renamed to `ip' and the old name remained as an alias for compatibility - reasons. + and also how to set up the IP routing table. It was originally called + `nfsaddrs', but now the boot-time IP configuration works independently of + NFS, so it was renamed to `ip' and the old name remained as an alias for + compatibility reasons. If this parameter is missing from the kernel command line, all fields are assumed to be empty, and the defaults mentioned below apply. In general - this means that the kernel tries to configure everything using both - RARP and BOOTP (depending on what has been enabled during kernel confi- - guration, and if both what protocol answer got in first). + this means that the kernel tries to configure everything using + autoconfiguration. + + The parameter can appear alone as the value to the `ip' + parameter (without all the ':' characters before) in which case auto- + configuration is used. + + IP address of the client. - IP address of the client. If empty, the address will either - be determined by RARP or BOOTP. What protocol is used de- - pends on what has been enabled during kernel configuration - and on the parameter. If this parameter is not - empty, neither RARP nor BOOTP will be used. + Default: Determined using autoconfiguration. IP address of the NFS server. If RARP is used to determine the client address and this parameter is NOT empty only - replies from the specified server are accepted. To use - different RARP and NFS server, specify your RARP server - here (or leave it blank), and specify your NFS server in - the `nfsroot' parameter (see above). If this entry is blank - the address of the server is used which answered the RARP - or BOOTP request. - - IP address of a gateway if the server is on a different - subnet. If this entry is empty no gateway is used and the - server is assumed to be on the local network, unless a - value has been received by BOOTP. - - Netmask for local network interface. If this is empty, + replies from the specified server are accepted. + + Only required for for NFS root. That is autoconfiguration + will not be triggered if it is missing and NFS root is not + in operation. + + Default: Determined using autoconfiguration. + The address of the autoconfiguration server is used. + + IP address of a gateway if the server is on a different subnet. + + Default: Determined using autoconfiguration. + + Netmask for local network interface. If unspecified the netmask is derived from the client IP address assuming - classful addressing, unless overridden in BOOTP reply. + classful addressing. - Name of the client. If empty, the client IP address is - used in ASCII notation, or the value received by BOOTP. + Default: Determined using autoconfiguration. - Name of network device to use. If this is empty, all - devices are used for RARP and BOOTP requests, and the - first one we receive a reply on is configured. If you have - only one device, you can safely leave this blank. + Name of the client. May be supplied by autoconfiguration, + but its absence will not trigger autoconfiguration. - Method to use for autoconfiguration. If this is either - 'rarp' or 'bootp', the specified protocol is used. - If the value is 'both' or empty, both protocols are used - so far as they have been enabled during kernel configura- - tion. 'off' means no autoconfiguration. + Default: Client IP address is used in ASCII notation. - The parameter can appear alone as the value to the `ip' - parameter (without all the ':' characters before) in which case auto- - configuration is used. + Name of network device to use. + + Default: If the host only has one device, it is used. + Otherwise the device is determined using + autoconfiguration. This is done by sending + autoconfiguration requests out of all devices, + and using the device that received the first reply. + Method to use for autoconfiguration. In the case of options + which specify multiple autoconfiguration protocols, + requests are sent using all protocols, and the first one + to reply is used. + Only autoconfiguration protocols that have been compiled + into the kernel will be used, regardless of the value of + this option. + off or none: don't use autoconfiguration (default) + on or any: use any protocol available in the kernel + dhcp: use DHCP + bootp: use BOOTP + rarp: use RARP + both: use both BOOTP and RARP but not DHCP + (old option kept for backwards compatibility) -3.) Kernel loader - ------------- + Default: any -To get the kernel into memory different approaches can be used. They -depend on what facilities are available: -3.1) Writing the kernel onto a floppy using dd: - As always you can just write the kernel onto a floppy using dd, - but then it's not possible to use kernel command lines at all. - To substitute the 'root=' parameter, create a dummy device on any - linux system with major number 0 and minor number 255 using mknod: - mknod /dev/boot255 c 0 255 +3.) Boot Loader + ---------- - Then copy the kernel zImage file onto a floppy using dd: +To get the kernel into memory different approaches can be used. +They depend on various facilities being available: - dd if=/usr/src/linux/arch/i386/boot/zImage of=/dev/fd0 - And finally use rdev to set the root device: +3.1) Booting from a floppy using syslinux - rdev /dev/fd0 /dev/boot255 + When building kernels, an easy way to create a boot floppy that uses + syslinux is to use the zdisk or bzdisk make targets which use + and bzimage images respectively. Both targets accept the + FDARGS parameter which can be used to set the kernel command line. - You can then remove the dummy device /dev/boot255 again. There - is no real device available for it. - The other two kernel command line parameters cannot be substi- - tuted with rdev. Therefore, using this method the kernel will - by default use RARP and/or BOOTP, and if it gets an answer via - RARP will mount the directory /tftpboot// as its - root. If it got a BOOTP answer the directory name in that answer - is used. + e.g. + make bzdisk FDARGS="root=/dev/nfs" + + Note that the user running this command will need to have + access to the floppy drive device, /dev/fd0 + + For more information on syslinux, including how to create bootdisks + for prebuilt kernels, see http://syslinux.zytor.com/ + + N.B: Previously it was possible to write a kernel directly to + a floppy using dd, configure the boot device using rdev, and + boot using the resulting floppy. Linux no longer supports this + method of booting. + +3.2) Booting from a cdrom using isolinux + + When building kernels, an easy way to create a bootable cdrom that + uses isolinux is to use the isoimage target which uses a bzimage + image. Like zdisk and bzdisk, this target accepts the FDARGS + parameter which can be used to set the kernel command line. + + e.g. + make isoimage FDARGS="root=/dev/nfs" + + The resulting iso image will be arch//boot/image.iso + This can be written to a cdrom using a variety of tools including + cdrecord. + + e.g. + cdrecord dev=ATAPI:1,0,0 arch/i386/boot/image.iso + + For more information on isolinux, including how to create bootdisks + for prebuilt kernels, see http://syslinux.zytor.com/ 3.2) Using LILO - When using LILO you can specify all necessary command line - parameters with the 'append=' command in the LILO configuration - file. However, to use the 'root=' command you also need to - set up a dummy device as described in 3.1 above. For how to use - LILO and its 'append=' command please refer to the LILO - documentation. + When using LILO all the necessary command line parameters may be + specified using the 'append=' directive in the LILO configuration + file. + + However, to use the 'root=' directive you also need to create + a dummy root device, which may be removed after LILO is run. + + mknod /dev/boot255 c 0 255 + + For information on configuring LILO, please refer to its documentation. 3.3) Using GRUB - When you use GRUB, you simply append the parameters after the kernel - specification: "kernel " (without the quotes). + When using GRUB, kernel parameter are simply appended after the kernel + specification: kernel 3.4) Using loadlin - When you want to boot Linux from a DOS command prompt without - having a local hard disk to mount as root, you can use loadlin. - I was told that it works, but haven't used it myself yet. In - general you should be able to create a kernel command line simi- - lar to how LILO is doing it. Please refer to the loadlin docu- - mentation for further information. + loadlin may be used to boot Linux from a DOS command prompt without + requiring a local hard disk to mount as root. This has not been + thoroughly tested by the authors of this document, but in general + it should be possible configure the kernel command line similarly + to the configuration of LILO. + + Please refer to the loadlin documentation for further information. 3.5) Using a boot ROM - This is probably the most elegant way of booting a diskless - client. With a boot ROM the kernel gets loaded using the TFTP - protocol. As far as I know, no commercial boot ROMs yet - support booting Linux over the network, but there are two - free implementations of a boot ROM available on sunsite.unc.edu - and its mirrors. They are called 'netboot-nfs' and 'etherboot'. - Both contain everything you need to boot a diskless Linux client. + This is probably the most elegant way of booting a diskless client. + With a boot ROM the kernel is loaded using the TFTP protocol. The + authors of this document are not aware of any no commercial boot + ROMs that support booting Linux over the network. However, there + are two free implementations of a boot ROM, netboot-nfs and + etherboot, both of which are available on sunsite.unc.edu, and both + of which contain everything you need to boot a diskless Linux client. 3.6) Using pxelinux - Using pxelinux you specify the kernel you built with + Pxelinux may be used to boot linux using the PXE boot loader + which is present on many modern network cards. + + When using pxelinux, the kernel image is specified using "kernel ". The nfsroot parameters are passed to the kernel by adding them to the "append" line. - You may perhaps also want to fine tune the console output, - see Documentation/serial-console.txt for serial console help. + It is common to use serial console in conjunction with pxeliunx, + see Documentation/serial-console.txt for more information. + + For more information on isolinux, including how to create bootdisks + for prebuilt kernels, see http://syslinux.zytor.com/ -- cgit v1.2.3 From eb0e71c7f388bfcb3f74897f23d0cf09310fc05b Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 10 Jul 2006 04:43:59 -0700 Subject: [PATCH] Add Computone IntelliPort Plus serial hotplug support Patch Description: Add "Computone IntelliPort Plus serial" hotplug support patch location: http://www.kernel.org/git/?p=linux/kernel/git/bcollins/ubuntu-dapper.git;a=commitdiff;h=8c36723187c0fa5efe0e5c6a9b1e66ed4b824792 [akpm@osdl.org: cleanup] Cc: Randy Dunlap Signed-off-by: Chuck Short Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/ip2/ip2main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 518ece7ac656..7907ae88c2f4 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -3186,3 +3186,10 @@ ip2trace (unsigned short pn, unsigned char cat, unsigned char label, unsigned lo MODULE_LICENSE("GPL"); + +static struct pci_device_id ip2main_pci_tbl[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_COMPUTONE, PCI_DEVICE_ID_COMPUTONE_IP2EX) }, + { } +}; + +MODULE_DEVICE_TABLE(pci, ip2main_pci_tbl); -- cgit v1.2.3 From 7691030bc9732f7d535522dda78cfdd36716def1 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Mon, 10 Jul 2006 04:43:59 -0700 Subject: [PATCH] Add Specialix IO8+ card support hotplug support Patch Description: Add "Specialix IO8+ card support" hotplug support patch location: http://www.kernel.org/git/?p=linux/kernel/git/bcollins/ubuntu-dapper.git;a=commitdiff;h=d795cfc591bb44f6b3d86d8f054a227cecb44bb4 [akpm@osdl.org: cleanup] Cc: Randy Dunlap Signed-off-by: Chuck Short Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/specialix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index cb2859249d49..a1d303f9a33d 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -2584,6 +2584,12 @@ static void __exit specialix_exit_module(void) func_exit(); } +static struct pci_device_id specialx_pci_tbl[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_SPECIALIX, PCI_DEVICE_ID_SPECIALIX_IO8) }, + { } +}; +MODULE_DEVICE_TABLE(pci, specialx_pci_tbl); + module_init(specialix_init_module); module_exit(specialix_exit_module); -- cgit v1.2.3 From 25e206b54b9a20e63b6f5194aeebfa13d37e015c Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 10 Jul 2006 04:44:00 -0700 Subject: [PATCH] partitions: let partitions inherit policy from disk Change the partition code in fs/partitions/check.c to initialize a newly detected partition's policy field with that of the containing block device (see patch below). My reasoning is that function set_disk_ro() in block/genhd.c modifies the policy field (read-only indicator) of a disk and all contained partitions. When a partition is detected after the call to set_disk_ro(), the policy field of this partition will currently not inherit the disk's policy field. This behavior poses a problem in cases where a block device can be 'logically de- and reactivated' like e.g. the s390 DASD driver because partition detection may run after the policy field has been modified. Signed-off-by: Peter Oberparleiter Acked-by: Al Viro Makes-sense-to: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/partitions/check.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 839634026eb5..51c6a748df49 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -339,6 +339,7 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) p->start_sect = start; p->nr_sects = len; p->partno = part; + p->policy = disk->policy; if (isdigit(disk->kobj.name[strlen(disk->kobj.name)-1])) snprintf(p->kobj.name,KOBJ_NAME_LEN,"%sp%d",disk->kobj.name,part); -- cgit v1.2.3 From 8f72e4028a1ff968000cec4a034f45619fbd7ec4 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 10 Jul 2006 04:44:01 -0700 Subject: [PATCH] fadvise: remove dead comments Cc: "Michael Kerrisk" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/fadvise.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/mm/fadvise.c b/mm/fadvise.c index 0a03357a1f8e..60a5d55e51d9 100644 --- a/mm/fadvise.c +++ b/mm/fadvise.c @@ -23,18 +23,6 @@ /* * POSIX_FADV_WILLNEED could set PG_Referenced, and POSIX_FADV_NOREUSE could * deactivate the pages and clear PG_Referenced. - * - * LINUX_FADV_ASYNC_WRITE: start async writeout of any dirty pages between file - * offsets `offset' and `offset+len' inclusive. Any pages which are currently - * under writeout are skipped, whether or not they are dirty. - * - * LINUX_FADV_WRITE_WAIT: wait upon writeout of any dirty pages between file - * offsets `offset' and `offset+len'. - * - * By combining these two operations the application may do several things: - * - * LINUX_FADV_ASYNC_WRITE: push some or all of the dirty pages at the disk. - * */ asmlinkage long sys_fadvise64_64(int fd, loff_t offset, loff_t len, int advice) { -- cgit v1.2.3 From f9829cceb686f3719215fe43c8593e5f3efe1710 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 10 Jul 2006 04:44:01 -0700 Subject: [PATCH] Minor cleanup to lockdep.c - Use printk formatting for indentation - Don't leave NTFS in the default event filter Signed-off-by: Andi Kleen Acked-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/lockdep.c | 44 ++++++++++++-------------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/kernel/lockdep.c b/kernel/lockdep.c index f32ca78c198d..42af5cc31a49 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -169,22 +169,17 @@ EXPORT_SYMBOL(lockdep_internal); */ static int class_filter(struct lock_class *class) { +#if 0 + /* Example */ if (class->name_version == 1 && - !strcmp(class->name, "&rl->lock")) + !strcmp(class->name, "lockname")) return 1; if (class->name_version == 1 && - !strcmp(class->name, "&ni->mrec_lock")) + !strcmp(class->name, "&struct->lockfield")) return 1; - if (class->name_version == 1 && - !strcmp(class->name, "mft_ni_runlist_lock")) - return 1; - if (class->name_version == 1 && - !strcmp(class->name, "mft_ni_mrec_lock")) - return 1; - if (class->name_version == 1 && - !strcmp(class->name, "&vol->lcnbmp_lock")) - return 1; - return 0; +#endif + /* Allow everything else. 0 would be filter everything else */ + return 1; } #endif @@ -408,23 +403,12 @@ static void lockdep_print_held_locks(struct task_struct *curr) print_lock(curr->held_locks + i); } } -/* - * Helper to print a nice hierarchy of lock dependencies: - */ -static void print_spaces(int nr) -{ - int i; - - for (i = 0; i < nr; i++) - printk(" "); -} static void print_lock_class_header(struct lock_class *class, int depth) { int bit; - print_spaces(depth); - printk("->"); + printk("%*s->", depth, ""); print_lock_name(class); printk(" ops: %lu", class->ops); printk(" {\n"); @@ -433,17 +417,14 @@ static void print_lock_class_header(struct lock_class *class, int depth) if (class->usage_mask & (1 << bit)) { int len = depth; - print_spaces(depth); - len += printk(" %s", usage_str[bit]); + len += printk("%*s %s", depth, "", usage_str[bit]); len += printk(" at:\n"); print_stack_trace(class->usage_traces + bit, len); } } - print_spaces(depth); - printk(" }\n"); + printk("%*s }\n", depth, ""); - print_spaces(depth); - printk(" ... key at: "); + printk("%*s ... key at: ",depth,""); print_ip_sym((unsigned long)class->key); } @@ -463,8 +444,7 @@ static void print_lock_dependencies(struct lock_class *class, int depth) DEBUG_LOCKS_WARN_ON(!entry->class); print_lock_dependencies(entry->class, depth + 1); - print_spaces(depth); - printk(" ... acquired at:\n"); + printk("%*s ... acquired at:\n",depth,""); print_stack_trace(&entry->trace, 2); printk("\n"); } -- cgit v1.2.3 From 5fca80e8b4bf5d69b900115b14342133ce81d79e Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 10 Jul 2006 04:44:02 -0700 Subject: [PATCH] lockdep: add more rwsem.h documentation Add more documentation to rwsem.h. Signed-off-by: Ingo Molnar Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/rwsem.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 658afb37c3f5..7b524b4109a0 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -61,12 +61,25 @@ extern void downgrade_write(struct rw_semaphore *sem); #ifdef CONFIG_DEBUG_LOCK_ALLOC /* - * nested locking: + * nested locking. NOTE: rwsems are not allowed to recurse + * (which occurs if the same task tries to acquire the same + * lock instance multiple times), but multiple locks of the + * same lock class might be taken, if the order of the locks + * is always the same. This ordering rule can be expressed + * to lockdep via the _nested() APIs, but enumerating the + * subclasses that are used. (If the nesting relationship is + * static then another method for expressing nested locking is + * the explicit definition of lock class keys and the use of + * lockdep_set_class() at lock initialization time. + * See Documentation/lockdep-design.txt for more details.) */ extern void down_read_nested(struct rw_semaphore *sem, int subclass); extern void down_write_nested(struct rw_semaphore *sem, int subclass); /* - * Take/release a lock when not the owner will release it: + * Take/release a lock when not the owner will release it. + * + * [ This API should be avoided as much as possible - the + * proper abstraction for this case is completions. ] */ extern void down_read_non_owner(struct rw_semaphore *sem); extern void up_read_non_owner(struct rw_semaphore *sem); -- cgit v1.2.3 From 55794a412fdf9af1744800e5020a4ec6b21e3cdc Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Mon, 10 Jul 2006 04:44:03 -0700 Subject: [PATCH] lockdep: improve debug output Make lockdep print which lock is held, in the "kfree() of a live lock" scenario. Signed-off-by: Arjan van de Ven Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/lockdep.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/lockdep.c b/kernel/lockdep.c index 42af5cc31a49..c1f34addd003 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -2551,7 +2551,7 @@ static inline int in_range(const void *start, const void *addr, const void *end) static void print_freed_lock_bug(struct task_struct *curr, const void *mem_from, - const void *mem_to) + const void *mem_to, struct held_lock *hlock) { if (!debug_locks_off()) return; @@ -2563,6 +2563,7 @@ print_freed_lock_bug(struct task_struct *curr, const void *mem_from, printk( "-------------------------\n"); printk("%s/%d is freeing memory %p-%p, with a lock still held there!\n", curr->comm, curr->pid, mem_from, mem_to-1); + print_lock(hlock); lockdep_print_held_locks(curr); printk("\nstack backtrace:\n"); @@ -2596,7 +2597,7 @@ void debug_check_no_locks_freed(const void *mem_from, unsigned long mem_len) !in_range(mem_from, lock_to, mem_to)) continue; - print_freed_lock_bug(curr, mem_from, mem_to); + print_freed_lock_bug(curr, mem_from, mem_to, hlock); break; } local_irq_restore(flags); -- cgit v1.2.3 From d6d897cec29252b8d0785198cfa6ca16d30c739d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 10 Jul 2006 04:44:04 -0700 Subject: [PATCH] lockdep: core, reduce per-lock class-cache size lockdep_map is embedded into every lock, which blows up data structure sizes all around the kernel. Reduce the class-cache to be for the default class only - that is used in 99.9% of the cases and even if we dont have a class cached, the lookup in the class-hash is lockless. This change reduces the per-lock dep_map overhead by 56 bytes on 64-bit platforms and by 28 bytes on 32-bit platforms. Signed-off-by: Ingo Molnar Cc: Arjan van de Ven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/lockdep.h | 2 +- kernel/lockdep.c | 87 ++++++++++++++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h index 316e0fb8d7b1..c040a8c969aa 100644 --- a/include/linux/lockdep.h +++ b/include/linux/lockdep.h @@ -120,7 +120,7 @@ struct lock_class { */ struct lockdep_map { struct lock_class_key *key; - struct lock_class *class[MAX_LOCKDEP_SUBCLASSES]; + struct lock_class *class_cache; const char *name; }; diff --git a/kernel/lockdep.c b/kernel/lockdep.c index c1f34addd003..9bad17884513 100644 --- a/kernel/lockdep.c +++ b/kernel/lockdep.c @@ -1104,7 +1104,7 @@ extern void __error_too_big_MAX_LOCKDEP_SUBCLASSES(void); * itself, so actual lookup of the hash should be once per lock object. */ static inline struct lock_class * -register_lock_class(struct lockdep_map *lock, unsigned int subclass) +look_up_lock_class(struct lockdep_map *lock, unsigned int subclass) { struct lockdep_subclass_key *key; struct list_head *hash_head; @@ -1148,7 +1148,26 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass) */ list_for_each_entry(class, hash_head, hash_entry) if (class->key == key) - goto out_set; + return class; + + return NULL; +} + +/* + * Register a lock's class in the hash-table, if the class is not present + * yet. Otherwise we look it up. We cache the result in the lock object + * itself, so actual lookup of the hash should be once per lock object. + */ +static inline struct lock_class * +register_lock_class(struct lockdep_map *lock, unsigned int subclass) +{ + struct lockdep_subclass_key *key; + struct list_head *hash_head; + struct lock_class *class; + + class = look_up_lock_class(lock, subclass); + if (likely(class)) + return class; /* * Debug-check: all keys must be persistent! @@ -1163,6 +1182,9 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass) return NULL; } + key = lock->key->subkeys + subclass; + hash_head = classhashentry(key); + __raw_spin_lock(&hash_lock); /* * We have to do the hash-walk again, to avoid races @@ -1209,8 +1231,8 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass) out_unlock_set: __raw_spin_unlock(&hash_lock); -out_set: - lock->class[subclass] = class; + if (!subclass) + lock->class_cache = class; DEBUG_LOCKS_WARN_ON(class->subclass != subclass); @@ -1914,7 +1936,7 @@ void lockdep_init_map(struct lockdep_map *lock, const char *name, } lock->name = name; lock->key = key; - memset(lock->class, 0, sizeof(lock->class[0])*MAX_LOCKDEP_SUBCLASSES); + lock->class_cache = NULL; } EXPORT_SYMBOL_GPL(lockdep_init_map); @@ -1928,8 +1950,8 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, unsigned long ip) { struct task_struct *curr = current; + struct lock_class *class = NULL; struct held_lock *hlock; - struct lock_class *class; unsigned int depth, id; int chain_head = 0; u64 chain_key; @@ -1947,8 +1969,11 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass, return 0; } - class = lock->class[subclass]; - /* not cached yet? */ + if (!subclass) + class = lock->class_cache; + /* + * Not cached yet or subclass? + */ if (unlikely(!class)) { class = register_lock_class(lock, subclass); if (!class) @@ -2449,48 +2474,44 @@ void lockdep_free_key_range(void *start, unsigned long size) void lockdep_reset_lock(struct lockdep_map *lock) { - struct lock_class *class, *next, *entry; + struct lock_class *class, *next; struct list_head *head; unsigned long flags; int i, j; raw_local_irq_save(flags); - __raw_spin_lock(&hash_lock); /* - * Remove all classes this lock has: + * Remove all classes this lock might have: + */ + for (j = 0; j < MAX_LOCKDEP_SUBCLASSES; j++) { + /* + * If the class exists we look it up and zap it: + */ + class = look_up_lock_class(lock, j); + if (class) + zap_class(class); + } + /* + * Debug check: in the end all mapped classes should + * be gone. */ + __raw_spin_lock(&hash_lock); for (i = 0; i < CLASSHASH_SIZE; i++) { head = classhash_table + i; if (list_empty(head)) continue; list_for_each_entry_safe(class, next, head, hash_entry) { - for (j = 0; j < MAX_LOCKDEP_SUBCLASSES; j++) { - entry = lock->class[j]; - if (class == entry) { - zap_class(class); - lock->class[j] = NULL; - break; - } + if (unlikely(class == lock->class_cache)) { + __raw_spin_unlock(&hash_lock); + DEBUG_LOCKS_WARN_ON(1); + goto out_restore; } } } - - /* - * Debug check: in the end all mapped classes should - * be gone. - */ - for (j = 0; j < MAX_LOCKDEP_SUBCLASSES; j++) { - entry = lock->class[j]; - if (!entry) - continue; - __raw_spin_unlock(&hash_lock); - DEBUG_LOCKS_WARN_ON(1); - raw_local_irq_restore(flags); - return; - } - __raw_spin_unlock(&hash_lock); + +out_restore: raw_local_irq_restore(flags); } -- cgit v1.2.3 From f86bf9b7bcc5d325687a8b80da8ee3eb56e02da7 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 10 Jul 2006 04:44:05 -0700 Subject: [PATCH] lockdep: clean up completion initializer in smpboot.c Clean up lockdep on-stack-completion initializer. (This also removes the dependency on waitqueue_lock_key.) Signed-off-by: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/smpboot.c | 4 +--- include/linux/completion.h | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c index b7c705969791..975380207b46 100644 --- a/arch/x86_64/kernel/smpboot.c +++ b/arch/x86_64/kernel/smpboot.c @@ -771,12 +771,10 @@ static int __cpuinit do_boot_cpu(int cpu, int apicid) unsigned long start_rip; struct create_idle c_idle = { .cpu = cpu, - .done = COMPLETION_INITIALIZER(c_idle.done), + .done = COMPLETION_INITIALIZER_ONSTACK(c_idle.done), }; DECLARE_WORK(work, do_fork_idle, &c_idle); - lockdep_set_class(&c_idle.done.wait.lock, &waitqueue_lock_key); - /* allocate memory for gdts of secondary cpus. Hotplug is considered */ if (!cpu_gdt_descr[cpu].address && !(cpu_gdt_descr[cpu].address = get_zeroed_page(GFP_KERNEL))) { diff --git a/include/linux/completion.h b/include/linux/completion.h index 251c41e3ddd5..268c5a4a2bd4 100644 --- a/include/linux/completion.h +++ b/include/linux/completion.h @@ -18,6 +18,9 @@ struct completion { #define COMPLETION_INITIALIZER(work) \ { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) } +#define COMPLETION_INITIALIZER_ONSTACK(work) \ + ({ init_completion(&work); work; }) + #define DECLARE_COMPLETION(work) \ struct completion work = COMPLETION_INITIALIZER(work) @@ -28,7 +31,7 @@ struct completion { */ #ifdef CONFIG_LOCKDEP # define DECLARE_COMPLETION_ONSTACK(work) \ - struct completion work = ({ init_completion(&work); work; }) + struct completion work = COMPLETION_INITIALIZER_ONSTACK(work) #else # define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work) #endif -- cgit v1.2.3 From 1454aed92b6b89cb1fbe3cbecd8ceaa7a122f3b7 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Mon, 10 Jul 2006 04:44:05 -0700 Subject: [PATCH] put a comment at register_die_notifier that the export is used {un}register_die_notifier() is used by kdb... document this so that future "remove dead export" rounds can skip this export. Signed-off-by: Arjan van de Ven Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/traps.c | 4 ++-- arch/x86_64/kernel/traps.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 2bf8b55b91f8..5cfd4f42eeba 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -100,13 +100,13 @@ int register_die_notifier(struct notifier_block *nb) vmalloc_sync_all(); return atomic_notifier_chain_register(&i386die_chain, nb); } -EXPORT_SYMBOL(register_die_notifier); +EXPORT_SYMBOL(register_die_notifier); /* used modular by kdb */ int unregister_die_notifier(struct notifier_block *nb) { return atomic_notifier_chain_unregister(&i386die_chain, nb); } -EXPORT_SYMBOL(unregister_die_notifier); +EXPORT_SYMBOL(unregister_die_notifier); /* used modular by kdb */ static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) { diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 79d05c482072..eb39a2775236 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c @@ -76,13 +76,13 @@ int register_die_notifier(struct notifier_block *nb) vmalloc_sync_all(); return atomic_notifier_chain_register(&die_chain, nb); } -EXPORT_SYMBOL(register_die_notifier); +EXPORT_SYMBOL(register_die_notifier); /* used modular by kdb */ int unregister_die_notifier(struct notifier_block *nb) { return atomic_notifier_chain_unregister(&die_chain, nb); } -EXPORT_SYMBOL(unregister_die_notifier); +EXPORT_SYMBOL(unregister_die_notifier); /* used modular by kdb */ static inline void conditional_sti(struct pt_regs *regs) { -- cgit v1.2.3 From 82a854ec4f46c5fbef11b06bb49078ecc5784a2d Mon Sep 17 00:00:00 2001 From: Urs Thuermann Date: Mon, 10 Jul 2006 04:44:06 -0700 Subject: [PATCH] RCU Documentation fix Updater should use _rcu variant of list_del(). Signed-off-by: Urs Thuermann Acked-by: "Paul E. McKenney" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/RCU/whatisRCU.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt index 4f41a60e5111..318df44259b3 100644 --- a/Documentation/RCU/whatisRCU.txt +++ b/Documentation/RCU/whatisRCU.txt @@ -687,8 +687,9 @@ diff shows how closely related RCU and reader-writer locking can be. + spin_lock(&listmutex); list_for_each_entry(p, head, lp) { if (p->key == key) { - list_del(&p->list); + - list_del(&p->list); - write_unlock(&listmutex); + + list_del_rcu(&p->list); + spin_unlock(&listmutex); + synchronize_rcu(); kfree(p); @@ -736,7 +737,7 @@ Or, for those who prefer a side-by-side listing: 5 write_lock(&listmutex); 5 spin_lock(&listmutex); 6 list_for_each_entry(p, head, lp) { 6 list_for_each_entry(p, head, lp) { 7 if (p->key == key) { 7 if (p->key == key) { - 8 list_del(&p->list); 8 list_del(&p->list); + 8 list_del(&p->list); 8 list_del_rcu(&p->list); 9 write_unlock(&listmutex); 9 spin_unlock(&listmutex); 10 synchronize_rcu(); 10 kfree(p); 11 kfree(p); -- cgit v1.2.3 From 5d8b2ebfa298ec4e6d9fa43e60fb013e8cd963aa Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 10 Jul 2006 04:44:07 -0700 Subject: [PATCH] VFS documentation tweak As I was looking over the get_sb() changes, I stumbled across a little mistake in the documentation updates. Unless we're getting into an interesting new object-oriented realm, I doubt that get_sb() should really return "struct int"... Signed-off-by: Jonathan Corbet Acked-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/Locking | 4 ++-- Documentation/filesystems/vfs.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index d31efbbdfe50..247d7f619aa2 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -142,8 +142,8 @@ see also dquot_operations section. --------------------------- file_system_type --------------------------- prototypes: - struct int (*get_sb) (struct file_system_type *, int, - const char *, void *, struct vfsmount *); + int (*get_sb) (struct file_system_type *, int, + const char *, void *, struct vfsmount *); void (*kill_sb) (struct super_block *); locking rules: may block BKL diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 9d3aed628bc1..1cb7e8be927a 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -113,8 +113,8 @@ members are defined: struct file_system_type { const char *name; int fs_flags; - struct int (*get_sb) (struct file_system_type *, int, - const char *, void *, struct vfsmount *); + int (*get_sb) (struct file_system_type *, int, + const char *, void *, struct vfsmount *); void (*kill_sb) (struct super_block *); struct module *owner; struct file_system_type * next; -- cgit v1.2.3 From 454d6fbc48374be8f53b9bafaa86530cf8eb3bc1 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 10 Jul 2006 04:44:08 -0700 Subject: [PATCH] cdrom: fix bad cgc.buflen assignment The code really means to mask off the high bits, not assign 0xff. Signed-off-by: Jens Axboe Cc: Marcus Meissner Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/cdrom/cdrom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index ca27ee89240b..d239cf8b20bd 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -1837,7 +1837,7 @@ static int dvd_read_bca(struct cdrom_device_info *cdi, dvd_struct *s) init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ); cgc.cmd[0] = GPCMD_READ_DVD_STRUCTURE; cgc.cmd[7] = s->type; - cgc.cmd[9] = cgc.buflen = 0xff; + cgc.cmd[9] = cgc.buflen & 0xff; if ((ret = cdo->generic_packet(cdi, &cgc))) return ret; -- cgit v1.2.3 From 73ca66b97b73257a7d832d502c36fc19fe847809 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Mon, 10 Jul 2006 04:44:09 -0700 Subject: [PATCH] release_firmware() fixes Use release_firmware() to free requested resources. According to Documentation/firmware_class/README the request_firmware() call should be followed by a release_firmware(). Some drivers do not however free the firmware previously allocated with request_firmware(). This patch tries to fix this by making sure that release_firmware() is used as expected. Signed-off-by: Magnus Damm Acked-by: Marcel Holtmann Cc: Mauro Carvalho Chehab Cc: "John W. Linville" Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/bluetooth/bcm203x.c | 1 + drivers/media/dvb/frontends/nxt200x.c | 4 ++-- drivers/media/dvb/frontends/or51211.c | 2 +- drivers/media/dvb/frontends/sp8870.c | 2 +- drivers/media/dvb/frontends/sp887x.c | 2 +- drivers/media/video/cx88/cx88-blackbird.c | 3 +++ drivers/net/wireless/spectrum_cs.c | 37 +++++++++++++------------------ 7 files changed, 25 insertions(+), 26 deletions(-) diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index 6f67141f4de0..13ba729cdd57 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -234,6 +234,7 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id data->fw_data = kmalloc(firmware->size, GFP_KERNEL); if (!data->fw_data) { BT_ERR("Can't allocate memory for firmware image"); + release_firmware(firmware); usb_free_urb(data->urb); kfree(data->buffer); kfree(data); diff --git a/drivers/media/dvb/frontends/nxt200x.c b/drivers/media/dvb/frontends/nxt200x.c index 55671cb5255e..87c286ee6a00 100644 --- a/drivers/media/dvb/frontends/nxt200x.c +++ b/drivers/media/dvb/frontends/nxt200x.c @@ -896,9 +896,9 @@ static int nxt2002_init(struct dvb_frontend* fe) } ret = nxt2002_load_firmware(fe, fw); + release_firmware(fw); if (ret) { printk("nxt2002: Writing firmware to device failed\n"); - release_firmware(fw); return ret; } printk("nxt2002: Firmware upload complete\n"); @@ -960,9 +960,9 @@ static int nxt2004_init(struct dvb_frontend* fe) } ret = nxt2004_load_firmware(fe, fw); + release_firmware(fw); if (ret) { printk("nxt2004: Writing firmware to device failed\n"); - release_firmware(fw); return ret; } printk("nxt2004: Firmware upload complete\n"); diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c index 26bed616fabe..2bf124b53689 100644 --- a/drivers/media/dvb/frontends/or51211.c +++ b/drivers/media/dvb/frontends/or51211.c @@ -437,10 +437,10 @@ static int or51211_init(struct dvb_frontend* fe) } ret = or51211_load_firmware(fe, fw); + release_firmware(fw); if (ret) { printk(KERN_WARNING "or51211: Writing firmware to " "device failed!\n"); - release_firmware(fw); return ret; } printk(KERN_INFO "or51211: Firmware upload complete.\n"); diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c index 44ec5b9a4695..d98fd5c2e13e 100644 --- a/drivers/media/dvb/frontends/sp8870.c +++ b/drivers/media/dvb/frontends/sp8870.c @@ -318,7 +318,6 @@ static int sp8870_init (struct dvb_frontend* fe) printk("sp8870: waiting for firmware upload (%s)...\n", SP8870_DEFAULT_FIRMWARE); if (state->config->request_firmware(fe, &fw, SP8870_DEFAULT_FIRMWARE)) { printk("sp8870: no firmware upload (timeout or file not found?)\n"); - release_firmware(fw); return -EIO; } @@ -327,6 +326,7 @@ static int sp8870_init (struct dvb_frontend* fe) release_firmware(fw); return -EIO; } + release_firmware(fw); printk("sp8870: firmware upload complete\n"); /* enable TS output and interface pins */ diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c index b0a2b02f6608..5c2f8f4e0ae5 100644 --- a/drivers/media/dvb/frontends/sp887x.c +++ b/drivers/media/dvb/frontends/sp887x.c @@ -520,9 +520,9 @@ static int sp887x_init(struct dvb_frontend* fe) } ret = sp887x_initial_setup(fe, fw); + release_firmware(fw); if (ret) { printk("sp887x: writing firmware to device failed\n"); - release_firmware(fw); return ret; } printk("sp887x: firmware upload complete\n"); diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 349632b48e93..b60177f173c3 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -453,11 +453,13 @@ static int blackbird_load_firmware(struct cx8802_dev *dev) if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n", firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); + release_firmware(firmware); return -1; } if (0 != memcmp(firmware->data, magic, 8)) { dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n"); + release_firmware(firmware); return -1; } @@ -478,6 +480,7 @@ static int blackbird_load_firmware(struct cx8802_dev *dev) } if (checksum) { dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n"); + release_firmware(firmware); return -1; } release_firmware(firmware); diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c index 15465278c789..7f78b7801fb3 100644 --- a/drivers/net/wireless/spectrum_cs.c +++ b/drivers/net/wireless/spectrum_cs.c @@ -34,8 +34,6 @@ #include "orinoco.h" -static unsigned char *primsym; -static unsigned char *secsym; static const char primary_fw_name[] = "symbol_sp24t_prim_fw"; static const char secondary_fw_name[] = "symbol_sp24t_sec_fw"; @@ -440,7 +438,7 @@ spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block) */ static int spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, - const unsigned char *image) + const unsigned char *image, int secondary) { int ret; const unsigned char *ptr; @@ -455,7 +453,7 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, first_block = (const struct dblock *) ptr; /* Read the PDA */ - if (image != primsym) { + if (secondary) { ret = spectrum_read_pda(hw, pda, sizeof(pda)); if (ret) return ret; @@ -472,7 +470,7 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, return ret; /* Write the PDA to the adapter */ - if (image != primsym) { + if (secondary) { ret = spectrum_apply_pda(hw, first_block, pda); if (ret) return ret; @@ -487,7 +485,7 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, ret = hermes_init(hw); /* hermes_reset() should return 0 with the secondary firmware */ - if (image != primsym && ret != 0) + if (secondary && ret != 0) return -ENODEV; /* And this should work with any firmware */ @@ -509,33 +507,30 @@ spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link) const struct firmware *fw_entry; if (request_firmware(&fw_entry, primary_fw_name, - &handle_to_dev(link)) == 0) { - primsym = fw_entry->data; - } else { + &handle_to_dev(link)) != 0) { printk(KERN_ERR PFX "Cannot find firmware: %s\n", primary_fw_name); return -ENOENT; } - if (request_firmware(&fw_entry, secondary_fw_name, - &handle_to_dev(link)) == 0) { - secsym = fw_entry->data; - } else { - printk(KERN_ERR PFX "Cannot find firmware: %s\n", - secondary_fw_name); - return -ENOENT; - } - /* Load primary firmware */ - ret = spectrum_dl_image(hw, link, primsym); + ret = spectrum_dl_image(hw, link, fw_entry->data, 0); + release_firmware(fw_entry); if (ret) { printk(KERN_ERR PFX "Primary firmware download failed\n"); return ret; } - /* Load secondary firmware */ - ret = spectrum_dl_image(hw, link, secsym); + if (request_firmware(&fw_entry, secondary_fw_name, + &handle_to_dev(link)) != 0) { + printk(KERN_ERR PFX "Cannot find firmware: %s\n", + secondary_fw_name); + return -ENOENT; + } + /* Load secondary firmware */ + ret = spectrum_dl_image(hw, link, fw_entry->data, 1); + release_firmware(fw_entry); if (ret) { printk(KERN_ERR PFX "Secondary firmware download failed\n"); } -- cgit v1.2.3 From acbf8bd738f1357e0e3b97b918bef5be272b176e Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Mon, 10 Jul 2006 04:44:11 -0700 Subject: [PATCH] Updates CREDITS file Updates my personal entry in the CREDITS file. Signed-off-by: Luiz Fernando N. Capitulino Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- CREDITS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CREDITS b/CREDITS index 66b9e7a9abff..29be6d1fdf49 100644 --- a/CREDITS +++ b/CREDITS @@ -528,11 +528,11 @@ S: Oxford S: United Kingdom N: Luiz Fernando N. Capitulino -E: lcapitulino@terra.com.br -E: lcapitulino@prefeitura.sp.gov.br -W: http://www.telecentros.sp.gov.br -D: Little fixes and a lot of janitorial work -S: E-GOV Telecentros SP +E: lcapitulino@mandriva.com.br +E: lcapitulino@gmail.com +W: http://www.cpu.eti.br +D: misc kernel hacking +S: Mandriva S: Brazil N: Remy Card -- cgit v1.2.3 From 67eb5db5874076db01febed5a1a9281628fa9fb4 Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Mon, 10 Jul 2006 04:44:11 -0700 Subject: [PATCH] hisax: fix usage of __init* Fix the warnings about the section mismatches for __init* in the HiSax driver. Signed-off-by: Karsten Keil Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/isdn/hisax/asuscom.c | 6 +++--- drivers/isdn/hisax/avm_a1.c | 2 +- drivers/isdn/hisax/avm_pci.c | 8 ++++---- drivers/isdn/hisax/bkm_a4t.c | 4 ++-- drivers/isdn/hisax/bkm_a8.c | 16 ++++++++-------- drivers/isdn/hisax/config.c | 2 +- drivers/isdn/hisax/diva.c | 14 +++++++------- drivers/isdn/hisax/enternow_pci.c | 4 ++-- drivers/isdn/hisax/gazel.c | 8 ++++---- drivers/isdn/hisax/hfc4s8s_l1.c | 2 +- drivers/isdn/hisax/hfc_2bds0.c | 4 ++-- drivers/isdn/hisax/hfc_2bs0.c | 4 ++-- drivers/isdn/hisax/hfc_pci.c | 6 +++--- drivers/isdn/hisax/hfcscard.c | 6 +++--- drivers/isdn/hisax/icc.c | 8 ++++---- drivers/isdn/hisax/icc.h | 2 +- drivers/isdn/hisax/ipacx.c | 18 +++++++++--------- drivers/isdn/hisax/isurf.c | 4 ++-- drivers/isdn/hisax/ix1_micro.c | 6 +++--- drivers/isdn/hisax/jade.c | 6 +++--- drivers/isdn/hisax/mic.c | 2 +- drivers/isdn/hisax/netjet.c | 2 +- drivers/isdn/hisax/niccy.c | 4 ++-- drivers/isdn/hisax/nj_s.c | 4 ++-- drivers/isdn/hisax/nj_u.c | 4 ++-- drivers/isdn/hisax/s0box.c | 2 +- drivers/isdn/hisax/saphir.c | 2 +- drivers/isdn/hisax/sportster.c | 4 ++-- drivers/isdn/hisax/teleint.c | 2 +- drivers/isdn/hisax/teles0.c | 2 +- drivers/isdn/hisax/telespci.c | 4 ++-- drivers/isdn/hisax/w6692.c | 10 +++++----- 32 files changed, 86 insertions(+), 86 deletions(-) diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c index a98c5e38bbbc..93ff941c48f1 100644 --- a/drivers/isdn/hisax/asuscom.c +++ b/drivers/isdn/hisax/asuscom.c @@ -297,7 +297,7 @@ Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg) } #ifdef __ISAPNP__ -static struct isapnp_device_id asus_ids[] __initdata = { +static struct isapnp_device_id asus_ids[] __devinitdata = { { ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688), ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688), (unsigned long) "Asus1688 PnP" }, @@ -313,11 +313,11 @@ static struct isapnp_device_id asus_ids[] __initdata = { { 0, } }; -static struct isapnp_device_id *ipid __initdata = &asus_ids[0]; +static struct isapnp_device_id *ipid __devinitdata = &asus_ids[0]; static struct pnp_card *pnp_c __devinitdata = NULL; #endif -int __init +int __devinit setup_asuscom(struct IsdnCard *card) { int bytecnt; diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c index 9a8b02557ff9..729e906bdc61 100644 --- a/drivers/isdn/hisax/avm_a1.c +++ b/drivers/isdn/hisax/avm_a1.c @@ -178,7 +178,7 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __devinit setup_avm_a1(struct IsdnCard *card) { u_char val; diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c index 04f5917bf5a1..369afd3a3a4b 100644 --- a/drivers/isdn/hisax/avm_pci.c +++ b/drivers/isdn/hisax/avm_pci.c @@ -639,7 +639,7 @@ clear_pending_hdlc_ints(struct IsdnCardState *cs) } #endif /* 0 */ -static void __init +static void inithdlc(struct IsdnCardState *cs) { cs->bcs[0].BC_SetStack = setstack_hdlc; @@ -727,13 +727,13 @@ AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) } #ifdef CONFIG_PCI -static struct pci_dev *dev_avm __initdata = NULL; +static struct pci_dev *dev_avm __devinitdata = NULL; #endif #ifdef __ISAPNP__ -static struct pnp_card *pnp_avm_c __initdata = NULL; +static struct pnp_card *pnp_avm_c __devinitdata = NULL; #endif -int __init +int __devinit setup_avm_pcipnp(struct IsdnCard *card) { u_int val, ver; diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c index 3cf1f2421789..87a630128a6c 100644 --- a/drivers/isdn/hisax/bkm_a4t.c +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -255,9 +255,9 @@ BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return (0); } -static struct pci_dev *dev_a4t __initdata = NULL; +static struct pci_dev *dev_a4t __devinitdata = NULL; -int __init +int __devinit setup_bkm_a4t(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c index 15681f3e73b8..dae090a9a489 100644 --- a/drivers/isdn/hisax/bkm_a8.c +++ b/drivers/isdn/hisax/bkm_a8.c @@ -260,7 +260,7 @@ BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) return (0); } -static int __init +static int __devinit sct_alloc_io(u_int adr, u_int len) { if (!request_region(adr, len, "scitel")) { @@ -272,16 +272,16 @@ sct_alloc_io(u_int adr, u_int len) return(0); } -static struct pci_dev *dev_a8 __initdata = NULL; -static u16 sub_vendor_id __initdata = 0; -static u16 sub_sys_id __initdata = 0; -static u_char pci_bus __initdata = 0; -static u_char pci_device_fn __initdata = 0; -static u_char pci_irq __initdata = 0; +static struct pci_dev *dev_a8 __devinitdata = NULL; +static u16 sub_vendor_id __devinitdata = 0; +static u16 sub_sys_id __devinitdata = 0; +static u_char pci_bus __devinitdata = 0; +static u_char pci_device_fn __devinitdata = 0; +static u_char pci_irq __devinitdata = 0; #endif /* CONFIG_PCI */ -int __init +int __devinit setup_sct_quadro(struct IsdnCard *card) { #ifdef CONFIG_PCI diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 5333be5d2c47..e10350360f2f 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1875,7 +1875,7 @@ static void EChannel_proc_rcv(struct hisax_d_if *d_if) #ifdef CONFIG_PCI #include -static struct pci_device_id hisax_pci_tbl[] __initdata = { +static struct pci_device_id hisax_pci_tbl[] __devinitdata = { #ifdef CONFIG_HISAX_FRITZPCI {PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID}, #endif diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c index 323a02ef3844..e294fa3918f3 100644 --- a/drivers/isdn/hisax/diva.c +++ b/drivers/isdn/hisax/diva.c @@ -887,13 +887,13 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -static struct pci_dev *dev_diva __initdata = NULL; -static struct pci_dev *dev_diva_u __initdata = NULL; -static struct pci_dev *dev_diva201 __initdata = NULL; -static struct pci_dev *dev_diva202 __initdata = NULL; +static struct pci_dev *dev_diva __devinitdata = NULL; +static struct pci_dev *dev_diva_u __devinitdata = NULL; +static struct pci_dev *dev_diva201 __devinitdata = NULL; +static struct pci_dev *dev_diva202 __devinitdata = NULL; #ifdef __ISAPNP__ -static struct isapnp_device_id diva_ids[] __initdata = { +static struct isapnp_device_id diva_ids[] __devinitdata = { { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51), (unsigned long) "Diva picola" }, @@ -915,12 +915,12 @@ static struct isapnp_device_id diva_ids[] __initdata = { { 0, } }; -static struct isapnp_device_id *ipid __initdata = &diva_ids[0]; +static struct isapnp_device_id *ipid __devinitdata = &diva_ids[0]; static struct pnp_card *pnp_c __devinitdata = NULL; #endif -int __init +int __devinit setup_diva(struct IsdnCard *card) { int bytecnt = 8; diff --git a/drivers/isdn/hisax/enternow_pci.c b/drivers/isdn/hisax/enternow_pci.c index 8fcbe2e7d765..76c7d29d1b2f 100644 --- a/drivers/isdn/hisax/enternow_pci.c +++ b/drivers/isdn/hisax/enternow_pci.c @@ -301,10 +301,10 @@ enpci_interrupt(int intno, void *dev_id, struct pt_regs *regs) } -static struct pci_dev *dev_netjet __initdata = NULL; +static struct pci_dev *dev_netjet __devinitdata = NULL; /* called by config.c */ -int __init +int __devinit setup_enternow_pci(struct IsdnCard *card) { int bytecnt; diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c index 3e7d92303582..fe2937267777 100644 --- a/drivers/isdn/hisax/gazel.c +++ b/drivers/isdn/hisax/gazel.c @@ -484,7 +484,7 @@ reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs) return 1; } -static int __init +static int __devinit setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs) { printk(KERN_INFO "Gazel: ISA PnP card automatic recognition\n"); @@ -532,9 +532,9 @@ setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs) return (0); } -static struct pci_dev *dev_tel __initdata = NULL; +static struct pci_dev *dev_tel __devinitdata = NULL; -static int __init +static int __devinit setup_gazelpci(struct IsdnCardState *cs) { u_int pci_ioaddr0 = 0, pci_ioaddr1 = 0; @@ -621,7 +621,7 @@ setup_gazelpci(struct IsdnCardState *cs) return (0); } -int __init +int __devinit setup_gazel(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c index 0f967b3df79d..3a5ca8a68fc4 100644 --- a/drivers/isdn/hisax/hfc4s8s_l1.c +++ b/drivers/isdn/hisax/hfc4s8s_l1.c @@ -1703,7 +1703,7 @@ hfc4s8s_module_init(void) /* driver module exit : */ /* release the HFC-4s/8s hardware */ /*************************************/ -static void +static void __exit hfc4s8s_module_exit(void) { pci_unregister_driver(&hfc4s8s_driver); diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c index 637a261c9312..6360e8214720 100644 --- a/drivers/isdn/hisax/hfc_2bds0.c +++ b/drivers/isdn/hisax/hfc_2bds0.c @@ -1015,7 +1015,7 @@ hfc_dbusy_timer(struct IsdnCardState *cs) { } -static unsigned int __init +static unsigned int *init_send_hfcd(int cnt) { int i, *send; @@ -1030,7 +1030,7 @@ static unsigned int __init return(send); } -void __init +void init2bds0(struct IsdnCardState *cs) { cs->setstack_d = setstack_hfcd; diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c index c964539cc43e..d0520ad30677 100644 --- a/drivers/isdn/hisax/hfc_2bs0.c +++ b/drivers/isdn/hisax/hfc_2bs0.c @@ -551,7 +551,7 @@ setstack_hfc(struct PStack *st, struct BCState *bcs) return (0); } -static void __init +static void init_send(struct BCState *bcs) { int i; @@ -565,7 +565,7 @@ init_send(struct BCState *bcs) bcs->hw.hfc.send[i] = 0x1fff; } -void __init +void inithfc(struct IsdnCardState *cs) { init_send(&cs->bcs[0]); diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c index 7241e73879ba..1df60ca9481f 100644 --- a/drivers/isdn/hisax/hfc_pci.c +++ b/drivers/isdn/hisax/hfc_pci.c @@ -1581,7 +1581,7 @@ hfcpci_bh(struct IsdnCardState *cs) /********************************/ /* called for card init message */ /********************************/ -static void __init +static void inithfcpci(struct IsdnCardState *cs) { cs->bcs[0].BC_SetStack = setstack_2b; @@ -1638,11 +1638,11 @@ hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) /* this variable is used as card index when more than one cards are present */ -static struct pci_dev *dev_hfcpci __initdata = NULL; +static struct pci_dev *dev_hfcpci __devinitdata = NULL; #endif /* CONFIG_PCI */ -int __init +int __devinit setup_hfcpci(struct IsdnCard *card) { u_long flags; diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c index 86ab1c13f6b1..4e7f472877e9 100644 --- a/drivers/isdn/hisax/hfcscard.c +++ b/drivers/isdn/hisax/hfcscard.c @@ -139,7 +139,7 @@ hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg) } #ifdef __ISAPNP__ -static struct isapnp_device_id hfc_ids[] __initdata = { +static struct isapnp_device_id hfc_ids[] __devinitdata = { { ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114), ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114), (unsigned long) "Acer P10" }, @@ -164,11 +164,11 @@ static struct isapnp_device_id hfc_ids[] __initdata = { { 0, } }; -static struct isapnp_device_id *ipid __initdata = &hfc_ids[0]; +static struct isapnp_device_id *ipid __devinitdata = &hfc_ids[0]; static struct pnp_card *pnp_c __devinitdata = NULL; #endif -int __init +int __devinit setup_hfcs(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/icc.c b/drivers/isdn/hisax/icc.c index c615752b96aa..2cf7b665609e 100644 --- a/drivers/isdn/hisax/icc.c +++ b/drivers/isdn/hisax/icc.c @@ -24,10 +24,10 @@ #define DBUSY_TIMER_VALUE 80 #define ARCOFI_USE 0 -static char *ICCVer[] __initdata = +static char *ICCVer[] = {"2070 A1/A3", "2070 B1", "2070 B2/B3", "2070 V2.4"}; -void __init +void ICCVersion(struct IsdnCardState *cs, char *s) { int val; @@ -613,7 +613,7 @@ dbusy_timer_handler(struct IsdnCardState *cs) } } -void __init +void initicc(struct IsdnCardState *cs) { cs->setstack_d = setstack_icc; @@ -646,7 +646,7 @@ initicc(struct IsdnCardState *cs) ph_command(cs, ICC_CMD_DI); } -void __init +void clear_pending_icc_ints(struct IsdnCardState *cs) { int val, eval; diff --git a/drivers/isdn/hisax/icc.h b/drivers/isdn/hisax/icc.h index b3bb3d5de532..e7f593967e43 100644 --- a/drivers/isdn/hisax/icc.h +++ b/drivers/isdn/hisax/icc.h @@ -65,7 +65,7 @@ #define ICC_IND_AIL 0xE #define ICC_IND_DC 0xF -extern void __init ICCVersion(struct IsdnCardState *cs, char *s); +extern void ICCVersion(struct IsdnCardState *cs, char *s); extern void initicc(struct IsdnCardState *cs); extern void icc_interrupt(struct IsdnCardState *cs, u_char val); extern void clear_pending_icc_ints(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/ipacx.c b/drivers/isdn/hisax/ipacx.c index df5fc92a89b2..00afd5538909 100644 --- a/drivers/isdn/hisax/ipacx.c +++ b/drivers/isdn/hisax/ipacx.c @@ -38,8 +38,8 @@ static void dbusy_timer_handler(struct IsdnCardState *cs); static void dch_empty_fifo(struct IsdnCardState *cs, int count); static void dch_fill_fifo(struct IsdnCardState *cs); static inline void dch_int(struct IsdnCardState *cs); -static void __devinit dch_setstack(struct PStack *st, struct IsdnCardState *cs); -static void __devinit dch_init(struct IsdnCardState *cs); +static void dch_setstack(struct PStack *st, struct IsdnCardState *cs); +static void dch_init(struct IsdnCardState *cs); static void bch_l2l1(struct PStack *st, int pr, void *arg); static void bch_empty_fifo(struct BCState *bcs, int count); static void bch_fill_fifo(struct BCState *bcs); @@ -48,8 +48,8 @@ static void bch_mode(struct BCState *bcs, int mode, int bc); static void bch_close_state(struct BCState *bcs); static int bch_open_state(struct IsdnCardState *cs, struct BCState *bcs); static int bch_setstack(struct PStack *st, struct BCState *bcs); -static void __devinit bch_init(struct IsdnCardState *cs, int hscx); -static void __init clear_pending_ints(struct IsdnCardState *cs); +static void bch_init(struct IsdnCardState *cs, int hscx); +static void clear_pending_ints(struct IsdnCardState *cs); //---------------------------------------------------------- // Issue Layer 1 command to chip @@ -408,7 +408,7 @@ dch_int(struct IsdnCardState *cs) //---------------------------------------------------------- //---------------------------------------------------------- -static void __devinit +static void dch_setstack(struct PStack *st, struct IsdnCardState *cs) { st->l1.l1hw = dch_l2l1; @@ -416,7 +416,7 @@ dch_setstack(struct PStack *st, struct IsdnCardState *cs) //---------------------------------------------------------- //---------------------------------------------------------- -static void __devinit +static void dch_init(struct IsdnCardState *cs) { printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n"); @@ -823,7 +823,7 @@ bch_setstack(struct PStack *st, struct BCState *bcs) //---------------------------------------------------------- //---------------------------------------------------------- -static void __devinit +static void bch_init(struct IsdnCardState *cs, int hscx) { cs->bcs[hscx].BC_SetStack = bch_setstack; @@ -861,7 +861,7 @@ interrupt_ipacx(struct IsdnCardState *cs) //---------------------------------------------------------- // Clears chip interrupt status //---------------------------------------------------------- -static void __init +static void clear_pending_ints(struct IsdnCardState *cs) { int ista; @@ -883,7 +883,7 @@ clear_pending_ints(struct IsdnCardState *cs) // Does chip configuration work // Work to do depends on bit mask in part //---------------------------------------------------------- -void __init +void init_ipacx(struct IsdnCardState *cs, int part) { if (part &1) { // initialise chip diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c index 33747afc984d..715a1a8cd694 100644 --- a/drivers/isdn/hisax/isurf.c +++ b/drivers/isdn/hisax/isurf.c @@ -196,10 +196,10 @@ isurf_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) { } #ifdef __ISAPNP__ -static struct pnp_card *pnp_c __initdata = NULL; +static struct pnp_card *pnp_c __devinitdata = NULL; #endif -int __init +int __devinit setup_isurf(struct IsdnCard *card) { int ver; diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c index 908a7e144421..39717506c678 100644 --- a/drivers/isdn/hisax/ix1_micro.c +++ b/drivers/isdn/hisax/ix1_micro.c @@ -210,7 +210,7 @@ ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg) } #ifdef __ISAPNP__ -static struct isapnp_device_id itk_ids[] __initdata = { +static struct isapnp_device_id itk_ids[] __devinitdata = { { ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25), ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25), (unsigned long) "ITK micro 2" }, @@ -220,12 +220,12 @@ static struct isapnp_device_id itk_ids[] __initdata = { { 0, } }; -static struct isapnp_device_id *ipid __initdata = &itk_ids[0]; +static struct isapnp_device_id *ipid __devinitdata = &itk_ids[0]; static struct pnp_card *pnp_c __devinitdata = NULL; #endif -int __init +int __devinit setup_ix1micro(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c index 2659fecc2674..43d61d1bc5b6 100644 --- a/drivers/isdn/hisax/jade.c +++ b/drivers/isdn/hisax/jade.c @@ -19,7 +19,7 @@ #include -int __init +int JadeVersion(struct IsdnCardState *cs, char *s) { int ver,i; @@ -253,7 +253,7 @@ setstack_jade(struct PStack *st, struct BCState *bcs) return (0); } -void __init +void clear_pending_jade_ints(struct IsdnCardState *cs) { int val; @@ -279,7 +279,7 @@ clear_pending_jade_ints(struct IsdnCardState *cs) cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0xF8); } -void __init +void initjade(struct IsdnCardState *cs) { cs->bcs[0].BC_SetStack = setstack_jade; diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c index fe11f226b285..8c82519593a8 100644 --- a/drivers/isdn/hisax/mic.c +++ b/drivers/isdn/hisax/mic.c @@ -189,7 +189,7 @@ mic_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __devinit setup_mic(struct IsdnCard *card) { int bytecnt; diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c index 47a47ef0968b..38f648f9b0ed 100644 --- a/drivers/isdn/hisax/netjet.c +++ b/drivers/isdn/hisax/netjet.c @@ -909,7 +909,7 @@ setstack_tiger(struct PStack *st, struct BCState *bcs) } -void __init +void inittiger(struct IsdnCardState *cs) { if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_TXSIZE * sizeof(unsigned int), diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c index 79a97b1c3812..489022bdef7b 100644 --- a/drivers/isdn/hisax/niccy.c +++ b/drivers/isdn/hisax/niccy.c @@ -232,12 +232,12 @@ niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -static struct pci_dev *niccy_dev __initdata = NULL; +static struct pci_dev *niccy_dev __devinitdata = NULL; #ifdef __ISAPNP__ static struct pnp_card *pnp_c __devinitdata = NULL; #endif -int __init +int __devinit setup_niccy(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c index e5b900a6fa0b..80025fd890f4 100644 --- a/drivers/isdn/hisax/nj_s.c +++ b/drivers/isdn/hisax/nj_s.c @@ -148,9 +148,9 @@ NETjet_S_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -static struct pci_dev *dev_netjet __initdata = NULL; +static struct pci_dev *dev_netjet __devinitdata = NULL; -int __init +int __devinit setup_netjet_s(struct IsdnCard *card) { int bytecnt,cfg; diff --git a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c index 7002b09936d4..37497162d539 100644 --- a/drivers/isdn/hisax/nj_u.c +++ b/drivers/isdn/hisax/nj_u.c @@ -128,9 +128,9 @@ NETjet_U_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -static struct pci_dev *dev_netjet __initdata = NULL; +static struct pci_dev *dev_netjet __devinitdata = NULL; -int __init +int __devinit setup_netjet_u(struct IsdnCard *card) { int bytecnt; diff --git a/drivers/isdn/hisax/s0box.c b/drivers/isdn/hisax/s0box.c index 7b63085ea6e5..e76042d323ea 100644 --- a/drivers/isdn/hisax/s0box.c +++ b/drivers/isdn/hisax/s0box.c @@ -211,7 +211,7 @@ S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __devinit setup_s0box(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c index 821776e1561a..d943d365890b 100644 --- a/drivers/isdn/hisax/saphir.c +++ b/drivers/isdn/hisax/saphir.c @@ -241,7 +241,7 @@ saphir_card_msg(struct IsdnCardState *cs, int mt, void *arg) } -int __init +int __devinit setup_saphir(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c index cdf35dc564c4..a49b694eb730 100644 --- a/drivers/isdn/hisax/sportster.c +++ b/drivers/isdn/hisax/sportster.c @@ -184,7 +184,7 @@ Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -static int __init +static int __devinit get_io_range(struct IsdnCardState *cs) { int i, j, adr; @@ -209,7 +209,7 @@ get_io_range(struct IsdnCardState *cs) } } -int __init +int __devinit setup_sportster(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c index a2b1816af37a..e94dc6f5bd62 100644 --- a/drivers/isdn/hisax/teleint.c +++ b/drivers/isdn/hisax/teleint.c @@ -261,7 +261,7 @@ TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __devinit setup_TeleInt(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c index 2b7df8f98233..f94af0930a17 100644 --- a/drivers/isdn/hisax/teles0.c +++ b/drivers/isdn/hisax/teles0.c @@ -265,7 +265,7 @@ Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -int __init +int __devinit setup_teles0(struct IsdnCard *card) { u_char val; diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c index 9382cdf04d8d..dca446865f24 100644 --- a/drivers/isdn/hisax/telespci.c +++ b/drivers/isdn/hisax/telespci.c @@ -284,9 +284,9 @@ TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg) return(0); } -static struct pci_dev *dev_tel __initdata = NULL; +static struct pci_dev *dev_tel __devinitdata = NULL; -int __init +int __devinit setup_telespci(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c index 6c68419c05ff..0595293b8659 100644 --- a/drivers/isdn/hisax/w6692.c +++ b/drivers/isdn/hisax/w6692.c @@ -44,11 +44,11 @@ static const char *w6692_revision = "$Revision: 1.18.2.4 $"; #define DBUSY_TIMER_VALUE 80 -static char *W6692Ver[] __initdata = +static char *W6692Ver[] = {"W6692 V00", "W6692 V01", "W6692 V10", "W6692 V11"}; -static void __init +static void W6692Version(struct IsdnCardState *cs, char *s) { int val; @@ -897,7 +897,7 @@ static void resetW6692(struct IsdnCardState *cs) } } -static void __init initW6692(struct IsdnCardState *cs, int part) +static void initW6692(struct IsdnCardState *cs, int part) { if (part & 1) { cs->setstack_d = setstack_W6692; @@ -992,9 +992,9 @@ w6692_card_msg(struct IsdnCardState *cs, int mt, void *arg) static int id_idx ; -static struct pci_dev *dev_w6692 __initdata = NULL; +static struct pci_dev *dev_w6692 __devinitdata = NULL; -int __init +int __devinit setup_w6692(struct IsdnCard *card) { struct IsdnCardState *cs = card->cs; -- cgit v1.2.3 From a8f340e394ff30b79ab5b03c67ab4c94b2ac3646 Mon Sep 17 00:00:00 2001 From: Jon Smirl Date: Mon, 10 Jul 2006 04:44:12 -0700 Subject: [PATCH] vt: Remove VT-specific declarations and definitions from tty.h MAX_NR_CONSOLES, fg_console, want_console and last_console are more of a function of the VT layer than the TTY one. Moving these to vt.h and vt_kern.h allows all of the framebuffer and VT console drivers to remove their dependency on tty.h. [akpm@osdl.org: fix alpha build] Signed-off-by: Jon Smirl Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/alpha/kernel/process.c | 1 + arch/parisc/kernel/pdc_cons.c | 2 ++ drivers/video/68328fb.c | 1 - drivers/video/S3triofb.c | 1 - drivers/video/amifb.c | 1 - drivers/video/arcfb.c | 1 - drivers/video/asiliantfb.c | 1 - drivers/video/atafb.c | 1 - drivers/video/aty/aty128fb.c | 1 - drivers/video/aty/radeon_base.c | 1 - drivers/video/chipsfb.c | 1 - drivers/video/cirrusfb.c | 1 - drivers/video/console/dummycon.c | 1 - drivers/video/console/fbcon.c | 1 - drivers/video/console/mdacon.c | 1 - drivers/video/console/newport_con.c | 1 - drivers/video/console/promcon.c | 1 - drivers/video/console/softcursor.c | 1 - drivers/video/console/sticon.c | 1 - drivers/video/console/vgacon.c | 1 - drivers/video/controlfb.c | 1 - drivers/video/cyber2000fb.c | 1 - drivers/video/cyberfb.c | 1 - drivers/video/dnfb.c | 1 - drivers/video/epson1355fb.c | 1 - drivers/video/fbcmap.c | 1 - drivers/video/fbmem.c | 2 +- drivers/video/fbmon.c | 1 - drivers/video/g364fb.c | 1 - drivers/video/geode/gx1fb_core.c | 1 - drivers/video/geode/gxfb_core.c | 1 - drivers/video/hgafb.c | 1 - drivers/video/hitfb.c | 1 - drivers/video/hpfb.c | 1 - drivers/video/i810/i810_main.c | 1 - drivers/video/igafb.c | 1 - drivers/video/imacfb.c | 2 +- drivers/video/imsttfb.c | 1 - drivers/video/intelfb/intelfbdrv.c | 2 +- drivers/video/intelfb/intelfbhw.c | 1 - drivers/video/kyro/fbdev.c | 1 - drivers/video/macfb.c | 1 - drivers/video/matrox/matroxfb_base.h | 1 - drivers/video/maxinefb.c | 1 - drivers/video/modedb.c | 1 - drivers/video/neofb.c | 1 - drivers/video/nvidia/nvidia.c | 1 - drivers/video/offb.c | 1 - drivers/video/platinumfb.c | 1 - drivers/video/pm2fb.c | 1 - drivers/video/pm3fb.c | 1 - drivers/video/pmag-aa-fb.c | 1 - drivers/video/pvr2fb.c | 1 - drivers/video/q40fb.c | 1 - drivers/video/retz3fb.c | 1 - drivers/video/riva/fbdev.c | 1 - drivers/video/s3c2410fb.c | 1 - drivers/video/savage/savagefb_driver.c | 1 - drivers/video/sis/sis_main.c | 6 ++++++ drivers/video/skeletonfb.c | 1 - drivers/video/sun3fb.c | 1 - drivers/video/tdfxfb.c | 1 - drivers/video/tgafb.c | 1 - drivers/video/tx3912fb.c | 1 - drivers/video/valkyriefb.c | 1 - drivers/video/vesafb.c | 2 +- drivers/video/vfb.c | 1 - drivers/video/vga16fb.c | 2 +- drivers/video/virgefb.c | 1 - include/linux/console_struct.h | 1 + include/linux/fb.h | 1 - include/linux/tty.h | 11 ----------- include/linux/vt.h | 10 ++++++++++ include/linux/vt_kern.h | 1 + 74 files changed, 26 insertions(+), 78 deletions(-) diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 41ebf51a107a..b3a8a2980365 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/parisc/kernel/pdc_cons.c b/arch/parisc/kernel/pdc_cons.c index ce78f412ff2e..aab05767427c 100644 --- a/arch/parisc/kernel/pdc_cons.c +++ b/arch/parisc/kernel/pdc_cons.c @@ -89,6 +89,8 @@ static int pdc_console_setup(struct console *co, char *options) } #if defined(CONFIG_PDC_CONSOLE) +#include + static struct tty_driver * pdc_console_device (struct console *c, int *index) { extern struct tty_driver console_driver; diff --git a/drivers/video/68328fb.c b/drivers/video/68328fb.c index 78488bb41aeb..0dda73da8628 100644 --- a/drivers/video/68328fb.c +++ b/drivers/video/68328fb.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/S3triofb.c b/drivers/video/S3triofb.c index e714e8449c1d..afd146f5f683 100644 --- a/drivers/video/S3triofb.c +++ b/drivers/video/S3triofb.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c index f9bc9f777e75..f1ba54f4fc39 100644 --- a/drivers/video/amifb.c +++ b/drivers/video/amifb.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/arcfb.c b/drivers/video/arcfb.c index fd95c2dbd4f7..70dd8115a4d8 100644 --- a/drivers/video/arcfb.c +++ b/drivers/video/arcfb.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/asiliantfb.c b/drivers/video/asiliantfb.c index eaeaf4d1a094..1fd22f460b0f 100644 --- a/drivers/video/asiliantfb.c +++ b/drivers/video/asiliantfb.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/atafb.c b/drivers/video/atafb.c index e69ab65f7843..5831893bf7a0 100644 --- a/drivers/video/atafb.c +++ b/drivers/video/atafb.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 72c589109471..1006f125fcc7 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 51b78f8de949..8d85fc58142e 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -58,7 +58,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index 0e465c80ef24..eba9d93ae360 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c index 7355da09c721..daf43f535a0b 100644 --- a/drivers/video/cirrusfb.c +++ b/drivers/video/cirrusfb.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 878707a04398..7c8eeeb30a47 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 94e9f7069bef..390439b3d899 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -64,7 +64,6 @@ #include #include #include /* MSch: for IRQ probe */ -#include #include #include #include diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c index c89f90edf8ac..52ed12b12acc 100644 --- a/drivers/video/console/mdacon.c +++ b/drivers/video/console/mdacon.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c index 03041311711b..7fa1afeae8dc 100644 --- a/drivers/video/console/newport_con.c +++ b/drivers/video/console/newport_con.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/console/promcon.c b/drivers/video/console/promcon.c index 5cd5e114d1e6..b78eac63459f 100644 --- a/drivers/video/console/promcon.c +++ b/drivers/video/console/promcon.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/console/softcursor.c b/drivers/video/console/softcursor.c index 3957fc7523e2..557c563e4aed 100644 --- a/drivers/video/console/softcursor.c +++ b/drivers/video/console/softcursor.c @@ -10,7 +10,6 @@ #include #include -#include #include #include diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c index 45c4f227e56e..45586aaabd1e 100644 --- a/drivers/video/console/sticon.c +++ b/drivers/video/console/sticon.c @@ -37,7 +37,6 @@ #include #include -#include #include #include #include diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 05735ff4e9c5..7f1f0e7a5b20 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index acdd6a103dbb..8cc6c0e2d27a 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c index 2e2924957d8f..aae6d9c26e88 100644 --- a/drivers/video/cyber2000fb.c +++ b/drivers/video/cyber2000fb.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/cyberfb.c b/drivers/video/cyberfb.c index a3e189f90a7d..c40e72dafb0e 100644 --- a/drivers/video/cyberfb.c +++ b/drivers/video/cyberfb.c @@ -81,7 +81,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/dnfb.c b/drivers/video/dnfb.c index 5abd3cb00671..b083ea7e9c69 100644 --- a/drivers/video/dnfb.c +++ b/drivers/video/dnfb.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/epson1355fb.c b/drivers/video/epson1355fb.c index f0a621ecc288..737257d278f0 100644 --- a/drivers/video/epson1355fb.c +++ b/drivers/video/epson1355fb.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/fbcmap.c b/drivers/video/fbcmap.c index 1f98392a43b3..e8b135f3d80d 100644 --- a/drivers/video/fbcmap.c +++ b/drivers/video/fbcmap.c @@ -13,7 +13,6 @@ #include #include -#include #include #include diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 33034f81114d..4fc9df426c1a 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 3ccfff715a51..de93139ccbb5 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c @@ -26,7 +26,6 @@ * for more details. * */ -#include #include #include #include diff --git a/drivers/video/g364fb.c b/drivers/video/g364fb.c index 605d1a132020..1b981b635675 100644 --- a/drivers/video/g364fb.c +++ b/drivers/video/g364fb.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/geode/gx1fb_core.c b/drivers/video/geode/gx1fb_core.c index 4d3a8871d3d1..bcf9cea54d8b 100644 --- a/drivers/video/geode/gx1fb_core.c +++ b/drivers/video/geode/gx1fb_core.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c index 5ef12a3dfa50..0d3643fc6293 100644 --- a/drivers/video/geode/gxfb_core.c +++ b/drivers/video/geode/gxfb_core.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/hgafb.c b/drivers/video/hgafb.c index 4e39035cf335..fb9e67228543 100644 --- a/drivers/video/hgafb.c +++ b/drivers/video/hgafb.c @@ -36,7 +36,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/hitfb.c b/drivers/video/hitfb.c index 01864767450d..4cc6b454265e 100644 --- a/drivers/video/hitfb.c +++ b/drivers/video/hitfb.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/hpfb.c b/drivers/video/hpfb.c index abd920a663a0..91cf3b577d15 100644 --- a/drivers/video/hpfb.c +++ b/drivers/video/hpfb.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c index fbe8a2c4b04c..a6ca02f2156a 100644 --- a/drivers/video/i810/i810_main.c +++ b/drivers/video/i810/i810_main.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index 8a0c2d3d3805..67f384f86758 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/imacfb.c b/drivers/video/imacfb.c index cdbae173d69a..ff233b84dec4 100644 --- a/drivers/video/imacfb.c +++ b/drivers/video/imacfb.c @@ -15,9 +15,9 @@ #include #include #include +#include #include #include -#include #include diff --git a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c index 5f393d985b11..5715b8ad0ddc 100644 --- a/drivers/video/imsttfb.c +++ b/drivers/video/imsttfb.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c index 3f39d84015f1..06af89d44a0d 100644 --- a/drivers/video/intelfb/intelfbdrv.c +++ b/drivers/video/intelfb/intelfbdrv.c @@ -113,7 +113,6 @@ #include #include #include -#include #include #include #include @@ -122,6 +121,7 @@ #include #include #include +#include #include diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c index 3b78a57924f0..2a9322f9cfdc 100644 --- a/drivers/video/intelfb/intelfbhw.c +++ b/drivers/video/intelfb/intelfbhw.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/kyro/fbdev.c b/drivers/video/kyro/fbdev.c index 2fdbe9b2b04b..f0d614a80f1f 100644 --- a/drivers/video/kyro/fbdev.c +++ b/drivers/video/kyro/fbdev.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/macfb.c b/drivers/video/macfb.c index e6cbd9de944a..80a043807161 100644 --- a/drivers/video/macfb.c +++ b/drivers/video/macfb.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h index b95779b57c06..9c25c2f7966b 100644 --- a/drivers/video/matrox/matroxfb_base.h +++ b/drivers/video/matrox/matroxfb_base.h @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/maxinefb.c b/drivers/video/maxinefb.c index f85421bf7cb5..38c8d38de4fd 100644 --- a/drivers/video/maxinefb.c +++ b/drivers/video/maxinefb.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index ff5454601e22..d1267904c280 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c @@ -12,7 +12,6 @@ */ #include -#include #include #include diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c index 773855a311e8..59a6f5fa5ae6 100644 --- a/drivers/video/neofb.c +++ b/drivers/video/neofb.c @@ -59,7 +59,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index b02d6033cc0c..ef58c3825501 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 71ce1fa45cf4..ce5f3031b99b 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c index 450e802e0aa8..983be3ec2345 100644 --- a/drivers/video/platinumfb.c +++ b/drivers/video/platinumfb.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c index 49a203e1591c..a560a2223825 100644 --- a/drivers/video/pm2fb.c +++ b/drivers/video/pm2fb.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/pm3fb.c b/drivers/video/pm3fb.c index 0e0f977b05ee..1d81ef47efd3 100644 --- a/drivers/video/pm3fb.c +++ b/drivers/video/pm3fb.c @@ -57,7 +57,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/pmag-aa-fb.c b/drivers/video/pmag-aa-fb.c index d92f352211ef..68ca3cc40770 100644 --- a/drivers/video/pmag-aa-fb.c +++ b/drivers/video/pmag-aa-fb.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c index 4a1e0e856920..940ba2be55e9 100644 --- a/drivers/video/pvr2fb.c +++ b/drivers/video/pvr2fb.c @@ -53,7 +53,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/q40fb.c b/drivers/video/q40fb.c index fc91dbf896d2..48536c3e58a4 100644 --- a/drivers/video/q40fb.c +++ b/drivers/video/q40fb.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/retz3fb.c b/drivers/video/retz3fb.c index 5e2c64f622cb..cf41ff177644 100644 --- a/drivers/video/retz3fb.c +++ b/drivers/video/retz3fb.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index 2788655e6e7d..baf494cecc10 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index f461eb10cc79..ad3bdd6f1ac1 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -76,7 +76,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index 4729af477fbf..461e094e7b45 100644 --- a/drivers/video/savage/savagefb_driver.c +++ b/drivers/video/savage/savagefb_driver.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c index b848ca7db7f9..f8dfabd73b00 100644 --- a/drivers/video/sis/sis_main.c +++ b/drivers/video/sis/sis_main.c @@ -44,7 +44,13 @@ #include #include #include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) #include +#else +#include +#endif + #include #include #include diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c index 67f429e93189..bb96cb65fdaa 100644 --- a/drivers/video/skeletonfb.c +++ b/drivers/video/skeletonfb.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/sun3fb.c b/drivers/video/sun3fb.c index e046e20f02b9..f80356dfa8e8 100644 --- a/drivers/video/sun3fb.c +++ b/drivers/video/sun3fb.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/tdfxfb.c b/drivers/video/tdfxfb.c index 239b1496874b..689ce0270b81 100644 --- a/drivers/video/tdfxfb.c +++ b/drivers/video/tdfxfb.c @@ -63,7 +63,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index 6c2c78ab9827..94fde625a6c0 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/tx3912fb.c b/drivers/video/tx3912fb.c index d904da44e1aa..07389ba01eff 100644 --- a/drivers/video/tx3912fb.c +++ b/drivers/video/tx3912fb.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c index 1d76c035050e..47f27924a7d7 100644 --- a/drivers/video/valkyriefb.c +++ b/drivers/video/valkyriefb.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c index 5718924b677f..2196448396ec 100644 --- a/drivers/video/vesafb.c +++ b/drivers/video/vesafb.c @@ -13,13 +13,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include