arcnet: com20020-pci: add led trigger support
authorMichael Grzeschik <m.grzeschik@pengutronix.de>
Wed, 17 Sep 2014 22:12:50 +0000 (00:12 +0200)
committerMichael Grzeschik <m.grzeschik@pengutronix.de>
Mon, 26 Oct 2015 08:10:56 +0000 (09:10 +0100)
The EAE PLX-PCI card has special leds on the the main io pci resource
bar. This patch adds support to trigger the conflict and data leds with
the packages.

Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
drivers/net/arcnet/arcdevice.h
drivers/net/arcnet/arcnet.c
drivers/net/arcnet/com20020-pci.c
drivers/net/arcnet/com20020.h
include/linux/leds.h

index d7fdea11e694c01345f55e955d29e1a2bbe7a8b2..2edc0c0ab7c7baeb5768ef5d3e78d86a6d9f847a 100644 (file)
@@ -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,11 @@ 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];
+
        /*
         * 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 +316,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 +328,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
index 542e2b46b9ebe7d56f3ef7a5909a5269c3158753..4242522ae86b8e4a0a9cd2f21fc99cfeb6aac3a9 100644 (file)
@@ -52,6 +52,8 @@
 #include <linux/init.h>
 #include <linux/jiffies.h>
 
+#include <linux/leds.h>
+
 #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.
@@ -425,6 +492,7 @@ int arcnet_open(struct net_device *dev)
 
        netif_start_queue(dev);
 
+       arcnet_led_event(dev, ARCNET_LED_EVENT_OPEN);
        return 0;
 
  out_module_put:
@@ -438,6 +506,7 @@ int arcnet_close(struct net_device *dev)
 {
        struct arcnet_local *lp = netdev_priv(dev);
 
+       arcnet_led_event(dev, ARCNET_LED_EVENT_STOP);
        netif_stop_queue(dev);
 
        /* flush TX and disable RX */
@@ -585,6 +654,8 @@ 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));
 
+       arcnet_led_event(dev, ARCNET_LED_EVENT_TX);
+
        spin_unlock_irqrestore(&lp->lock, flags);
        return retval;          /* no need to try again */
 }
@@ -837,6 +908,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id)
 
                        arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n",
                                   status);
+                       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");
index 637a6110cec6011ce6ae812adbe783c52d4dd1d4..239de38fbd6a588bbb0e90e3452ea60ca1e5a161 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/pci.h>
 #include <linux/list.h>
 #include <linux/io.h>
+#include <linux/leds.h>
 
 #include "arcdevice.h"
 #include "com20020.h"
@@ -62,6 +63,36 @@ 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,
@@ -170,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);
        }
 
@@ -261,6 +319,12 @@ static struct com20020_pci_card_info card_info_eae_arc1 = {
                .offset = 0x10,
                .size = 0x04,
        },
+       .leds = {
+               {
+                       .green = 0x0,
+                       .red = 0x1,
+               },
+       },
        .rotary = 0x0,
        .flags = ARC_CAN_10MBIT,
 };
@@ -284,6 +348,15 @@ static struct com20020_pci_card_info card_info_eae_ma1 = {
                .offset = 0x10,
                .size = 0x04,
        },
+       .leds = {
+               {
+                       .green = 0x0,
+                       .red = 0x1,
+               }, {
+                       .green = 0x2,
+                       .red = 0x3,
+               },
+       },
        .rotary = 0x0,
        .flags = ARC_CAN_10MBIT,
 };
index f2ed2eff3ae3e73c2d3828c007fe0d84176f0520..0bcc5d0a6903c34e305bab525b4989d30d912f59 100644 (file)
@@ -26,6 +26,7 @@
  */
 #ifndef __COM20020_H
 #define __COM20020_H
+#include <linux/leds.h>
 
 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;
@@ -49,6 +55,7 @@ struct com20020_pci_card_info {
        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;
@@ -64,6 +71,9 @@ 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;
 };
index b122eeafb5dc17b8a8b1a1852dc1c420ecf0f8d2..fa359c79c825e666789ec1ce65392b6ff184d93c 100644 (file)
@@ -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) {}