From fef6108d4556917c45cd9ba397c1c7597f3990e1 Mon Sep 17 00:00:00 2001 From: Andy Fleming Date: Thu, 20 Apr 2006 16:44:29 -0500 Subject: [PATCH] Fix locking in gianfar This patch fixes several bugs in the gianfar driver, including a major one where spinlocks were horribly broken: * Split gianfar locks into two types: TX and RX * Made it so gfar_start() now clears RHALT * Fixed a bug where calling gfar_start_xmit() with interrupts off would corrupt the interrupt state * Fixed a bug where a frame could potentially arrive, and never be handled (if no more frames arrived * Fixed a bug where the rx_work_limit would never be observed by the rx completion code * Fixed a bug where the interrupt handlers were not actually protected by their spinlocks Signed-off-by: Andy Fleming Signed-off-by: Jeff Garzik --- drivers/net/gianfar.c | 56 ++++++++++++++++++------------------ drivers/net/gianfar.h | 67 +++++++++++++++++++++++++++++-------------- drivers/net/gianfar_ethtool.c | 20 +++++++++---- drivers/net/gianfar_sysfs.c | 24 ++++++++-------- 4 files changed, 100 insertions(+), 67 deletions(-) (limited to 'drivers') diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 771e25d..218d317 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -210,7 +210,8 @@ static int gfar_probe(struct platform_device *pdev) goto regs_fail; } - spin_lock_init(&priv->lock); + spin_lock_init(&priv->txlock); + spin_lock_init(&priv->rxlock); platform_set_drvdata(pdev, dev); @@ -515,11 +516,13 @@ void stop_gfar(struct net_device *dev) phy_stop(priv->phydev); /* Lock it down */ - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); gfar_halt(dev); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); /* Free the IRQs */ if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) { @@ -605,14 +608,15 @@ void gfar_start(struct net_device *dev) tempval |= DMACTRL_INIT_SETTINGS; gfar_write(&priv->regs->dmactrl, tempval); - /* Clear THLT, so that the DMA starts polling now */ - gfar_write(®s->tstat, TSTAT_CLEAR_THALT); - /* Make sure we aren't stopped */ tempval = gfar_read(&priv->regs->dmactrl); tempval &= ~(DMACTRL_GRS | DMACTRL_GTS); gfar_write(&priv->regs->dmactrl, tempval); + /* Clear THLT/RHLT, so that the DMA starts polling now */ + gfar_write(®s->tstat, TSTAT_CLEAR_THALT); + gfar_write(®s->rstat, RSTAT_CLEAR_RHALT); + /* Unmask the interrupts we look for */ gfar_write(®s->imask, IMASK_DEFAULT); } @@ -928,12 +932,13 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) struct txfcb *fcb = NULL; struct txbd8 *txbdp; u16 status; + unsigned long flags; /* Update transmit stats */ priv->stats.tx_bytes += skb->len; /* Lock priv now */ - spin_lock_irq(&priv->lock); + spin_lock_irqsave(&priv->txlock, flags); /* Point at the first free tx descriptor */ txbdp = priv->cur_tx; @@ -1004,7 +1009,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); /* Unlock priv */ - spin_unlock_irq(&priv->lock); + spin_unlock_irqrestore(&priv->txlock, flags); return 0; } @@ -1049,7 +1054,7 @@ static void gfar_vlan_rx_register(struct net_device *dev, unsigned long flags; u32 tempval; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->rxlock, flags); priv->vlgrp = grp; @@ -1076,7 +1081,7 @@ static void gfar_vlan_rx_register(struct net_device *dev, gfar_write(&priv->regs->rctrl, tempval); } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->rxlock, flags); } @@ -1085,12 +1090,12 @@ static void gfar_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid) struct gfar_private *priv = netdev_priv(dev); unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->rxlock, flags); if (priv->vlgrp) priv->vlgrp->vlan_devices[vid] = NULL; - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->rxlock, flags); } @@ -1179,7 +1184,7 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs) gfar_write(&priv->regs->ievent, IEVENT_TX_MASK); /* Lock priv */ - spin_lock(&priv->lock); + spin_lock(&priv->txlock); bdp = priv->dirty_tx; while ((bdp->status & TXBD_READY) == 0) { /* If dirty_tx and cur_tx are the same, then either the */ @@ -1224,7 +1229,7 @@ static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs) else gfar_write(&priv->regs->txic, 0); - spin_unlock(&priv->lock); + spin_unlock(&priv->txlock); return IRQ_HANDLED; } @@ -1305,9 +1310,10 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_id; struct gfar_private *priv = netdev_priv(dev); - #ifdef CONFIG_GFAR_NAPI u32 tempval; +#else + unsigned long flags; #endif /* Clear IEVENT, so rx interrupt isn't called again @@ -1330,7 +1336,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs) } #else - spin_lock(&priv->lock); + spin_lock_irqsave(&priv->rxlock, flags); gfar_clean_rx_ring(dev, priv->rx_ring_size); /* If we are coalescing interrupts, update the timer */ @@ -1341,7 +1347,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs) else gfar_write(&priv->regs->rxic, 0); - spin_unlock(&priv->lock); + spin_unlock_irqrestore(&priv->rxlock, flags); #endif return IRQ_HANDLED; @@ -1490,13 +1496,6 @@ int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) /* Update the current rxbd pointer to be the next one */ priv->cur_rx = bdp; - /* If no packets have arrived since the - * last one we processed, clear the IEVENT RX and - * BSY bits so that another interrupt won't be - * generated when we set IMASK */ - if (bdp->status & RXBD_EMPTY) - gfar_write(&priv->regs->ievent, IEVENT_RX_MASK); - return howmany; } @@ -1516,7 +1515,7 @@ static int gfar_poll(struct net_device *dev, int *budget) rx_work_limit -= howmany; *budget -= howmany; - if (rx_work_limit >= 0) { + if (rx_work_limit > 0) { netif_rx_complete(dev); /* Clear the halt bit in RSTAT */ @@ -1533,7 +1532,8 @@ static int gfar_poll(struct net_device *dev, int *budget) gfar_write(&priv->regs->rxic, 0); } - return (rx_work_limit < 0) ? 1 : 0; + /* Return 1 if there's more work to do */ + return (rx_work_limit > 0) ? 0 : 1; } #endif @@ -1629,7 +1629,7 @@ static void adjust_link(struct net_device *dev) struct phy_device *phydev = priv->phydev; int new_state = 0; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->txlock, flags); if (phydev->link) { u32 tempval = gfar_read(®s->maccfg2); u32 ecntrl = gfar_read(®s->ecntrl); @@ -1694,7 +1694,7 @@ static void adjust_link(struct net_device *dev) if (new_state && netif_msg_link(priv)) phy_print_status(phydev); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->txlock, flags); } /* Update the hash table based on the current list of multicast diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index d37d540..127c98c 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -656,43 +656,62 @@ struct gfar { * the buffer descriptor determines the actual condition. */ struct gfar_private { - /* pointers to arrays of skbuffs for tx and rx */ + /* Fields controlled by TX lock */ + spinlock_t txlock; + + /* Pointer to the array of skbuffs */ struct sk_buff ** tx_skbuff; - struct sk_buff ** rx_skbuff; - /* indices pointing to the next free sbk in skb arrays */ + /* next free skb in the array */ u16 skb_curtx; - u16 skb_currx; - /* index of the first skb which hasn't been transmitted - * yet. */ + /* First skb in line to be transmitted */ u16 skb_dirtytx; /* Configuration info for the coalescing features */ unsigned char txcoalescing; unsigned short txcount; unsigned short txtime; + + /* Buffer descriptor pointers */ + struct txbd8 *tx_bd_base; /* First tx buffer descriptor */ + struct txbd8 *cur_tx; /* Next free ring entry */ + struct txbd8 *dirty_tx; /* First buffer in line + to be transmitted */ + unsigned int tx_ring_size; + + /* RX Locked fields */ + spinlock_t rxlock; + + /* skb array and index */ + struct sk_buff ** rx_skbuff; + u16 skb_currx; + + /* RX Coalescing values */ unsigned char rxcoalescing; unsigned short rxcount; unsigned short rxtime; - /* GFAR addresses */ - struct rxbd8 *rx_bd_base; /* Base addresses of Rx and Tx Buffers */ - struct txbd8 *tx_bd_base; + struct rxbd8 *rx_bd_base; /* First Rx buffers */ struct rxbd8 *cur_rx; /* Next free rx ring entry */ - struct txbd8 *cur_tx; /* Next free ring entry */ - struct txbd8 *dirty_tx; /* The Ring entry to be freed. */ - struct gfar __iomem *regs; /* Pointer to the GFAR memory mapped Registers */ - u32 __iomem *hash_regs[16]; - int hash_width; - struct net_device_stats stats; /* linux network statistics */ - struct gfar_extra_stats extra_stats; - spinlock_t lock; + + /* RX parameters */ + unsigned int rx_ring_size; unsigned int rx_buffer_size; unsigned int rx_stash_size; unsigned int rx_stash_index; - unsigned int tx_ring_size; - unsigned int rx_ring_size; + + struct vlan_group *vlgrp; + + /* Unprotected fields */ + /* Pointer to the GFAR memory mapped Registers */ + struct gfar __iomem *regs; + + /* Hash registers and their width */ + u32 __iomem *hash_regs[16]; + int hash_width; + + /* global parameters */ unsigned int fifo_threshold; unsigned int fifo_starve; unsigned int fifo_starve_off; @@ -702,13 +721,15 @@ struct gfar_private { extended_hash:1, bd_stash_en:1; unsigned short padding; - struct vlan_group *vlgrp; - /* Info structure initialized by board setup code */ + unsigned int interruptTransmit; unsigned int interruptReceive; unsigned int interruptError; + + /* info structure initialized by platform code */ struct gianfar_platform_data *einfo; + /* PHY stuff */ struct phy_device *phydev; struct mii_bus *mii_bus; int oldspeed; @@ -716,6 +737,10 @@ struct gfar_private { int oldlink; uint32_t msg_enable; + + /* Network Statistics */ + struct net_device_stats stats; + struct gfar_extra_stats extra_stats; }; static inline u32 gfar_read(volatile unsigned __iomem *addr) diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 5de7b2e..d69698c 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -455,10 +455,14 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva /* Halt TX and RX, and process the frames which * have already been received */ - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); + gfar_halt(dev); gfar_clean_rx_ring(dev, priv->rx_ring_size); - spin_unlock_irqrestore(&priv->lock, flags); + + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); /* Now we take down the rings to rebuild them */ stop_gfar(dev); @@ -488,10 +492,14 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) /* Halt TX and RX, and process the frames which * have already been received */ - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->txlock, flags); + spin_lock(&priv->rxlock); + gfar_halt(dev); gfar_clean_rx_ring(dev, priv->rx_ring_size); - spin_unlock_irqrestore(&priv->lock, flags); + + spin_unlock(&priv->rxlock); + spin_unlock_irqrestore(&priv->txlock, flags); /* Now we take down the rings to rebuild them */ stop_gfar(dev); @@ -523,7 +531,7 @@ static int gfar_set_tx_csum(struct net_device *dev, uint32_t data) if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) return -EOPNOTSUPP; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->txlock, flags); gfar_halt(dev); if (data) @@ -532,7 +540,7 @@ static int gfar_set_tx_csum(struct net_device *dev, uint32_t data) dev->features &= ~NETIF_F_IP_CSUM; gfar_start(dev); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->txlock, flags); return 0; } diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c index 51ef181..a6d5c43 100644 --- a/drivers/net/gianfar_sysfs.c +++ b/drivers/net/gianfar_sysfs.c @@ -82,7 +82,7 @@ static ssize_t gfar_set_bd_stash(struct class_device *cdev, else return count; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->rxlock, flags); /* Set the new stashing value */ priv->bd_stash_en = new_setting; @@ -96,7 +96,7 @@ static ssize_t gfar_set_bd_stash(struct class_device *cdev, gfar_write(&priv->regs->attr, temp); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->rxlock, flags); return count; } @@ -118,7 +118,7 @@ static ssize_t gfar_set_rx_stash_size(struct class_device *cdev, u32 temp; unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->rxlock, flags); if (length > priv->rx_buffer_size) return count; @@ -142,7 +142,7 @@ static ssize_t gfar_set_rx_stash_size(struct class_device *cdev, gfar_write(&priv->regs->attr, temp); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->rxlock, flags); return count; } @@ -166,7 +166,7 @@ static ssize_t gfar_set_rx_stash_index(struct class_device *cdev, u32 temp; unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->rxlock, flags); if (index > priv->rx_stash_size) return count; @@ -180,7 +180,7 @@ static ssize_t gfar_set_rx_stash_index(struct class_device *cdev, temp |= ATTRELI_EI(index); gfar_write(&priv->regs->attreli, flags); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->rxlock, flags); return count; } @@ -205,7 +205,7 @@ static ssize_t gfar_set_fifo_threshold(struct class_device *cdev, if (length > GFAR_MAX_FIFO_THRESHOLD) return count; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->txlock, flags); priv->fifo_threshold = length; @@ -214,7 +214,7 @@ static ssize_t gfar_set_fifo_threshold(struct class_device *cdev, temp |= length; gfar_write(&priv->regs->fifo_tx_thr, temp); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->txlock, flags); return count; } @@ -240,7 +240,7 @@ static ssize_t gfar_set_fifo_starve(struct class_device *cdev, if (num > GFAR_MAX_FIFO_STARVE) return count; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->txlock, flags); priv->fifo_starve = num; @@ -249,7 +249,7 @@ static ssize_t gfar_set_fifo_starve(struct class_device *cdev, temp |= num; gfar_write(&priv->regs->fifo_tx_starve, temp); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->txlock, flags); return count; } @@ -274,7 +274,7 @@ static ssize_t gfar_set_fifo_starve_off(struct class_device *cdev, if (num > GFAR_MAX_FIFO_STARVE_OFF) return count; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->txlock, flags); priv->fifo_starve_off = num; @@ -283,7 +283,7 @@ static ssize_t gfar_set_fifo_starve_off(struct class_device *cdev, temp |= num; gfar_write(&priv->regs->fifo_tx_starve_shutoff, temp); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irqrestore(&priv->txlock, flags); return count; } -- cgit v1.1 From c1311af12c7ca176a790a911a3fb6fed1f3bb387 Mon Sep 17 00:00:00 2001 From: Brent Casavant Date: Thu, 20 Apr 2006 15:38:16 -0500 Subject: [IA64] IOC4 config option ordering SERIAL_SGI_IOC4 and BLK_DEV_SGIIOC4 depend upon SGI_IOC4, and SERIAL_SGI_IOC3 depends upon SGI_IOC3. Currently the definitions are out of order in the config sequence. Fix by including drivers/sn/Kconfig immediately after SGI_SN, upon which SGI_IOC4 and SGI_IOC3 depend. Signed-off-by: Brent Casavant Signed-off-by: Tony Luck --- drivers/Kconfig | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/Kconfig b/drivers/Kconfig index 5c91d6a..aeb5ab2 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -68,8 +68,6 @@ source "drivers/leds/Kconfig" source "drivers/infiniband/Kconfig" -source "drivers/sn/Kconfig" - source "drivers/edac/Kconfig" source "drivers/rtc/Kconfig" -- cgit v1.1 From 67a5a59d3301949f51f2d617d689f005c6d21470 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Mon, 27 Mar 2006 19:52:14 +0000 Subject: [PARISC] Misc. janitorial work Fix a spelling mistake, add a KERN_INFO flag, and fix some whitespace uglies. Signed-off-by: Helge Deller Signed-off-by: Kyle McMartin --- drivers/parisc/pdc_stable.c | 2 +- drivers/parisc/superio.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index 4e53be9..bbeabe3 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c @@ -535,7 +535,7 @@ pdcs_auto_read(struct subsystem *entry, char *buf, int knob) { char *out = buf; struct pdcspath_entry *pathentry; - + if (!entry || !buf) return -EINVAL; diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c index 719b863..828eb45 100644 --- a/drivers/parisc/superio.c +++ b/drivers/parisc/superio.c @@ -155,7 +155,7 @@ superio_init(struct pci_dev *pcidev) struct pci_dev *pdev = sio->lio_pdev; u16 word; - if (sio->suckyio_irq_enabled) + if (sio->suckyio_irq_enabled) return; BUG_ON(!pdev); @@ -194,7 +194,7 @@ superio_init(struct pci_dev *pcidev) request_region (sio->acpi_base, 0x1f, "acpi"); /* Enable the legacy I/O function */ - pci_read_config_word (pdev, PCI_COMMAND, &word); + pci_read_config_word (pdev, PCI_COMMAND, &word); word |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_IO; pci_write_config_word (pdev, PCI_COMMAND, word); -- cgit v1.1 From b312c33e362696d873931d8f84a89b3e894077c8 Mon Sep 17 00:00:00 2001 From: Grant Grundler Date: Thu, 30 Mar 2006 07:13:21 +0000 Subject: [PARISC] Document that we tolerate "Relaxed Ordering" This means "DMA Read returns" can bypass "MMIO Writes". Violating the PCI specs in this case improves outbound DMA "flows" and is currently not required by any drivers. This is NOT a new behavior. Previous chipsets did this already and I believe ZX1 PDC was already setting this for hpux. I just want to further document the behavior. Signed-off-by: Grant Grundler Signed-off-by: Kyle McMartin --- drivers/parisc/sba_iommu.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 42b32ff..278f325 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -178,6 +178,11 @@ extern struct proc_dir_entry * proc_mckinley_root; #define ROPE6_CTL 0x230 #define ROPE7_CTL 0x238 +#define IOC_ROPE0_CFG 0x500 /* pluto only */ +#define IOC_ROPE_AO 0x10 /* Allow "Relaxed Ordering" */ + + + #define HF_ENABLE 0x40 @@ -1759,19 +1764,33 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, sba_dev->num_ioc = num_ioc; for (i = 0; i < num_ioc; i++) { - /* - ** Make sure the box crashes if we get any errors on a rope. - */ - WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE0_CTL); - WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE1_CTL); - WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE2_CTL); - WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE3_CTL); - WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE4_CTL); - WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE5_CTL); - WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE6_CTL); - WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); - - /* flush out the writes */ + unsigned long ioc_hpa = sba_dev->ioc[i].ioc_hpa; + unsigned int j; + + for (j=0; j < sizeof(u64) * ROPES_PER_IOC; j+=sizeof(u64)) { + + /* + * Clear ROPE(N)_CONFIG AO bit. + * Disables "NT Ordering" (~= !"Relaxed Ordering") + * Overrides bit 1 in DMA Hint Sets. + * Improves netperf UDP_STREAM by ~10% for bcm5701. + */ + if (IS_PLUTO(sba_dev->iodc)) { + unsigned long rope_cfg, cfg_val; + + rope_cfg = ioc_hpa + IOC_ROPE0_CFG + j; + cfg_val = READ_REG(rope_cfg); + cfg_val &= ~IOC_ROPE_AO; + WRITE_REG(cfg_val, rope_cfg); + } + + /* + ** Make sure the box crashes on rope errors. + */ + WRITE_REG(HF_ENABLE, ioc_hpa + ROPE0_CTL + j); + } + + /* flush out the last writes */ READ_REG(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); DBG_INIT(" ioc[%d] ROPE_CFG 0x%Lx ROPE_DBG 0x%Lx\n", -- cgit v1.1 From d668da80d613def981c573354e1853e38bd0698d Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Mon, 3 Apr 2006 13:44:17 +0000 Subject: [PARISC] Fix up hil_kbd.c mismerge Signed-off-by: Matthew Wilcox Signed-off-by: Kyle McMartin --- drivers/input/keyboard/hil_kbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index 1dca3cf..2e4abdc 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -350,11 +350,11 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) return 0; bail2: serio_close(serio); + serio_set_drvdata(serio, NULL); bail1: input_free_device(kbd->dev); bail0: kfree(kbd); - serio_set_drvdata(serio, NULL); return -EIO; } -- cgit v1.1 From 6542729809baa3674b16a76a68346f449266c6dd Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 4 Apr 2006 10:17:52 +0200 Subject: [PATCH] pcmcia: add new ID to pcnet_cs This adds a new ID to pcnet_cs, as noted by Kuro Moji. Signed-off-by: Dominik Brodowski --- drivers/net/pcmcia/pcnet_cs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index 506e777..d090df4 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -1639,6 +1639,7 @@ static struct pcmcia_device_id pcnet_ids[] = { PCMCIA_DEVICE_PROD_ID12("CONTEC", "C-NET(PC)C-10L", 0x21cab552, 0xf6f90722), PCMCIA_DEVICE_PROD_ID12("corega", "FEther PCC-TXF", 0x0a21501a, 0xa51564a2), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-T", 0x5261440f, 0xfa9d85bd), + PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega EtherII PCC-TD", 0x5261440f, 0xc49bd73d), PCMCIA_DEVICE_PROD_ID12("Corega K.K.", "corega EtherII PCC-TD", 0xd4fdcbd8, 0xc49bd73d), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega Ether PCC-T", 0x5261440f, 0x6705fcaa), PCMCIA_DEVICE_PROD_ID12("corega K.K.", "corega FastEther PCC-TX", 0x5261440f, 0x485e85d9), -- cgit v1.1 From 6171b88b436ceb91d602ca570e63a0dcdd56648e Mon Sep 17 00:00:00 2001 From: Komuro Date: Sun, 2 Apr 2006 17:39:27 +0900 Subject: [PATCH] pcmcia: unload second device first Use list_add instead of list_add_tail for pcmcia_device_add so that second device of multi-function-card will be unloaded first. Signed-off-by: komurojun-mbn@nifty.com Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index ae10d1e..7582362 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -628,7 +628,7 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f } /* Add to the list in pcmcia_bus_socket */ - list_add_tail(&p_dev->socket_device_list, &s->devices_list); + list_add(&p_dev->socket_device_list, &s->devices_list); spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); -- cgit v1.1 From 90ff87008df12da7f2486178d0dee13745c1de6b Mon Sep 17 00:00:00 2001 From: Komuro Date: Sun, 12 Mar 2006 11:32:07 +0900 Subject: [PATCH] pcmcia: fix comment for pcmcia_load_firmware The comment of "pcmcia_load_firmware" is wrong: the firmware(*.cis) files reside in /lib/firmware/ _not_ /lib/firmware/cis/ . Signed-off-by: komurojun-mbn@nifty.com Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 7582362..7b7428c 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -236,11 +236,11 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv) /** * pcmcia_load_firmware - load CIS from userspace if device-provided is broken * @dev - the pcmcia device which needs a CIS override - * @filename - requested filename in /lib/firmware/cis/ + * @filename - requested filename in /lib/firmware/ * * This uses the in-kernel firmware loading mechanism to use a "fake CIS" if * the one provided by the card is broken. The firmware files reside in - * /lib/firmware/cis/ in userspace. + * /lib/firmware/ in userspace. */ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename) { -- cgit v1.1 From a0aab14322a74ab5665704c6155bf48fbc38f445 Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Tue, 4 Apr 2006 11:09:26 +0200 Subject: [PATCH] pcmcia: do not set dev_node to NULL too early If we set dev_node to NULL too early, some drivers which used this to determine whether unregister_netdev() needs to be called fail when removing a PCMCIA card. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 2 ++ drivers/pcmcia/pcmcia_resource.c | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 7b7428c..0f98cab 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -476,6 +476,8 @@ static int pcmcia_device_remove(struct device * dev) if (p_drv->remove) p_drv->remove(p_dev); + p_dev->dev_node = NULL; + /* check for proper unloading */ if (p_dev->_irq || p_dev->_io || p_dev->_locked) printk(KERN_INFO "pcmcia: driver %s did not release config properly\n", diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 45063b4..2539c0b 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -947,7 +947,5 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev) { pcmcia_release_irq(p_dev, &p_dev->irq); if (&p_dev->win) pcmcia_release_window(p_dev->win); - - p_dev->dev_node = NULL; } EXPORT_SYMBOL(pcmcia_disable_device); -- cgit v1.1 From 80a55e923c76e022de298929e0c09bcca5c247d9 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 10 Apr 2006 23:24:57 -0700 Subject: [PATCH] pcmcia: remove unneeded forward declarations Also remove a couple of unneeded typecasts. Signed-off-by: Andrew Morton Signed-off-by: Dominik Brodowski --- drivers/pcmcia/ds.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 0f98cab..48d3b3d 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -298,9 +298,6 @@ static inline int pcmcia_load_firmware(struct pcmcia_device *dev, char * filenam * * Registers a PCMCIA driver with the PCMCIA bus core. */ -static int pcmcia_device_probe(struct device *dev); -static int pcmcia_device_remove(struct device * dev); - int pcmcia_register_driver(struct pcmcia_driver *driver) { if (!driver) @@ -400,7 +397,7 @@ static int pcmcia_device_probe(struct device * dev) * call which will then check whether there are two * pseudo devices, and if not, add the second one. */ - did = (struct pcmcia_device_id *) p_dev->dev.driver_data; + did = p_dev->dev.driver_data; if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && (p_dev->socket->device_count == 1) && (p_dev->device_no == 0)) pcmcia_add_pseudo_device(p_dev->socket); @@ -448,7 +445,6 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le return; } - static int pcmcia_device_remove(struct device * dev) { struct pcmcia_device *p_dev; @@ -463,7 +459,7 @@ static int pcmcia_device_remove(struct device * dev) * pseudo multi-function card, we need to unbind * all devices */ - did = (struct pcmcia_device_id *) p_dev->dev.driver_data; + did = p_dev->dev.driver_data; if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && (p_dev->socket->device_count != 0) && (p_dev->device_no == 0)) -- cgit v1.1 From 2aff541c691b28cecb95ce710c367d16c0a84d8c Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Thu, 13 Apr 2006 19:06:49 +0200 Subject: [PATCH] pcmcia: fix oops in static mapping case As static maps do not have IO resources, this setting oopses. However, as we do not ever read this value, we can safely remove it. Signed-off-by: Dominik Brodowski --- drivers/pcmcia/pcmcia_resource.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 2539c0b..cc3402c 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -88,7 +88,6 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, } if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { *base = s->io_offset | (*base & 0x0fff); - s->io[0].res->flags = (s->io[0].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS); return 0; } /* Check for an already-allocated window that must conflict with -- cgit v1.1 From daaeb72bdf22873e6fa6497550c9e1d9a8825fea Mon Sep 17 00:00:00 2001 From: Yoichi Yuasa Date: Thu, 6 Apr 2006 15:08:29 +0900 Subject: [PATCH] vrc4171: update config This patch updates "depends on" for PCMCIA_VRC4171. CONFIG_VRC4171 has been removed, so replace it with CPU_VR41XX && ISA. Signed-off-by: Yoichi Yuasa Signed-off-by: Dominik Brodowski --- drivers/pcmcia/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index cba6c9e..61cb4b2 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -250,7 +250,7 @@ config M32R_CFC_NUM config PCMCIA_VRC4171 tristate "NEC VRC4171 Card Controllers support" - depends on VRC4171 && PCMCIA + depends on CPU_VR41XX && ISA && PCMCIA config PCMCIA_VRC4173 tristate "NEC VRC4173 CARDU support" -- cgit v1.1 From 48b950ff241fca03a6969a5eb6a42a02722678d4 Mon Sep 17 00:00:00 2001 From: Daniel Ritz Date: Fri, 14 Apr 2006 17:42:13 +0200 Subject: [PATCH] pcmcia/pcmcia_resource.c: fix crash when using Cardbus cards Using the old ioctl interface together with cardbus card gives a NULL pointer dereference since cardbus devices don't have a struct pcmcia_device. also s->io[0].res can be NULL as well. Fix is to move the pcmcia code after the cardbus code and to check for a null pointer. Signed-off-by: Daniel Ritz Signed-off-by: Dominik Brodowski --- drivers/pcmcia/pcmcia_resource.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index cc3402c..3131bb0 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -208,7 +208,6 @@ int pccard_get_configuration_info(struct pcmcia_socket *s, if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; - config->Function = p_dev->func; #ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) { @@ -222,14 +221,22 @@ int pccard_get_configuration_info(struct pcmcia_socket *s, config->AssignedIRQ = s->irq.AssignedIRQ; if (config->AssignedIRQ) config->Attributes |= CONF_ENABLE_IRQ; - config->BasePort1 = s->io[0].res->start; - config->NumPorts1 = s->io[0].res->end - config->BasePort1 + 1; + if (s->io[0].res) { + config->BasePort1 = s->io[0].res->start; + config->NumPorts1 = s->io[0].res->end - config->BasePort1 + 1; + } } return CS_SUCCESS; } #endif - c = (p_dev) ? p_dev->function_config : NULL; + if (p_dev) { + c = p_dev->function_config; + config->Function = p_dev->func; + } else { + c = NULL; + config->Function = 0; + } if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { config->Attributes = 0; -- cgit v1.1 From 73a88814542d3f5b8973f3db9d7f380bd29957c4 Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Sat, 22 Apr 2006 02:35:30 -0700 Subject: [PATCH] isdn4linux: Siemens Gigaset base driver: fix disconnect handling Fix a possible Oops in the Siemens Gigaset base driver when the device is unplugged while an ISDN connection is still active, and makes sure that the isdn4linux link level (LL) is properly informed if a connection is broken by the USB cable being unplugged. - Avoid unsafe checks of URB status fields outside the URB completion handlers, keep track of in-use URBs myself instead. - If an isochronous transfer URB completes with status==0, also check the status of the frame descriptors. - Verify length of interrupt messages received from the device. - Align the length limit on transmitted AT commands with the device documentation. - In case of AT response receive overrun, keep newly arrived instead of old unread data. - Remove redundant check of device ID in the USB probe function. - Correct and improve some comments and formatting. Signed-off-by: Tilman Schmidt Acked-by: Hansjoerg Lipp Cc: Karsten Keil Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/isdn/gigaset/bas-gigaset.c | 597 ++++++++++++++++++++----------------- drivers/isdn/gigaset/common.c | 3 +- drivers/isdn/gigaset/ev-layer.c | 3 + drivers/isdn/gigaset/gigaset.h | 7 +- drivers/isdn/gigaset/i4l.c | 2 +- drivers/isdn/gigaset/isocdata.c | 10 +- 6 files changed, 342 insertions(+), 280 deletions(-) (limited to 'drivers') diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index f86ed6a..eb41aba 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -5,8 +5,6 @@ * Tilman Schmidt , * Stefan Eilers. * - * Based on usb-gigaset.c. - * * ===================================================================== * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -46,19 +44,20 @@ MODULE_PARM_DESC(cidmode, "Call-ID mode"); #define GIGASET_DEVFSNAME "gig/bas/" #define GIGASET_DEVNAME "ttyGB" -#define IF_WRITEBUF 256 //FIXME +/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */ +#define IF_WRITEBUF 264 /* Values for the Gigaset 307x */ #define USB_GIGA_VENDOR_ID 0x0681 -#define USB_GIGA_PRODUCT_ID 0x0001 -#define USB_4175_PRODUCT_ID 0x0002 +#define USB_3070_PRODUCT_ID 0x0001 +#define USB_3075_PRODUCT_ID 0x0002 #define USB_SX303_PRODUCT_ID 0x0021 #define USB_SX353_PRODUCT_ID 0x0022 /* table of devices that work with this driver */ static struct usb_device_id gigaset_table [] = { - { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_GIGA_PRODUCT_ID) }, - { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_4175_PRODUCT_ID) }, + { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) }, + { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) }, { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) }, { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX353_PRODUCT_ID) }, { } /* Terminating entry */ @@ -77,6 +76,10 @@ static int gigaset_probe(struct usb_interface *interface, /* Function will be called if the device is unplugged */ static void gigaset_disconnect(struct usb_interface *interface); +static void read_ctrl_callback(struct urb *, struct pt_regs *); +static void stopurbs(struct bas_bc_state *); +static int atwrite_submit(struct cardstate *, unsigned char *, int); +static int start_cbsend(struct cardstate *); /*==============================================================================*/ @@ -111,12 +114,14 @@ struct bas_cardstate { }; /* status of direct USB connection to 307x base (bits in basstate) */ -#define BS_ATOPEN 0x001 -#define BS_B1OPEN 0x002 -#define BS_B2OPEN 0x004 -#define BS_ATREADY 0x008 -#define BS_INIT 0x010 -#define BS_ATTIMER 0x020 +#define BS_ATOPEN 0x001 /* AT channel open */ +#define BS_B1OPEN 0x002 /* B channel 1 open */ +#define BS_B2OPEN 0x004 /* B channel 2 open */ +#define BS_ATREADY 0x008 /* base ready for AT command */ +#define BS_INIT 0x010 /* base has signalled INIT_OK */ +#define BS_ATTIMER 0x020 /* waiting for HD_READY_SEND_ATDATA */ +#define BS_ATRDPEND 0x040 /* urb_cmd_in in use */ +#define BS_ATWRPEND 0x080 /* urb_cmd_out in use */ static struct gigaset_driver *driver = NULL; @@ -130,6 +135,47 @@ static struct usb_driver gigaset_usb_driver = { .id_table = gigaset_table, }; +/* get message text for usb_submit_urb return code + */ +static char *get_usb_rcmsg(int rc) +{ + static char unkmsg[28]; + + switch (rc) { + case 0: + return "success"; + case -ENOMEM: + return "out of memory"; + case -ENODEV: + return "device not present"; + case -ENOENT: + return "endpoint not present"; + case -ENXIO: + return "URB type not supported"; + case -EINVAL: + return "invalid argument"; + case -EAGAIN: + return "start frame too early or too much scheduled"; + case -EFBIG: + return "too many isochronous frames requested"; + case -EPIPE: + return "endpoint stalled"; + case -EMSGSIZE: + return "invalid packet size"; + case -ENOSPC: + return "would overcommit USB bandwidth"; + case -ESHUTDOWN: + return "device shut down"; + case -EPERM: + return "reject flag set"; + case -EHOSTUNREACH: + return "device suspended"; + default: + snprintf(unkmsg, sizeof(unkmsg), "unknown error %d", rc); + return unkmsg; + } +} + /* get message text for USB status code */ static char *get_usb_statmsg(int status) @@ -140,43 +186,37 @@ static char *get_usb_statmsg(int status) case 0: return "success"; case -ENOENT: - return "canceled"; - case -ECONNRESET: - return "canceled (async)"; + return "unlinked (sync)"; case -EINPROGRESS: return "pending"; case -EPROTO: - return "bit stuffing or unknown USB error"; + return "bit stuffing error, timeout, or unknown USB error"; case -EILSEQ: - return "Illegal byte sequence (CRC mismatch)"; - case -EPIPE: - return "babble detect or endpoint stalled"; - case -ENOSR: - return "buffer error"; + return "CRC mismatch, timeout, or unknown USB error"; case -ETIMEDOUT: return "timed out"; - case -ENODEV: - return "device not present"; + case -EPIPE: + return "endpoint stalled"; + case -ECOMM: + return "IN buffer overrun"; + case -ENOSR: + return "OUT buffer underrun"; + case -EOVERFLOW: + return "too much data"; case -EREMOTEIO: return "short packet detected"; + case -ENODEV: + return "device removed"; case -EXDEV: return "partial isochronous transfer"; case -EINVAL: return "invalid argument"; - case -ENXIO: - return "URB already queued"; - case -EAGAIN: - return "isochronous start frame too early or too much scheduled"; - case -EFBIG: - return "too many isochronous frames requested"; - case -EMSGSIZE: - return "endpoint message size zero"; + case -ECONNRESET: + return "unlinked (async)"; case -ESHUTDOWN: - return "endpoint shutdown"; - case -EBUSY: - return "another request pending"; + return "device shut down"; default: - snprintf(unkmsg, sizeof(unkmsg), "unknown error %d", status); + snprintf(unkmsg, sizeof(unkmsg), "unknown status %d", status); return unkmsg; } } @@ -277,18 +317,17 @@ static inline void error_hangup(struct bc_state *bcs) gig_dbg(DEBUG_ANY, "%s: scheduling HUP for channel %d", __func__, bcs->channel); - if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) { - //FIXME what should we do? - return; - } + if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) + dev_err(cs->dev, "event queue full\n"); gigaset_schedule_event(cs); } /* error_reset * reset Gigaset device because of an unrecoverable error - * This function may be called from any context and takes care of scheduling - * the necessary actions for execution outside of interrupt context. + * This function may be called from any context, and should take care of + * scheduling the necessary actions for execution outside of interrupt context. + * Right now, it just generates a kernel message calling for help. * argument: * controller state structure */ @@ -364,36 +403,38 @@ static void cmd_in_timeout(unsigned long data) { struct cardstate *cs = (struct cardstate *) data; struct bas_cardstate *ucs = cs->hw.bas; - unsigned long flags; - spin_lock_irqsave(&cs->lock, flags); - if (unlikely(!cs->connected)) { - gig_dbg(DEBUG_USBREQ, "%s: disconnected", __func__); - spin_unlock_irqrestore(&cs->lock, flags); - return; - } if (!ucs->rcvbuf_size) { gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__); - spin_unlock_irqrestore(&cs->lock, flags); return; } - spin_unlock_irqrestore(&cs->lock, flags); dev_err(cs->dev, "timeout reading AT response\n"); error_reset(cs); //FIXME retry? } +/* set/clear bits in base connection state, return previous state + */ +inline static int update_basstate(struct bas_cardstate *ucs, + int set, int clear) +{ + unsigned long flags; + int state; -static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs); + spin_lock_irqsave(&ucs->lock, flags); + state = atomic_read(&ucs->basstate); + atomic_set(&ucs->basstate, (state & ~clear) | set); + spin_unlock_irqrestore(&ucs->lock, flags); + return state; +} /* atread_submit - * submit an HD_READ_ATMESSAGE command URB + * submit an HD_READ_ATMESSAGE command URB and optionally start a timeout * parameters: * cs controller state structure * timeout timeout in 1/10 sec., 0: none * return value: * 0 on success - * -EINVAL if a NULL pointer is encountered somewhere * -EBUSY if another request is pending * any URB submission error code */ @@ -405,7 +446,7 @@ static int atread_submit(struct cardstate *cs, int timeout) gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)", ucs->rcvbuf_size); - if (ucs->urb_cmd_in->status == -EINPROGRESS) { + if (update_basstate(ucs, BS_ATRDPEND, 0) & BS_ATRDPEND) { dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: URB busy\n"); return -EBUSY; @@ -423,6 +464,7 @@ static int atread_submit(struct cardstate *cs, int timeout) read_ctrl_callback, cs->inbuf); if ((ret = usb_submit_urb(ucs->urb_cmd_in, SLAB_ATOMIC)) != 0) { + update_basstate(ucs, 0, BS_ATRDPEND); dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n", get_usb_statmsg(ret)); return ret; @@ -438,26 +480,6 @@ static int atread_submit(struct cardstate *cs, int timeout) return 0; } -static void stopurbs(struct bas_bc_state *); -static int start_cbsend(struct cardstate *); - -/* set/clear bits in base connection state - */ -inline static void update_basstate(struct bas_cardstate *ucs, - int set, int clear) -{ - unsigned long flags; - int state; - - spin_lock_irqsave(&ucs->lock, flags); - state = atomic_read(&ucs->basstate); - state &= ~clear; - state |= set; - atomic_set(&ucs->basstate, state); - spin_unlock_irqrestore(&ucs->lock, flags); -} - - /* read_int_callback * USB completion handler for interrupt pipe input * called by the USB subsystem in interrupt context @@ -471,20 +493,25 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs) struct bas_cardstate *ucs = cs->hw.bas; struct bc_state *bcs; unsigned long flags; - int status; + int rc; unsigned l; int channel; switch (urb->status) { case 0: /* success */ break; - case -ENOENT: /* canceled */ - case -ECONNRESET: /* canceled (async) */ + case -ENOENT: /* cancelled */ + case -ECONNRESET: /* cancelled (async) */ case -EINPROGRESS: /* pending */ /* ignore silently */ gig_dbg(DEBUG_USBREQ, "%s: %s", __func__, get_usb_statmsg(urb->status)); return; + case -ENODEV: /* device removed */ + case -ESHUTDOWN: /* device shut down */ + //FIXME use this as disconnect indicator? + gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__); + return; default: /* severe trouble */ dev_warn(cs->dev, "interrupt read: %s\n", get_usb_statmsg(urb->status)); @@ -492,6 +519,13 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs) goto resubmit; } + /* drop incomplete packets even if the missing bytes wouldn't matter */ + if (unlikely(urb->actual_length < 3)) { + dev_warn(cs->dev, "incomplete interrupt packet (%d bytes)\n", + urb->actual_length); + goto resubmit; + } + l = (unsigned) ucs->int_in_buf[1] + (((unsigned) ucs->int_in_buf[2]) << 8); @@ -558,25 +592,28 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs) } spin_lock_irqsave(&cs->lock, flags); if (ucs->rcvbuf_size) { - spin_unlock_irqrestore(&cs->lock, flags); + /* throw away previous buffer - we have no queue */ dev_err(cs->dev, - "receive AT data overrun, %d bytes lost\n", l); - error_reset(cs); //FIXME reschedule - break; + "receive AT data overrun, %d bytes lost\n", + ucs->rcvbuf_size); + kfree(ucs->rcvbuf); + ucs->rcvbuf_size = 0; } if ((ucs->rcvbuf = kmalloc(l, GFP_ATOMIC)) == NULL) { spin_unlock_irqrestore(&cs->lock, flags); - dev_err(cs->dev, "out of memory, %d bytes lost\n", l); - error_reset(cs); //FIXME reschedule + dev_err(cs->dev, "out of memory receiving AT data\n"); + error_reset(cs); break; } ucs->rcvbuf_size = l; ucs->retry_cmd_in = 0; - if ((status = atread_submit(cs, BAS_TIMEOUT)) < 0) { + if ((rc = atread_submit(cs, BAS_TIMEOUT)) < 0) { kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; - error_reset(cs); //FIXME reschedule + if (rc != -ENODEV) + //FIXME corrective action? + error_reset(cs); } spin_unlock_irqrestore(&cs->lock, flags); break; @@ -598,12 +635,10 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs) check_pending(ucs); resubmit: - spin_lock_irqsave(&cs->lock, flags); - status = cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV; - spin_unlock_irqrestore(&cs->lock, flags); - if (unlikely(status)) { + rc = usb_submit_urb(urb, SLAB_ATOMIC); + if (unlikely(rc != 0 && rc != -ENODEV)) { dev_err(cs->dev, "could not resubmit interrupt URB: %s\n", - get_usb_statmsg(status)); + get_usb_rcmsg(rc)); error_reset(cs); } } @@ -622,18 +657,12 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs) struct bas_cardstate *ucs = cs->hw.bas; int have_data = 0; unsigned numbytes; - unsigned long flags; + int rc; - spin_lock_irqsave(&cs->lock, flags); - if (unlikely(!cs->connected)) { - warn("%s: disconnected", __func__); - spin_unlock_irqrestore(&cs->lock, flags); - return; - } + update_basstate(ucs, 0, BS_ATRDPEND); if (!ucs->rcvbuf_size) { dev_warn(cs->dev, "%s: no receive in progress\n", __func__); - spin_unlock_irqrestore(&cs->lock, flags); return; } @@ -666,9 +695,11 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs) } break; - case -ENOENT: /* canceled */ - case -ECONNRESET: /* canceled (async) */ + case -ENOENT: /* cancelled */ + case -ECONNRESET: /* cancelled (async) */ case -EINPROGRESS: /* pending */ + case -ENODEV: /* device removed */ + case -ESHUTDOWN: /* device shut down */ /* no action necessary */ gig_dbg(DEBUG_USBREQ, "%s: %s", __func__, get_usb_statmsg(urb->status)); @@ -681,11 +712,11 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs) if (ucs->retry_cmd_in++ < BAS_RETRY) { dev_notice(cs->dev, "control read: retry %d\n", ucs->retry_cmd_in); - if (atread_submit(cs, BAS_TIMEOUT) >= 0) { - /* resubmitted - bypass regular exit block */ - spin_unlock_irqrestore(&cs->lock, flags); + rc = atread_submit(cs, BAS_TIMEOUT); + if (rc >= 0 || rc == -ENODEV) + /* resubmitted or disconnected */ + /* - bypass regular exit block */ return; - } } else { dev_err(cs->dev, "control read: giving up after %d tries\n", @@ -697,7 +728,6 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs) kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; - spin_unlock_irqrestore(&cs->lock, flags); if (have_data) { gig_dbg(DEBUG_INTR, "%s-->BH", __func__); gigaset_schedule_event(cs); @@ -719,8 +749,11 @@ static void read_iso_callback(struct urb *urb, struct pt_regs *regs) int i, rc; /* status codes not worth bothering the tasklet with */ - if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || - urb->status == -EINPROGRESS)) { + if (unlikely(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -EINPROGRESS || + urb->status == -ENODEV || + urb->status == -ESHUTDOWN)) { gig_dbg(DEBUG_ISO, "%s: %s", __func__, get_usb_statmsg(urb->status)); return; @@ -740,9 +773,9 @@ static void read_iso_callback(struct urb *urb, struct pt_regs *regs) for (i = 0; i < BAS_NUMFRAMES; i++) { ubc->isoinlost += urb->iso_frame_desc[i].actual_length; if (unlikely(urb->iso_frame_desc[i].status != 0 && - urb->iso_frame_desc[i].status != -EINPROGRESS)) { + urb->iso_frame_desc[i].status != + -EINPROGRESS)) ubc->loststatus = urb->iso_frame_desc[i].status; - } urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } @@ -754,10 +787,10 @@ static void read_iso_callback(struct urb *urb, struct pt_regs *regs) gig_dbg(DEBUG_ISO, "%s: isoc read overrun/resubmit", __func__); rc = usb_submit_urb(urb, SLAB_ATOMIC); - if (unlikely(rc != 0)) { + if (unlikely(rc != 0 && rc != -ENODEV)) { dev_err(bcs->cs->dev, "could not resubmit isochronous read " - "URB: %s\n", get_usb_statmsg(rc)); + "URB: %s\n", get_usb_rcmsg(rc)); dump_urb(DEBUG_ISO, "isoc read", urb); error_hangup(bcs); } @@ -780,8 +813,11 @@ static void write_iso_callback(struct urb *urb, struct pt_regs *regs) unsigned long flags; /* status codes not worth bothering the tasklet with */ - if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || - urb->status == -EINPROGRESS)) { + if (unlikely(urb->status == -ENOENT || + urb->status == -ECONNRESET || + urb->status == -EINPROGRESS || + urb->status == -ENODEV || + urb->status == -ESHUTDOWN)) { gig_dbg(DEBUG_ISO, "%s: %s", __func__, get_usb_statmsg(urb->status)); return; @@ -822,7 +858,6 @@ static int starturbs(struct bc_state *bcs) for (k = 0; k < BAS_INURBS; k++) { urb = ubc->isoinurbs[k]; if (!urb) { - dev_err(bcs->cs->dev, "isoinurbs[%d]==NULL\n", k); rc = -EFAULT; goto error; } @@ -844,12 +879,8 @@ static int starturbs(struct bc_state *bcs) } dump_urb(DEBUG_ISO, "Initial isoc read", urb); - if ((rc = usb_submit_urb(urb, SLAB_ATOMIC)) != 0) { - dev_err(bcs->cs->dev, - "could not submit isochronous read URB %d: %s\n", - k, get_usb_statmsg(rc)); + if ((rc = usb_submit_urb(urb, SLAB_ATOMIC)) != 0) goto error; - } } /* initialize L2 transmission */ @@ -859,7 +890,6 @@ static int starturbs(struct bc_state *bcs) for (k = 0; k < BAS_OUTURBS; ++k) { urb = ubc->isoouturbs[k].urb; if (!urb) { - dev_err(bcs->cs->dev, "isoouturbs[%d].urb==NULL\n", k); rc = -EFAULT; goto error; } @@ -885,12 +915,8 @@ static int starturbs(struct bc_state *bcs) for (k = 0; k < 2; ++k) { dump_urb(DEBUG_ISO, "Initial isoc write", urb); rc = usb_submit_urb(ubc->isoouturbs[k].urb, SLAB_ATOMIC); - if (rc != 0) { - dev_err(bcs->cs->dev, - "could not submit isochronous write URB %d: %s\n", - k, get_usb_statmsg(rc)); + if (rc != 0) goto error; - } } dump_urb(DEBUG_ISO, "Initial isoc write (free)", urb); ubc->isooutfree = &ubc->isoouturbs[2]; @@ -916,15 +942,15 @@ static void stopurbs(struct bas_bc_state *ubc) for (k = 0; k < BAS_INURBS; ++k) { rc = usb_unlink_urb(ubc->isoinurbs[k]); gig_dbg(DEBUG_ISO, - "%s: isoc input URB %d unlinked, result = %d", - __func__, k, rc); + "%s: isoc input URB %d unlinked, result = %s", + __func__, k, get_usb_rcmsg(rc)); } for (k = 0; k < BAS_OUTURBS; ++k) { rc = usb_unlink_urb(ubc->isoouturbs[k].urb); gig_dbg(DEBUG_ISO, - "%s: isoc output URB %d unlinked, result = %d", - __func__, k, rc); + "%s: isoc output URB %d unlinked, result = %s", + __func__, k, get_usb_rcmsg(rc)); } } @@ -934,7 +960,7 @@ static void stopurbs(struct bas_bc_state *ubc) /* submit_iso_write_urb * fill and submit the next isochronous write URB * parameters: - * bcs B channel state structure + * ucx context structure containing URB * return value: * number of frames submitted in URB * 0 if URB not submitted because no data available (isooutbuf busy) @@ -946,7 +972,6 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) struct bas_bc_state *ubc = ucx->bcs->hw.bas; struct usb_iso_packet_descriptor *ifd; int corrbytes, nframe, rc; - unsigned long flags; /* urb->dev is clobbered by USB subsystem */ urb->dev = ucx->bcs->cs->hw.bas->udev; @@ -992,20 +1017,22 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) ifd->status = 0; ifd->actual_length = 0; } - if ((urb->number_of_packets = nframe) > 0) { - spin_lock_irqsave(&ucx->bcs->cs->lock, flags); - rc = ucx->bcs->cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV; - spin_unlock_irqrestore(&ucx->bcs->cs->lock, flags); + if (unlikely(nframe == 0)) + return 0; /* no data to send */ + urb->number_of_packets = nframe; - if (rc) { + rc = usb_submit_urb(urb, SLAB_ATOMIC); + if (unlikely(rc)) { + if (rc == -ENODEV) + /* device removed - give up silently */ + gig_dbg(DEBUG_ISO, "%s: disconnected", __func__); + else dev_err(ucx->bcs->cs->dev, "could not submit isochronous write URB: %s\n", - get_usb_statmsg(rc)); - dump_urb(DEBUG_ISO, "isoc write", urb); - return rc; - } - ++ubc->numsub; + get_usb_rcmsg(rc)); + return rc; } + ++ubc->numsub; return nframe; } @@ -1028,6 +1055,7 @@ static void write_iso_tasklet(unsigned long data) int i; struct sk_buff *skb; int len; + int rc; /* loop while completed URBs arrive in time */ for (;;) { @@ -1057,7 +1085,8 @@ static void write_iso_tasklet(unsigned long data) ubc->isooutfree = NULL; spin_unlock_irqrestore(&ubc->isooutlock, flags); if (next) { - if (submit_iso_write_urb(next) <= 0) { + rc = submit_iso_write_urb(next); + if (unlikely(rc <= 0 && rc != -ENODEV)) { /* could not submit URB, put it back */ spin_lock_irqsave(&ubc->isooutlock, flags); if (ubc->isooutfree == NULL) { @@ -1077,17 +1106,18 @@ static void write_iso_tasklet(unsigned long data) /* process completed URB */ urb = done->urb; switch (urb->status) { + case -EXDEV: /* partial completion */ + gig_dbg(DEBUG_ISO, "%s: URB partially completed", + __func__); + /* fall through - what's the difference anyway? */ case 0: /* normal completion */ - break; - case -EXDEV: /* inspect individual frames */ - /* assumptions (for lack of documentation): - * - actual_length bytes of the frame in error are + /* inspect individual frames + * assumptions (for lack of documentation): + * - actual_length bytes of first frame in error are * successfully sent * - all following frames are not sent at all */ - gig_dbg(DEBUG_ISO, "%s: URB partially completed", - __func__); - offset = done->limit; /* just in case */ + offset = done->limit; /* default (no error) */ for (i = 0; i < BAS_NUMFRAMES; i++) { ifd = &urb->iso_frame_desc[i]; if (ifd->status || @@ -1122,7 +1152,7 @@ static void write_iso_tasklet(unsigned long data) } #endif break; - case -EPIPE: //FIXME is this the code for "underrun"? + case -EPIPE: /* stall - probably underrun */ dev_err(cs->dev, "isochronous write stalled\n"); error_hangup(bcs); break; @@ -1142,7 +1172,8 @@ static void write_iso_tasklet(unsigned long data) spin_unlock_irqrestore(&ubc->isooutlock, flags); if (next) { /* only one URB still active - resubmit one */ - if (submit_iso_write_urb(next) <= 0) { + rc = submit_iso_write_urb(next); + if (unlikely(rc <= 0 && rc != -ENODEV)) { /* couldn't submit */ error_hangup(bcs); } @@ -1222,10 +1253,9 @@ static void read_iso_tasklet(unsigned long data) break; case -ENOENT: case -ECONNRESET: - gig_dbg(DEBUG_ISO, "%s: URB canceled", __func__); - continue; /* -> skip */ - case -EINPROGRESS: /* huh? */ - gig_dbg(DEBUG_ISO, "%s: URB still pending", __func__); + case -EINPROGRESS: + gig_dbg(DEBUG_ISO, "%s: %s", + __func__, get_usb_statmsg(urb->status)); continue; /* -> skip */ case -EPIPE: dev_err(cs->dev, "isochronous read stalled\n"); @@ -1290,13 +1320,11 @@ static void read_iso_tasklet(unsigned long data) urb->dev = bcs->cs->hw.bas->udev; urb->transfer_flags = URB_ISO_ASAP; urb->number_of_packets = BAS_NUMFRAMES; - spin_lock_irqsave(&cs->lock, flags); - rc = cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV; - spin_unlock_irqrestore(&cs->lock, flags); - if (rc) { + rc = usb_submit_urb(urb, SLAB_ATOMIC); + if (unlikely(rc != 0 && rc != -ENODEV)) { dev_err(cs->dev, "could not resubmit isochronous read URB: %s\n", - get_usb_statmsg(rc)); + get_usb_rcmsg(rc)); dump_urb(DEBUG_ISO, "resubmit iso read", urb); error_hangup(bcs); } @@ -1397,7 +1425,6 @@ static void write_ctrl_callback(struct urb *urb, struct pt_regs *regs) * timeout timeout in seconds (0: no timeout) * return value: * 0 on success - * -EINVAL if a NULL pointer is encountered somewhere * -EBUSY if another request is pending * any URB submission error code */ @@ -1418,12 +1445,6 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout) req, ucs->pending); return -EBUSY; } - if (ucs->urb_ctrl->status == -EINPROGRESS) { - spin_unlock_irqrestore(&ucs->lock, flags); - dev_err(bcs->cs->dev, - "could not submit request 0x%02x: URB busy\n", req); - return -EBUSY; - } ucs->dr_ctrl.bRequestType = OUT_VENDOR_REQ; ucs->dr_ctrl.bRequest = req; @@ -1465,22 +1486,36 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout) static int gigaset_init_bchannel(struct bc_state *bcs) { int req, ret; + unsigned long flags; + + spin_lock_irqsave(&bcs->cs->lock, flags); + if (unlikely(!bcs->cs->connected)) { + gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__); + spin_unlock_irqrestore(&bcs->cs->lock, flags); + return -ENODEV; + } if ((ret = starturbs(bcs)) < 0) { dev_err(bcs->cs->dev, - "could not start isochronous I/O for channel %d\n", - bcs->channel + 1); - error_hangup(bcs); + "could not start isochronous I/O for channel B%d: %s\n", + bcs->channel + 1, + ret == -EFAULT ? "null URB" : get_usb_rcmsg(ret)); + if (ret != -ENODEV) + error_hangup(bcs); + spin_unlock_irqrestore(&bcs->cs->lock, flags); return ret; } req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL; if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) { - dev_err(bcs->cs->dev, "could not open channel %d: %s\n", - bcs->channel + 1, get_usb_statmsg(ret)); + dev_err(bcs->cs->dev, "could not open channel B%d\n", + bcs->channel + 1); stopurbs(bcs->hw.bas); - error_hangup(bcs); + if (ret != -ENODEV) + error_hangup(bcs); } + + spin_unlock_irqrestore(&bcs->cs->lock, flags); return ret; } @@ -1497,19 +1532,30 @@ static int gigaset_init_bchannel(struct bc_state *bcs) static int gigaset_close_bchannel(struct bc_state *bcs) { int req, ret; + unsigned long flags; + + spin_lock_irqsave(&bcs->cs->lock, flags); + if (unlikely(!bcs->cs->connected)) { + spin_unlock_irqrestore(&bcs->cs->lock, flags); + gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__); + return -ENODEV; + } if (!(atomic_read(&bcs->cs->hw.bas->basstate) & (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) { /* channel not running: just signal common.c */ + spin_unlock_irqrestore(&bcs->cs->lock, flags); gigaset_bchannel_down(bcs); return 0; } + /* channel running: tell device to close it */ req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL; if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) - dev_err(bcs->cs->dev, - "could not submit HD_CLOSE_BxCHANNEL request: %s\n", - get_usb_statmsg(ret)); + dev_err(bcs->cs->dev, "closing channel B%d failed\n", + bcs->channel + 1); + + spin_unlock_irqrestore(&bcs->cs->lock, flags); return ret; } @@ -1545,8 +1591,6 @@ static void complete_cb(struct cardstate *cs) kfree(cb); } -static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len); - /* write_command_callback * USB completion handler for AT command transmission * called by the USB subsystem in interrupt context @@ -1560,13 +1604,17 @@ static void write_command_callback(struct urb *urb, struct pt_regs *regs) struct bas_cardstate *ucs = cs->hw.bas; unsigned long flags; + update_basstate(ucs, 0, BS_ATWRPEND); + /* check status */ switch (urb->status) { case 0: /* normal completion */ break; - case -ENOENT: /* canceled */ - case -ECONNRESET: /* canceled (async) */ + case -ENOENT: /* cancelled */ + case -ECONNRESET: /* cancelled (async) */ case -EINPROGRESS: /* pending */ + case -ENODEV: /* device removed */ + case -ESHUTDOWN: /* device shut down */ /* ignore silently */ gig_dbg(DEBUG_USBREQ, "%s: %s", __func__, get_usb_statmsg(urb->status)); @@ -1627,19 +1675,17 @@ static void atrdy_timeout(unsigned long data) * len length of command to send * return value: * 0 on success - * -EFAULT if a NULL pointer is encountered somewhere * -EBUSY if another request is pending * any URB submission error code */ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len) { struct bas_cardstate *ucs = cs->hw.bas; - unsigned long flags; - int ret; + int rc; gig_dbg(DEBUG_USBREQ, "-------> HD_WRITE_ATMESSAGE (%d)", len); - if (ucs->urb_cmd_out->status == -EINPROGRESS) { + if (update_basstate(ucs, BS_ATWRPEND, 0) & BS_ATWRPEND) { dev_err(cs->dev, "could not submit HD_WRITE_ATMESSAGE: URB busy\n"); return -EBUSY; @@ -1654,29 +1700,22 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len) usb_sndctrlpipe(ucs->udev, 0), (unsigned char*) &ucs->dr_cmd_out, buf, len, write_command_callback, cs); - - spin_lock_irqsave(&cs->lock, flags); - ret = cs->connected ? usb_submit_urb(ucs->urb_cmd_out, SLAB_ATOMIC) : -ENODEV; - spin_unlock_irqrestore(&cs->lock, flags); - - if (ret) { + rc = usb_submit_urb(ucs->urb_cmd_out, SLAB_ATOMIC); + if (unlikely(rc)) { + update_basstate(ucs, 0, BS_ATWRPEND); dev_err(cs->dev, "could not submit HD_WRITE_ATMESSAGE: %s\n", - get_usb_statmsg(ret)); - return ret; + get_usb_rcmsg(rc)); + return rc; } - /* submitted successfully */ - update_basstate(ucs, 0, BS_ATREADY); - - /* start timeout if necessary */ - if (!(atomic_read(&ucs->basstate) & BS_ATTIMER)) { + /* submitted successfully, start timeout if necessary */ + if (!(update_basstate(ucs, BS_ATTIMER, BS_ATREADY) & BS_ATTIMER)) { gig_dbg(DEBUG_OUTPUT, "setting ATREADY timeout of %d/10 secs", ATRDY_TIMEOUT); ucs->timer_atrdy.expires = jiffies + ATRDY_TIMEOUT * HZ / 10; ucs->timer_atrdy.data = (unsigned long) cs; ucs->timer_atrdy.function = atrdy_timeout; add_timer(&ucs->timer_atrdy); - update_basstate(ucs, BS_ATTIMER, 0); } return 0; } @@ -1702,7 +1741,6 @@ static int start_cbsend(struct cardstate *cs) gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "AT channel not open"); rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT); if (rc < 0) { - dev_err(cs->dev, "could not open AT channel\n"); /* flush command queue */ spin_lock_irqsave(&cs->cmdlock, flags); while (cs->cmdbuf != NULL) @@ -1786,8 +1824,14 @@ static int gigaset_write_cmd(struct cardstate *cs, cs->lastcmdbuf = cb; spin_unlock_irqrestore(&cs->cmdlock, flags); + spin_lock_irqsave(&cs->lock, flags); + if (unlikely(!cs->connected)) { + spin_unlock_irqrestore(&cs->lock, flags); + gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__); + return -ENODEV; + } status = start_cbsend(cs); - + spin_unlock_irqrestore(&cs->lock, flags); return status < 0 ? status : len; } @@ -1849,12 +1893,32 @@ static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6]) */ static int gigaset_freebcshw(struct bc_state *bcs) { - if (!bcs->hw.bas) + struct bas_bc_state *ubc = bcs->hw.bas; + int i; + + if (!ubc) return 0; - if (bcs->hw.bas->isooutbuf) - kfree(bcs->hw.bas->isooutbuf); - kfree(bcs->hw.bas); + /* kill URBs and tasklets before freeing - better safe than sorry */ + atomic_set(&ubc->running, 0); + for (i = 0; i < BAS_OUTURBS; ++i) + if (ubc->isoouturbs[i].urb) { + gig_dbg(DEBUG_INIT, "%s: killing iso out URB %d", + __func__, i); + usb_kill_urb(ubc->isoouturbs[i].urb); + usb_free_urb(ubc->isoouturbs[i].urb); + } + for (i = 0; i < BAS_INURBS; ++i) + if (ubc->isoinurbs[i]) { + gig_dbg(DEBUG_INIT, "%s: killing iso in URB %d", + __func__, i); + usb_kill_urb(ubc->isoinurbs[i]); + usb_free_urb(ubc->isoinurbs[i]); + } + tasklet_kill(&ubc->sent_tasklet); + tasklet_kill(&ubc->rcvd_tasklet); + kfree(ubc->isooutbuf); + kfree(ubc); bcs->hw.bas = NULL; return 1; } @@ -1931,13 +1995,9 @@ static void gigaset_reinitbcshw(struct bc_state *bcs) static void gigaset_freecshw(struct cardstate *cs) { - struct bas_cardstate *ucs = cs->hw.bas; - - del_timer(&ucs->timer_ctrl); - del_timer(&ucs->timer_atrdy); - del_timer(&ucs->timer_cmd_in); - + /* timers, URBs and rcvbuf are disposed of in disconnect */ kfree(cs->hw.bas); + cs->hw.bas = NULL; } static int gigaset_initcshw(struct cardstate *cs) @@ -2041,23 +2101,13 @@ static int gigaset_probe(struct usb_interface *interface, struct bas_bc_state *ubc; struct usb_endpoint_descriptor *endpoint; int i, j; - int ret; + int rc; gig_dbg(DEBUG_ANY, "%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)", __func__, le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); - /* See if the device offered us matches what we can accept */ - if ((le16_to_cpu(udev->descriptor.idVendor) != USB_GIGA_VENDOR_ID) || - (le16_to_cpu(udev->descriptor.idProduct) != USB_GIGA_PRODUCT_ID && - le16_to_cpu(udev->descriptor.idProduct) != USB_4175_PRODUCT_ID && - le16_to_cpu(udev->descriptor.idProduct) != USB_SX303_PRODUCT_ID && - le16_to_cpu(udev->descriptor.idProduct) != USB_SX353_PRODUCT_ID)) { - gig_dbg(DEBUG_ANY, "%s: unmatched ID - exiting", __func__); - return -ENODEV; - } - /* set required alternate setting */ hostif = interface->cur_altsetting; if (hostif->desc.bAlternateSetting != 3) { @@ -2105,45 +2155,22 @@ static int gigaset_probe(struct usb_interface *interface, * - three for the different uses of the default control pipe * - three for each isochronous pipe */ - ucs->urb_int_in = usb_alloc_urb(0, SLAB_KERNEL); - if (!ucs->urb_int_in) { - dev_err(cs->dev, "no free urbs available\n"); - goto error; - } - ucs->urb_cmd_in = usb_alloc_urb(0, SLAB_KERNEL); - if (!ucs->urb_cmd_in) { - dev_err(cs->dev, "no free urbs available\n"); - goto error; - } - ucs->urb_cmd_out = usb_alloc_urb(0, SLAB_KERNEL); - if (!ucs->urb_cmd_out) { - dev_err(cs->dev, "no free urbs available\n"); - goto error; - } - ucs->urb_ctrl = usb_alloc_urb(0, SLAB_KERNEL); - if (!ucs->urb_ctrl) { - dev_err(cs->dev, "no free urbs available\n"); - goto error; - } + if (!(ucs->urb_int_in = usb_alloc_urb(0, SLAB_KERNEL)) || + !(ucs->urb_cmd_in = usb_alloc_urb(0, SLAB_KERNEL)) || + !(ucs->urb_cmd_out = usb_alloc_urb(0, SLAB_KERNEL)) || + !(ucs->urb_ctrl = usb_alloc_urb(0, SLAB_KERNEL))) + goto allocerr; for (j = 0; j < 2; ++j) { ubc = cs->bcs[j].hw.bas; - for (i = 0; i < BAS_OUTURBS; ++i) { - ubc->isoouturbs[i].urb = - usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL); - if (!ubc->isoouturbs[i].urb) { - dev_err(cs->dev, "no free urbs available\n"); - goto error; - } - } - for (i = 0; i < BAS_INURBS; ++i) { - ubc->isoinurbs[i] = - usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL); - if (!ubc->isoinurbs[i]) { - dev_err(cs->dev, "no free urbs available\n"); - goto error; - } - } + for (i = 0; i < BAS_OUTURBS; ++i) + if (!(ubc->isoouturbs[i].urb = + usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL))) + goto allocerr; + for (i = 0; i < BAS_INURBS; ++i) + if (!(ubc->isoinurbs[i] = + usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL))) + goto allocerr; } ucs->rcvbuf = NULL; @@ -2156,15 +2183,14 @@ static int gigaset_probe(struct usb_interface *interface, (endpoint->bEndpointAddress) & 0x0f), ucs->int_in_buf, 3, read_int_callback, cs, endpoint->bInterval); - ret = usb_submit_urb(ucs->urb_int_in, SLAB_KERNEL); - if (ret) { + if ((rc = usb_submit_urb(ucs->urb_int_in, SLAB_KERNEL)) != 0) { dev_err(cs->dev, "could not submit interrupt URB: %s\n", - get_usb_statmsg(ret)); + get_usb_rcmsg(rc)); goto error; } /* tell the device that the driver is ready */ - if ((ret = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0)) != 0) + if ((rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0)) != 0) goto error; /* tell common part that the device is ready */ @@ -2179,6 +2205,8 @@ static int gigaset_probe(struct usb_interface *interface, return 0; +allocerr: + dev_err(cs->dev, "could not allocate URBs\n"); error: freeurbs(cs); usb_set_intfdata(interface, NULL); @@ -2193,19 +2221,34 @@ static void gigaset_disconnect(struct usb_interface *interface) { struct cardstate *cs; struct bas_cardstate *ucs; + int j; cs = usb_get_intfdata(interface); ucs = cs->hw.bas; dev_info(cs->dev, "disconnecting Gigaset base\n"); + + /* mark base as not ready, all channels disconnected */ + atomic_set(&ucs->basstate, 0); + + /* tell LL all channels are down */ + //FIXME shouldn't gigaset_stop() do this? + for (j = 0; j < 2; ++j) + gigaset_bchannel_down(cs->bcs + j); + + /* stop driver (common part) */ gigaset_stop(cs); + + /* stop timers and URBs, free ressources */ + del_timer_sync(&ucs->timer_ctrl); + del_timer_sync(&ucs->timer_atrdy); + del_timer_sync(&ucs->timer_cmd_in); freeurbs(cs); usb_set_intfdata(interface, NULL); kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; - atomic_set(&ucs->basstate, 0); usb_put_dev(ucs->udev); ucs->interface = NULL; ucs->udev = NULL; @@ -2277,6 +2320,8 @@ error: if (cardstate) */ static void __exit bas_gigaset_exit(void) { + struct bas_cardstate *ucs = cardstate->hw.bas; + gigaset_blockdriver(driver); /* => probe will fail * => no gigaset_start any more */ @@ -2284,14 +2329,26 @@ static void __exit bas_gigaset_exit(void) gigaset_shutdown(cardstate); /* from now on, no isdn callback should be possible */ - if (atomic_read(&cardstate->hw.bas->basstate) & BS_ATOPEN) { - gig_dbg(DEBUG_ANY, "closing AT channel"); - if (req_submit(cardstate->bcs, - HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT) >= 0) { - /* successfully submitted */ - //FIXME wait for completion? - } + /* close all still open channels */ + if (atomic_read(&ucs->basstate) & BS_B1OPEN) { + gig_dbg(DEBUG_INIT, "closing B1 channel"); + usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0), + HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ, 0, 0, + NULL, 0, BAS_TIMEOUT); + } + if (atomic_read(&ucs->basstate) & BS_B2OPEN) { + gig_dbg(DEBUG_INIT, "closing B2 channel"); + usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0), + HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ, 0, 0, + NULL, 0, BAS_TIMEOUT); + } + if (atomic_read(&ucs->basstate) & BS_ATOPEN) { + gig_dbg(DEBUG_INIT, "closing AT channel"); + usb_control_msg(ucs->udev, usb_sndctrlpipe(ucs->udev, 0), + HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ, 0, 0, + NULL, 0, BAS_TIMEOUT); } + atomic_set(&ucs->basstate, 0); /* deregister this driver with the USB subsystem */ usb_deregister(&gigaset_usb_driver); diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 749b3da..e55767b 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -781,8 +781,7 @@ error: if (cs) } EXPORT_SYMBOL_GPL(gigaset_initcs); -/* ReInitialize the b-channel structure */ -/* e.g. called on hangup, disconnect */ +/* ReInitialize the b-channel structure on hangup */ void gigaset_bcs_reinit(struct bc_state *bcs) { struct sk_buff *skb; diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index 1ba3424..18e05c0 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c @@ -373,6 +373,9 @@ struct reply_t gigaset_tab_cid_m10x[] = /* for M10x */ {EV_TIMEOUT, 750,750, -1, 0, 0, {ACT_CONNTIMEOUT}}, + /* B channel closed (general case) */ + {EV_BC_CLOSED, -1, -1, -1, -1,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME + /* misc. */ {EV_PROTO_L2, -1, -1, -1, -1,-1, {ACT_PROTO_L2}}, //FIXME diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h index 9d21ba8..22b9693 100644 --- a/drivers/isdn/gigaset/gigaset.h +++ b/drivers/isdn/gigaset/gigaset.h @@ -75,7 +75,7 @@ extern int gigaset_debuglevel; /* "needs" cast to (enum debuglevel) */ * e.g. 'insmod usb_gigaset.o debug=0x2c' will set DEBUG_OPEN, DEBUG_CMD and * DEBUG_INTR. */ -enum debuglevel { /* up to 24 bits (atomic_t) */ +enum debuglevel { DEBUG_REG = 0x0002, /* serial port I/O register operations */ DEBUG_OPEN = 0x0004, /* open/close serial port */ DEBUG_INTR = 0x0008, /* interrupt processing */ @@ -141,7 +141,7 @@ enum debuglevel { /* up to 24 bits (atomic_t) */ printk(KERN_DEBUG KBUILD_MODNAME ": " format "\n", \ ## arg); \ } while (0) -#define DEBUG_DEFAULT (DEBUG_INIT | DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ) +#define DEBUG_DEFAULT (DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ) #else @@ -627,8 +627,7 @@ struct gigaset_ops { /* Called by gigaset_freecs() for freeing bcs->hw.xxx */ int (*freebcshw)(struct bc_state *bcs); - /* Called by gigaset_stop() or gigaset_bchannel_down() for resetting - bcs->hw.xxx */ + /* Called by gigaset_bchannel_down() for resetting bcs->hw.xxx */ void (*reinitbcshw)(struct bc_state *bcs); /* Called by gigaset_initcs() for setting up cs->hw.xxx */ diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c index 0815dbf..1654fa4 100644 --- a/drivers/isdn/gigaset/i4l.c +++ b/drivers/isdn/gigaset/i4l.c @@ -73,7 +73,7 @@ static int writebuf_from_LL(int driverID, int channel, int ack, len, skblen, (unsigned) skb->head[0], (unsigned) skb->head[1]); /* pass to device-specific module */ - return cs->ops->send_skb(bcs, skb); //FIXME cs->ops->send_skb() must handle !cs->connected correctly + return cs->ops->send_skb(bcs, skb); } void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c index 45f017e..8667daa 100644 --- a/drivers/isdn/gigaset/isocdata.c +++ b/drivers/isdn/gigaset/isocdata.c @@ -992,14 +992,18 @@ int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb) int len = skb->len; unsigned long flags; + spin_lock_irqsave(&bcs->cs->lock, flags); + if (!bcs->cs->connected) { + spin_unlock_irqrestore(&bcs->cs->lock, flags); + return -ENODEV; + } + skb_queue_tail(&bcs->squeue, skb); gig_dbg(DEBUG_ISO, "%s: skb queued, qlen=%d", __func__, skb_queue_len(&bcs->squeue)); /* tasklet submits URB if necessary */ - spin_lock_irqsave(&bcs->cs->lock, flags); - if (bcs->cs->connected) - tasklet_schedule(&bcs->hw.bas->sent_tasklet); + tasklet_schedule(&bcs->hw.bas->sent_tasklet); spin_unlock_irqrestore(&bcs->cs->lock, flags); return len; /* ok so far */ -- cgit v1.1 From 8c4335a87c9785d2102ab23f09393038e1663314 Mon Sep 17 00:00:00 2001 From: "akpm@osdl.org" Date: Sat, 22 Apr 2006 02:36:15 -0700 Subject: [PATCH] Altix snsc: duplicate kobject fix from: Greg Howard Fix Altix system controller (snsc) device names to include the slot number of the blade whose associated system controller is the target of the device interface. Including the slot number avoids a problem we're currently having where slots within the same enclosure are attempting to create multiple kobjects with identical names. Signed-off-by: Greg Howard Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/snsc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c index b543821..56c8243 100644 --- a/drivers/char/snsc.c +++ b/drivers/char/snsc.c @@ -390,7 +390,8 @@ scdrv_init(void) format_module_id(devnamep, geo_module(geoid), MODULE_FORMAT_BRIEF); devnamep = devname + strlen(devname); - sprintf(devnamep, "#%d", geo_slab(geoid)); + sprintf(devnamep, "^%d#%d", geo_slot(geoid), + geo_slab(geoid)); /* allocate sysctl device data */ scd = kzalloc(sizeof (struct sysctl_data_s), -- cgit v1.1 From 59e89f3a091d5cf93f4b176aedcfded61ece5252 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:36:35 -0700 Subject: [PATCH] tpm: fix memory leak The eventname was kmalloc'd and not freed in the *_show functions. This bug was found by Coverity. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_bios.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c index 537aa45..0549e2a 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_bios.c @@ -306,6 +306,7 @@ static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) /* 5th: delimiter */ seq_putc(m, '\0'); + kfree(eventname); return 0; } @@ -353,6 +354,7 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) /* 4th: eventname <= max + \'0' delimiter */ seq_printf(m, " %s\n", eventname); + kfree(eventname); return 0; } -- cgit v1.1 From 7c69a47f1badf40dfa2febac71df98d32b1b56d7 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:36:46 -0700 Subject: [PATCH] tpm: fix missing string A string corresponding to the tcpa_pc_event_id POST_CONTENTS was missing causing an overflow bug when access was attempted in the get_event_name function. This bug was found by Coverity. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_bios.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c index 0549e2a..5bcbaef 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_bios.c @@ -120,6 +120,7 @@ static const char* tcpa_pc_event_id_strings[] = { "S-CRTM Version", "S-CRTM Contents", "S-CRTM POST Contents", + "POST Contents", }; /* returns pointer to start of pos. entry of tcg log */ -- cgit v1.1 From 3c2f606a098b07f053904ec8b8f4d0e101c28b35 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:36:56 -0700 Subject: [PATCH] tpm: spacing cleanups The following patch set contains numerous changes to the base tpm driver (tpm.c) to support the next generation of TPM chips. The changes include new sysfs files because of more relevant data being available, a function to access the timeout and duration values for the chip, and changes to make use of those duration values. Duration in the TPM specification is defined as the maximum amount of time the chip could take to return the results. Commands are in one of three categories short, medium and long. Also included are cleanups of how the commands for the sysfs files are composed to reduce a bunch of redundant arrays. This patch: Fix minor spacing issues. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 5a38704..379c5d4 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -46,7 +46,7 @@ static void user_reader_timeout(unsigned long ptr) schedule_work(&chip->work); } -static void timeout_work(void * ptr) +static void timeout_work(void *ptr) { struct tpm_chip *chip = ptr; @@ -387,7 +387,7 @@ int tpm_release(struct inode *inode, struct file *file) EXPORT_SYMBOL_GPL(tpm_release); ssize_t tpm_write(struct file *file, const char __user *buf, - size_t size, loff_t * off) + size_t size, loff_t *off) { struct tpm_chip *chip = file->private_data; int in_size = size, out_size; @@ -419,11 +419,10 @@ ssize_t tpm_write(struct file *file, const char __user *buf, return in_size; } - EXPORT_SYMBOL_GPL(tpm_write); -ssize_t tpm_read(struct file * file, char __user *buf, - size_t size, loff_t * off) +ssize_t tpm_read(struct file *file, char __user *buf, + size_t size, loff_t *off) { struct tpm_chip *chip = file->private_data; int ret_size; -- cgit v1.1 From beed53a1aaeaae4eb93297c23f1598a726716adf Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:37:05 -0700 Subject: [PATCH] tpm: reorganize sysfs files Many of the sysfs files were calling the TPM_GetCapability command with array. Since for 1.2 more sysfs files of this type are coming I am generalizing the array so there can be one array and the unique parts can be filled in just before the command is called. Signed-off-by: Kylene Hall Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm.c | 145 +++++++++++++++++++++++++++++-------------------- 1 file changed, 86 insertions(+), 59 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 379c5d4..187bcda 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -119,17 +119,57 @@ out: } #define TPM_DIGEST_SIZE 20 -#define CAP_PCR_RESULT_SIZE 18 -static const u8 cap_pcr[] = { +#define TPM_ERROR_SIZE 10 +#define TPM_RET_CODE_IDX 6 +#define TPM_GET_CAP_RET_SIZE_IDX 10 +#define TPM_GET_CAP_RET_UINT32_1_IDX 14 +#define TPM_GET_CAP_RET_UINT32_2_IDX 18 +#define TPM_GET_CAP_RET_UINT32_3_IDX 22 +#define TPM_GET_CAP_RET_UINT32_4_IDX 26 + +#define TPM_CAP_IDX 13 +#define TPM_CAP_SUBCAP_IDX 21 + +enum tpm_capabilities { + TPM_CAP_PROP = 5, +}; + +enum tpm_sub_capabilities { + TPM_CAP_PROP_PCR = 0x1, + TPM_CAP_PROP_MANUFACTURER = 0x3, +}; + +/* + * This is a semi generic GetCapability command for use + * with the capability type TPM_CAP_PROP or TPM_CAP_FLAG + * and their associated sub_capabilities. + */ + +static const u8 tpm_cap[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 22, /* length */ 0, 0, 0, 101, /* TPM_ORD_GetCapability */ - 0, 0, 0, 5, - 0, 0, 0, 4, - 0, 0, 1, 1 + 0, 0, 0, 0, /* TPM_CAP_ */ + 0, 0, 0, 4, /* TPM_CAP_SUB_ size */ + 0, 0, 1, 0 /* TPM_CAP_SUB_ */ }; -#define READ_PCR_RESULT_SIZE 30 +static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len, + char *desc) +{ + int err; + + len = tpm_transmit(chip, data, len); + if (len < 0) + return len; + if (len == TPM_ERROR_SIZE) { + err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))); + dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); + return err; + } + return 0; +} + static const u8 pcrread[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 14, /* length */ @@ -140,8 +180,8 @@ static const u8 pcrread[] = { ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, char *buf) { - u8 data[READ_PCR_RESULT_SIZE]; - ssize_t len; + u8 data[30]; + ssize_t rc; int i, j, num_pcrs; __be32 index; char *str = buf; @@ -150,29 +190,24 @@ ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, if (chip == NULL) return -ENODEV; - memcpy(data, cap_pcr, sizeof(cap_pcr)); - if ((len = tpm_transmit(chip, data, sizeof(data))) - < CAP_PCR_RESULT_SIZE) { - dev_dbg(chip->dev, "A TPM error (%d) occurred " - "attempting to determine the number of PCRS\n", - be32_to_cpu(*((__be32 *) (data + 6)))); + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_PROP; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_PCR; + + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the number of PCRS"); + if (rc) return 0; - } num_pcrs = be32_to_cpu(*((__be32 *) (data + 14))); - for (i = 0; i < num_pcrs; i++) { memcpy(data, pcrread, sizeof(pcrread)); index = cpu_to_be32(i); memcpy(data + 10, &index, 4); - if ((len = tpm_transmit(chip, data, sizeof(data))) - < READ_PCR_RESULT_SIZE){ - dev_dbg(chip->dev, "A TPM error (%d) occurred" - " attempting to read PCR %d of %d\n", - be32_to_cpu(*((__be32 *) (data + 6))), - i, num_pcrs); + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to read a PCR"); + if (rc) goto out; - } str += sprintf(str, "PCR-%02d: ", i); for (j = 0; j < TPM_DIGEST_SIZE; j++) str += sprintf(str, "%02X ", *(data + 10 + j)); @@ -194,7 +229,7 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, char *buf) { u8 *data; - ssize_t len; + ssize_t err; int i, rc; char *str = buf; @@ -208,14 +243,10 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, memcpy(data, readpubek, sizeof(readpubek)); - if ((len = tpm_transmit(chip, data, READ_PUBEK_RESULT_SIZE)) < - READ_PUBEK_RESULT_SIZE) { - dev_dbg(chip->dev, "A TPM error (%d) occurred " - "attempting to read the PUBEK\n", - be32_to_cpu(*((__be32 *) (data + 6)))); - rc = 0; + err = transmit_cmd(chip, data, READ_PUBEK_RESULT_SIZE, + "attempting to read the PUBEK"); + if (err) goto out; - } /* ignore header 10 bytes @@ -245,63 +276,59 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, if ((i + 1) % 16 == 0) str += sprintf(str, "\n"); } - rc = str - buf; out: + rc = str - buf; kfree(data); return rc; } EXPORT_SYMBOL_GPL(tpm_show_pubek); -#define CAP_VER_RESULT_SIZE 18 +#define CAP_VERSION_1_1 6 +#define CAP_VERSION_IDX 13 static const u8 cap_version[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 18, /* length */ 0, 0, 0, 101, /* TPM_ORD_GetCapability */ - 0, 0, 0, 6, + 0, 0, 0, 0, 0, 0, 0, 0 }; -#define CAP_MANUFACTURER_RESULT_SIZE 18 -static const u8 cap_manufacturer[] = { - 0, 193, /* TPM_TAG_RQU_COMMAND */ - 0, 0, 0, 22, /* length */ - 0, 0, 0, 101, /* TPM_ORD_GetCapability */ - 0, 0, 0, 5, - 0, 0, 0, 4, - 0, 0, 1, 3 -}; - ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, char *buf) { - u8 data[sizeof(cap_manufacturer)]; - ssize_t len; + u8 data[30]; + ssize_t rc; char *str = buf; struct tpm_chip *chip = dev_get_drvdata(dev); if (chip == NULL) return -ENODEV; - memcpy(data, cap_manufacturer, sizeof(cap_manufacturer)); + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_PROP; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER; - if ((len = tpm_transmit(chip, data, sizeof(data))) < - CAP_MANUFACTURER_RESULT_SIZE) - return len; + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the manufacturer"); + if (rc) + return 0; str += sprintf(str, "Manufacturer: 0x%x\n", - be32_to_cpu(*((__be32 *) (data + 14)))); + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)))); memcpy(data, cap_version, sizeof(cap_version)); + data[CAP_VERSION_IDX] = CAP_VERSION_1_1; + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the 1.1 version"); + if (rc) + goto out; - if ((len = tpm_transmit(chip, data, sizeof(data))) < - CAP_VER_RESULT_SIZE) - return len; - - str += - sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n", - (int) data[14], (int) data[15], (int) data[16], - (int) data[17]); + str += sprintf(str, + "TCG version: %d.%d\nFirmware version: %d.%d\n", + (int) data[14], (int) data[15], (int) data[16], + (int) data[17]); +out: return str - buf; } EXPORT_SYMBOL_GPL(tpm_show_caps); -- cgit v1.1 From 90dda520c1962d55a0e1d2571deed0d75fd6d6f1 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:37:15 -0700 Subject: [PATCH] tpm: chip struct update To assist with chip management and better support the possibility of having multiple TPMs in the system of the same kind, the struct tpm_vendor_specific member of the tpm_chip was changed from a pointer to an instance. This patch changes that declaration and fixes up all accesses to the structure member except in tpm_infineon which is coming in a patch from Marcel Selhorst. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm.c | 48 +++++++++++++++++++++----------------------- drivers/char/tpm/tpm.h | 2 +- drivers/char/tpm/tpm_atmel.c | 26 ++++++++++++------------ drivers/char/tpm/tpm_nsc.c | 32 ++++++++++++++--------------- 4 files changed, 53 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 187bcda..50013eb 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -78,7 +78,7 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, down(&chip->tpm_mutex); - if ((rc = chip->vendor->send(chip, (u8 *) buf, count)) < 0) { + if ((rc = chip->vendor.send(chip, (u8 *) buf, count)) < 0) { dev_err(chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc); goto out; @@ -86,13 +86,12 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, stop = jiffies + 2 * 60 * HZ; do { - u8 status = chip->vendor->status(chip); - if ((status & chip->vendor->req_complete_mask) == - chip->vendor->req_complete_val) { + u8 status = chip->vendor.status(chip); + if ((status & chip->vendor.req_complete_mask) == + chip->vendor.req_complete_val) goto out_recv; - } - if ((status == chip->vendor->req_canceled)) { + if ((status == chip->vendor.req_canceled)) { dev_err(chip->dev, "Operation Canceled\n"); rc = -ECANCELED; goto out; @@ -102,14 +101,13 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, rmb(); } while (time_before(jiffies, stop)); - - chip->vendor->cancel(chip); + chip->vendor.cancel(chip); dev_err(chip->dev, "Operation Timed out\n"); rc = -ETIME; goto out; out_recv: - rc = chip->vendor->recv(chip, (u8 *) buf, bufsiz); + rc = chip->vendor.recv(chip, (u8 *) buf, bufsiz); if (rc < 0) dev_err(chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc); @@ -340,7 +338,7 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, if (chip == NULL) return 0; - chip->vendor->cancel(chip); + chip->vendor.cancel(chip); return count; } EXPORT_SYMBOL_GPL(tpm_store_cancel); @@ -356,7 +354,7 @@ int tpm_open(struct inode *inode, struct file *file) spin_lock(&driver_lock); list_for_each_entry(pos, &tpm_chip_list, list) { - if (pos->vendor->miscdev.minor == minor) { + if (pos->vendor.miscdev.minor == minor) { chip = pos; break; } @@ -488,14 +486,14 @@ void tpm_remove_hardware(struct device *dev) spin_unlock(&driver_lock); dev_set_drvdata(dev, NULL); - misc_deregister(&chip->vendor->miscdev); - kfree(chip->vendor->miscdev.name); + misc_deregister(&chip->vendor.miscdev); + kfree(chip->vendor.miscdev.name); - sysfs_remove_group(&dev->kobj, chip->vendor->attr_group); + sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); tpm_bios_log_teardown(chip->bios_dir); - dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES ] &= - ~(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES)); + dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES] &= + ~(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES)); kfree(chip); @@ -569,7 +567,7 @@ int tpm_register_hardware(struct device *dev, struct tpm_vendor_specific *entry) chip->user_read_timer.function = user_reader_timeout; chip->user_read_timer.data = (unsigned long) chip; - chip->vendor = entry; + memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific)); chip->dev_num = -1; @@ -588,22 +586,22 @@ dev_num_search_complete: kfree(chip); return -ENODEV; } else if (chip->dev_num == 0) - chip->vendor->miscdev.minor = TPM_MINOR; + chip->vendor.miscdev.minor = TPM_MINOR; else - chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR; + chip->vendor.miscdev.minor = MISC_DYNAMIC_MINOR; devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL); scnprintf(devname, DEVNAME_SIZE, "%s%d", "tpm", chip->dev_num); - chip->vendor->miscdev.name = devname; + chip->vendor.miscdev.name = devname; - chip->vendor->miscdev.dev = dev; + chip->vendor.miscdev.dev = dev; chip->dev = get_device(dev); - if (misc_register(&chip->vendor->miscdev)) { + if (misc_register(&chip->vendor.miscdev)) { dev_err(chip->dev, "unable to misc_register %s, minor %d\n", - chip->vendor->miscdev.name, - chip->vendor->miscdev.minor); + chip->vendor.miscdev.name, + chip->vendor.miscdev.minor); put_device(dev); kfree(chip); dev_mask[i] &= !(1 << j); @@ -618,7 +616,7 @@ dev_num_search_complete: spin_unlock(&driver_lock); - sysfs_create_group(&dev->kobj, chip->vendor->attr_group); + sysfs_create_group(&dev->kobj, chip->vendor.attr_group); chip->bios_dir = tpm_bios_log_setup(devname); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index dec0224..a203963 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -81,7 +81,7 @@ struct tpm_chip { struct work_struct work; struct semaphore tpm_mutex; /* tpm is processing */ - struct tpm_vendor_specific *vendor; + struct tpm_vendor_specific vendor; struct dentry **bios_dir; diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index ff36549..2678797 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -47,12 +47,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) return -EIO; for (i = 0; i < 6; i++) { - status = ioread8(chip->vendor->iobase + 1); + status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { dev_err(chip->dev, "error reading header\n"); return -EIO; } - *buf++ = ioread8(chip->vendor->iobase); + *buf++ = ioread8(chip->vendor.iobase); } /* size of the data received */ @@ -63,7 +63,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) dev_err(chip->dev, "Recv size(%d) less than available space\n", size); for (; i < size; i++) { /* clear the waiting data anyway */ - status = ioread8(chip->vendor->iobase + 1); + status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { dev_err(chip->dev, "error reading data\n"); return -EIO; @@ -74,16 +74,16 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) /* read all the data available */ for (; i < size; i++) { - status = ioread8(chip->vendor->iobase + 1); + status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { dev_err(chip->dev, "error reading data\n"); return -EIO; } - *buf++ = ioread8(chip->vendor->iobase); + *buf++ = ioread8(chip->vendor.iobase); } /* make sure data available is gone */ - status = ioread8(chip->vendor->iobase + 1); + status = ioread8(chip->vendor.iobase + 1); if (status & ATML_STATUS_DATA_AVAIL) { dev_err(chip->dev, "data available is stuck\n"); @@ -100,7 +100,7 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) dev_dbg(chip->dev, "tpm_atml_send:\n"); for (i = 0; i < count; i++) { dev_dbg(chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); - iowrite8(buf[i], chip->vendor->iobase); + iowrite8(buf[i], chip->vendor.iobase); } return count; @@ -108,12 +108,12 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) static void tpm_atml_cancel(struct tpm_chip *chip) { - iowrite8(ATML_STATUS_ABORT, chip->vendor->iobase + 1); + iowrite8(ATML_STATUS_ABORT, chip->vendor.iobase + 1); } static u8 tpm_atml_status(struct tpm_chip *chip) { - return ioread8(chip->vendor->iobase + 1); + return ioread8(chip->vendor.iobase + 1); } static struct file_operations atmel_ops = { @@ -159,10 +159,10 @@ static void atml_plat_remove(void) struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); if (chip) { - if (chip->vendor->have_region) - atmel_release_region(chip->vendor->base, - chip->vendor->region_size); - atmel_put_base_addr(chip->vendor); + if (chip->vendor.have_region) + atmel_release_region(chip->vendor.base, + chip->vendor.region_size); + atmel_put_base_addr(chip->vendor.iobase); tpm_remove_hardware(chip->dev); platform_device_unregister(pdev); } diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 680a8e3..3dbbe96 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -71,7 +71,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) unsigned long stop; /* status immediately available check */ - *data = inb(chip->vendor->base + NSC_STATUS); + *data = inb(chip->vendor.base + NSC_STATUS); if ((*data & mask) == val) return 0; @@ -79,7 +79,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) stop = jiffies + 10 * HZ; do { msleep(TPM_TIMEOUT); - *data = inb(chip->vendor->base + 1); + *data = inb(chip->vendor.base + 1); if ((*data & mask) == val) return 0; } @@ -94,9 +94,9 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) unsigned long stop; /* status immediately available check */ - status = inb(chip->vendor->base + NSC_STATUS); + status = inb(chip->vendor.base + NSC_STATUS); if (status & NSC_STATUS_OBF) - status = inb(chip->vendor->base + NSC_DATA); + status = inb(chip->vendor.base + NSC_DATA); if (status & NSC_STATUS_RDY) return 0; @@ -104,9 +104,9 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) stop = jiffies + 100; do { msleep(TPM_TIMEOUT); - status = inb(chip->vendor->base + NSC_STATUS); + status = inb(chip->vendor.base + NSC_STATUS); if (status & NSC_STATUS_OBF) - status = inb(chip->vendor->base + NSC_DATA); + status = inb(chip->vendor.base + NSC_DATA); if (status & NSC_STATUS_RDY) return 0; } @@ -132,7 +132,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; } if ((data = - inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_NORMAL) { + inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) { dev_err(chip->dev, "not in normal mode (0x%x)\n", data); return -EIO; @@ -148,7 +148,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) } if (data & NSC_STATUS_F0) break; - *p = inb(chip->vendor->base + NSC_DATA); + *p = inb(chip->vendor.base + NSC_DATA); } if ((data & NSC_STATUS_F0) == 0 && @@ -156,7 +156,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) dev_err(chip->dev, "F0 not set\n"); return -EIO; } - if ((data = inb(chip->vendor->base + NSC_DATA)) != NSC_COMMAND_EOC) { + if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) { dev_err(chip->dev, "expected end of command(0x%x)\n", data); return -EIO; @@ -182,7 +182,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) * fix it. Not sure why this is needed, we followed the flow * chart in the manual to the letter. */ - outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND); + outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND); if (nsc_wait_for_ready(chip) != 0) return -EIO; @@ -192,7 +192,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; } - outb(NSC_COMMAND_NORMAL, chip->vendor->base + NSC_COMMAND); + outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND); if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) { dev_err(chip->dev, "IBR timeout\n"); return -EIO; @@ -204,26 +204,26 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) "IBF timeout (while writing data)\n"); return -EIO; } - outb(buf[i], chip->vendor->base + NSC_DATA); + outb(buf[i], chip->vendor.base + NSC_DATA); } if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { dev_err(chip->dev, "IBF timeout\n"); return -EIO; } - outb(NSC_COMMAND_EOC, chip->vendor->base + NSC_COMMAND); + outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND); return count; } static void tpm_nsc_cancel(struct tpm_chip *chip) { - outb(NSC_COMMAND_CANCEL, chip->vendor->base + NSC_COMMAND); + outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND); } static u8 tpm_nsc_status(struct tpm_chip *chip) { - return inb(chip->vendor->base + NSC_STATUS); + return inb(chip->vendor.base + NSC_STATUS); } static struct file_operations nsc_ops = { @@ -268,7 +268,7 @@ static void __devexit tpm_nsc_remove(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); if ( chip ) { - release_region(chip->vendor->base, 2); + release_region(chip->vendor.base, 2); tpm_remove_hardware(chip->dev); } } -- cgit v1.1 From e0dd03caf20d040a0a86b6bd74028ec9bda545f5 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:37:26 -0700 Subject: [PATCH] tpm: return chip from tpm_register_hardware Changes in the 1.2 TPM Specification make it necessary to update some fields of the chip structure in the initialization function after it is registered with tpm.c thus tpm_register_hardware was modified to return a pointer to the structure. This patch makes that change and the associated changes in tpm_atmel and tpm_nsc. The changes to tpm_infineon will be coming in a patch from Marcel Selhorst. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm.c | 11 ++++++----- drivers/char/tpm/tpm.h | 10 +++++----- drivers/char/tpm/tpm_atmel.c | 32 ++++++++++++++++++++++---------- drivers/char/tpm/tpm_atmel.h | 25 +++++++++++-------------- drivers/char/tpm/tpm_nsc.c | 17 +++++++++++------ 5 files changed, 55 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 50013eb..5e58296 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -544,7 +544,8 @@ EXPORT_SYMBOL_GPL(tpm_pm_resume); * upon errant exit from this function specific probe function should call * pci_disable_device */ -int tpm_register_hardware(struct device *dev, struct tpm_vendor_specific *entry) +struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vendor_specific + *entry) { #define DEVNAME_SIZE 7 @@ -555,7 +556,7 @@ int tpm_register_hardware(struct device *dev, struct tpm_vendor_specific *entry) /* Driver specific per-device data */ chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) - return -ENOMEM; + return NULL; init_MUTEX(&chip->buffer_mutex); init_MUTEX(&chip->tpm_mutex); @@ -584,7 +585,7 @@ dev_num_search_complete: if (chip->dev_num < 0) { dev_err(dev, "No available tpm device numbers\n"); kfree(chip); - return -ENODEV; + return NULL; } else if (chip->dev_num == 0) chip->vendor.miscdev.minor = TPM_MINOR; else @@ -605,7 +606,7 @@ dev_num_search_complete: put_device(dev); kfree(chip); dev_mask[i] &= !(1 << j); - return -ENODEV; + return NULL; } spin_lock(&driver_lock); @@ -620,7 +621,7 @@ dev_num_search_complete: chip->bios_dir = tpm_bios_log_setup(devname); - return 0; + return chip; } EXPORT_SYMBOL_GPL(tpm_register_hardware); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index a203963..4f00534 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -48,9 +48,9 @@ extern ssize_t tpm_store_cancel(struct device *, struct device_attribute *attr, struct tpm_chip; struct tpm_vendor_specific { - u8 req_complete_mask; - u8 req_complete_val; - u8 req_canceled; + const u8 req_complete_mask; + const u8 req_complete_val; + const u8 req_canceled; void __iomem *iobase; /* ioremapped address */ unsigned long base; /* TPM base address */ @@ -100,8 +100,8 @@ static inline void tpm_write_index(int base, int index, int value) outb(value & 0xFF, base+1); } -extern int tpm_register_hardware(struct device *, - struct tpm_vendor_specific *); +extern struct tpm_chip* tpm_register_hardware(struct device *, + const struct tpm_vendor_specific *); extern int tpm_open(struct inode *, struct file *); extern int tpm_release(struct inode *, struct file *); extern ssize_t tpm_write(struct file *, const char __user *, size_t, diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 2678797..58a258ce 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -140,7 +140,7 @@ static struct attribute* atmel_attrs[] = { static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs }; -static struct tpm_vendor_specific tpm_atmel = { +static const struct tpm_vendor_specific tpm_atmel = { .recv = tpm_atml_recv, .send = tpm_atml_send, .cancel = tpm_atml_cancel, @@ -179,18 +179,22 @@ static struct device_driver atml_drv = { static int __init init_atmel(void) { int rc = 0; + void __iomem *iobase = NULL; + int have_region, region_size; + unsigned long base; + struct tpm_chip *chip; driver_register(&atml_drv); - if ((tpm_atmel.iobase = atmel_get_base_addr(&tpm_atmel)) == NULL) { + if ((iobase = atmel_get_base_addr(&base, ®ion_size)) == NULL) { rc = -ENODEV; goto err_unreg_drv; } - tpm_atmel.have_region = + have_region = (atmel_request_region - (tpm_atmel.base, tpm_atmel.region_size, - "tpm_atmel0") == NULL) ? 0 : 1; + (tpm_atmel.base, region_size, "tpm_atmel0") == NULL) ? 0 : 1; + if (IS_ERR (pdev = @@ -199,17 +203,25 @@ static int __init init_atmel(void) goto err_rel_reg; } - if ((rc = tpm_register_hardware(&pdev->dev, &tpm_atmel)) < 0) + if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_atmel))) { + rc = -ENODEV; goto err_unreg_dev; + } + + chip->vendor.iobase = iobase; + chip->vendor.base = base; + chip->vendor.have_region = have_region; + chip->vendor.region_size = region_size; + return 0; err_unreg_dev: platform_device_unregister(pdev); err_rel_reg: - atmel_put_base_addr(&tpm_atmel); - if (tpm_atmel.have_region) - atmel_release_region(tpm_atmel.base, - tpm_atmel.region_size); + atmel_put_base_addr(iobase); + if (have_region) + atmel_release_region(base, + region_size); err_unreg_drv: driver_unregister(&atml_drv); return rc; diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index d3478aa..2e68eeb 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -28,13 +28,12 @@ #define atmel_request_region request_mem_region #define atmel_release_region release_mem_region -static inline void atmel_put_base_addr(struct tpm_vendor_specific - *vendor) +static inline void atmel_put_base_addr(void __iomem *iobase) { - iounmap(vendor->iobase); + iounmap(iobase); } -static void __iomem * atmel_get_base_addr(struct tpm_vendor_specific *vendor) +static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) { struct device_node *dn; unsigned long address, size; @@ -71,9 +70,9 @@ static void __iomem * atmel_get_base_addr(struct tpm_vendor_specific *vendor) else size = reg[naddrc]; - vendor->base = address; - vendor->region_size = size; - return ioremap(vendor->base, vendor->region_size); + *base = address; + *region_size = size; + return ioremap(*base, *region_size); } #else #define atmel_getb(chip, offset) inb(chip->vendor->base + offset) @@ -106,14 +105,12 @@ static int atmel_verify_tpm11(void) return 0; } -static inline void atmel_put_base_addr(struct tpm_vendor_specific - *vendor) +static inline void atmel_put_base_addr(void __iomem *iobase) { } /* Determine where to talk to device */ -static void __iomem * atmel_get_base_addr(struct tpm_vendor_specific - *vendor) +static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) { int lo, hi; @@ -123,9 +120,9 @@ static void __iomem * atmel_get_base_addr(struct tpm_vendor_specific lo = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_LO); hi = tpm_read_index(TPM_ADDR, TPM_ATMEL_BASE_ADDR_HI); - vendor->base = (hi << 8) | lo; - vendor->region_size = 2; + *base = (hi << 8) | lo; + *region_size = 2; - return ioport_map(vendor->base, vendor->region_size); + return ioport_map(*base, *region_size); } #endif diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 3dbbe96..4c8bc06 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -250,7 +250,7 @@ static struct attribute * nsc_attrs[] = { static struct attribute_group nsc_attr_grp = { .attrs = nsc_attrs }; -static struct tpm_vendor_specific tpm_nsc = { +static const struct tpm_vendor_specific tpm_nsc = { .recv = tpm_nsc_recv, .send = tpm_nsc_send, .cancel = tpm_nsc_cancel, @@ -286,7 +286,8 @@ static int __init init_nsc(void) int rc = 0; int lo, hi; int nscAddrBase = TPM_ADDR; - + struct tpm_chip *chip; + unsigned long base; /* verify that it is a National part (SID) */ if (tpm_read_index(TPM_ADDR, NSC_SID_INDEX) != 0xEF) { @@ -300,7 +301,7 @@ static int __init init_nsc(void) hi = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_HI); lo = tpm_read_index(nscAddrBase, TPM_NSC_BASE0_LO); - tpm_nsc.base = (hi<<8) | lo; + base = (hi<<8) | lo; /* enable the DPM module */ tpm_write_index(nscAddrBase, NSC_LDC_INDEX, 0x01); @@ -320,13 +321,15 @@ static int __init init_nsc(void) if ((rc = platform_device_register(pdev)) < 0) goto err_free_dev; - if (request_region(tpm_nsc.base, 2, "tpm_nsc0") == NULL ) { + if (request_region(base, 2, "tpm_nsc0") == NULL ) { rc = -EBUSY; goto err_unreg_dev; } - if ((rc = tpm_register_hardware(&pdev->dev, &tpm_nsc)) < 0) + if (!(chip = tpm_register_hardware(&pdev->dev, &tpm_nsc))) { + rc = -ENODEV; goto err_rel_reg; + } dev_dbg(&pdev->dev, "NSC TPM detected\n"); dev_dbg(&pdev->dev, @@ -361,10 +364,12 @@ static int __init init_nsc(void) "NSC TPM revision %d\n", tpm_read_index(nscAddrBase, 0x27) & 0x1F); + chip->vendor.base = base; + return 0; err_rel_reg: - release_region(tpm_nsc.base, 2); + release_region(base, 2); err_unreg_dev: platform_device_unregister(pdev); err_free_dev: -- cgit v1.1 From 9e18ee19179a7742999d0e2d4bfcba75b5562439 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:37:38 -0700 Subject: [PATCH] tpm: command duration update With the TPM 1.2 Specification, each command is classified as short, medium or long and the chip tells you the maximum amount of time for a response to each class of command. This patch provides and array of the classifications and a function to determine how long the response should be waited for. Also, it uses that information in the command processing to determine how long to poll for. The function is exported so the 1.2 driver can use the functionality to determine how long to wait for a DataAvailable interrupt if interrupts are being used. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++++- drivers/char/tpm/tpm.h | 2 + 2 files changed, 311 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 5e58296..e54bcb4 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -35,10 +35,290 @@ enum tpm_const { TPM_NUM_MASK_ENTRIES = TPM_NUM_DEVICES / (8 * sizeof(int)) }; +enum tpm_duration { + TPM_SHORT = 0, + TPM_MEDIUM = 1, + TPM_LONG = 2, + TPM_UNDEFINED, +}; + +#define TPM_MAX_ORDINAL 243 +#define TPM_MAX_PROTECTED_ORDINAL 12 +#define TPM_PROTECTED_ORDINAL_MASK 0xFF + static LIST_HEAD(tpm_chip_list); static DEFINE_SPINLOCK(driver_lock); static int dev_mask[TPM_NUM_MASK_ENTRIES]; +/* + * Array with one entry per ordinal defining the maximum amount + * of time the chip could take to return the result. The ordinal + * designation of short, medium or long is defined in a table in + * TCG Specification TPM Main Part 2 TPM Structures Section 17. The + * values of the SHORT, MEDIUM, and LONG durations are retrieved + * from the chip during initialization with a call to tpm_get_timeouts. + */ +static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = { + TPM_UNDEFINED, /* 0 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 5 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 10 */ + TPM_SHORT, +}; + +static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = { + TPM_UNDEFINED, /* 0 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 5 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 10 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_LONG, + TPM_LONG, + TPM_MEDIUM, /* 15 */ + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, + TPM_LONG, + TPM_SHORT, /* 20 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, /* 25 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 30 */ + TPM_LONG, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 35 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 40 */ + TPM_LONG, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 45 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_LONG, + TPM_MEDIUM, /* 50 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 55 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 60 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 65 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 70 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 75 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 80 */ + TPM_UNDEFINED, + TPM_MEDIUM, + TPM_LONG, + TPM_SHORT, + TPM_UNDEFINED, /* 85 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 90 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 95 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 100 */ + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 105 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 110 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 115 */ + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 120 */ + TPM_LONG, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 125 */ + TPM_SHORT, + TPM_LONG, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 130 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_SHORT, + TPM_MEDIUM, + TPM_UNDEFINED, /* 135 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 140 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 145 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 150 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 155 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 160 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 165 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 170 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 175 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 180 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, /* 185 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 190 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 195 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 200 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 205 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 210 */ + TPM_UNDEFINED, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_UNDEFINED, /* 215 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 220 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 225 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 230 */ + TPM_LONG, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 235 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 240 */ + TPM_UNDEFINED, + TPM_MEDIUM, +}; + static void user_reader_timeout(unsigned long ptr) { struct tpm_chip *chip = (struct tpm_chip *) ptr; @@ -57,17 +337,43 @@ static void timeout_work(void *ptr) } /* + * Returns max number of jiffies to wait + */ +unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, + u32 ordinal) +{ + int duration_idx = TPM_UNDEFINED; + int duration = 0; + + if (ordinal < TPM_MAX_ORDINAL) + duration_idx = tpm_ordinal_duration[ordinal]; + else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < + TPM_MAX_PROTECTED_ORDINAL) + duration_idx = + tpm_protected_ordinal_duration[ordinal & + TPM_PROTECTED_ORDINAL_MASK]; + + if (duration_idx != TPM_UNDEFINED) + duration = chip->vendor.duration[duration_idx] * HZ / 1000; + if (duration <= 0) + return 2 * 60 * HZ; + else + return duration; +} +EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); + +/* * Internal kernel interface to transmit TPM commands */ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz) { ssize_t rc; - u32 count; + u32 count, ordinal; unsigned long stop; count = be32_to_cpu(*((__be32 *) (buf + 2))); - + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); if (count == 0) return -ENODATA; if (count > bufsiz) { @@ -84,7 +390,7 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, goto out; } - stop = jiffies + 2 * 60 * HZ; + stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { u8 status = chip->vendor.status(chip); if ((status & chip->vendor.req_complete_mask) == diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 4f00534..1d28485 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -63,6 +63,7 @@ struct tpm_vendor_specific { u8 (*status) (struct tpm_chip *); struct miscdevice miscdev; struct attribute_group *attr_group; + u32 duration[3]; }; struct tpm_chip { @@ -100,6 +101,7 @@ static inline void tpm_write_index(int base, int index, int value) outb(value & 0xFF, base+1); } +extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32); extern struct tpm_chip* tpm_register_hardware(struct device *, const struct tpm_vendor_specific *); extern int tpm_open(struct inode *, struct file *); -- cgit v1.1 From 08e96e486dd1345ae0ad70247387d0d4fd346889 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:37:50 -0700 Subject: [PATCH] tpm: new 1.2 sysfs files Many of the sysfs files were calling the TPM_GetCapability command with array. Since for 1.2 more sysfs files of this type are coming I am generalizing the array so there can be one array and the unique parts can be filled in just before the command is called. This updated version of the patch breaks the multi-value sysfs file into separate files pointed out by Greg. It also addresses the code redundancy and ugliness in the tpm_show_* functions pointed out on another patch by Dave Hansen. Signed-off-by: Kylene Hall Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++- drivers/char/tpm/tpm.h | 14 +++ 2 files changed, 247 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index e54bcb4..24c4423 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -430,17 +430,27 @@ out: #define TPM_GET_CAP_RET_UINT32_2_IDX 18 #define TPM_GET_CAP_RET_UINT32_3_IDX 22 #define TPM_GET_CAP_RET_UINT32_4_IDX 26 +#define TPM_GET_CAP_PERM_DISABLE_IDX 16 +#define TPM_GET_CAP_PERM_INACTIVE_IDX 18 +#define TPM_GET_CAP_RET_BOOL_1_IDX 14 +#define TPM_GET_CAP_TEMP_INACTIVE_IDX 16 #define TPM_CAP_IDX 13 #define TPM_CAP_SUBCAP_IDX 21 enum tpm_capabilities { + TPM_CAP_FLAG = 4, TPM_CAP_PROP = 5, }; enum tpm_sub_capabilities { TPM_CAP_PROP_PCR = 0x1, TPM_CAP_PROP_MANUFACTURER = 0x3, + TPM_CAP_FLAG_PERM = 0x8, + TPM_CAP_FLAG_VOL = 0x9, + TPM_CAP_PROP_OWNER = 0x11, + TPM_CAP_PROP_TIS_TIMEOUT = 0x15, + TPM_CAP_PROP_TIS_DURATION = 0x20, }; /* @@ -474,6 +484,180 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len, return 0; } +void tpm_gen_interrupt(struct tpm_chip *chip) +{ + u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)]; + ssize_t rc; + + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_PROP; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT; + + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the timeouts"); +} +EXPORT_SYMBOL_GPL(tpm_gen_interrupt); + +void tpm_get_timeouts(struct tpm_chip *chip) +{ + u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)]; + ssize_t rc; + u32 timeout; + + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_PROP; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT; + + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the timeouts"); + if (rc) + goto duration; + + if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX))) + != 4 * sizeof(u32)) + goto duration; + + /* Don't overwrite default if value is 0 */ + timeout = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))); + if (timeout) + chip->vendor.timeout_a = timeout; + timeout = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX))); + if (timeout) + chip->vendor.timeout_b = timeout; + timeout = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX))); + if (timeout) + chip->vendor.timeout_c = timeout; + timeout = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX))); + if (timeout) + chip->vendor.timeout_d = timeout; + +duration: + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_PROP; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION; + + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the durations"); + if (rc) + return; + + if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX))) + != 3 * sizeof(u32)) + return; + + chip->vendor.duration[TPM_SHORT] = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))); + chip->vendor.duration[TPM_MEDIUM] = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX))); + chip->vendor.duration[TPM_LONG] = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX))); +} +EXPORT_SYMBOL_GPL(tpm_get_timeouts); + +void tpm_continue_selftest(struct tpm_chip *chip) +{ + u8 data[] = { + 0, 193, /* TPM_TAG_RQU_COMMAND */ + 0, 0, 0, 10, /* length */ + 0, 0, 0, 83, /* TPM_ORD_GetCapability */ + }; + + tpm_transmit(chip, data, sizeof(data)); +} +EXPORT_SYMBOL_GPL(tpm_continue_selftest); + +ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr, + char *buf) +{ + u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)]; + ssize_t rc; + + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip == NULL) + return -ENODEV; + + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_FLAG; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM; + + rc = transmit_cmd(chip, data, sizeof(data), + "attemtping to determine the permanent state"); + if (rc) + return 0; + return sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]); +} +EXPORT_SYMBOL_GPL(tpm_show_enabled); + +ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr, + char *buf) +{ + u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)]; + ssize_t rc; + + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip == NULL) + return -ENODEV; + + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_FLAG; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM; + + rc = transmit_cmd(chip, data, sizeof(data), + "attemtping to determine the permanent state"); + if (rc) + return 0; + return sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]); +} +EXPORT_SYMBOL_GPL(tpm_show_active); + +ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr, + char *buf) +{ + u8 data[sizeof(tpm_cap)]; + ssize_t rc; + + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip == NULL) + return -ENODEV; + + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_PROP; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER; + + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the owner state"); + if (rc) + return 0; + return sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]); +} +EXPORT_SYMBOL_GPL(tpm_show_owned); + +ssize_t tpm_show_temp_deactivated(struct device * dev, + struct device_attribute * attr, char *buf) +{ + u8 data[sizeof(tpm_cap)]; + ssize_t rc; + + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip == NULL) + return -ENODEV; + + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_FLAG; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL; + + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the temporary state"); + if (rc) + return 0; + return sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]); +} +EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated); + static const u8 pcrread[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ 0, 0, 0, 14, /* length */ @@ -484,7 +668,7 @@ static const u8 pcrread[] = { ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, char *buf) { - u8 data[30]; + u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(pcrread)), 30)]; ssize_t rc; int i, j, num_pcrs; __be32 index; @@ -588,6 +772,7 @@ out: EXPORT_SYMBOL_GPL(tpm_show_pubek); #define CAP_VERSION_1_1 6 +#define CAP_VERSION_1_2 0x1A #define CAP_VERSION_IDX 13 static const u8 cap_version[] = { 0, 193, /* TPM_TAG_RQU_COMMAND */ @@ -600,7 +785,7 @@ static const u8 cap_version[] = { ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, char *buf) { - u8 data[30]; + u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)]; ssize_t rc; char *str = buf; @@ -637,6 +822,52 @@ out: } EXPORT_SYMBOL_GPL(tpm_show_caps); +ssize_t tpm_show_caps_1_2(struct device * dev, + struct device_attribute * attr, char *buf) +{ + u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)]; + ssize_t len; + char *str = buf; + + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip == NULL) + return -ENODEV; + + memcpy(data, tpm_cap, sizeof(tpm_cap)); + data[TPM_CAP_IDX] = TPM_CAP_PROP; + data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER; + + if ((len = tpm_transmit(chip, data, sizeof(data))) <= + TPM_ERROR_SIZE) { + dev_dbg(chip->dev, "A TPM error (%d) occurred " + "attempting to determine the manufacturer\n", + be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)))); + return 0; + } + + str += sprintf(str, "Manufacturer: 0x%x\n", + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)))); + + memcpy(data, cap_version, sizeof(cap_version)); + data[CAP_VERSION_IDX] = CAP_VERSION_1_2; + + if ((len = tpm_transmit(chip, data, sizeof(data))) <= + TPM_ERROR_SIZE) { + dev_err(chip->dev, "A TPM error (%d) occurred " + "attempting to determine the 1.2 version\n", + be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)))); + goto out; + } + str += sprintf(str, + "TCG version: %d.%d\nFirmware version: %d.%d\n", + (int) data[16], (int) data[17], (int) data[18], + (int) data[19]); + +out: + return str - buf; +} +EXPORT_SYMBOL_GPL(tpm_show_caps_1_2); + ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 1d28485..75d105c 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -42,8 +42,18 @@ extern ssize_t tpm_show_pcrs(struct device *, struct device_attribute *attr, char *); extern ssize_t tpm_show_caps(struct device *, struct device_attribute *attr, char *); +extern ssize_t tpm_show_caps_1_2(struct device *, struct device_attribute *attr, + char *); extern ssize_t tpm_store_cancel(struct device *, struct device_attribute *attr, const char *, size_t); +extern ssize_t tpm_show_enabled(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_show_active(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_show_temp_deactivated(struct device *, + struct device_attribute *attr, char *); struct tpm_chip; @@ -63,6 +73,7 @@ struct tpm_vendor_specific { u8 (*status) (struct tpm_chip *); struct miscdevice miscdev; struct attribute_group *attr_group; + u32 timeout_a, timeout_b, timeout_c, timeout_d; u32 duration[3]; }; @@ -101,6 +112,9 @@ static inline void tpm_write_index(int base, int index, int value) outb(value & 0xFF, base+1); } +extern void tpm_get_timeouts(struct tpm_chip *); +extern void tpm_gen_interrupt(struct tpm_chip *); +extern void tpm_continue_selftest(struct tpm_chip *); extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32); extern struct tpm_chip* tpm_register_hardware(struct device *, const struct tpm_vendor_specific *); -- cgit v1.1 From 27084efee0c3dc0eb15b5ed750aa9f1adb3983c3 Mon Sep 17 00:00:00 2001 From: Leendert van Doorn Date: Sat, 22 Apr 2006 02:38:03 -0700 Subject: [PATCH] tpm: driver for next generation TPM chips The driver for the next generation of TPM chips version 1.2 including support for interrupts. The Trusted Computing Group has written the TPM Interface Specification (TIS) which defines a common interface for all manufacturer's 1.2 TPM's thus the name tpm_tis. Signed-off-by: Leendert van Doorn Signed-off-by: Kylene Hall Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/Kconfig | 11 +- drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm.c | 3 + drivers/char/tpm/tpm.h | 9 + drivers/char/tpm/tpm_tis.c | 647 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 670 insertions(+), 1 deletion(-) create mode 100644 drivers/char/tpm/tpm_tis.c (limited to 'drivers') diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index a6873bf..1efde3b 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -20,9 +20,18 @@ config TCG_TPM Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI and CONFIG_PNPACPI. +config TCG_TIS + tristate "TPM Interface Specification 1.2 Interface" + depends on TCG_TPM + ---help--- + If you have a TPM security chip that is compliant with the + TCG TIS 1.2 TPM specification say Yes and it will be accessible + from within Linux. To compile this driver as a module, choose + M here; the module will be called tpm_tis. + config TCG_NSC tristate "National Semiconductor TPM Interface" - depends on TCG_TPM + depends on TCG_TPM && PNPACPI ---help--- If you have a TPM security chip from National Semicondutor say Yes and it will be accessible from within Linux. To diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index ba4582d..ea3a1e0 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o endif +obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 24c4423..150c86a 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -390,6 +390,9 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, goto out; } + if (chip->vendor.irq) + goto out_recv; + stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { u8 status = chip->vendor.status(chip); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 75d105c..24541a5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -64,6 +64,8 @@ struct tpm_vendor_specific { void __iomem *iobase; /* ioremapped address */ unsigned long base; /* TPM base address */ + int irq; + int region_size; int have_region; @@ -73,8 +75,13 @@ struct tpm_vendor_specific { u8 (*status) (struct tpm_chip *); struct miscdevice miscdev; struct attribute_group *attr_group; + struct list_head list; + int locality; u32 timeout_a, timeout_b, timeout_c, timeout_d; u32 duration[3]; + + wait_queue_head_t read_queue; + wait_queue_head_t int_queue; }; struct tpm_chip { @@ -100,6 +107,8 @@ struct tpm_chip { struct list_head list; }; +#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) + static inline int tpm_read_index(int base, int index) { outb(index, base); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c new file mode 100644 index 0000000..0275930 --- /dev/null +++ b/drivers/char/tpm/tpm_tis.c @@ -0,0 +1,647 @@ +/* + * Copyright (C) 2005, 2006 IBM Corporation + * + * Authors: + * Leendert van Doorn + * Kylene Hall + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This device driver implements the TPM interface as defined in + * the TCG TPM Interface Spec version 1.2, revision 1.0. + * + * 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, version 2 of the + * License. + */ +#include +#include +#include +#include "tpm.h" + +#define TPM_HEADER_SIZE 10 + +enum tis_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum tis_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; + +enum tis_int_flags { + TPM_GLOBAL_INT_ENABLE = 0x80000000, + TPM_INTF_BURST_COUNT_STATIC = 0x100, + TPM_INTF_CMD_READY_INT = 0x080, + TPM_INTF_INT_EDGE_FALLING = 0x040, + TPM_INTF_INT_EDGE_RISING = 0x020, + TPM_INTF_INT_LEVEL_LOW = 0x010, + TPM_INTF_INT_LEVEL_HIGH = 0x008, + TPM_INTF_LOCALITY_CHANGE_INT = 0x004, + TPM_INTF_STS_VALID_INT = 0x002, + TPM_INTF_DATA_AVAIL_INT = 0x001, +}; + +#define TPM_ACCESS(l) (0x0000 | ((l) << 12)) +#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) +#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12)) +#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12)) +#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12)) +#define TPM_STS(l) (0x0018 | ((l) << 12)) +#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) + +#define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) +#define TPM_RID(l) (0x0F04 | ((l) << 12)) + +static LIST_HEAD(tis_chips); +static DEFINE_SPINLOCK(tis_lock); + +static int check_locality(struct tpm_chip *chip, int l) +{ + if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) + return chip->vendor.locality = l; + + return -1; +} + +static void release_locality(struct tpm_chip *chip, int l, int force) +{ + if (force || (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & + (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == + (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) + iowrite8(TPM_ACCESS_ACTIVE_LOCALITY, + chip->vendor.iobase + TPM_ACCESS(l)); +} + +static int request_locality(struct tpm_chip *chip, int l) +{ + unsigned long stop; + long rc; + + if (check_locality(chip, l) >= 0) + return l; + + iowrite8(TPM_ACCESS_REQUEST_USE, + chip->vendor.iobase + TPM_ACCESS(l)); + + if (chip->vendor.irq) { + rc = wait_event_interruptible_timeout(chip->vendor. + int_queue, + (check_locality + (chip, l) >= 0), + msecs_to_jiffies + (chip->vendor. + timeout_a)); + if (rc > 0) + return l; + + } else { + /* wait for burstcount */ + stop = jiffies + (HZ * chip->vendor.timeout_a / 1000); + do { + if (check_locality(chip, l) >= 0) + return l; + msleep(TPM_TIMEOUT); + } + while (time_before(jiffies, stop)); + } + return -1; +} + +static u8 tpm_tis_status(struct tpm_chip *chip) +{ + return ioread8(chip->vendor.iobase + + TPM_STS(chip->vendor.locality)); +} + +static void tpm_tis_ready(struct tpm_chip *chip) +{ + /* this causes the current command to be aborted */ + iowrite8(TPM_STS_COMMAND_READY, + chip->vendor.iobase + TPM_STS(chip->vendor.locality)); +} + +static int get_burstcount(struct tpm_chip *chip) +{ + unsigned long stop; + int burstcnt; + + /* wait for burstcount */ + /* which timeout value, spec has 2 answers (c & d) */ + stop = jiffies + (HZ * chip->vendor.timeout_d / 1000); + do { + burstcnt = ioread8(chip->vendor.iobase + + TPM_STS(chip->vendor.locality) + 1); + burstcnt += ioread8(chip->vendor.iobase + + TPM_STS(chip->vendor.locality) + + 2) << 8; + if (burstcnt) + return burstcnt; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + return -EBUSY; +} + +static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout, + wait_queue_head_t *queue) +{ + unsigned long stop; + long rc; + u8 status; + + /* check current status */ + status = tpm_tis_status(chip); + if ((status & mask) == mask) + return 0; + + if (chip->vendor.irq) { + rc = wait_event_interruptible_timeout(*queue, + ((tpm_tis_status + (chip) & mask) == + mask), + msecs_to_jiffies + (timeout)); + if (rc > 0) + return 0; + } else { + stop = jiffies + (HZ * timeout / 1000); + do { + msleep(TPM_TIMEOUT); + status = tpm_tis_status(chip); + if ((status & mask) == mask) + return 0; + } while (time_before(jiffies, stop)); + } + return -ETIME; +} + +static int recv_data(struct tpm_chip *chip, u8 * buf, size_t count) +{ + int size = 0, burstcnt; + while (size < count && + wait_for_stat(chip, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + chip->vendor.timeout_c, + &chip->vendor.read_queue) + == 0) { + burstcnt = get_burstcount(chip); + for (; burstcnt > 0 && size < count; burstcnt--) + buf[size++] = ioread8(chip->vendor.iobase + + TPM_DATA_FIFO(chip->vendor. + locality)); + } + return size; +} + +static int tpm_tis_recv(struct tpm_chip *chip, u8 * buf, size_t count) +{ + int size = 0; + int expected, status; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + /* read first 10 bytes, including tag, paramsize, and result */ + if ((size = + recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) { + dev_err(chip->dev, "Unable to read header\n"); + goto out; + } + + expected = be32_to_cpu(*(__be32 *) (buf + 2)); + if (expected > count) { + size = -EIO; + goto out; + } + + if ((size += + recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE)) < expected) { + dev_err(chip->dev, "Unable to read remainder of result\n"); + size = -ETIME; + goto out; + } + + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, + &chip->vendor.int_queue); + status = tpm_tis_status(chip); + if (status & TPM_STS_DATA_AVAIL) { /* retry? */ + dev_err(chip->dev, "Error left over data\n"); + size = -EIO; + goto out; + } + +out: + tpm_tis_ready(chip); + release_locality(chip, chip->vendor.locality, 0); + return size; +} + +/* + * If interrupts are used (signaled by an irq set in the vendor structure) + * tpm.c can skip polling for the data to be available as the interrupt is + * waited for here + */ +static int tpm_tis_send(struct tpm_chip *chip, u8 * buf, size_t len) +{ + int rc, status, burstcnt; + size_t count = 0; + u32 ordinal; + + if (request_locality(chip, 0) < 0) + return -EBUSY; + + status = tpm_tis_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + tpm_tis_ready(chip); + if (wait_for_stat + (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, + &chip->vendor.int_queue) < 0) { + rc = -ETIME; + goto out_err; + } + } + + while (count < len - 1) { + burstcnt = get_burstcount(chip); + for (; burstcnt > 0 && count < len - 1; burstcnt--) { + iowrite8(buf[count], chip->vendor.iobase + + TPM_DATA_FIFO(chip->vendor.locality)); + count++; + } + + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, + &chip->vendor.int_queue); + status = tpm_tis_status(chip); + if ((status & TPM_STS_DATA_EXPECT) == 0) { + rc = -EIO; + goto out_err; + } + } + + /* write last byte */ + iowrite8(buf[count], + chip->vendor.iobase + + TPM_DATA_FIFO(chip->vendor.locality)); + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, + &chip->vendor.int_queue); + status = tpm_tis_status(chip); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + rc = -EIO; + goto out_err; + } + + /* go and do it */ + iowrite8(TPM_STS_GO, + chip->vendor.iobase + TPM_STS(chip->vendor.locality)); + + if (chip->vendor.irq) { + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + if (wait_for_stat + (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, + tpm_calc_ordinal_duration(chip, ordinal), + &chip->vendor.read_queue) < 0) { + rc = -ETIME; + goto out_err; + } + } + return len; +out_err: + tpm_tis_ready(chip); + release_locality(chip, chip->vendor.locality, 0); + return rc; +} + +static struct file_operations tis_ops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = tpm_open, + .read = tpm_read, + .write = tpm_write, + .release = tpm_release, +}; + +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, + NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); + +static struct attribute *tis_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, NULL, +}; + +static struct attribute_group tis_attr_grp = { + .attrs = tis_attrs +}; + +static struct tpm_vendor_specific tpm_tis = { + .status = tpm_tis_status, + .recv = tpm_tis_recv, + .send = tpm_tis_send, + .cancel = tpm_tis_ready, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = TPM_STS_COMMAND_READY, + .attr_group = &tis_attr_grp, + .miscdev = { + .fops = &tis_ops,}, +}; + +static irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs + *regs) +{ + struct tpm_chip *chip = (struct tpm_chip *) dev_id; + u32 interrupt; + + interrupt = ioread32(chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)); + + if (interrupt == 0) + return IRQ_NONE; + + chip->vendor.irq = irq; + + /* Clear interrupts handled with TPM_EOI */ + iowrite32(interrupt, + chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)); + return IRQ_HANDLED; +} + +static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs + *regs) +{ + struct tpm_chip *chip = (struct tpm_chip *) dev_id; + u32 interrupt; + int i; + + interrupt = ioread32(chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)); + + if (interrupt == 0) + return IRQ_NONE; + + if (interrupt & TPM_INTF_DATA_AVAIL_INT) + wake_up_interruptible(&chip->vendor.read_queue); + if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) + for (i = 0; i < 5; i++) + if (check_locality(chip, i) >= 0) + break; + if (interrupt & + (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | + TPM_INTF_CMD_READY_INT)) + wake_up_interruptible(&chip->vendor.int_queue); + + /* Clear interrupts handled with TPM_EOI */ + iowrite32(interrupt, + chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)); + return IRQ_HANDLED; +} + +static int __devinit tpm_tis_pnp_init(struct pnp_dev + *pnp_dev, const struct + pnp_device_id + *pnp_id) +{ + u32 vendor, intfcaps, intmask; + int rc, i; + unsigned long start, len; + struct tpm_chip *chip; + + start = pnp_mem_start(pnp_dev, 0); + len = pnp_mem_len(pnp_dev, 0); + + if (!(chip = tpm_register_hardware(&pnp_dev->dev, &tpm_tis))) + return -ENODEV; + + chip->vendor.iobase = ioremap(start, len); + if (!chip->vendor.iobase) { + rc = -EIO; + goto out_err; + } + + vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); + if ((vendor & 0xFFFF) == 0xFFFF) { + rc = -ENODEV; + goto out_err; + } + + /* Default timeouts */ + chip->vendor.timeout_a = 750; /* ms */ + chip->vendor.timeout_b = 2000; /* 2 sec */ + chip->vendor.timeout_c = 750; /* ms */ + chip->vendor.timeout_d = 750; /* ms */ + + dev_info(&pnp_dev->dev, + "1.2 TPM (device-id 0x%X, rev-id %d)\n", + vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); + + /* Figure out the capabilities */ + intfcaps = + ioread32(chip->vendor.iobase + + TPM_INTF_CAPS(chip->vendor.locality)); + dev_dbg(&pnp_dev->dev, "TPM interface capabilities (0x%x):\n", + intfcaps); + if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) + dev_dbg(&pnp_dev->dev, "\tBurst Count Static\n"); + if (intfcaps & TPM_INTF_CMD_READY_INT) + dev_dbg(&pnp_dev->dev, "\tCommand Ready Int Support\n"); + if (intfcaps & TPM_INTF_INT_EDGE_FALLING) + dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Falling\n"); + if (intfcaps & TPM_INTF_INT_EDGE_RISING) + dev_dbg(&pnp_dev->dev, "\tInterrupt Edge Rising\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_LOW) + dev_dbg(&pnp_dev->dev, "\tInterrupt Level Low\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) + dev_dbg(&pnp_dev->dev, "\tInterrupt Level High\n"); + if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) + dev_dbg(&pnp_dev->dev, "\tLocality Change Int Support\n"); + if (intfcaps & TPM_INTF_STS_VALID_INT) + dev_dbg(&pnp_dev->dev, "\tSts Valid Int Support\n"); + if (intfcaps & TPM_INTF_DATA_AVAIL_INT) + dev_dbg(&pnp_dev->dev, "\tData Avail Int Support\n"); + + if (request_locality(chip, 0) != 0) { + rc = -ENODEV; + goto out_err; + } + + /* INTERRUPT Setup */ + init_waitqueue_head(&chip->vendor.read_queue); + init_waitqueue_head(&chip->vendor.int_queue); + + intmask = + ioread32(chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + + intmask |= TPM_INTF_CMD_READY_INT + | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT + | TPM_INTF_STS_VALID_INT; + + iowrite32(intmask, + chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + + chip->vendor.irq = + ioread8(chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + + for (i = 3; i < 16 && chip->vendor.irq == 0; i++) { + iowrite8(i, + chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + if (request_irq + (i, tis_int_probe, SA_SHIRQ, + chip->vendor.miscdev.name, chip) != 0) { + dev_info(chip->dev, + "Unable to request irq: %d for probe\n", + i); + continue; + } + + /* Clear all existing */ + iowrite32(ioread32 + (chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)), + chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)); + + /* Turn on */ + iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, + chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + + /* Generate Interrupts */ + tpm_gen_interrupt(chip); + + /* Turn off */ + iowrite32(intmask, + chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + free_irq(i, chip); + } + if (chip->vendor.irq) { + iowrite8(chip->vendor.irq, + chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + if (request_irq + (chip->vendor.irq, tis_int_handler, SA_SHIRQ, + chip->vendor.miscdev.name, chip) != 0) { + dev_info(chip->dev, + "Unable to request irq: %d for use\n", i); + chip->vendor.irq = 0; + } else { + /* Clear all existing */ + iowrite32(ioread32 + (chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)), + chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)); + + /* Turn on */ + iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, + chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + } + } + + INIT_LIST_HEAD(&chip->vendor.list); + spin_lock(&tis_lock); + list_add(&chip->vendor.list, &tis_chips); + spin_unlock(&tis_lock); + + tpm_get_timeouts(chip); + tpm_continue_selftest(chip); + + return 0; +out_err: + if (chip->vendor.iobase) + iounmap(chip->vendor.iobase); + tpm_remove_hardware(chip->dev); + return rc; +} + +static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg) +{ + return tpm_pm_suspend(&dev->dev, msg); +} + +static int tpm_tis_pnp_resume(struct pnp_dev *dev) +{ + return tpm_pm_resume(&dev->dev); +} + +static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { + {"PNP0C31", 0}, /* TPM */ + {"", 0} +}; + +static struct pnp_driver tis_pnp_driver = { + .name = "tpm_tis", + .id_table = tpm_pnp_tbl, + .probe = tpm_tis_pnp_init, + .suspend = tpm_tis_pnp_suspend, + .resume = tpm_tis_pnp_resume, +}; + +static int __init init_tis(void) +{ + return pnp_register_driver(&tis_pnp_driver); +} + +static void __exit cleanup_tis(void) +{ + struct tpm_vendor_specific *i, *j; + struct tpm_chip *chip; + spin_lock(&tis_lock); + list_for_each_entry_safe(i, j, &tis_chips, list) { + chip = to_tpm_chip(i); + iowrite32(~TPM_GLOBAL_INT_ENABLE & + ioread32(chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor. + locality)), + chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + release_locality(chip, chip->vendor.locality, 1); + if (chip->vendor.irq) + free_irq(chip->vendor.irq, chip); + iounmap(i->iobase); + list_del(&i->list); + tpm_remove_hardware(chip->dev); + } + spin_unlock(&tis_lock); + pnp_unregister_driver(&tis_pnp_driver); +} + +module_init(init_tis); +module_exit(cleanup_tis); +MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); +MODULE_DESCRIPTION("TPM Driver"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("GPL"); -- cgit v1.1 From 36b20020e537036c4f9eb5b69140c88ead5da7dc Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:38:19 -0700 Subject: [PATCH] tpm: msecs_to_jiffies cleanups The timeout and duration values used in the tpm driver are not exposed to userspace. This patch converts the storage units to jiffies with msecs_to_jiffies. They were always being used in jiffies so this simplifies things removing the need for calculation all over the place. The change necessitated a type change in the tpm_chip struct to hold jiffies. Signed-off-by: Kylie Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm.c | 22 ++++++++++++++-------- drivers/char/tpm/tpm.h | 4 ++-- drivers/char/tpm/tpm_tis.c | 32 ++++++++++++++++---------------- 3 files changed, 32 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 150c86a..160dc08 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -354,7 +354,7 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, TPM_PROTECTED_ORDINAL_MASK]; if (duration_idx != TPM_UNDEFINED) - duration = chip->vendor.duration[duration_idx] * HZ / 1000; + duration = chip->vendor.duration[duration_idx]; if (duration <= 0) return 2 * 60 * HZ; else @@ -524,19 +524,19 @@ void tpm_get_timeouts(struct tpm_chip *chip) timeout = be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))); if (timeout) - chip->vendor.timeout_a = timeout; + chip->vendor.timeout_a = msecs_to_jiffies(timeout); timeout = be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX))); if (timeout) - chip->vendor.timeout_b = timeout; + chip->vendor.timeout_b = msecs_to_jiffies(timeout); timeout = be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX))); if (timeout) - chip->vendor.timeout_c = timeout; + chip->vendor.timeout_c = msecs_to_jiffies(timeout); timeout = be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX))); if (timeout) - chip->vendor.timeout_d = timeout; + chip->vendor.timeout_d = msecs_to_jiffies(timeout); duration: memcpy(data, tpm_cap, sizeof(tpm_cap)); @@ -553,11 +553,17 @@ duration: return; chip->vendor.duration[TPM_SHORT] = - be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))); + msecs_to_jiffies(be32_to_cpu + (*((__be32 *) (data + + TPM_GET_CAP_RET_UINT32_1_IDX)))); chip->vendor.duration[TPM_MEDIUM] = - be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX))); + msecs_to_jiffies(be32_to_cpu + (*((__be32 *) (data + + TPM_GET_CAP_RET_UINT32_2_IDX)))); chip->vendor.duration[TPM_LONG] = - be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX))); + msecs_to_jiffies(be32_to_cpu + (*((__be32 *) (data + + TPM_GET_CAP_RET_UINT32_3_IDX)))); } EXPORT_SYMBOL_GPL(tpm_get_timeouts); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 24541a5..54a4c80 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -77,8 +77,8 @@ struct tpm_vendor_specific { struct attribute_group *attr_group; struct list_head list; int locality; - u32 timeout_a, timeout_b, timeout_c, timeout_d; - u32 duration[3]; + unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */ + unsigned long duration[3]; /* jiffies */ wait_queue_head_t read_queue; wait_queue_head_t int_queue; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 0275930..1cb5a7f 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -51,6 +51,11 @@ enum tis_int_flags { TPM_INTF_DATA_AVAIL_INT = 0x001, }; +enum tis_defaults { + TIS_SHORT_TIMEOUT = 750, /* ms */ + TIS_LONG_TIMEOUT = 2000, /* 2 sec */ +}; + #define TPM_ACCESS(l) (0x0000 | ((l) << 12)) #define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) #define TPM_INT_VECTOR(l) (0x000C | ((l) << 12)) @@ -96,19 +101,16 @@ static int request_locality(struct tpm_chip *chip, int l) chip->vendor.iobase + TPM_ACCESS(l)); if (chip->vendor.irq) { - rc = wait_event_interruptible_timeout(chip->vendor. - int_queue, + rc = wait_event_interruptible_timeout(chip->vendor.int_queue, (check_locality (chip, l) >= 0), - msecs_to_jiffies - (chip->vendor. - timeout_a)); + chip->vendor.timeout_a); if (rc > 0) return l; } else { /* wait for burstcount */ - stop = jiffies + (HZ * chip->vendor.timeout_a / 1000); + stop = jiffies + chip->vendor.timeout_a; do { if (check_locality(chip, l) >= 0) return l; @@ -139,7 +141,7 @@ static int get_burstcount(struct tpm_chip *chip) /* wait for burstcount */ /* which timeout value, spec has 2 answers (c & d) */ - stop = jiffies + (HZ * chip->vendor.timeout_d / 1000); + stop = jiffies + chip->vendor.timeout_d; do { burstcnt = ioread8(chip->vendor.iobase + TPM_STS(chip->vendor.locality) + 1); @@ -153,7 +155,7 @@ static int get_burstcount(struct tpm_chip *chip) return -EBUSY; } -static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout, +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, wait_queue_head_t *queue) { unsigned long stop; @@ -169,13 +171,11 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u32 timeout, rc = wait_event_interruptible_timeout(*queue, ((tpm_tis_status (chip) & mask) == - mask), - msecs_to_jiffies - (timeout)); + mask), timeout); if (rc > 0) return 0; } else { - stop = jiffies + (HZ * timeout / 1000); + stop = jiffies + timeout; do { msleep(TPM_TIMEOUT); status = tpm_tis_status(chip); @@ -453,10 +453,10 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev } /* Default timeouts */ - chip->vendor.timeout_a = 750; /* ms */ - chip->vendor.timeout_b = 2000; /* 2 sec */ - chip->vendor.timeout_c = 750; /* ms */ - chip->vendor.timeout_d = 750; /* ms */ + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); dev_info(&pnp_dev->dev, "1.2 TPM (device-id 0x%X, rev-id %d)\n", -- cgit v1.1 From 10685a95301d02fde2b10f6047e405c69d2af82a Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:38:32 -0700 Subject: [PATCH] tpm: use clear_bit Use set_bit() and clear_bit() for dev_mask manipulation. Signed-off-by: Kylie Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 160dc08..6889e7d 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -32,7 +32,6 @@ enum tpm_const { TPM_MINOR = 224, /* officially assigned */ TPM_BUFSIZE = 2048, TPM_NUM_DEVICES = 256, - TPM_NUM_MASK_ENTRIES = TPM_NUM_DEVICES / (8 * sizeof(int)) }; enum tpm_duration { @@ -48,7 +47,7 @@ enum tpm_duration { static LIST_HEAD(tpm_chip_list); static DEFINE_SPINLOCK(driver_lock); -static int dev_mask[TPM_NUM_MASK_ENTRIES]; +static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES); /* * Array with one entry per ordinal defining the maximum amount @@ -1038,8 +1037,7 @@ void tpm_remove_hardware(struct device *dev) sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); tpm_bios_log_teardown(chip->bios_dir); - dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES] &= - ~(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES)); + clear_bit(chip->dev_num, dev_mask); kfree(chip); @@ -1097,7 +1095,6 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend char *devname; struct tpm_chip *chip; - int i, j; /* Driver specific per-device data */ chip = kzalloc(sizeof(*chip), GFP_KERNEL); @@ -1116,19 +1113,9 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific)); - chip->dev_num = -1; + chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES); - for (i = 0; i < TPM_NUM_MASK_ENTRIES; i++) - for (j = 0; j < 8 * sizeof(int); j++) - if ((dev_mask[i] & (1 << j)) == 0) { - chip->dev_num = - i * TPM_NUM_MASK_ENTRIES + j; - dev_mask[i] |= 1 << j; - goto dev_num_search_complete; - } - -dev_num_search_complete: - if (chip->dev_num < 0) { + if (chip->dev_num >= TPM_NUM_DEVICES) { dev_err(dev, "No available tpm device numbers\n"); kfree(chip); return NULL; @@ -1137,6 +1124,8 @@ dev_num_search_complete: else chip->vendor.miscdev.minor = MISC_DYNAMIC_MINOR; + set_bit(chip->dev_num, dev_mask); + devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL); scnprintf(devname, DEVNAME_SIZE, "%s%d", "tpm", chip->dev_num); chip->vendor.miscdev.name = devname; @@ -1150,8 +1139,8 @@ dev_num_search_complete: chip->vendor.miscdev.name, chip->vendor.miscdev.minor); put_device(dev); + clear_bit(chip->dev_num, dev_mask); kfree(chip); - dev_mask[i] &= !(1 << j); return NULL; } -- cgit v1.1 From e496f540540f0a0bffcc3f83785f9954dacf1b83 Mon Sep 17 00:00:00 2001 From: Marcel Selhorst Date: Sat, 22 Apr 2006 02:38:42 -0700 Subject: [PATCH] tpm: tpm_infineon updated to latest interface changes Apply the latest changes in the TPM interface to the Infineon TPM-driver. Signed-off-by: Marcel Selhorst Acked-by: Kylie Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_infineon.c | 58 ++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 24095f6..90cae8f 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -104,7 +104,7 @@ static int empty_fifo(struct tpm_chip *chip, int clear_wrfifo) if (clear_wrfifo) { for (i = 0; i < 4096; i++) { - status = inb(chip->vendor->base + WRFIFO); + status = inb(chip->vendor.base + WRFIFO); if (status == 0xff) { if (check == 5) break; @@ -124,8 +124,8 @@ static int empty_fifo(struct tpm_chip *chip, int clear_wrfifo) */ i = 0; do { - status = inb(chip->vendor->base + RDFIFO); - status = inb(chip->vendor->base + STAT); + status = inb(chip->vendor.base + RDFIFO); + status = inb(chip->vendor.base + STAT); i++; if (i == TPM_MAX_TRIES) return -EIO; @@ -138,7 +138,7 @@ static int wait(struct tpm_chip *chip, int wait_for_bit) int status; int i; for (i = 0; i < TPM_MAX_TRIES; i++) { - status = inb(chip->vendor->base + STAT); + status = inb(chip->vendor.base + STAT); /* check the status-register if wait_for_bit is set */ if (status & 1 << wait_for_bit) break; @@ -157,7 +157,7 @@ static int wait(struct tpm_chip *chip, int wait_for_bit) static void wait_and_send(struct tpm_chip *chip, u8 sendbyte) { wait(chip, STAT_XFE); - outb(sendbyte, chip->vendor->base + WRFIFO); + outb(sendbyte, chip->vendor.base + WRFIFO); } /* Note: WTX means Waiting-Time-Extension. Whenever the TPM needs more @@ -204,7 +204,7 @@ recv_begin: ret = wait(chip, STAT_RDA); if (ret) return -EIO; - buf[i] = inb(chip->vendor->base + RDFIFO); + buf[i] = inb(chip->vendor.base + RDFIFO); } if (buf[0] != TPM_VL_VER) { @@ -219,7 +219,7 @@ recv_begin: for (i = 0; i < size; i++) { wait(chip, STAT_RDA); - buf[i] = inb(chip->vendor->base + RDFIFO); + buf[i] = inb(chip->vendor.base + RDFIFO); } if ((size == 0x6D00) && (buf[1] == 0x80)) { @@ -268,7 +268,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count) u8 count_high, count_low, count_4, count_3, count_2, count_1; /* Disabling Reset, LP and IRQC */ - outb(RESET_LP_IRQC_DISABLE, chip->vendor->base + CMD); + outb(RESET_LP_IRQC_DISABLE, chip->vendor.base + CMD); ret = empty_fifo(chip, 1); if (ret) { @@ -319,7 +319,7 @@ static void tpm_inf_cancel(struct tpm_chip *chip) static u8 tpm_inf_status(struct tpm_chip *chip) { - return inb(chip->vendor->base + STAT); + return inb(chip->vendor.base + STAT); } static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); @@ -346,7 +346,7 @@ static struct file_operations inf_ops = { .release = tpm_release, }; -static struct tpm_vendor_specific tpm_inf = { +static const struct tpm_vendor_specific tpm_inf = { .recv = tpm_inf_recv, .send = tpm_inf_send, .cancel = tpm_inf_cancel, @@ -375,6 +375,7 @@ static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, int version[2]; int productid[2]; char chipname[20]; + struct tpm_chip *chip; /* read IO-ports through PnP */ if (pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && @@ -395,14 +396,13 @@ static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, goto err_last; } /* publish my base address and request region */ - tpm_inf.base = TPM_INF_BASE; if (request_region - (tpm_inf.base, TPM_INF_PORT_LEN, "tpm_infineon0") == NULL) { + (TPM_INF_BASE, TPM_INF_PORT_LEN, "tpm_infineon0") == NULL) { rc = -EINVAL; goto err_last; } - if (request_region(TPM_INF_ADDR, TPM_INF_ADDR_LEN, - "tpm_infineon0") == NULL) { + if (request_region + (TPM_INF_ADDR, TPM_INF_ADDR_LEN, "tpm_infineon0") == NULL) { rc = -EINVAL; goto err_last; } @@ -442,9 +442,9 @@ static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, /* configure TPM with IO-ports */ outb(IOLIMH, TPM_INF_ADDR); - outb(((tpm_inf.base >> 8) & 0xff), TPM_INF_DATA); + outb(((TPM_INF_BASE >> 8) & 0xff), TPM_INF_DATA); outb(IOLIML, TPM_INF_ADDR); - outb((tpm_inf.base & 0xff), TPM_INF_DATA); + outb((TPM_INF_BASE & 0xff), TPM_INF_DATA); /* control if IO-ports are set correctly */ outb(IOLIMH, TPM_INF_ADDR); @@ -452,10 +452,10 @@ static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, outb(IOLIML, TPM_INF_ADDR); iol = inb(TPM_INF_DATA); - if ((ioh << 8 | iol) != tpm_inf.base) { + if ((ioh << 8 | iol) != TPM_INF_BASE) { dev_err(&dev->dev, - "Could not set IO-ports to 0x%lx\n", - tpm_inf.base); + "Could not set IO-ports to 0x%x\n", + TPM_INF_BASE); rc = -EIO; goto err_release_region; } @@ -466,15 +466,15 @@ static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, outb(DISABLE_REGISTER_PAIR, TPM_INF_ADDR); /* disable RESET, LP and IRQC */ - outb(RESET_LP_IRQC_DISABLE, tpm_inf.base + CMD); + outb(RESET_LP_IRQC_DISABLE, TPM_INF_BASE + CMD); /* Finally, we're done, print some infos */ dev_info(&dev->dev, "TPM found: " "config base 0x%x, " "io base 0x%x, " - "chip version %02x%02x, " - "vendor id %x%x (Infineon), " - "product id %02x%02x" + "chip version 0x%02x%02x, " + "vendor id 0x%x%x (Infineon), " + "product id 0x%02x%02x" "%s\n", TPM_INF_ADDR, TPM_INF_BASE, @@ -482,11 +482,10 @@ static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, vendorid[0], vendorid[1], productid[0], productid[1], chipname); - rc = tpm_register_hardware(&dev->dev, &tpm_inf); - if (rc < 0) { - rc = -ENODEV; + if (!(chip = tpm_register_hardware(&dev->dev, &tpm_inf))) { goto err_release_region; } + chip->vendor.base = TPM_INF_BASE; return 0; } else { rc = -ENODEV; @@ -494,7 +493,7 @@ static int __devinit tpm_inf_pnp_probe(struct pnp_dev *dev, } err_release_region: - release_region(tpm_inf.base, TPM_INF_PORT_LEN); + release_region(TPM_INF_BASE, TPM_INF_PORT_LEN); release_region(TPM_INF_ADDR, TPM_INF_ADDR_LEN); err_last: @@ -506,7 +505,8 @@ static __devexit void tpm_inf_pnp_remove(struct pnp_dev *dev) struct tpm_chip *chip = pnp_get_drvdata(dev); if (chip) { - release_region(chip->vendor->base, TPM_INF_PORT_LEN); + release_region(TPM_INF_BASE, TPM_INF_PORT_LEN); + release_region(TPM_INF_ADDR, TPM_INF_ADDR_LEN); tpm_remove_hardware(chip->dev); } } @@ -538,5 +538,5 @@ module_exit(cleanup_inf); MODULE_AUTHOR("Marcel Selhorst "); MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); -MODULE_VERSION("1.7"); +MODULE_VERSION("1.8"); MODULE_LICENSE("GPL"); -- cgit v1.1 From b09d53009db21228adde29b468eb4583e66cbe7c Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:38:55 -0700 Subject: [PATCH] tpm: check mem start and len The memory start and length values obtained from the ACPI entry need to be checked and filled in with the default values from the specification if they don't exist. This patch fills in the default values and uses them appropriately. Signed-off-by: Kylie Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_tis.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 1cb5a7f..9c0727b 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -52,6 +52,8 @@ enum tis_int_flags { }; enum tis_defaults { + TIS_MEM_BASE = 0xFED4000, + TIS_MEM_LEN = 0x5000, TIS_SHORT_TIMEOUT = 750, /* ms */ TIS_LONG_TIMEOUT = 2000, /* 2 sec */ }; @@ -437,6 +439,11 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev start = pnp_mem_start(pnp_dev, 0); len = pnp_mem_len(pnp_dev, 0); + if (!start) + start = TIS_MEM_BASE; + if (!len) + len = TIS_MEM_LEN; + if (!(chip = tpm_register_hardware(&pnp_dev->dev, &tpm_tis))) return -ENODEV; -- cgit v1.1 From 8b006db604527c566dc1dd0aebae37714143aaef Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:39:07 -0700 Subject: [PATCH] tpm: update bios log code for 1.2 The acpi table which contains the BIOS log events was updated for 1.2. There are now client and server modes as defined in the specifications with slightly different formats. Additionally, the start field was even too small for the 1.1 version but had been working anyway. This patch updates the code to deal with any of the three types of headers probperly (1.1, 1.2 client and 1.2 server). Signed-off-by: Kylie Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_bios.c | 49 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c index 5bcbaef..e45f0d3 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_bios.c @@ -29,6 +29,11 @@ #define MAX_TEXT_EVENT 1000 /* Max event string length */ #define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ +enum bios_platform_class { + BIOS_CLIENT = 0x00, + BIOS_SERVER = 0x01, +}; + struct tpm_bios_log { void *bios_event_log; void *bios_event_log_end; @@ -36,9 +41,18 @@ struct tpm_bios_log { struct acpi_tcpa { struct acpi_table_header hdr; - u16 reserved; - u32 log_max_len __attribute__ ((packed)); - u32 log_start_addr __attribute__ ((packed)); + u16 platform_class; + union { + struct client_hdr { + u32 log_max_len __attribute__ ((packed)); + u64 log_start_addr __attribute__ ((packed)); + } client; + struct server_hdr { + u16 reserved; + u64 log_max_len __attribute__ ((packed)); + u64 log_start_addr __attribute__ ((packed)); + } server; + }; }; struct tcpa_event { @@ -379,6 +393,7 @@ static int read_log(struct tpm_bios_log *log) struct acpi_tcpa *buff; acpi_status status; struct acpi_table_header *virt; + u64 len, start; if (log->bios_event_log != NULL) { printk(KERN_ERR @@ -399,27 +414,37 @@ static int read_log(struct tpm_bios_log *log) return -EIO; } - if (buff->log_max_len == 0) { + switch(buff->platform_class) { + case BIOS_SERVER: + len = buff->server.log_max_len; + start = buff->server.log_start_addr; + break; + case BIOS_CLIENT: + default: + len = buff->client.log_max_len; + start = buff->client.log_start_addr; + break; + } + if (!len) { printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); return -EIO; } /* malloc EventLog space */ - log->bios_event_log = kmalloc(buff->log_max_len, GFP_KERNEL); + log->bios_event_log = kmalloc(len, GFP_KERNEL); if (!log->bios_event_log) { - printk - ("%s: ERROR - Not enough Memory for BIOS measurements\n", - __func__); + printk("%s: ERROR - Not enough Memory for BIOS measurements\n", + __func__); return -ENOMEM; } - log->bios_event_log_end = log->bios_event_log + buff->log_max_len; + log->bios_event_log_end = log->bios_event_log + len; - acpi_os_map_memory(buff->log_start_addr, buff->log_max_len, (void *) &virt); + acpi_os_map_memory(start, len, (void *) &virt); - memcpy(log->bios_event_log, virt, buff->log_max_len); + memcpy(log->bios_event_log, virt, len); - acpi_os_unmap_memory(virt, buff->log_max_len); + acpi_os_unmap_memory(virt, len); return 0; } -- cgit v1.1 From 397c718299d848ff305ecd955838a9bd32f1f881 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 22 Apr 2006 02:39:18 -0700 Subject: [PATCH] tpm_infineon section fixup Use __devexit_p() for the exit/remove function to protect against discarding it. WARNING: drivers/char/tpm/tpm_infineon.o - Section mismatch: reference to .exit.text:tpm_inf_pnp_remove from .data between 'tpm_inf_pnp' (at offset 0x20) and 'tpm_inf' Signed-off-by: Randy Dunlap Cc: Kylene Jo Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_infineon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 90cae8f..adfff21 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -15,6 +15,7 @@ * License. */ +#include #include #include "tpm.h" @@ -520,7 +521,7 @@ static struct pnp_driver tpm_inf_pnp = { }, .id_table = tpm_pnp_tbl, .probe = tpm_inf_pnp_probe, - .remove = tpm_inf_pnp_remove, + .remove = __devexit_p(tpm_inf_pnp_remove), }; static int __init init_inf(void) -- cgit v1.1 From cb5354253af2bc30ed449b8be4b3bddf3b3a2746 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:39:31 -0700 Subject: [PATCH] tpm: spacing cleanups 2 Fixes minor spacing issues. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_tis.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 9c0727b..3985147 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -54,8 +54,8 @@ enum tis_int_flags { enum tis_defaults { TIS_MEM_BASE = 0xFED4000, TIS_MEM_LEN = 0x5000, - TIS_SHORT_TIMEOUT = 750, /* ms */ - TIS_LONG_TIMEOUT = 2000, /* 2 sec */ + TIS_SHORT_TIMEOUT = 750, /* ms */ + TIS_LONG_TIMEOUT = 2000, /* 2 sec */ }; #define TPM_ACCESS(l) (0x0000 | ((l) << 12)) @@ -188,7 +188,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, return -ETIME; } -static int recv_data(struct tpm_chip *chip, u8 * buf, size_t count) +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) { int size = 0, burstcnt; while (size < count && @@ -206,7 +206,7 @@ static int recv_data(struct tpm_chip *chip, u8 * buf, size_t count) return size; } -static int tpm_tis_recv(struct tpm_chip *chip, u8 * buf, size_t count) +static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) { int size = 0; int expected, status; @@ -257,7 +257,7 @@ out: * tpm.c can skip polling for the data to be available as the interrupt is * waited for here */ -static int tpm_tis_send(struct tpm_chip *chip, u8 * buf, size_t len) +static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) { int rc, status, burstcnt; size_t count = 0; @@ -374,8 +374,7 @@ static struct tpm_vendor_specific tpm_tis = { .fops = &tis_ops,}, }; -static irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs - *regs) +static irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs *regs) { struct tpm_chip *chip = (struct tpm_chip *) dev_id; u32 interrupt; @@ -395,8 +394,7 @@ static irqreturn_t tis_int_probe(int irq, void *dev_id, struct pt_regs return IRQ_HANDLED; } -static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs - *regs) +static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs *regs) { struct tpm_chip *chip = (struct tpm_chip *) dev_id; u32 interrupt; @@ -426,10 +424,8 @@ static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs return IRQ_HANDLED; } -static int __devinit tpm_tis_pnp_init(struct pnp_dev - *pnp_dev, const struct - pnp_device_id - *pnp_id) +static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, + const struct pnp_device_id *pnp_id) { u32 vendor, intfcaps, intmask; int rc, i; -- cgit v1.1 From 5713556843aee24f484f445db6540f9fef976439 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:39:44 -0700 Subject: [PATCH] tpm: add interrupt module parameter This patch adds a boolean module parameter that allows the user to turn interrupt support on and off. The default behavior is to attempt to use interrupts. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_tis.c | 78 +++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 3985147..447f763 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -16,6 +16,9 @@ * published by the Free Software Foundation, version 2 of the * License. */ +#include +#include +#include #include #include #include @@ -424,6 +427,10 @@ static irqreturn_t tis_int_handler(int irq, void *dev_id, struct pt_regs *regs) return IRQ_HANDLED; } +static int interrupts = 1; +module_param(interrupts, bool, 0444); +MODULE_PARM_DESC(interrupts, "Enable interrupts"); + static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, const struct pnp_device_id *pnp_id) { @@ -510,44 +517,44 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); + if (interrupts) { + chip->vendor.irq = + ioread8(chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + + for (i = 3; i < 16 && chip->vendor.irq == 0; i++) { + iowrite8(i, chip->vendor.iobase + + TPM_INT_VECTOR(chip->vendor.locality)); + if (request_irq + (i, tis_int_probe, SA_SHIRQ, + chip->vendor.miscdev.name, chip) != 0) { + dev_info(chip->dev, + "Unable to request irq: %d for probe\n", + i); + continue; + } - chip->vendor.irq = - ioread8(chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); - - for (i = 3; i < 16 && chip->vendor.irq == 0; i++) { - iowrite8(i, - chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); - if (request_irq - (i, tis_int_probe, SA_SHIRQ, - chip->vendor.miscdev.name, chip) != 0) { - dev_info(chip->dev, - "Unable to request irq: %d for probe\n", - i); - continue; - } - - /* Clear all existing */ - iowrite32(ioread32 - (chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)), - chip->vendor.iobase + - TPM_INT_STATUS(chip->vendor.locality)); + /* Clear all existing */ + iowrite32(ioread32 + (chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)), + chip->vendor.iobase + + TPM_INT_STATUS(chip->vendor.locality)); - /* Turn on */ - iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, - chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); + /* Turn on */ + iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, + chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); - /* Generate Interrupts */ - tpm_gen_interrupt(chip); + /* Generate Interrupts */ + tpm_gen_interrupt(chip); - /* Turn off */ - iowrite32(intmask, - chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); - free_irq(i, chip); + /* Turn off */ + iowrite32(intmask, + chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality)); + free_irq(i, chip); + } } if (chip->vendor.irq) { iowrite8(chip->vendor.irq, @@ -557,7 +564,8 @@ static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, (chip->vendor.irq, tis_int_handler, SA_SHIRQ, chip->vendor.miscdev.name, chip) != 0) { dev_info(chip->dev, - "Unable to request irq: %d for use\n", i); + "Unable to request irq: %d for use\n", + chip->vendor.irq); chip->vendor.irq = 0; } else { /* Clear all existing */ -- cgit v1.1 From 93e1b7d42e1edb4ddde6257e9a02513fef26f715 Mon Sep 17 00:00:00 2001 From: Kylene Jo Hall Date: Sat, 22 Apr 2006 02:39:52 -0700 Subject: [PATCH] tpm: add HID module parameter I recently found that not all BIOS manufacturers are using the specified generic PNP id in their TPM ACPI table entry. I have added the vendor specific IDs that I know about and added a module parameter that a user can specify another HID to the probe list if their device isn't being found by the default list. Signed-off-by: Kylene Hall Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tpm/tpm_tis.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 447f763..b9cae9a 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -610,7 +610,13 @@ static int tpm_tis_pnp_resume(struct pnp_dev *dev) static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { {"PNP0C31", 0}, /* TPM */ - {"", 0} + {"ATM1200", 0}, /* Atmel */ + {"IFX0102", 0}, /* Infineon */ + {"BCM0101", 0}, /* Broadcom */ + {"NSC1200", 0}, /* National */ + /* Add new here */ + {"", 0}, /* User Specified */ + {"", 0} /* Terminator */ }; static struct pnp_driver tis_pnp_driver = { @@ -621,6 +627,11 @@ static struct pnp_driver tis_pnp_driver = { .resume = tpm_tis_pnp_resume, }; +#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2 +module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, + sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444); +MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); + static int __init init_tis(void) { return pnp_register_driver(&tis_pnp_driver); -- cgit v1.1 From caa98c41c0db9bfda5bc9a0e680f304283089268 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Sun, 23 Apr 2006 18:14:00 +1000 Subject: drm: fixup r300 scratch on BE machines This fixes the r300 scratch stuff to work on PPC, from Ben Herrenschmidt on IRC. Signed-off-by: Dave Airlie --- drivers/char/drm/r300_cmdbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/char/drm/r300_cmdbuf.c b/drivers/char/drm/r300_cmdbuf.c index b108c7f91..26bdf2c 100644 --- a/drivers/char/drm/r300_cmdbuf.c +++ b/drivers/char/drm/r300_cmdbuf.c @@ -723,7 +723,7 @@ static int r300_scratch(drm_radeon_private_t *dev_priv, dev_priv->scratch_ages[header.scratch.reg]++; - ref_age_base = *(u32 **)cmdbuf->buf; + ref_age_base = (u32 *)(unsigned long)*((uint64_t *)cmdbuf->buf); cmdbuf->buf += sizeof(u64); cmdbuf->bufsz -= sizeof(u64); -- cgit v1.1 From 5d23fafb1bf8ef071738026c2e5071a92186d5f8 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Sun, 23 Apr 2006 18:26:40 +1000 Subject: drm: possible cleanups This patch contains the following possible cleanups: - make the following needlessly global function static: - drm_bufs.c: drm_addbufs_fb() - remove the following unused EXPORT_SYMBOL's: - drm_agpsupport.c: drm_agp_bind_memory - drm_bufs.c: drm_rmmap_locked - drm_bufs.c: drm_rmmap - drm_stub.c: drm_get_dev Signed-off-by: Adrian Bunk Signed-off-by: Dave Airlie --- drivers/char/drm/drmP.h | 1 - drivers/char/drm/drm_agpsupport.c | 2 -- drivers/char/drm/drm_bufs.c | 5 +---- drivers/char/drm/drm_stub.c | 2 -- 4 files changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index e1aadae..cb76e5c 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -889,7 +889,6 @@ extern int drm_lock_free(drm_device_t * dev, /* Buffer management support (drm_bufs.h) */ extern int drm_addbufs_agp(drm_device_t * dev, drm_buf_desc_t * request); extern int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request); -extern int drm_addbufs_fb(drm_device_t *dev, drm_buf_desc_t *request); extern int drm_addmap(drm_device_t * dev, unsigned int offset, unsigned int size, drm_map_type_t type, drm_map_flags_t flags, drm_local_map_t ** map_ptr); diff --git a/drivers/char/drm/drm_agpsupport.c b/drivers/char/drm/drm_agpsupport.c index fabc930..40bfd9b 100644 --- a/drivers/char/drm/drm_agpsupport.c +++ b/drivers/char/drm/drm_agpsupport.c @@ -503,8 +503,6 @@ int drm_agp_bind_memory(DRM_AGP_MEM * handle, off_t start) return agp_bind_memory(handle, start); } -EXPORT_SYMBOL(drm_agp_bind_memory); - /** Calls agp_unbind_memory() */ int drm_agp_unbind_memory(DRM_AGP_MEM * handle) { diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c index 8a9cf12..006b06d 100644 --- a/drivers/char/drm/drm_bufs.c +++ b/drivers/char/drm/drm_bufs.c @@ -386,7 +386,6 @@ int drm_rmmap_locked(drm_device_t *dev, drm_local_map_t *map) return 0; } -EXPORT_SYMBOL(drm_rmmap_locked); int drm_rmmap(drm_device_t *dev, drm_local_map_t *map) { @@ -398,7 +397,6 @@ int drm_rmmap(drm_device_t *dev, drm_local_map_t *map) return ret; } -EXPORT_SYMBOL(drm_rmmap); /* The rmmap ioctl appears to be unnecessary. All mappings are torn down on * the last close of the device, and this is necessary for cleanup when things @@ -1053,7 +1051,7 @@ static int drm_addbufs_sg(drm_device_t * dev, drm_buf_desc_t * request) return 0; } -int drm_addbufs_fb(drm_device_t * dev, drm_buf_desc_t * request) +static int drm_addbufs_fb(drm_device_t * dev, drm_buf_desc_t * request) { drm_device_dma_t *dma = dev->dma; drm_buf_entry_t *entry; @@ -1212,7 +1210,6 @@ int drm_addbufs_fb(drm_device_t * dev, drm_buf_desc_t * request) atomic_dec(&dev->buf_alloc); return 0; } -EXPORT_SYMBOL(drm_addbufs_fb); /** diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c index 68073e1..9a842a3 100644 --- a/drivers/char/drm/drm_stub.c +++ b/drivers/char/drm/drm_stub.c @@ -229,8 +229,6 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent, return ret; } -EXPORT_SYMBOL(drm_get_dev); - /** * Put a device minor number. * -- cgit v1.1 From 3d63abe56be2147891b3438cb3bd37a9be72bda7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 24 Apr 2006 11:27:02 +0100 Subject: [MMC] pxamci: fix data timeout calculation The MMC layer gives us two parts for the timeout calculation - a fixed timeout in nanoseconds, and a card clock-speed dependent part. The PXA MMC hardware allows for a timeout based on the fixed host clock speed only. This resulted in some cards being given a short timeout, and therefore failing to work. Signed-off-by: Russell King --- drivers/mmc/pxamci.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index eb9a882..eb42cb3 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -65,11 +65,6 @@ struct pxamci_host { unsigned int dma_dir; }; -static inline unsigned int ns_to_clocks(unsigned int ns) -{ - return (ns * (CLOCKRATE / 1000000) + 999) / 1000; -} - static void pxamci_stop_clock(struct pxamci_host *host) { if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { @@ -113,6 +108,7 @@ static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) { unsigned int nob = data->blocks; + unsigned long long clks; unsigned int timeout; u32 dcmd; int i; @@ -125,7 +121,9 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) writel(nob, host->base + MMC_NOB); writel(1 << data->blksz_bits, host->base + MMC_BLKLEN); - timeout = ns_to_clocks(data->timeout_ns) + data->timeout_clks; + clks = (unsigned long long)data->timeout_ns * CLOCKRATE; + do_div(clks, 1000000000UL); + timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); writel((timeout + 255) / 256, host->base + MMC_RDTO); if (data->flags & MMC_DATA_READ) { -- cgit v1.1 From dac322e39a2b82871cf514c9a533f24a1b4c7e19 Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Mon, 17 Apr 2006 11:36:43 -0400 Subject: [PATCH] Fix crash on big-endian systems during scan The original code was doing arithmetics on a little-endian value. Reported by Stelios Koroneos Signed-off-by: Pavel Roskin Signed-off-by: John W. Linville --- drivers/net/wireless/hostap/hostap_ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index 8b37e82..8399de5 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -1860,7 +1860,7 @@ static char * __prism2_translate_scan(local_info_t *local, memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; if (scan) { - chan = scan->chid; + chan = le16_to_cpu(scan->chid); } else if (bss) { chan = bss->chan; } else { @@ -1868,7 +1868,7 @@ static char * __prism2_translate_scan(local_info_t *local, } if (chan > 0) { - iwe.u.freq.m = freq_list[le16_to_cpu(chan - 1)] * 100000; + iwe.u.freq.m = freq_list[chan - 1] * 100000; iwe.u.freq.e = 1; current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); -- cgit v1.1 From 7c241d37fe0e6442c5cf3b5d73f7f58f2dc66352 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sun, 23 Apr 2006 13:23:10 +0200 Subject: [PATCH] bcm43xx: make PIO mode usable This patch fixes PIO mode on the softmac bcm43xx driver. (A dscape patch will follow). It mainly fixes endianess issues. This patch is tested on PowerPC32 and i386. Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/bcm43xx/bcm43xx_dma.h | 8 +++ drivers/net/wireless/bcm43xx/bcm43xx_pio.c | 92 ++++++++++++++++++++---------- drivers/net/wireless/bcm43xx/bcm43xx_pio.h | 16 +++++- 3 files changed, 84 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.h b/drivers/net/wireless/bcm43xx/bcm43xx_dma.h index 2d520e4..b7d7763 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_dma.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.h @@ -213,6 +213,14 @@ static inline void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring) { } +static inline +void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring) +{ +} +static inline +void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring) +{ +} #endif /* CONFIG_BCM43XX_DMA */ #endif /* BCM43xx_DMA_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c index c59ddd4..0aa1bd2 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c @@ -27,6 +27,7 @@ #include "bcm43xx_pio.h" #include "bcm43xx_main.h" #include "bcm43xx_xmit.h" +#include "bcm43xx_power.h" #include @@ -44,10 +45,10 @@ static void tx_octet(struct bcm43xx_pioqueue *queue, bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, octet); bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, - BCM43xx_PIO_TXCTL_WRITEHI); + BCM43xx_PIO_TXCTL_WRITELO); } else { bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, - BCM43xx_PIO_TXCTL_WRITEHI); + BCM43xx_PIO_TXCTL_WRITELO); bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, octet); } @@ -103,7 +104,7 @@ static void tx_complete(struct bcm43xx_pioqueue *queue, bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, skb->data[skb->len - 1]); bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, - BCM43xx_PIO_TXCTL_WRITEHI | + BCM43xx_PIO_TXCTL_WRITELO | BCM43xx_PIO_TXCTL_COMPLETE); } else { bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, @@ -112,9 +113,10 @@ static void tx_complete(struct bcm43xx_pioqueue *queue, } static u16 generate_cookie(struct bcm43xx_pioqueue *queue, - int packetindex) + struct bcm43xx_pio_txpacket *packet) { u16 cookie = 0x0000; + int packetindex; /* We use the upper 4 bits for the PIO * controller ID and the lower 12 bits @@ -135,6 +137,7 @@ static u16 generate_cookie(struct bcm43xx_pioqueue *queue, default: assert(0); } + packetindex = pio_txpacket_getindex(packet); assert(((u16)packetindex & 0xF000) == 0x0000); cookie |= (u16)packetindex; @@ -184,7 +187,7 @@ static void pio_tx_write_fragment(struct bcm43xx_pioqueue *queue, bcm43xx_generate_txhdr(queue->bcm, &txhdr, skb->data, skb->len, (packet->xmitted_frags == 0), - generate_cookie(queue, pio_txpacket_getindex(packet))); + generate_cookie(queue, packet)); tx_start(queue); octets = skb->len + sizeof(txhdr); @@ -241,7 +244,7 @@ static int pio_tx_packet(struct bcm43xx_pio_txpacket *packet) queue->tx_devq_packets++; queue->tx_devq_used += octets; - assert(packet->xmitted_frags <= packet->txb->nr_frags); + assert(packet->xmitted_frags < packet->txb->nr_frags); packet->xmitted_frags++; packet->xmitted_octets += octets; } @@ -257,8 +260,14 @@ static void tx_tasklet(unsigned long d) unsigned long flags; struct bcm43xx_pio_txpacket *packet, *tmp_packet; int err; + u16 txctl; bcm43xx_lock_mmio(bcm, flags); + + txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); + if (txctl & BCM43xx_PIO_TXCTL_SUSPEND) + goto out_unlock; + list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) { assert(packet->xmitted_frags < packet->txb->nr_frags); if (packet->xmitted_frags == 0) { @@ -288,6 +297,7 @@ static void tx_tasklet(unsigned long d) next_packet: continue; } +out_unlock: bcm43xx_unlock_mmio(bcm, flags); } @@ -330,12 +340,19 @@ struct bcm43xx_pioqueue * bcm43xx_setup_pioqueue(struct bcm43xx_private *bcm, (unsigned long)queue); value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); - value |= BCM43xx_SBF_XFER_REG_BYTESWAP; + value &= ~BCM43xx_SBF_XFER_REG_BYTESWAP; bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value); qsize = bcm43xx_read16(bcm, queue->mmio_base + BCM43xx_PIO_TXQBUFSIZE); + if (qsize == 0) { + printk(KERN_ERR PFX "ERROR: This card does not support PIO " + "operation mode. Please use DMA mode " + "(module parameter pio=0).\n"); + goto err_freequeue; + } if (qsize <= BCM43xx_PIO_TXQADJUST) { - printk(KERN_ERR PFX "PIO tx device-queue too small (%u)\n", qsize); + printk(KERN_ERR PFX "PIO tx device-queue too small (%u)\n", + qsize); goto err_freequeue; } qsize -= BCM43xx_PIO_TXQADJUST; @@ -444,15 +461,10 @@ int bcm43xx_pio_tx(struct bcm43xx_private *bcm, { struct bcm43xx_pioqueue *queue = bcm43xx_current_pio(bcm)->queue1; struct bcm43xx_pio_txpacket *packet; - u16 tmp; assert(!queue->tx_suspended); assert(!list_empty(&queue->txfree)); - tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); - if (tmp & BCM43xx_PIO_TXCTL_SUSPEND) - return -EBUSY; - packet = list_entry(queue->txfree.next, struct bcm43xx_pio_txpacket, list); packet->txb = txb; packet->xmitted_frags = 0; @@ -462,7 +474,7 @@ int bcm43xx_pio_tx(struct bcm43xx_private *bcm, assert(queue->nr_txfree < BCM43xx_PIO_MAXTXPACKETS); /* Suspend TX, if we are out of packets in the "free" queue. */ - if (unlikely(list_empty(&queue->txfree))) { + if (list_empty(&queue->txfree)) { netif_stop_queue(queue->bcm->net_dev); queue->tx_suspended = 1; } @@ -480,15 +492,15 @@ void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, queue = parse_cookie(bcm, status->cookie, &packet); assert(queue); -//TODO -if (!queue) -return; + free_txpacket(packet, 1); - if (unlikely(queue->tx_suspended)) { + if (queue->tx_suspended) { queue->tx_suspended = 0; netif_wake_queue(queue->bcm->net_dev); } - /* If there are packets on the txqueue, poke the tasklet. */ + /* If there are packets on the txqueue, poke the tasklet + * to transmit them. + */ if (!list_empty(&queue->txqueue)) tasklet_schedule(&queue->txtask); } @@ -519,12 +531,9 @@ void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue) int i, preamble_readwords; struct sk_buff *skb; -return; tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL); - if (!(tmp & BCM43xx_PIO_RXCTL_DATAAVAILABLE)) { - dprintkl(KERN_ERR PFX "PIO RX: No data available\n");//TODO: remove this printk. + if (!(tmp & BCM43xx_PIO_RXCTL_DATAAVAILABLE)) return; - } bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL, BCM43xx_PIO_RXCTL_DATAAVAILABLE); @@ -538,8 +547,7 @@ return; return; data_ready: -//FIXME: endianess in this function. - len = le16_to_cpu(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA)); + len = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); if (unlikely(len > 0x700)) { pio_rx_error(queue, 0, "len > 0x700"); return; @@ -555,7 +563,7 @@ data_ready: preamble_readwords = 18 / sizeof(u16); for (i = 0; i < preamble_readwords; i++) { tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); - preamble[i + 1] = cpu_to_be16(tmp);//FIXME? + preamble[i + 1] = cpu_to_le16(tmp); } rxhdr = (struct bcm43xx_rxhdr *)preamble; rxflags2 = le16_to_cpu(rxhdr->flags2); @@ -591,16 +599,40 @@ data_ready: } skb_put(skb, len); for (i = 0; i < len - 1; i += 2) { - tmp = cpu_to_be16(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA)); - *((u16 *)(skb->data + i)) = tmp; + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + *((u16 *)(skb->data + i)) = cpu_to_le16(tmp); } if (len % 2) { tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); skb->data[len - 1] = (tmp & 0x00FF); +/* The specs say the following is required, but + * it is wrong and corrupts the PLCP. If we don't do + * this, the PLCP seems to be correct. So ifdef it out for now. + */ +#if 0 if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) - skb->data[0x20] = (tmp & 0xFF00) >> 8; + skb->data[2] = (tmp & 0xFF00) >> 8; else - skb->data[0x1E] = (tmp & 0xFF00) >> 8; + skb->data[0] = (tmp & 0xFF00) >> 8; +#endif } + skb_trim(skb, len - IEEE80211_FCS_LEN); bcm43xx_rx(queue->bcm, skb, rxhdr); } + +void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue) +{ + bcm43xx_power_saving_ctl_bits(queue->bcm, -1, 1); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL) + | BCM43xx_PIO_TXCTL_SUSPEND); +} + +void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) +{ + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL) + & ~BCM43xx_PIO_TXCTL_SUSPEND); + bcm43xx_power_saving_ctl_bits(queue->bcm, -1, -1); + tasklet_schedule(&queue->txtask); +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h index 970627b..dfc7820 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h @@ -14,8 +14,8 @@ #define BCM43xx_PIO_RXCTL 0x08 #define BCM43xx_PIO_RXDATA 0x0A -#define BCM43xx_PIO_TXCTL_WRITEHI (1 << 0) -#define BCM43xx_PIO_TXCTL_WRITELO (1 << 1) +#define BCM43xx_PIO_TXCTL_WRITELO (1 << 0) +#define BCM43xx_PIO_TXCTL_WRITEHI (1 << 1) #define BCM43xx_PIO_TXCTL_COMPLETE (1 << 2) #define BCM43xx_PIO_TXCTL_INIT (1 << 3) #define BCM43xx_PIO_TXCTL_SUSPEND (1 << 7) @@ -95,6 +95,7 @@ void bcm43xx_pio_write(struct bcm43xx_pioqueue *queue, u16 offset, u16 value) { bcm43xx_write16(queue->bcm, queue->mmio_base + offset, value); + mmiowb(); } @@ -107,6 +108,9 @@ void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, struct bcm43xx_xmitstatus *status); void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue); +void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue); +void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue); + #else /* CONFIG_BCM43XX_PIO */ static inline @@ -133,6 +137,14 @@ static inline void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue) { } +static inline +void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue) +{ +} +static inline +void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) +{ +} #endif /* CONFIG_BCM43XX_PIO */ #endif /* BCM43xx_PIO_H_ */ -- cgit v1.1 From bcb49197ed9a2e8a0a8d990723dccfccffa7566f Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 25 Apr 2006 22:50:04 -0700 Subject: e1000: Update truesize with the length of the packet for packet split Update skb with the real packet size. Signed-off-by: Jesse Brandeburg Signed-off-by: Auke Kok Signed-off-by: John Ronciak --- drivers/net/e1000/e1000_main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index add8dc4..c99e878 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -3768,6 +3768,7 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, ps_page->ps_page[j] = NULL; skb->len += length; skb->data_len += length; + skb->truesize += length; } copydone: -- cgit v1.1 From 734cbc363b159caee158d5a83408c72d98bcacf0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 25 Apr 2006 10:58:50 -0700 Subject: [PATCH] sky2: reschedule if irq still pending This is a workaround for the case edge-triggered irq's. Several users seem to have broken configurations sharing edge-triggered irq's. To avoid losing IRQ's, reshedule if more work arrives. The changes to netdevice.h are to extract the part that puts device back in list into separate inline. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 67b0eab..618fde86 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2093,6 +2093,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) int work_done = 0; u32 status = sky2_read32(hw, B0_Y2_SP_EISR); + restart_poll: if (unlikely(status & ~Y2_IS_STAT_BMU)) { if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); @@ -2123,7 +2124,7 @@ static int sky2_poll(struct net_device *dev0, int *budget) } if (status & Y2_IS_STAT_BMU) { - work_done = sky2_status_intr(hw, work_limit); + work_done += sky2_status_intr(hw, work_limit - work_done); *budget -= work_done; dev0->quota -= work_done; @@ -2133,9 +2134,22 @@ static int sky2_poll(struct net_device *dev0, int *budget) sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); } - netif_rx_complete(dev0); + local_irq_disable(); + __netif_rx_complete(dev0); status = sky2_read32(hw, B0_Y2_SP_LISR); + + if (unlikely(status)) { + /* More work pending, try and keep going */ + if (__netif_rx_schedule_prep(dev0)) { + __netif_rx_reschedule(dev0, work_done); + status = sky2_read32(hw, B0_Y2_SP_EISR); + local_irq_enable(); + goto restart_poll; + } + } + + local_irq_enable(); return 0; } @@ -2153,8 +2167,6 @@ static irqreturn_t sky2_intr(int irq, void *dev_id, struct pt_regs *regs) prefetch(&hw->st_le[hw->st_idx]); if (likely(__netif_rx_schedule_prep(dev0))) __netif_rx_schedule(dev0); - else - printk(KERN_DEBUG PFX "irq race detected\n"); return IRQ_HANDLED; } -- cgit v1.1 From d27ed38765d6e01eaab443a7909f53a37f090e99 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 25 Apr 2006 10:58:51 -0700 Subject: [PATCH] sky2: add fake idle irq timer Add an fake NAPI schedule once a second. This is an attempt to work around for broken configurations with edge-triggered interrupts. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 22 +++++++++++++++++++++- drivers/net/sky2.h | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 618fde86..6673ddf 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2086,6 +2086,20 @@ static void sky2_descriptor_error(struct sky2_hw *hw, unsigned port, } } +/* If idle then force a fake soft NAPI poll once a second + * to work around cases where sharing an edge triggered interrupt. + */ +static void sky2_idle(unsigned long arg) +{ + struct net_device *dev = (struct net_device *) arg; + + local_irq_disable(); + if (__netif_rx_schedule_prep(dev)) + __netif_rx_schedule(dev); + local_irq_enable(); +} + + static int sky2_poll(struct net_device *dev0, int *budget) { struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw; @@ -2134,6 +2148,8 @@ static int sky2_poll(struct net_device *dev0, int *budget) sky2_write32(hw, STAT_CTRL, SC_STAT_CLR_IRQ); } + mod_timer(&hw->idle_timer, jiffies + HZ); + local_irq_disable(); __netif_rx_complete(dev0); @@ -3288,6 +3304,8 @@ static int __devinit sky2_probe(struct pci_dev *pdev, sky2_write32(hw, B0_IMSK, Y2_IS_BASE); + setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) dev); + pci_set_drvdata(pdev, hw); return 0; @@ -3323,13 +3341,15 @@ static void __devexit sky2_remove(struct pci_dev *pdev) if (!hw) return; + del_timer_sync(&hw->idle_timer); + + sky2_write32(hw, B0_IMSK, 0); dev0 = hw->dev[0]; dev1 = hw->dev[1]; if (dev1) unregister_netdev(dev1); unregister_netdev(dev0); - sky2_write32(hw, B0_IMSK, 0); sky2_set_power_state(hw, PCI_D3hot); sky2_write16(hw, B0_Y2LED, LED_STAT_OFF); sky2_write8(hw, B0_CTST, CS_RST_SET); diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 89dd18cd..b026f56 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -1880,6 +1880,8 @@ struct sky2_hw { struct sky2_status_le *st_le; u32 st_idx; dma_addr_t st_dma; + + struct timer_list idle_timer; int msi_detected; wait_queue_head_t msi_wait; }; -- cgit v1.1 From 4a15d56f78936ec15a5d747546f25ace8fef9a03 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 25 Apr 2006 10:58:52 -0700 Subject: [PATCH] sky2: use ALIGN() macro The ALIGN() macro in kernel.h does the same math that the sky2 driver was using for padding. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 6673ddf..393ee63 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -925,8 +925,7 @@ static inline struct sk_buff *sky2_alloc_skb(unsigned int size, gfp_t gfp_mask) skb = alloc_skb(size + RX_SKB_ALIGN, gfp_mask); if (likely(skb)) { unsigned long p = (unsigned long) skb->data; - skb_reserve(skb, - ((p + RX_SKB_ALIGN - 1) & ~(RX_SKB_ALIGN - 1)) - p); + skb_reserve(skb, ALIGN(p, RX_SKB_ALIGN) - p); } return skb; @@ -1686,13 +1685,12 @@ static void sky2_tx_timeout(struct net_device *dev) } -#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* Want receive buffer size to be multiple of 64 bits * and incl room for vlan and truncation */ static inline unsigned sky2_buf_size(int mtu) { - return roundup(mtu + ETH_HLEN + VLAN_HLEN, 8) + 8; + return ALIGN(mtu + ETH_HLEN + VLAN_HLEN, 8) + 8; } static int sky2_change_mtu(struct net_device *dev, int new_mtu) -- cgit v1.1 From 98712e5e3325247bf22a175d225526c9d5f8439b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 25 Apr 2006 10:58:53 -0700 Subject: [PATCH] sky2: reset function can be devinit The sky2_reset function only called from sky2_probe. Maybe the compiler was smart enough to figure this out already. Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 393ee63..ce1a8af 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -2219,7 +2219,7 @@ static inline u32 sky2_clk2us(const struct sky2_hw *hw, u32 clk) } -static int sky2_reset(struct sky2_hw *hw) +static int __devinit sky2_reset(struct sky2_hw *hw) { u16 status; u8 t8, pmd_type; -- cgit v1.1 From bdf9c27d020ba50b42949c383c1956216c9fd522 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 25 Apr 2006 10:58:54 -0700 Subject: [PATCH] sky2: version 1.2 Update to version 1.2 Signed-off-by: Stephen Hemminger Signed-off-by: Jeff Garzik --- drivers/net/sky2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index ce1a8af..227df98 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -51,7 +51,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.1" +#define DRV_VERSION "1.2" #define PFX DRV_NAME " " /* -- cgit v1.1 From 86a0f04387bfa814618bf0c2c8b203899c4fa5d2 Mon Sep 17 00:00:00 2001 From: Ayaz Abdulla Date: Mon, 24 Apr 2006 18:41:31 -0400 Subject: [PATCH] forcedeth: fix initialization This patch fixes the nic initialization. If the nic was in low power mode, it brings it back to normal power. Also, it utilizes a new hardware reset during the init. I am resending based on feedback, I corrected the register size mapping and delay after posted write. Signed-Off-By: Ayaz Abdulla Signed-off-by: Jeff Garzik --- drivers/net/forcedeth.c | 79 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index 7627a75..9788b1e 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -105,6 +105,7 @@ * 0.50: 20 Jan 2006: Add 8021pq tagging support. * 0.51: 20 Jan 2006: Add 64bit consistent memory allocation for rings. * 0.52: 20 Jan 2006: Add MSI/MSIX support. + * 0.53: 19 Mar 2006: Fix init from low power mode and add hw reset. * * Known bugs: * We suspect that on some hardware no TX done interrupts are generated. @@ -116,7 +117,7 @@ * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few * superfluous timer interrupts from the nic. */ -#define FORCEDETH_VERSION "0.52" +#define FORCEDETH_VERSION "0.53" #define DRV_NAME "forcedeth" #include @@ -160,6 +161,7 @@ #define DEV_HAS_VLAN 0x0020 /* device supports vlan tagging and striping */ #define DEV_HAS_MSI 0x0040 /* device supports MSI */ #define DEV_HAS_MSI_X 0x0080 /* device supports MSI-X */ +#define DEV_HAS_POWER_CNTRL 0x0100 /* device supports power savings */ enum { NvRegIrqStatus = 0x000, @@ -203,6 +205,8 @@ enum { #define NVREG_MISC1_HD 0x02 #define NVREG_MISC1_FORCE 0x3b0f3c + NvRegMacReset = 0x3c, +#define NVREG_MAC_RESET_ASSERT 0x0F3 NvRegTransmitterControl = 0x084, #define NVREG_XMITCTL_START 0x01 NvRegTransmitterStatus = 0x088, @@ -326,6 +330,10 @@ enum { NvRegMSIXMap0 = 0x3e0, NvRegMSIXMap1 = 0x3e4, NvRegMSIXIrqStatus = 0x3f0, + + NvRegPowerState2 = 0x600, +#define NVREG_POWERSTATE2_POWERUP_MASK 0x0F11 +#define NVREG_POWERSTATE2_POWERUP_REV_A3 0x0001 }; /* Big endian: should work, but is untested */ @@ -414,7 +422,8 @@ typedef union _ring_type { #define NV_RX3_VLAN_TAG_MASK (0x0000FFFF) /* Miscelaneous hardware related defines: */ -#define NV_PCI_REGSZ 0x270 +#define NV_PCI_REGSZ_VER1 0x270 +#define NV_PCI_REGSZ_VER2 0x604 /* various timeout delays: all in usec */ #define NV_TXRX_RESET_DELAY 4 @@ -431,6 +440,7 @@ typedef union _ring_type { #define NV_MIIBUSY_DELAY 50 #define NV_MIIPHY_DELAY 10 #define NV_MIIPHY_DELAYMAX 10000 +#define NV_MAC_RESET_DELAY 64 #define NV_WAKEUPPATTERNS 5 #define NV_WAKEUPMASKENTRIES 4 @@ -552,6 +562,8 @@ struct fe_priv { u32 desc_ver; u32 txrxctl_bits; u32 vlanctl_bits; + u32 driver_data; + u32 register_size; void __iomem *base; @@ -919,6 +931,24 @@ static void nv_txrx_reset(struct net_device *dev) pci_push(base); } +static void nv_mac_reset(struct net_device *dev) +{ + struct fe_priv *np = netdev_priv(dev); + u8 __iomem *base = get_hwbase(dev); + + dprintk(KERN_DEBUG "%s: nv_mac_reset\n", dev->name); + writel(NVREG_TXRXCTL_BIT2 | NVREG_TXRXCTL_RESET | np->txrxctl_bits, base + NvRegTxRxControl); + pci_push(base); + writel(NVREG_MAC_RESET_ASSERT, base + NvRegMacReset); + pci_push(base); + udelay(NV_MAC_RESET_DELAY); + writel(0, base + NvRegMacReset); + pci_push(base); + udelay(NV_MAC_RESET_DELAY); + writel(NVREG_TXRXCTL_BIT2 | np->txrxctl_bits, base + NvRegTxRxControl); + pci_push(base); +} + /* * nv_get_stats: dev->get_stats function * Get latest stats value from the nic. @@ -1331,7 +1361,7 @@ static void nv_tx_timeout(struct net_device *dev) dev->name, (unsigned long)np->ring_addr, np->next_tx, np->nic_tx); printk(KERN_INFO "%s: Dumping tx registers\n", dev->name); - for (i=0;i<0x400;i+= 32) { + for (i=0;i<=np->register_size;i+= 32) { printk(KERN_INFO "%3x: %08x %08x %08x %08x %08x %08x %08x %08x\n", i, readl(base + i + 0), readl(base + i + 4), @@ -2488,11 +2518,11 @@ static int nv_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) } #define FORCEDETH_REGS_VER 1 -#define FORCEDETH_REGS_SIZE 0x400 /* 256 32-bit registers */ static int nv_get_regs_len(struct net_device *dev) { - return FORCEDETH_REGS_SIZE; + struct fe_priv *np = netdev_priv(dev); + return np->register_size; } static void nv_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *buf) @@ -2504,7 +2534,7 @@ static void nv_get_regs(struct net_device *dev, struct ethtool_regs *regs, void regs->version = FORCEDETH_REGS_VER; spin_lock_irq(&np->lock); - for (i=0;iregister_size/sizeof(u32); i++) rbuf[i] = readl(base + i*sizeof(u32)); spin_unlock_irq(&np->lock); } @@ -2608,6 +2638,8 @@ static int nv_open(struct net_device *dev) dprintk(KERN_DEBUG "nv_open: begin\n"); /* 1) erase previous misconfiguration */ + if (np->driver_data & DEV_HAS_POWER_CNTRL) + nv_mac_reset(dev); /* 4.1-1: stop adapter: ignored, 4.3 seems to be overkill */ writel(NVREG_MCASTADDRA_FORCE, base + NvRegMulticastAddrA); writel(0, base + NvRegMulticastAddrB); @@ -2878,6 +2910,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i unsigned long addr; u8 __iomem *base; int err, i; + u32 powerstate; dev = alloc_etherdev(sizeof(struct fe_priv)); err = -ENOMEM; @@ -2910,6 +2943,11 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i if (err < 0) goto out_disable; + if (id->driver_data & (DEV_HAS_VLAN|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL)) + np->register_size = NV_PCI_REGSZ_VER2; + else + np->register_size = NV_PCI_REGSZ_VER1; + err = -EINVAL; addr = 0; for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { @@ -2918,7 +2956,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i pci_resource_len(pci_dev, i), pci_resource_flags(pci_dev, i)); if (pci_resource_flags(pci_dev, i) & IORESOURCE_MEM && - pci_resource_len(pci_dev, i) >= NV_PCI_REGSZ) { + pci_resource_len(pci_dev, i) >= np->register_size) { addr = pci_resource_start(pci_dev, i); break; } @@ -2929,6 +2967,9 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i goto out_relreg; } + /* copy of driver data */ + np->driver_data = id->driver_data; + /* handle different descriptor versions */ if (id->driver_data & DEV_HAS_HIGH_DMA) { /* packet format 3: supports 40-bit addressing */ @@ -2986,7 +3027,7 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i } err = -ENOMEM; - np->base = ioremap(addr, NV_PCI_REGSZ); + np->base = ioremap(addr, np->register_size); if (!np->base) goto out_relreg; dev->base_addr = (unsigned long)np->base; @@ -3062,6 +3103,20 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i writel(0, base + NvRegWakeUpFlags); np->wolenabled = 0; + if (id->driver_data & DEV_HAS_POWER_CNTRL) { + u8 revision_id; + pci_read_config_byte(pci_dev, PCI_REVISION_ID, &revision_id); + + /* take phy and nic out of low power mode */ + powerstate = readl(base + NvRegPowerState2); + powerstate &= ~NVREG_POWERSTATE2_POWERUP_MASK; + if ((id->device == PCI_DEVICE_ID_NVIDIA_NVENET_12 || + id->device == PCI_DEVICE_ID_NVIDIA_NVENET_13) && + revision_id >= 0xA3) + powerstate |= NVREG_POWERSTATE2_POWERUP_REV_A3; + writel(powerstate, base + NvRegPowerState2); + } + if (np->desc_ver == DESC_VER_1) { np->tx_flags = NV_TX_VALID; } else { @@ -3223,19 +3278,19 @@ static struct pci_device_id pci_tbl[] = { }, { /* MCP51 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_12), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL, }, { /* MCP51 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_13), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL, }, { /* MCP55 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_14), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL, }, { /* MCP55 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_15), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_VLAN|DEV_HAS_MSI|DEV_HAS_MSI_X|DEV_HAS_POWER_CNTRL, }, {0,}, }; -- cgit v1.1 From 1ebd32fc54bd04de6b3944587f25513c0681f98e Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 26 Apr 2006 14:40:08 +0200 Subject: [PATCH] splice: add ->splice_write support for /dev/null Useful for testing. Signed-off-by: Jens Axboe --- drivers/char/mem.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 66719f9..1fa9fa15 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -578,6 +579,18 @@ static ssize_t write_null(struct file * file, const char __user * buf, return count; } +static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf, + struct splice_desc *sd) +{ + return sd->len; +} + +static ssize_t splice_write_null(struct pipe_inode_info *pipe,struct file *out, + loff_t *ppos, size_t len, unsigned int flags) +{ + return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_null); +} + #ifdef CONFIG_MMU /* * For fun, we are using the MMU for this. @@ -785,6 +798,7 @@ static struct file_operations null_fops = { .llseek = null_lseek, .read = read_null, .write = write_null, + .splice_write = splice_write_null, }; #if defined(CONFIG_ISA) || !defined(__mc68000__) -- cgit v1.1 From c82ffb07cd1aa356c599999c4f0dc5155a91d318 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 26 Apr 2006 07:20:48 +0100 Subject: [PATCH] fix leak in activate_ep_files() Signed-off-by: Al Viro Signed-off-by: Linus Torvalds --- drivers/usb/gadget/inode.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 42b4570..0eb010a 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -1614,6 +1614,7 @@ static int activate_ep_files (struct dev_data *dev) data, &ep_config_operations, &data->dentry); if (!data->inode) { + usb_ep_free_request(ep, data->req); kfree (data); goto enomem; } -- cgit v1.1 From 83d722f7e198b034699b1500d98729beff930efd Mon Sep 17 00:00:00 2001 From: Chandra Seetharaman Date: Mon, 24 Apr 2006 19:35:21 -0700 Subject: [PATCH] Remove __devinit and __cpuinit from notifier_call definitions Few of the notifier_chain_register() callers use __init in the definition of notifier_call. It is incorrect as the function definition should be available after the initializations (they do not unregister them during initializations). This patch fixes all such usages to _not_ have the notifier_call __init section. Signed-off-by: Chandra Seetharaman Signed-off-by: Linus Torvalds --- drivers/base/topology.c | 2 +- drivers/cpufreq/cpufreq.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/base/topology.c b/drivers/base/topology.c index 915810f6..8c52421 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -107,7 +107,7 @@ static int __cpuinit topology_remove_dev(struct sys_device * sys_dev) return 0; } -static int __cpuinit topology_cpu_callback(struct notifier_block *nfb, +static int topology_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 9759d05..29b2fa5 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1497,7 +1497,7 @@ int cpufreq_update_policy(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_update_policy); -static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb, +static int cpufreq_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned long)hcpu; -- cgit v1.1 From 67ca0284f69992ad71ac12dc375f2b158d9d703d Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Sun, 23 Apr 2006 19:59:23 +0200 Subject: [PATCH] USB: Resource leak fix for whiteheat driver We may return from drivers/usb/serial/whiteheat.c::whiteheat_attach() without freeing `result' if we leave via the no_firmware: label. Spotted by the coverity checker as #670 Signed-off-by: Jesper Juhl Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/whiteheat.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 557411c..f806553 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -508,6 +508,7 @@ no_firmware: err("%s: Unable to retrieve firmware version, try replugging\n", serial->type->description); err("%s: If the firmware is not running (status led not blinking)\n", serial->type->description); err("%s: please contact support@connecttech.com\n", serial->type->description); + kfree(result); return -ENODEV; no_command_private: -- cgit v1.1 From 58381719845d9ee19a321c2eb69cfa9b7886be9a Mon Sep 17 00:00:00 2001 From: Wang Jun Date: Wed, 19 Apr 2006 16:32:07 +0800 Subject: [PATCH] USB: add new iTegno usb CDMA 1x card support for pl2303 Add new iTegno usb CDMA 1x card (usbid '0eba:2080') support to pl2303 driver Signed-off-by: Wang Jun Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/pl2303.c | 1 + drivers/usb/serial/pl2303.h | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index ccf746b..c96714b 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -61,6 +61,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID_UCSGT) }, { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID) }, + { USB_DEVICE(ITEGNO_VENDOR_ID, ITEGNO_PRODUCT_ID_2080) }, { USB_DEVICE(MA620_VENDOR_ID, MA620_PRODUCT_ID) }, { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID) }, { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 09f379b..7f29e81 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -26,6 +26,7 @@ #define ITEGNO_VENDOR_ID 0x0eba #define ITEGNO_PRODUCT_ID 0x1080 +#define ITEGNO_PRODUCT_ID_2080 0x2080 #define MA620_VENDOR_ID 0x0df7 #define MA620_PRODUCT_ID 0x0620 -- cgit v1.1 From 2120638354a6881b9c442b10fc21f28ecadc7402 Mon Sep 17 00:00:00 2001 From: Phil Dibowitz Date: Sun, 16 Apr 2006 19:18:36 -0700 Subject: [PATCH] USB: Storage: unusual devs update This patch removes the Protocol portion of the Iomega Click! device as it's not needed. Not-needed message reported by Kenneth Crudup Signed-off-by: Phil Dibowitz Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/unusual_devs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index c4a9dcf..55cce57 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -411,7 +411,7 @@ UNUSUAL_DEV( 0x050d, 0x0115, 0x0133, 0x0133, UNUSUAL_DEV( 0x0525, 0xa140, 0x0100, 0x0100, "Iomega", "USB Clik! 40", - US_SC_8070, US_PR_BULK, NULL, + US_SC_8070, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), /* Yakumo Mega Image 37 -- cgit v1.1 From f430c405ca23dd5a9389d1f62dcdeb1fd6ce6024 Mon Sep 17 00:00:00 2001 From: Olivier Blondeau Date: Sun, 16 Apr 2006 19:19:25 -0700 Subject: [PATCH] USB: storage: atmel unusual dev update Originally submitted by Olivier Blondeau , with re-diffing by me. Adds a new atmel unusual_dev entry. Signed-off-by: Phil Dibowitz --- drivers/usb/storage/unusual_devs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 55cce57..aec5ea8 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -773,6 +773,13 @@ UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +/* Reported by Olivier Blondeau */ +UNUSUAL_DEV( 0x0727, 0x0306, 0x0100, 0x0100, + "ATMEL", + "SND1 Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), + /* Submitted by Roman Hodek */ UNUSUAL_DEV( 0x0781, 0x0001, 0x0200, 0x0200, "Sandisk", -- cgit v1.1 From a29fccd7993a3d411674e148cb0759a017be3e21 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 14 Apr 2006 16:40:00 -0400 Subject: [PATCH] USB: net2280: Handle STALLs for 0-length control-IN requests This patch (as668) fixes a typo in net2280. The handler for 0-length control-IN requests should check that the endpoint _isn't_ halted before sending a 0-length packet. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/net2280.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 6a4b93a..2d5cede 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2166,7 +2166,7 @@ static void handle_ep_small (struct net2280_ep *ep) ep->stopped = 1; set_halt (ep); mode = 2; - } else if (!req && ep->stopped) + } else if (!req && !ep->stopped) write_fifo (ep, NULL); } } else { -- cgit v1.1 From 317e83b842ba39776054219ae29844127876416a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 14 Apr 2006 16:42:03 -0400 Subject: [PATCH] USB: net2280: send 0-length packets for ep0 This patch (as669) fixes a bug in the net2280 driver. Now it will properly send zero-length packets on ep0 until the control status stage occurs. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/net2280.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index 2d5cede..c842b19 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2280,9 +2280,7 @@ static void handle_ep_small (struct net2280_ep *ep) /* if we wrote it all, we're usually done */ if (req->req.actual == req->req.length) { if (ep->num == 0) { - /* wait for control status */ - if (mode != 2) - req = NULL; + /* send zlps until the status stage */ } else if (!req->req.zero || len != ep->ep.maxpacket) mode = 2; } -- cgit v1.1 From 658ad5e001a17be5fadaa8d57d1aa7f7c62628c1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 14 Apr 2006 16:44:11 -0400 Subject: [PATCH] USB: net2280: check for shared IRQs This patch (as670) adds a check for whether a shared IRQ was actually generated by the net2280 device. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/net2280.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index c842b19..b2d507f 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2742,6 +2742,10 @@ static irqreturn_t net2280_irq (int irq, void *_dev, struct pt_regs * r) { struct net2280 *dev = _dev; + /* shared interrupt, not ours */ + if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED))) + return IRQ_NONE; + spin_lock (&dev->lock); /* handle disconnect, dma, and more */ -- cgit v1.1 From 9fb81ce63671f9743517f628dac935269f2581a9 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 14 Apr 2006 16:46:28 -0400 Subject: [PATCH] USB: net2280: set driver data before it is used This patch (as671) fixes a bug in the error pathway for the net2280 probe routine. A failure during probe will cause the driver to call pci_get_drvdata before the corresponding pci_set_drvdata has been set. The patch also does a kzalloc conversion. Signed-off-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/net2280.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c index b2d507f..0b92934 100644 --- a/drivers/usb/gadget/net2280.c +++ b/drivers/usb/gadget/net2280.c @@ -2833,13 +2833,13 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) } /* alloc, and start init */ - dev = kmalloc (sizeof *dev, SLAB_KERNEL); + dev = kzalloc (sizeof *dev, SLAB_KERNEL); if (dev == NULL){ retval = -ENOMEM; goto done; } - memset (dev, 0, sizeof *dev); + pci_set_drvdata (pdev, dev); spin_lock_init (&dev->lock); dev->pdev = pdev; dev->gadget.ops = &net2280_ops; @@ -2952,7 +2952,6 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff; /* done */ - pci_set_drvdata (pdev, dev); INFO (dev, "%s\n", driver_desc); INFO (dev, "irq %s, pci mem %p, chip rev %04x\n", bufp, base, dev->chiprev); -- cgit v1.1 From c67808eee61a01c3128298c5972426a1a67b9093 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 9 Apr 2006 20:07:35 +0200 Subject: [PATCH] USB: Use new PCI_CLASS_SERIAL_USB_* defines We could use the recently added PCI_CLASS_SERIAL_USB_UHCI, PCI_CLASS_SERIAL_USB_OHCI and PCI_CLASS_SERIAL_USB_EHCI defines in more places, for slightly shorter and clearer code. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-pci.c | 2 +- drivers/usb/host/ohci-pci.c | 2 +- drivers/usb/host/uhci-hcd.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 1e03f1a..a1bd2be 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -350,7 +350,7 @@ static const struct hc_driver ehci_pci_hc_driver = { /* PCI driver selection metadata; PCI hotplugging uses this */ static const struct pci_device_id pci_ids [] = { { /* handle any USB 2.0 EHCI controller */ - PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0), + PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0), .driver_data = (unsigned long) &ehci_pci_hc_driver, }, { /* end: all zeroes */ } diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index 1bfe96f..b268537 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -206,7 +206,7 @@ static const struct hc_driver ohci_pci_hc_driver = { static const struct pci_device_id pci_ids [] = { { /* handle any USB OHCI controller */ - PCI_DEVICE_CLASS((PCI_CLASS_SERIAL_USB << 8) | 0x10, ~0), + PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0), .driver_data = (unsigned long) &ohci_pci_hc_driver, }, { /* end: all zeroes */ } }; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c0c4db7..d225e11 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -858,7 +858,7 @@ static const struct hc_driver uhci_driver = { static const struct pci_device_id uhci_pci_ids[] = { { /* handle any USB UHCI controller */ - PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x00), ~0), + PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0), .driver_data = (unsigned long) &uhci_driver, }, { /* end: all zeroes */ } }; -- cgit v1.1 From cdd3b1565a8d563ed84cf1c2af6cabf461f3c317 Mon Sep 17 00:00:00 2001 From: Nathan Bronson Date: Mon, 10 Apr 2006 00:05:09 -0400 Subject: [PATCH] USB: ftdi_sio vendor code for RR-CirKits LocoBuffer USB This patch adds recognition of the RR-CirKits LocoBuffer USB to the existing FTDI driver. http://www.rr-cirkits.com Signed-off-by: Nathan Bronson Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index f5851db..5eb646e 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -493,6 +493,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_777_PID) }, { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) }, { USB_DEVICE(ICOM_ID1_VID, ICOM_ID1_PID) }, { USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) }, { }, /* Optional parameter entry */ diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 2155f0e..b6c50a1 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -400,6 +400,11 @@ #define FTDI_WESTREX_MODEL_8900F_PID 0xDC01 /* Model 8900F */ /* + * RR-CirKits LocoBuffer USB (http://www.rr-cirkits.com) + */ +#define FTDI_RRCIRKITS_LOCOBUFFER_PID 0xc7d0 /* LocoBuffer USB */ + +/* * Eclo (http://www.eclo.pt/) product IDs. * PID 0xEA90 submitted by Martin Grill. */ -- cgit v1.1 From 69737dfaacd000b10fc4a1e9eb518b630b43c3ad Mon Sep 17 00:00:00 2001 From: "Luiz Fernando N. Capitulino" Date: Tue, 11 Apr 2006 15:52:41 -0300 Subject: [PATCH] USB: ftdi_sio: Adds support for iPlus device. Adds support in ftdi_sio usbserial driver for USB modems sold by Plus GSM Company in Poland. Signed-off-by: Luiz Fernando Capitulino Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 5eb646e..20a9846 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -308,6 +308,7 @@ static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = { static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_IRTRANS_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IPLUS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index b6c50a1..7c31d59 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -39,6 +39,9 @@ /* www.thoughttechnology.com/ TT-USB provide with procomp use ftdi_sio */ #define FTDI_TTUSB_PID 0xFF20 /* Product Id */ +/* iPlus device */ +#define FTDI_IPLUS_PID 0xD070 /* Product Id */ + /* www.crystalfontz.com devices - thanx for providing free devices for evaluation ! */ /* they use the ftdi chipset for the USB interface and the vendor id is the same */ #define FTDI_XF_632_PID 0xFC08 /* 632: 16x2 Character Display */ -- cgit v1.1 From 7e0258fd28762c09b997edb56849ecfa29284b79 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 12 Apr 2006 15:20:35 +0100 Subject: [PATCH] USB: ftdi_sio: add support for ASK RDR 400 series card reader This patch adds support for an ASK RDR 400 series contactless card reader to the ftdi_sio driver's device ID table. The product ID was supplied by Adriano Couto on the ftdi-usb-sio-devel list. Signed-off-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/ftdi_sio.h | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 20a9846..8215120 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -495,6 +495,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_WESTREX_MODEL_8900F_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) }, { USB_DEVICE(ICOM_ID1_VID, ICOM_ID1_PID) }, { USB_DEVICE(PAPOUCH_VID, PAPOUCH_TMU_PID) }, { }, /* Optional parameter entry */ diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 7c31d59..2c55a5e 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -156,6 +156,11 @@ #define ICOM_ID1_PID 0x0004 /* + * ASK.fr devices + */ +#define FTDI_ASK_RDR400_PID 0xC991 /* ASK RDR 400 series card reader */ + +/* * DSS-20 Sync Station for Sony Ericsson P800 */ -- cgit v1.1 From f01f4182597a3bb4b6fbf92e041faf7a1016f4b6 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Mon, 17 Apr 2006 04:02:54 +0200 Subject: [PATCH] PCI: fix potential resource leak in drivers/pci/msi.c The coverity checker spotted (as entry #599) that we might leak `entry' in drivers/pci/msi.c::msix_capability_init() This patch should take care of that. Signed-off-by: Jesper Juhl Signed-off-by: Greg Kroah-Hartman --- drivers/pci/msi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 2087a39..9855c4c 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -793,8 +793,10 @@ static int msix_capability_init(struct pci_dev *dev, if (!entry) break; vector = get_msi_vector(dev); - if (vector < 0) + if (vector < 0) { + kmem_cache_free(msi_cachep, entry); break; + } j = entries[i].entry; entries[i].vector = vector; -- cgit v1.1 From 75cf7456dd87335f574dcd53c4ae616a2ad71a11 Mon Sep 17 00:00:00 2001 From: Chris Wedgwood Date: Tue, 18 Apr 2006 23:57:09 -0700 Subject: [PATCH] PCI quirk: VIA IRQ fixup should only run for VIA southbridges Alan Cox pointed out that the VIA 'IRQ fixup' was erroneously running on my system which has no VIA southbridge (but I do have a VIA IEEE 1394 device). This should address that. I also changed "Via IRQ" to "VIA IRQ" (initially I read Via as a capitalized via (by way/means of). Signed-off-by: Chris Wedgwood Acked-by: Jeff Garzik Signed-off-by: Greg Kroah-Hartman --- drivers/pci/quirks.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index c42ae2c..19e2b17 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -642,13 +642,15 @@ static void quirk_via_irq(struct pci_dev *dev) new_irq = dev->irq & 0xf; pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); if (new_irq != irq) { - printk(KERN_INFO "PCI: Via IRQ fixup for %s, from %d to %d\n", + printk(KERN_INFO "PCI: VIA IRQ fixup for %s, from %d to %d\n", pci_name(dev), irq, new_irq); udelay(15); /* unknown if delay really needed */ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, new_irq); } } -DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_via_irq); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_irq); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4, quirk_via_irq); +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5, quirk_via_irq); /* * VIA VT82C598 has its device ID settable and many BIOSes -- cgit v1.1 From 913e7ec545462b9a49fa308d0c81697236f7d29d Mon Sep 17 00:00:00 2001 From: Jon Smirl Date: Wed, 12 Apr 2006 19:43:35 -0400 Subject: [PATCH] Frame buffer: remove cmap sysfs interface Remove it as it does not work properly due to sysfs core changes. Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbsysfs.c | 92 ++----------------------------------------------- 1 file changed, 3 insertions(+), 89 deletions(-) (limited to 'drivers') diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index b72b052..34e0739 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -305,94 +305,6 @@ static ssize_t show_stride(struct class_device *class_device, char *buf) return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->fix.line_length); } -/* Format for cmap is "%02x%c%4x%4x%4x\n" */ -/* %02x entry %c transp %4x red %4x blue %4x green \n */ -/* 256 rows at 16 chars equals 4096, the normal page size */ -/* the code will automatically adjust for different page sizes */ -static ssize_t store_cmap(struct class_device *class_device, const char *buf, - size_t count) -{ - struct fb_info *fb_info = class_get_devdata(class_device); - int rc, i, start, length, transp = 0; - - if ((count > PAGE_SIZE) || ((count % 16) != 0)) - return -EINVAL; - - if (!fb_info->fbops->fb_setcolreg && !fb_info->fbops->fb_setcmap) - return -EINVAL; - - sscanf(buf, "%02x", &start); - length = count / 16; - - for (i = 0; i < length; i++) - if (buf[i * 16 + 2] != ' ') - transp = 1; - - /* If we can batch, do it */ - if (fb_info->fbops->fb_setcmap && length > 1) { - struct fb_cmap umap; - - memset(&umap, 0, sizeof(umap)); - if ((rc = fb_alloc_cmap(&umap, length, transp))) - return rc; - - umap.start = start; - for (i = 0; i < length; i++) { - sscanf(&buf[i * 16 + 3], "%4hx", &umap.red[i]); - sscanf(&buf[i * 16 + 7], "%4hx", &umap.blue[i]); - sscanf(&buf[i * 16 + 11], "%4hx", &umap.green[i]); - if (transp) - umap.transp[i] = (buf[i * 16 + 2] != ' '); - } - rc = fb_info->fbops->fb_setcmap(&umap, fb_info); - fb_copy_cmap(&umap, &fb_info->cmap); - fb_dealloc_cmap(&umap); - - return rc ?: count; - } - for (i = 0; i < length; i++) { - u16 red, blue, green, tsp; - - sscanf(&buf[i * 16 + 3], "%4hx", &red); - sscanf(&buf[i * 16 + 7], "%4hx", &blue); - sscanf(&buf[i * 16 + 11], "%4hx", &green); - tsp = (buf[i * 16 + 2] != ' '); - if ((rc = fb_info->fbops->fb_setcolreg(start++, - red, green, blue, tsp, fb_info))) - return rc; - - fb_info->cmap.red[i] = red; - fb_info->cmap.blue[i] = blue; - fb_info->cmap.green[i] = green; - if (transp) - fb_info->cmap.transp[i] = tsp; - } - return count; -} - -static ssize_t show_cmap(struct class_device *class_device, char *buf) -{ - struct fb_info *fb_info = class_get_devdata(class_device); - unsigned int i; - - if (!fb_info->cmap.red || !fb_info->cmap.blue || - !fb_info->cmap.green) - return -EINVAL; - - if (fb_info->cmap.len > PAGE_SIZE / 16) - return -EINVAL; - - /* don't mess with the format, the buffer is PAGE_SIZE */ - /* 256 entries at 16 chars per line equals 4096 = PAGE_SIZE */ - for (i = 0; i < fb_info->cmap.len; i++) { - snprintf(&buf[ i * 16], PAGE_SIZE - i * 16, "%02x%c%4x%4x%4x\n", i + fb_info->cmap.start, - ((fb_info->cmap.transp && fb_info->cmap.transp[i]) ? '*' : ' '), - fb_info->cmap.red[i], fb_info->cmap.blue[i], - fb_info->cmap.green[i]); - } - return 16 * fb_info->cmap.len; -} - static ssize_t store_blank(struct class_device *class_device, const char * buf, size_t count) { @@ -502,10 +414,12 @@ static ssize_t show_fbstate(struct class_device *class_device, char *buf) return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->state); } +/* When cmap is added back in it should be a binary attribute + * not a text one. Consideration should also be given to converting + * fbdev to use configfs instead of sysfs */ static struct class_device_attribute class_device_attrs[] = { __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp), __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank), - __ATTR(color_map, S_IRUGO|S_IWUSR, show_cmap, store_cmap), __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console), __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor), __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode), -- cgit v1.1 From 1269277a5e7c6d7ae1852e648a8bcdb78035e9fa Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 24 Apr 2006 23:22:17 +0100 Subject: [PATCH] powerpc: Use check_legacy_ioport() on ppc32 too. Some people report that we die on some Macs when we are expecting to catch machine checks after poking at some random I/O address. I'd seen it happen on my dual G4 with serial ports until we fixed those to use OF, but now other users are reporting it with i8042. This expands the use of check_legacy_ioport() to avoid that situation even on 32-bit kernels. Signed-off-by: David Woodhouse Signed-off-by: Paul Mackerras --- drivers/block/floppy.c | 2 +- drivers/input/serio/i8042-io.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index bedb689..dff1e67 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4301,7 +4301,7 @@ static int __init floppy_init(void) } use_virtual_dma = can_use_virtual_dma & 1; -#if defined(CONFIG_PPC64) +#if defined(CONFIG_PPC_MERGE) if (check_legacy_ioport(FDC1)) { del_timer(&fd_timeout); err = -ENODEV; diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index 9a92216..cc21914 100644 --- a/drivers/input/serio/i8042-io.h +++ b/drivers/input/serio/i8042-io.h @@ -67,14 +67,14 @@ static inline int i8042_platform_init(void) * On some platforms touching the i8042 data register region can do really * bad things. Because of this the region is always reserved on such boxes. */ -#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) && !defined(CONFIG_PPC64) +#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) && !defined(CONFIG_PPC_MERGE) if (!request_region(I8042_DATA_REG, 16, "i8042")) return -EBUSY; #endif i8042_reset = 1; -#if defined(CONFIG_PPC64) +#if defined(CONFIG_PPC_MERGE) if (check_legacy_ioport(I8042_DATA_REG)) return -EBUSY; if (!request_region(I8042_DATA_REG, 16, "i8042")) -- cgit v1.1 From e27987cddd8db3a72a0f4734b5d94d06c7677323 Mon Sep 17 00:00:00 2001 From: Vitaly Bordug Date: Tue, 25 Apr 2006 20:26:41 +0400 Subject: [PATCH] ppc32 CPM_UART: Convert to use platform devices This is intended to make the driver code more generic and flexible, to get rid of board-specific layouts within driver, and generic rehaul, yet keeping compatibility with the existing stuff utilizing it, being compatible with legacy behavior (but with complaints that legacy mode used). Signed-off-by: Vitaly Bordug Signed-off-by: Paul Mackerras --- drivers/serial/cpm_uart/cpm_uart.h | 16 +- drivers/serial/cpm_uart/cpm_uart_core.c | 259 ++++++++++++++++++++++++++------ drivers/serial/cpm_uart/cpm_uart_cpm1.c | 47 ------ drivers/serial/cpm_uart/cpm_uart_cpm2.c | 9 -- 4 files changed, 220 insertions(+), 111 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h index 73c8a08..17f2c7a 100644 --- a/drivers/serial/cpm_uart/cpm_uart.h +++ b/drivers/serial/cpm_uart/cpm_uart.h @@ -10,6 +10,8 @@ #define CPM_UART_H #include +#include +#include #if defined(CONFIG_CPM2) #include "cpm_uart_cpm2.h" @@ -26,14 +28,14 @@ #define FLAG_SMC 0x00000002 #define FLAG_CONSOLE 0x00000001 -#define UART_SMC1 0 -#define UART_SMC2 1 -#define UART_SCC1 2 -#define UART_SCC2 3 -#define UART_SCC3 4 -#define UART_SCC4 5 +#define UART_SMC1 fsid_smc1_uart +#define UART_SMC2 fsid_smc2_uart +#define UART_SCC1 fsid_scc1_uart +#define UART_SCC2 fsid_scc2_uart +#define UART_SCC3 fsid_scc3_uart +#define UART_SCC4 fsid_scc4_uart -#define UART_NR 6 +#define UART_NR fs_uart_nr #define RX_NUM_FIFO 4 #define RX_BUF_SIZE 32 diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index b7bf4c6..9a5b044 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -60,7 +61,7 @@ /* Track which ports are configured as uarts */ int cpm_uart_port_map[UART_NR]; /* How many ports did we config as uarts */ -int cpm_uart_nr; +int cpm_uart_nr = 0; /**************************************************************/ @@ -85,6 +86,37 @@ static inline void *cpm2cpu_addr(unsigned long addr) return bus_to_virt(addr); } +/* Place-holder for board-specific stuff */ +struct platform_device* __attribute__ ((weak)) __init +early_uart_get_pdev(int index) +{ + return NULL; +} + + +void cpm_uart_count(void) +{ + cpm_uart_nr = 0; +#ifdef CONFIG_SERIAL_CPM_SMC1 + cpm_uart_port_map[cpm_uart_nr++] = UART_SMC1; +#endif +#ifdef CONFIG_SERIAL_CPM_SMC2 + cpm_uart_port_map[cpm_uart_nr++] = UART_SMC2; +#endif +#ifdef CONFIG_SERIAL_CPM_SCC1 + cpm_uart_port_map[cpm_uart_nr++] = UART_SCC1; +#endif +#ifdef CONFIG_SERIAL_CPM_SCC2 + cpm_uart_port_map[cpm_uart_nr++] = UART_SCC2; +#endif +#ifdef CONFIG_SERIAL_CPM_SCC3 + cpm_uart_port_map[cpm_uart_nr++] = UART_SCC3; +#endif +#ifdef CONFIG_SERIAL_CPM_SCC4 + cpm_uart_port_map[cpm_uart_nr++] = UART_SCC4; +#endif +} + /* * Check, if transmit buffers are processed */ @@ -829,14 +861,6 @@ static int cpm_uart_request_port(struct uart_port *port) if (pinfo->flags & FLAG_CONSOLE) return 0; - /* - * Setup any port IO, connect any baud rate generators, - * etc. This is expected to be handled by board - * dependant code - */ - if (pinfo->set_lineif) - pinfo->set_lineif(pinfo); - if (IS_SMC(pinfo)) { pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); @@ -988,6 +1012,54 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = { }, }; +int cpm_uart_drv_get_platform_data(struct platform_device *pdev, int is_con) +{ + struct resource *r; + struct fs_uart_platform_info *pdata = pdev->dev.platform_data; + int idx = pdata->fs_no; /* It is UART_SMCx or UART_SCCx index */ + struct uart_cpm_port *pinfo; + int line; + u32 mem, pram; + + for (line=0; linefs_no; line++); + + pinfo = (struct uart_cpm_port *) &cpm_uart_ports[idx]; + + pinfo->brg = pdata->brg; + + if (!is_con) { + pinfo->port.line = line; + pinfo->port.flags = UPF_BOOT_AUTOCONF; + } + + if (!(r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"))) + return -EINVAL; + mem = r->start; + + if (!(r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram"))) + return -EINVAL; + pram = r->start; + + if(idx > fsid_smc2_uart) { + pinfo->sccp = (scc_t *)mem; + pinfo->sccup = (scc_uart_t *)pram; + } else { + pinfo->smcp = (smc_t *)mem; + pinfo->smcup = (smc_uart_t *)pram; + } + pinfo->tx_nrfifos = pdata->tx_num_fifo; + pinfo->tx_fifosize = pdata->tx_buf_size; + + pinfo->rx_nrfifos = pdata->rx_num_fifo; + pinfo->rx_fifosize = pdata->rx_buf_size; + + pinfo->port.uartclk = pdata->uart_clk; + pinfo->port.mapbase = (unsigned long)mem; + pinfo->port.irq = platform_get_irq(pdev, 0); + + return 0; +} + #ifdef CONFIG_SERIAL_CPM_CONSOLE /* * Print a string to the serial port trying not to disturb @@ -1067,9 +1139,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, pinfo->tx_cur = (volatile cbd_t *) bdp; } -/* - * Setup console. Be careful is called early ! - */ + static int __init cpm_uart_console_setup(struct console *co, char *options) { struct uart_port *port; @@ -1080,9 +1150,27 @@ static int __init cpm_uart_console_setup(struct console *co, char *options) int flow = 'n'; int ret; + struct fs_uart_platform_info *pdata; + struct platform_device* pdev = early_uart_get_pdev(co->index); + port = (struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]]; pinfo = (struct uart_cpm_port *)port; + if (!pdev) { + pr_info("cpm_uart: console: compat mode\n"); + /* compatibility - will be cleaned up */ + cpm_uart_init_portdesc(); + + if (pinfo->set_lineif) + pinfo->set_lineif(pinfo); + } else { + pdata = pdev->dev.platform_data; + if (pdata) + if (pdata->init_ioports) + pdata->init_ioports(); + + cpm_uart_drv_get_platform_data(pdev, 1); + } pinfo->flags |= FLAG_CONSOLE; @@ -1097,14 +1185,6 @@ static int __init cpm_uart_console_setup(struct console *co, char *options) baud = 9600; } - /* - * Setup any port IO, connect any baud rate generators, - * etc. This is expected to be handled by board - * dependant code - */ - if (pinfo->set_lineif) - pinfo->set_lineif(pinfo); - if (IS_SMC(pinfo)) { pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX); pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); @@ -1143,11 +1223,8 @@ static struct console cpm_scc_uart_console = { int __init cpm_uart_console_init(void) { - int ret = cpm_uart_init_portdesc(); - - if (!ret) - register_console(&cpm_scc_uart_console); - return ret; + register_console(&cpm_scc_uart_console); + return 0; } console_initcall(cpm_uart_console_init); @@ -1165,44 +1242,130 @@ static struct uart_driver cpm_reg = { .minor = SERIAL_CPM_MINOR, .cons = CPM_UART_CONSOLE, }; - -static int __init cpm_uart_init(void) +static int cpm_uart_drv_probe(struct device *dev) { - int ret, i; - - printk(KERN_INFO "Serial: CPM driver $Revision: 0.01 $\n"); + struct platform_device *pdev = to_platform_device(dev); + struct fs_uart_platform_info *pdata; + int ret = -ENODEV; -#ifndef CONFIG_SERIAL_CPM_CONSOLE - ret = cpm_uart_init_portdesc(); - if (ret) + if(!pdev) { + printk(KERN_ERR"CPM UART: platform data missing!\n"); return ret; -#endif + } - cpm_reg.nr = cpm_uart_nr; - ret = uart_register_driver(&cpm_reg); + pdata = pdev->dev.platform_data; + pr_debug("cpm_uart_drv_probe: Adding CPM UART %d\n", + cpm_uart_port_map[pdata->fs_no]); - if (ret) + if ((ret = cpm_uart_drv_get_platform_data(pdev, 0))) return ret; - for (i = 0; i < cpm_uart_nr; i++) { - int con = cpm_uart_port_map[i]; - cpm_uart_ports[con].port.line = i; - cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF; - uart_add_one_port(&cpm_reg, &cpm_uart_ports[con].port); - } + if (pdata->init_ioports) + pdata->init_ioports(); - return ret; + ret = uart_add_one_port(&cpm_reg, &cpm_uart_ports[pdata->fs_no].port); + + return ret; } -static void __exit cpm_uart_exit(void) +static int cpm_uart_drv_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fs_uart_platform_info *pdata = pdev->dev.platform_data; + + pr_debug("cpm_uart_drv_remove: Removing CPM UART %d\n", + cpm_uart_port_map[pdata->fs_no]); + + uart_remove_one_port(&cpm_reg, &cpm_uart_ports[pdata->fs_no].port); + return 0; +} + +static struct device_driver cpm_smc_uart_driver = { + .name = "fsl-cpm-smc:uart", + .bus = &platform_bus_type, + .probe = cpm_uart_drv_probe, + .remove = cpm_uart_drv_remove, +}; + +static struct device_driver cpm_scc_uart_driver = { + .name = "fsl-cpm-scc:uart", + .bus = &platform_bus_type, + .probe = cpm_uart_drv_probe, + .remove = cpm_uart_drv_remove, +}; + +/* + This is supposed to match uart devices on platform bus, + */ +static int match_is_uart (struct device* dev, void* data) { + struct platform_device* pdev = container_of(dev, struct platform_device, dev); + int ret = 0; + /* this was setfunc as uart */ + if(strstr(pdev->name,":uart")) { + ret = 1; + } + return ret; +} + + +static int cpm_uart_init(void) { + + int ret; int i; + struct device *dev; + printk(KERN_INFO "Serial: CPM driver $Revision: 0.02 $\n"); + + /* lookup the bus for uart devices */ + dev = bus_find_device(&platform_bus_type, NULL, 0, match_is_uart); + + /* There are devices on the bus - all should be OK */ + if (dev) { + cpm_uart_count(); + cpm_reg.nr = cpm_uart_nr; + + if (!(ret = uart_register_driver(&cpm_reg))) { + if ((ret = driver_register(&cpm_smc_uart_driver))) { + uart_unregister_driver(&cpm_reg); + return ret; + } + if ((ret = driver_register(&cpm_scc_uart_driver))) { + driver_unregister(&cpm_scc_uart_driver); + uart_unregister_driver(&cpm_reg); + } + } + } else { + /* No capable platform devices found - falling back to legacy mode */ + pr_info("cpm_uart: WARNING: no UART devices found on platform bus!\n"); + pr_info( + "cpm_uart: the driver will guess configuration, but this mode is no longer supported.\n"); +#ifndef CONFIG_SERIAL_CPM_CONSOLE + ret = cpm_uart_init_portdesc(); + if (ret) + return ret; +#endif + + cpm_reg.nr = cpm_uart_nr; + ret = uart_register_driver(&cpm_reg); + + if (ret) + return ret; + + for (i = 0; i < cpm_uart_nr; i++) { + int con = cpm_uart_port_map[i]; + cpm_uart_ports[con].port.line = i; + cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF; + uart_add_one_port(&cpm_reg, &cpm_uart_ports[con].port); + } - for (i = 0; i < cpm_uart_nr; i++) { - int con = cpm_uart_port_map[i]; - uart_remove_one_port(&cpm_reg, &cpm_uart_ports[con].port); } + return ret; +} +static void __exit cpm_uart_exit(void) +{ + driver_unregister(&cpm_scc_uart_driver); + driver_unregister(&cpm_smc_uart_driver); uart_unregister_driver(&cpm_reg); } diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c index d789ee5..31223aa 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -81,58 +81,11 @@ void cpm_line_cr_cmd(int line, int cmd) void smc1_lineif(struct uart_cpm_port *pinfo) { - volatile cpm8xx_t *cp = cpmp; - - (void)cp; /* fix warning */ -#if defined (CONFIG_MPC885ADS) - /* Enable SMC1 transceivers */ - { - cp->cp_pepar |= 0x000000c0; - cp->cp_pedir &= ~0x000000c0; - cp->cp_peso &= ~0x00000040; - cp->cp_peso |= 0x00000080; - } -#elif defined (CONFIG_MPC86XADS) - unsigned int iobits = 0x000000c0; - - if (!pinfo->is_portb) { - cp->cp_pbpar |= iobits; - cp->cp_pbdir &= ~iobits; - cp->cp_pbodr &= ~iobits; - } else { - ((immap_t *)IMAP_ADDR)->im_ioport.iop_papar |= iobits; - ((immap_t *)IMAP_ADDR)->im_ioport.iop_padir &= ~iobits; - ((immap_t *)IMAP_ADDR)->im_ioport.iop_paodr &= ~iobits; - } -#endif pinfo->brg = 1; } void smc2_lineif(struct uart_cpm_port *pinfo) { - volatile cpm8xx_t *cp = cpmp; - - (void)cp; /* fix warning */ -#if defined (CONFIG_MPC885ADS) - cp->cp_pepar |= 0x00000c00; - cp->cp_pedir &= ~0x00000c00; - cp->cp_peso &= ~0x00000400; - cp->cp_peso |= 0x00000800; -#elif defined (CONFIG_MPC86XADS) - unsigned int iobits = 0x00000c00; - - if (!pinfo->is_portb) { - cp->cp_pbpar |= iobits; - cp->cp_pbdir &= ~iobits; - cp->cp_pbodr &= ~iobits; - } else { - ((immap_t *)IMAP_ADDR)->im_ioport.iop_papar |= iobits; - ((immap_t *)IMAP_ADDR)->im_ioport.iop_padir &= ~iobits; - ((immap_t *)IMAP_ADDR)->im_ioport.iop_paodr &= ~iobits; - } - -#endif - pinfo->brg = 2; } diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index fd9e53e..c9c3b1d 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -142,14 +142,6 @@ void scc2_lineif(struct uart_cpm_port *pinfo) * be supported in a sane fashion. */ #ifndef CONFIG_STX_GP3 -#ifdef CONFIG_MPC8560_ADS - volatile iop_cpm2_t *io = &cpm2_immr->im_ioport; - io->iop_ppard |= 0x00000018; - io->iop_psord &= ~0x00000008; /* Rx */ - io->iop_psord &= ~0x00000010; /* Tx */ - io->iop_pdird &= ~0x00000008; /* Rx */ - io->iop_pdird |= 0x00000010; /* Tx */ -#else volatile iop_cpm2_t *io = &cpm2_immr->im_ioport; io->iop_pparb |= 0x008b0000; io->iop_pdirb |= 0x00880000; @@ -157,7 +149,6 @@ void scc2_lineif(struct uart_cpm_port *pinfo) io->iop_pdirb &= ~0x00030000; io->iop_psorb &= ~0x00030000; #endif -#endif cpm2_immr->im_cpmux.cmx_scr &= 0xff00ffff; cpm2_immr->im_cpmux.cmx_scr |= 0x00090000; pinfo->brg = 2; -- cgit v1.1 From 09b03b6c29638eb5c79b02e585cb1b20d91a8ea0 Mon Sep 17 00:00:00 2001 From: Vitaly Bordug Date: Tue, 25 Apr 2006 20:26:46 +0400 Subject: [PATCH] ppc32 CPM_UART: Fixed odd address translations Current address translation methods can produce wrong results, because virt_to_bus and vice versa may not produce correct offsets on dma-allocated memory. The right way is, while tracking both phys and virt address of the window that has been allocated for boffer descriptors, and use those numbers to compute the offset and make translation properly. Signed-off-by: Vitaly Bordug Signed-off-by: Paul Mackerras --- drivers/serial/cpm_uart/cpm_uart.h | 33 +++++++++++++++++++++++++++++++++ drivers/serial/cpm_uart/cpm_uart_core.c | 31 +++++++++---------------------- drivers/serial/cpm_uart/cpm_uart_cpm1.c | 7 ++++--- drivers/serial/cpm_uart/cpm_uart_cpm2.c | 5 ++++- 4 files changed, 50 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h index 17f2c7a..aa5eb7d 100644 --- a/drivers/serial/cpm_uart/cpm_uart.h +++ b/drivers/serial/cpm_uart/cpm_uart.h @@ -66,6 +66,7 @@ struct uart_cpm_port { uint dp_addr; void *mem_addr; dma_addr_t dma_addr; + u32 mem_size; /* helpers */ int baud; int bits; @@ -92,4 +93,36 @@ void scc2_lineif(struct uart_cpm_port *pinfo); void scc3_lineif(struct uart_cpm_port *pinfo); void scc4_lineif(struct uart_cpm_port *pinfo); +/* + virtual to phys transtalion +*/ +static inline unsigned long cpu2cpm_addr(void* addr, struct uart_cpm_port *pinfo) +{ + int offset; + u32 val = (u32)addr; + /* sane check */ + if ((val >= (u32)pinfo->mem_addr) && + (val<((u32)pinfo->mem_addr + pinfo->mem_size))) { + offset = val - (u32)pinfo->mem_addr; + return pinfo->dma_addr+offset; + } + printk("%s(): address %x to translate out of range!\n", __FUNCTION__, val); + return 0; +} + +static inline void *cpm2cpu_addr(unsigned long addr, struct uart_cpm_port *pinfo) +{ + int offset; + u32 val = addr; + /* sane check */ + if ((val >= pinfo->dma_addr) && + (val<(pinfo->dma_addr + pinfo->mem_size))) { + offset = val - (u32)pinfo->dma_addr; + return (void*)(pinfo->mem_addr+offset); + } + printk("%s(): address %x to translate out of range!\n", __FUNCTION__, val); + return 0; +} + + #endif /* CPM_UART_H */ diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index 9a5b044..ced193b 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -72,19 +72,6 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo); /**************************************************************/ -static inline unsigned long cpu2cpm_addr(void *addr) -{ - if ((unsigned long)addr >= CPM_ADDR) - return (unsigned long)addr; - return virt_to_bus(addr); -} - -static inline void *cpm2cpu_addr(unsigned long addr) -{ - if (addr >= CPM_ADDR) - return (void *)addr; - return bus_to_virt(addr); -} /* Place-holder for board-specific stuff */ struct platform_device* __attribute__ ((weak)) __init @@ -290,7 +277,7 @@ static void cpm_uart_int_rx(struct uart_port *port, struct pt_regs *regs) } /* get pointer */ - cp = cpm2cpu_addr(bdp->cbd_bufaddr); + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); /* loop through the buffer */ while (i-- > 0) { @@ -633,7 +620,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) /* Pick next descriptor and fill from buffer */ bdp = pinfo->tx_cur; - p = cpm2cpu_addr(bdp->cbd_bufaddr); + p = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); *p++ = port->x_char; bdp->cbd_datlen = 1; @@ -660,7 +647,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) while (!(bdp->cbd_sc & BD_SC_READY) && (xmit->tail != xmit->head)) { count = 0; - p = cpm2cpu_addr(bdp->cbd_bufaddr); + p = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); while (count < pinfo->tx_fifosize) { *p++ = xmit->buf[xmit->tail]; xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -709,12 +696,12 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo) mem_addr = pinfo->mem_addr; bdp = pinfo->rx_cur = pinfo->rx_bd_base; for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) { - bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr); + bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo); bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; mem_addr += pinfo->rx_fifosize; } - bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr); + bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo); bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; /* Set the physical address of the host memory @@ -724,12 +711,12 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo) mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize); bdp = pinfo->tx_cur = pinfo->tx_bd_base; for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) { - bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr); + bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo); bdp->cbd_sc = BD_SC_INTRPT; mem_addr += pinfo->tx_fifosize; } - bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr); + bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo); bdp->cbd_sc = BD_SC_WRAP | BD_SC_INTRPT; } @@ -1099,7 +1086,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, * If the buffer address is in the CPM DPRAM, don't * convert it. */ - cp = cpm2cpu_addr(bdp->cbd_bufaddr); + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); *cp = *s; @@ -1116,7 +1103,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, while ((bdp->cbd_sc & BD_SC_READY) != 0) ; - cp = cpm2cpu_addr(bdp->cbd_bufaddr); + cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo); *cp = 13; bdp->cbd_datlen = 1; diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c index 31223aa..a5a3062 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -144,7 +144,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) /* was hostalloc but changed cause it blows away the */ /* large tlb mapping when pinning the kernel area */ mem_addr = (u8 *) cpm_dpram_addr(cpm_dpalloc(memsz, 8)); - dma_addr = 0; + dma_addr = (u32)mem_addr; } else mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr, GFP_KERNEL); @@ -157,8 +157,9 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) } pinfo->dp_addr = dp_offset; - pinfo->mem_addr = mem_addr; - pinfo->dma_addr = dma_addr; + pinfo->mem_addr = mem_addr; /* virtual address*/ + pinfo->dma_addr = dma_addr; /* physical address*/ + pinfo->mem_size = memsz; pinfo->rx_buf = mem_addr; pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index c9c3b1d..7c6b07a 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -209,8 +209,10 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); - if (is_con) + if (is_con) { mem_addr = alloc_bootmem(memsz); + dma_addr = mem_addr; + } else mem_addr = dma_alloc_coherent(NULL, memsz, &dma_addr, GFP_KERNEL); @@ -225,6 +227,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) pinfo->dp_addr = dp_offset; pinfo->mem_addr = mem_addr; pinfo->dma_addr = dma_addr; + pinfo->mem_size = memsz; pinfo->rx_buf = mem_addr; pinfo->tx_buf = pinfo->rx_buf + L1_CACHE_ALIGN(pinfo->rx_nrfifos -- cgit v1.1