Expose these LEDs by means of a platform device which
can be controlled by a suitable LED driver.

Signed-off-by: Vishal Thanki <vishaltha...@gmail.com>
---
 drivers/net/phy/at803x.c   | 55 +++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/phy/at803x.h | 45 +++++++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/phy/at803x.h

diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 1e901c7..8fdb5ff 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -18,6 +18,8 @@
 #include <linux/etherdevice.h>
 #include <linux/of_gpio.h>
 #include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/phy/at803x.h>
 
 #define AT803X_INTR_ENABLE                     0x12
 #define AT803X_INTR_ENABLE_AUTONEG_ERR         BIT(15)
@@ -34,6 +36,7 @@
 
 #define AT803X_SMART_SPEED                     0x14
 #define AT803X_LED_CONTROL                     0x18
+#define AT803X_LED_MANUAL_CTRL                 0x19
 
 #define AT803X_DEVICE_ADDR                     0x03
 #define AT803X_LOC_MAC_ADDR_0_15_OFFSET                0x804C
@@ -117,6 +120,46 @@ static inline int at803x_enable_tx_delay(struct phy_device 
*phydev)
                                        AT803X_DEBUG_TX_CLK_DLY_EN);
 }
 
+static struct at803x_phy_led leds[] = {
+       {
+               .cdev.name = "at803x-link",
+               .id = AT803X_LINK,
+               .reg = AT803X_LED_MANUAL_CTRL,
+       },
+       {
+               .cdev.name = "at803x-act",
+               .id = AT803X_ACT,
+               .reg = AT803X_LED_MANUAL_CTRL,
+       },
+};
+
+static struct at803x_phy_leds led_grp = {
+       .leds = leds,
+       .nr_leds = ARRAY_SIZE(leds),
+};
+
+static struct platform_device *pdev;
+
+static int at803x_led_init(struct phy_device *phydev)
+{
+       led_grp.phydev = phydev;
+
+       pdev = platform_device_alloc("at803x-led", 0);
+       if (!pdev) {
+               pr_info("Failed to allocate LED\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, &led_grp);
+
+       return platform_device_add(pdev);
+}
+
+static void at803x_led_cleanup(struct phy_device *phydev)
+{
+       platform_device_del(pdev);
+}
+
 /* save relevant PHY registers to private copy */
 static void at803x_context_save(struct phy_device *phydev,
                                struct at803x_context *context)
@@ -285,9 +328,18 @@ static int at803x_probe(struct phy_device *phydev)
 
        phydev->priv = priv;
 
+       if (phydev->drv->flags & PHY_HAS_LED_CTRL)
+               return at803x_led_init(phydev);
+
        return 0;
 }
 
+static void at803x_remove(struct phy_device *phydev)
+{
+       if (phydev->drv->flags & PHY_HAS_LED_CTRL)
+               at803x_led_cleanup(phydev);
+}
+
 static int at803x_config_init(struct phy_device *phydev)
 {
        int ret;
@@ -411,11 +463,12 @@ static struct phy_driver at803x_driver[] = {
        .suspend                = at803x_suspend,
        .resume                 = at803x_resume,
        .features               = PHY_BASIC_FEATURES,
-       .flags                  = PHY_HAS_INTERRUPT,
+       .flags                  = PHY_HAS_INTERRUPT | PHY_HAS_LED_CTRL,
        .config_aneg            = genphy_config_aneg,
        .read_status            = genphy_read_status,
        .ack_interrupt          = at803x_ack_interrupt,
        .config_intr            = at803x_config_intr,
+       .remove                 = at803x_remove,
 }, {
        /* ATHEROS 8031 */
        .phy_id                 = ATH8031_PHY_ID,
diff --git a/include/linux/phy/at803x.h b/include/linux/phy/at803x.h
new file mode 100644
index 0000000..f33bfcf
--- /dev/null
+++ b/include/linux/phy/at803x.h
@@ -0,0 +1,45 @@
+#ifndef _AT803X_H
+#define _AT803X_H
+
+#include <linux/leds.h>
+#include <linux/phy/phy.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+
+enum at803x_led_type {
+       AT803X_LINK,
+       AT803X_ACT,
+};
+
+union at803x_led_manual_ctrl {
+       struct {
+               u16 led_tx:2;
+               u16 led_rx:2;
+               u16 res3:2;
+               u16 led_lnk_10_100_ctrl:2;
+               u16 res2:4;
+               u16 led_act_ctrl:1;
+               u16 res1:3;
+
+       } field;
+       u16 value;
+};
+
+struct at803x_phy_led {
+       struct work_struct work;
+       struct led_classdev cdev;
+       enum led_brightness value;
+       union at803x_led_manual_ctrl regval;
+       enum at803x_led_type id;
+       struct at803x_phy_leds *led_grp;
+       spinlock_t lock;
+       u16 reg;
+};
+
+struct at803x_phy_leds {
+       struct phy_device *phydev;
+       struct at803x_phy_led *leds;
+       int nr_leds;
+};
+
+#endif
-- 
2.4.3

Reply via email to