diff --git a/drivers/net/arcnet/arcdevice.h b/drivers/net/arcnet/arcdevice.h index d7fdea11e694..20bfb9ba83ea 100644 --- a/drivers/net/arcnet/arcdevice.h +++ b/drivers/net/arcnet/arcdevice.h @@ -237,6 +237,8 @@ struct Outgoing { numsegs; /* number of segments */ }; +#define ARCNET_LED_NAME_SZ (IFNAMSIZ + 6) + struct arcnet_local { uint8_t config, /* current value of CONFIG register */ timeout, /* Extended timeout for COM20020 */ @@ -260,6 +262,13 @@ struct arcnet_local { /* On preemtive and SMB a lock is needed */ spinlock_t lock; + struct led_trigger *tx_led_trig; + char tx_led_trig_name[ARCNET_LED_NAME_SZ]; + struct led_trigger *recon_led_trig; + char recon_led_trig_name[ARCNET_LED_NAME_SZ]; + + struct timer_list timer; + /* * Buffer management: an ARCnet card has 4 x 512-byte buffers, each of * which can be used for either sending or receiving. The new dynamic @@ -309,6 +318,8 @@ struct arcnet_local { int (*reset)(struct net_device *dev, int really_reset); void (*open)(struct net_device *dev); void (*close)(struct net_device *dev); + void (*datatrigger) (struct net_device * dev, int enable); + void (*recontrigger) (struct net_device * dev, int enable); void (*copy_to_card)(struct net_device *dev, int bufnum, int offset, void *buf, int count); @@ -319,6 +330,16 @@ struct arcnet_local { void __iomem *mem_start; /* pointer to ioremap'ed MMIO */ }; +enum arcnet_led_event { + ARCNET_LED_EVENT_RECON, + ARCNET_LED_EVENT_OPEN, + ARCNET_LED_EVENT_STOP, + ARCNET_LED_EVENT_TX, +}; + +void arcnet_led_event(struct net_device *netdev, enum arcnet_led_event event); +void devm_arcnet_led_init(struct net_device *netdev, int index, int subid); + #if ARCNET_DEBUG_MAX & D_SKB void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc); #else diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index e41dd36fe832..6ea963e3b89a 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -52,6 +52,8 @@ #include #include +#include + #include "arcdevice.h" #include "com9026.h" @@ -189,6 +191,71 @@ static void arcnet_dump_packet(struct net_device *dev, int bufnum, #endif +/* Trigger a LED event in response to a ARCNET device event */ +void arcnet_led_event(struct net_device *dev, enum arcnet_led_event event) +{ + struct arcnet_local *lp = netdev_priv(dev); + unsigned long led_delay = 350; + unsigned long tx_delay = 50; + + switch (event) { + case ARCNET_LED_EVENT_RECON: + led_trigger_blink_oneshot(lp->recon_led_trig, + &led_delay, &led_delay, 0); + break; + case ARCNET_LED_EVENT_OPEN: + led_trigger_event(lp->tx_led_trig, LED_OFF); + led_trigger_event(lp->recon_led_trig, LED_OFF); + break; + case ARCNET_LED_EVENT_STOP: + led_trigger_event(lp->tx_led_trig, LED_OFF); + led_trigger_event(lp->recon_led_trig, LED_OFF); + break; + case ARCNET_LED_EVENT_TX: + led_trigger_blink_oneshot(lp->tx_led_trig, + &tx_delay, &tx_delay, 0); + break; + } +} +EXPORT_SYMBOL_GPL(arcnet_led_event); + +static void arcnet_led_release(struct device *gendev, void *res) +{ + struct arcnet_local *lp = netdev_priv(to_net_dev(gendev)); + + led_trigger_unregister_simple(lp->tx_led_trig); + led_trigger_unregister_simple(lp->recon_led_trig); +} + +/* Register ARCNET LED triggers for a arcnet device + * + * This is normally called from a driver's probe function + */ +void devm_arcnet_led_init(struct net_device *netdev, int index, int subid) +{ + struct arcnet_local *lp = netdev_priv(netdev); + void *res; + + res = devres_alloc(arcnet_led_release, 0, GFP_KERNEL); + if (!res) { + netdev_err(netdev, "cannot register LED triggers\n"); + return; + } + + snprintf(lp->tx_led_trig_name, sizeof(lp->tx_led_trig_name), + "arc%d-%d-tx", index, subid); + snprintf(lp->recon_led_trig_name, sizeof(lp->recon_led_trig_name), + "arc%d-%d-recon", index, subid); + + led_trigger_register_simple(lp->tx_led_trig_name, + &lp->tx_led_trig); + led_trigger_register_simple(lp->recon_led_trig_name, + &lp->recon_led_trig); + + devres_add(&netdev->dev, res); +} +EXPORT_SYMBOL_GPL(devm_arcnet_led_init); + /* Unregister a protocol driver from the arc_proto_map. Protocol drivers * are responsible for registering themselves, but the unregister routine * is pretty generic so we'll do it here. @@ -314,6 +381,16 @@ static void arcdev_setup(struct net_device *dev) dev->flags = IFF_BROADCAST; } +static void arcnet_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + + if (!netif_carrier_ok(dev)) { + netif_carrier_on(dev); + netdev_info(dev, "link up\n"); + } +} + struct net_device *alloc_arcdev(const char *name) { struct net_device *dev; @@ -325,6 +402,9 @@ struct net_device *alloc_arcdev(const char *name) struct arcnet_local *lp = netdev_priv(dev); spin_lock_init(&lp->lock); + init_timer(&lp->timer); + lp->timer.data = (unsigned long) dev; + lp->timer.function = arcnet_timer; } return dev; @@ -423,8 +503,11 @@ int arcnet_open(struct net_device *dev) lp->hw.intmask(dev, lp->intmask); arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + netif_carrier_off(dev); netif_start_queue(dev); + mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000)); + arcnet_led_event(dev, ARCNET_LED_EVENT_OPEN); return 0; out_module_put: @@ -438,7 +521,11 @@ int arcnet_close(struct net_device *dev) { struct arcnet_local *lp = netdev_priv(dev); + arcnet_led_event(dev, ARCNET_LED_EVENT_STOP); + del_timer_sync(&lp->timer); + netif_stop_queue(dev); + netif_carrier_off(dev); /* flush TX and disable RX */ lp->hw.intmask(dev, 0); @@ -515,7 +602,7 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, struct ArcProto *proto; int txbuf; unsigned long flags; - int freeskb, retval; + int retval; arc_printk(D_DURING, dev, "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n", @@ -554,15 +641,13 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, * the package later - forget about it now */ dev->stats.tx_bytes += skb->len; - freeskb = 1; + dev_kfree_skb(skb); } else { /* do it the 'split' way */ lp->outgoing.proto = proto; lp->outgoing.skb = skb; lp->outgoing.pkt = pkt; - freeskb = 0; - if (proto->continue_tx && proto->continue_tx(dev, txbuf)) { arc_printk(D_NORMAL, dev, @@ -574,7 +659,6 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, lp->next_tx = txbuf; } else { retval = NETDEV_TX_BUSY; - freeskb = 0; } arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n", @@ -588,10 +672,9 @@ netdev_tx_t arcnet_send_packet(struct sk_buff *skb, arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n", __FILE__, __LINE__, __func__, lp->hw.status(dev)); - spin_unlock_irqrestore(&lp->lock, flags); - if (freeskb) - dev_kfree_skb(skb); + arcnet_led_event(dev, ARCNET_LED_EVENT_TX); + spin_unlock_irqrestore(&lp->lock, flags); return retval; /* no need to try again */ } EXPORT_SYMBOL(arcnet_send_packet); @@ -843,6 +926,13 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n", status); + if (netif_carrier_ok(dev)) { + netif_carrier_off(dev); + netdev_info(dev, "link down\n"); + } + mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000)); + + arcnet_led_event(dev, ARCNET_LED_EVENT_RECON); /* MYRECON bit is at bit 7 of diagstatus */ if (diagstatus & 0x80) arc_printk(D_RECON, dev, "Put out that recon myself\n"); @@ -893,6 +983,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id) lp->num_recons = lp->network_down = 0; arc_printk(D_DURING, dev, "not recon: clearing counters anyway.\n"); + netif_carrier_on(dev); } if (didsomething) diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index a12bf83be750..239de38fbd6a 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "arcdevice.h" #include "com20020.h" @@ -62,12 +63,43 @@ module_param(clockp, int, 0); module_param(clockm, int, 0); MODULE_LICENSE("GPL"); +static void led_tx_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct com20020_dev *card; + struct com20020_priv *priv; + struct com20020_pci_card_info *ci; + + card = container_of(led_cdev, struct com20020_dev, tx_led); + + priv = card->pci_priv; + ci = priv->ci; + + outb(!!value, priv->misc + ci->leds[card->index].green); +} + +static void led_recon_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct com20020_dev *card; + struct com20020_priv *priv; + struct com20020_pci_card_info *ci; + + card = container_of(led_cdev, struct com20020_dev, recon_led); + + priv = card->pci_priv; + ci = priv->ci; + + outb(!!value, priv->misc + ci->leds[card->index].red); +} + static void com20020pci_remove(struct pci_dev *pdev); static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct com20020_pci_card_info *ci; + struct com20020_pci_channel_map *mm; struct net_device *dev; struct arcnet_local *lp; struct com20020_priv *priv; @@ -84,9 +116,22 @@ static int com20020pci_probe(struct pci_dev *pdev, ci = (struct com20020_pci_card_info *)id->driver_data; priv->ci = ci; + mm = &ci->misc_map; INIT_LIST_HEAD(&priv->list_dev); + if (mm->size) { + ioaddr = pci_resource_start(pdev, mm->bar) + mm->offset; + r = devm_request_region(&pdev->dev, ioaddr, mm->size, + "com20020-pci"); + if (!r) { + pr_err("IO region %xh-%xh already allocated.\n", + ioaddr, ioaddr + mm->size - 1); + return -EBUSY; + } + priv->misc = ioaddr; + } + for (i = 0; i < ci->devcount; i++) { struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i]; struct com20020_dev *card; @@ -96,6 +141,7 @@ static int com20020pci_probe(struct pci_dev *pdev, ret = -ENOMEM; goto out_port; } + dev->dev_port = i; dev->netdev_ops = &com20020_netdev_ops; @@ -131,6 +177,13 @@ static int com20020pci_probe(struct pci_dev *pdev, lp->timeout = timeout; lp->hw.owner = THIS_MODULE; + /* Get the dev_id from the PLX rotary coder */ + if (!strncmp(ci->name, "EAE PLX-PCI MA1", 15)) + dev->dev_id = 0xc; + dev->dev_id ^= inb(priv->misc + ci->rotary) >> 4; + + snprintf(dev->name, sizeof(dev->name), "arc%d-%d", dev->dev_id, i); + if (arcnet_inb(ioaddr, COM20020_REG_R_STATUS) == 0xFF) { pr_err("IO address %Xh is empty!\n", ioaddr); ret = -EIO; @@ -148,14 +201,41 @@ static int com20020pci_probe(struct pci_dev *pdev, card->index = i; card->pci_priv = priv; + card->tx_led.brightness_set = led_tx_set; + card->tx_led.default_trigger = devm_kasprintf(&pdev->dev, + GFP_KERNEL, "arc%d-%d-tx", + dev->dev_id, i); + card->tx_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "pci:green:tx:%d-%d", + dev->dev_id, i); + + card->tx_led.dev = &dev->dev; + card->recon_led.brightness_set = led_recon_set; + card->recon_led.default_trigger = devm_kasprintf(&pdev->dev, + GFP_KERNEL, "arc%d-%d-recon", + dev->dev_id, i); + card->recon_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "pci:red:recon:%d-%d", + dev->dev_id, i); + card->recon_led.dev = &dev->dev; card->dev = dev; + ret = devm_led_classdev_register(&pdev->dev, &card->tx_led); + if (ret) + goto out_port; + + ret = devm_led_classdev_register(&pdev->dev, &card->recon_led); + if (ret) + goto out_port; + dev_set_drvdata(&dev->dev, card); ret = com20020_found(dev, IRQF_SHARED); if (ret) goto out_port; + devm_arcnet_led_init(dev, dev->dev_id, i); + list_add(&card->list, &priv->list_dev); } @@ -234,6 +314,18 @@ static struct com20020_pci_card_info card_info_eae_arc1 = { .size = 0x08, }, }, + .misc_map = { + .bar = 2, + .offset = 0x10, + .size = 0x04, + }, + .leds = { + { + .green = 0x0, + .red = 0x1, + }, + }, + .rotary = 0x0, .flags = ARC_CAN_10MBIT, }; @@ -251,6 +343,21 @@ static struct com20020_pci_card_info card_info_eae_ma1 = { .size = 0x08, } }, + .misc_map = { + .bar = 2, + .offset = 0x10, + .size = 0x04, + }, + .leds = { + { + .green = 0x0, + .red = 0x1, + }, { + .green = 0x2, + .red = 0x3, + }, + }, + .rotary = 0x0, .flags = ARC_CAN_10MBIT, }; diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index c82f323a8c2b..13d9ad4b3f5c 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -118,7 +118,7 @@ int com20020_check(struct net_device *dev) arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND); } - lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; + lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */ arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); arcnet_outb(0x42, ioaddr, COM20020_REG_W_XREG); @@ -131,11 +131,6 @@ int com20020_check(struct net_device *dev) } arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n", status); - /* Enable TX */ - lp->config |= TXENcfg; - arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); - arcnet_outb(arcnet_inb(ioaddr, 8), ioaddr, COM20020_REG_W_XREG); - arcnet_outb(CFLAGScmd | RESETclear | CONFIGclear, ioaddr, COM20020_REG_W_COMMAND); status = arcnet_inb(ioaddr, COM20020_REG_R_STATUS); @@ -169,9 +164,33 @@ static int com20020_set_hwaddr(struct net_device *dev, void *addr) return 0; } +static int com20020_netdev_open(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct arcnet_local *lp = netdev_priv(dev); + + lp->config |= TXENcfg; + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + + return arcnet_open(dev); +} + +static int com20020_netdev_close(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct arcnet_local *lp = netdev_priv(dev); + + arcnet_close(dev); + + /* disable transmitter */ + lp->config &= ~TXENcfg; + arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); + return 0; +} + const struct net_device_ops com20020_netdev_ops = { - .ndo_open = arcnet_open, - .ndo_stop = arcnet_close, + .ndo_open = com20020_netdev_open, + .ndo_stop = com20020_netdev_close, .ndo_start_xmit = arcnet_send_packet, .ndo_tx_timeout = arcnet_timeout, .ndo_set_mac_address = com20020_set_hwaddr, @@ -215,7 +234,7 @@ int com20020_found(struct net_device *dev, int shared) arcnet_outb(STARTIOcmd, ioaddr, COM20020_REG_W_COMMAND); } - lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; + lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; /* Default 0x38 + register: Node ID */ arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); arcnet_outb(dev->dev_addr[0], ioaddr, COM20020_REG_W_XREG); @@ -274,7 +293,7 @@ static int com20020_reset(struct net_device *dev, int really_reset) dev->name, arcnet_inb(ioaddr, COM20020_REG_R_STATUS)); arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); - lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2); + lp->config |= (lp->timeout << 3) | (lp->backplane << 2); /* power-up defaults */ arcnet_outb(lp->config, ioaddr, COM20020_REG_W_CONFIG); arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); diff --git a/drivers/net/arcnet/com20020.h b/drivers/net/arcnet/com20020.h index 22a460f39fb9..0bcc5d0a6903 100644 --- a/drivers/net/arcnet/com20020.h +++ b/drivers/net/arcnet/com20020.h @@ -26,6 +26,7 @@ */ #ifndef __COM20020_H #define __COM20020_H +#include int com20020_check(struct net_device *dev); int com20020_found(struct net_device *dev, int shared); @@ -36,6 +37,11 @@ extern const struct net_device_ops com20020_netdev_ops; #define PLX_PCI_MAX_CARDS 2 +struct ledoffsets { + int green; + int red; +}; + struct com20020_pci_channel_map { u32 bar; u32 offset; @@ -47,6 +53,10 @@ struct com20020_pci_card_info { int devcount; struct com20020_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CARDS]; + struct com20020_pci_channel_map misc_map; + + struct ledoffsets leds[PLX_PCI_MAX_CARDS]; + int rotary; unsigned int flags; }; @@ -54,12 +64,16 @@ struct com20020_pci_card_info { struct com20020_priv { struct com20020_pci_card_info *ci; struct list_head list_dev; + resource_size_t misc; }; struct com20020_dev { struct list_head list; struct net_device *dev; + struct led_classdev tx_led; + struct led_classdev recon_led; + struct com20020_priv *pci_priv; int index; }; diff --git a/include/linux/leds.h b/include/linux/leds.h index b122eeafb5dc..fa359c79c825 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -283,6 +283,13 @@ static inline void led_trigger_register_simple(const char *name, static inline void led_trigger_unregister_simple(struct led_trigger *trigger) {} static inline void led_trigger_event(struct led_trigger *trigger, enum led_brightness event) {} +static inline void led_trigger_blink(struct led_trigger *trigger, + unsigned long *delay_on, + unsigned long *delay_off) {} +static inline void led_trigger_blink_oneshot(struct led_trigger *trigger, + unsigned long *delay_on, + unsigned long *delay_off, + int invert) {} static inline void led_trigger_set_default(struct led_classdev *led_cdev) {} static inline void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) {}