1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f1d2b4d3SLarry Finger /*
3f1d2b4d3SLarry Finger * Linux LED driver for RTL8187
4f1d2b4d3SLarry Finger *
5f1d2b4d3SLarry Finger * Copyright 2009 Larry Finger <Larry.Finger@lwfinger.net>
6f1d2b4d3SLarry Finger *
7f1d2b4d3SLarry Finger * Based on the LED handling in the r8187 driver, which is:
8f1d2b4d3SLarry Finger * Copyright (c) Realtek Semiconductor Corp. All rights reserved.
9f1d2b4d3SLarry Finger *
10f1d2b4d3SLarry Finger * Thanks to Realtek for their support!
11f1d2b4d3SLarry Finger */
12f1d2b4d3SLarry Finger
13f1d2b4d3SLarry Finger #ifdef CONFIG_RTL8187_LEDS
14f1d2b4d3SLarry Finger
15f1d2b4d3SLarry Finger #include <net/mac80211.h>
16f1d2b4d3SLarry Finger #include <linux/usb.h>
17f1d2b4d3SLarry Finger #include <linux/eeprom_93cx6.h>
18f1d2b4d3SLarry Finger
19f1d2b4d3SLarry Finger #include "rtl8187.h"
20f1d2b4d3SLarry Finger #include "leds.h"
21f1d2b4d3SLarry Finger
led_turn_on(struct work_struct * work)22f1d2b4d3SLarry Finger static void led_turn_on(struct work_struct *work)
23f1d2b4d3SLarry Finger {
24f1d2b4d3SLarry Finger /* As this routine does read/write operations on the hardware, it must
25f1d2b4d3SLarry Finger * be run from a work queue.
26f1d2b4d3SLarry Finger */
27f1d2b4d3SLarry Finger u8 reg;
28f1d2b4d3SLarry Finger struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv,
29f1d2b4d3SLarry Finger led_on.work);
30f1d2b4d3SLarry Finger struct rtl8187_led *led = &priv->led_tx;
31f1d2b4d3SLarry Finger
32f1d2b4d3SLarry Finger /* Don't change the LED, when the device is down. */
33f1d2b4d3SLarry Finger if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED)
34f1d2b4d3SLarry Finger return ;
35f1d2b4d3SLarry Finger
36f1d2b4d3SLarry Finger /* Skip if the LED is not registered. */
37f1d2b4d3SLarry Finger if (!led->dev)
38f1d2b4d3SLarry Finger return;
39f1d2b4d3SLarry Finger mutex_lock(&priv->conf_mutex);
40f1d2b4d3SLarry Finger switch (led->ledpin) {
41f1d2b4d3SLarry Finger case LED_PIN_GPIO0:
42f1d2b4d3SLarry Finger rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01);
43f1d2b4d3SLarry Finger rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00);
44f1d2b4d3SLarry Finger break;
45f1d2b4d3SLarry Finger case LED_PIN_LED0:
46f1d2b4d3SLarry Finger reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 4);
47f1d2b4d3SLarry Finger rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
48f1d2b4d3SLarry Finger break;
49f1d2b4d3SLarry Finger case LED_PIN_LED1:
50f1d2b4d3SLarry Finger reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) & ~(1 << 5);
51f1d2b4d3SLarry Finger rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
52f1d2b4d3SLarry Finger break;
53f1d2b4d3SLarry Finger case LED_PIN_HW:
54f1d2b4d3SLarry Finger default:
55f1d2b4d3SLarry Finger break;
56f1d2b4d3SLarry Finger }
57f1d2b4d3SLarry Finger mutex_unlock(&priv->conf_mutex);
58f1d2b4d3SLarry Finger }
59f1d2b4d3SLarry Finger
led_turn_off(struct work_struct * work)60f1d2b4d3SLarry Finger static void led_turn_off(struct work_struct *work)
61f1d2b4d3SLarry Finger {
62f1d2b4d3SLarry Finger /* As this routine does read/write operations on the hardware, it must
63f1d2b4d3SLarry Finger * be run from a work queue.
64f1d2b4d3SLarry Finger */
65f1d2b4d3SLarry Finger u8 reg;
66f1d2b4d3SLarry Finger struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv,
67f1d2b4d3SLarry Finger led_off.work);
68f1d2b4d3SLarry Finger struct rtl8187_led *led = &priv->led_tx;
69f1d2b4d3SLarry Finger
70f1d2b4d3SLarry Finger /* Don't change the LED, when the device is down. */
71f1d2b4d3SLarry Finger if (!priv->vif || priv->vif->type == NL80211_IFTYPE_UNSPECIFIED)
72f1d2b4d3SLarry Finger return ;
73f1d2b4d3SLarry Finger
74f1d2b4d3SLarry Finger /* Skip if the LED is not registered. */
75f1d2b4d3SLarry Finger if (!led->dev)
76f1d2b4d3SLarry Finger return;
77f1d2b4d3SLarry Finger mutex_lock(&priv->conf_mutex);
78f1d2b4d3SLarry Finger switch (led->ledpin) {
79f1d2b4d3SLarry Finger case LED_PIN_GPIO0:
80f1d2b4d3SLarry Finger rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01);
81f1d2b4d3SLarry Finger rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01);
82f1d2b4d3SLarry Finger break;
83f1d2b4d3SLarry Finger case LED_PIN_LED0:
84f1d2b4d3SLarry Finger reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 4);
85f1d2b4d3SLarry Finger rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
86f1d2b4d3SLarry Finger break;
87f1d2b4d3SLarry Finger case LED_PIN_LED1:
88f1d2b4d3SLarry Finger reg = rtl818x_ioread8(priv, &priv->map->PGSELECT) | (1 << 5);
89f1d2b4d3SLarry Finger rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
90f1d2b4d3SLarry Finger break;
91f1d2b4d3SLarry Finger case LED_PIN_HW:
92f1d2b4d3SLarry Finger default:
93f1d2b4d3SLarry Finger break;
94f1d2b4d3SLarry Finger }
95f1d2b4d3SLarry Finger mutex_unlock(&priv->conf_mutex);
96f1d2b4d3SLarry Finger }
97f1d2b4d3SLarry Finger
98f1d2b4d3SLarry Finger /* Callback from the LED subsystem. */
rtl8187_led_brightness_set(struct led_classdev * led_dev,enum led_brightness brightness)99f1d2b4d3SLarry Finger static void rtl8187_led_brightness_set(struct led_classdev *led_dev,
100f1d2b4d3SLarry Finger enum led_brightness brightness)
101f1d2b4d3SLarry Finger {
102f1d2b4d3SLarry Finger struct rtl8187_led *led = container_of(led_dev, struct rtl8187_led,
103f1d2b4d3SLarry Finger led_dev);
104f1d2b4d3SLarry Finger struct ieee80211_hw *hw = led->dev;
105f1d2b4d3SLarry Finger struct rtl8187_priv *priv;
106f1d2b4d3SLarry Finger static bool radio_on;
107f1d2b4d3SLarry Finger
108f1d2b4d3SLarry Finger if (!hw)
109f1d2b4d3SLarry Finger return;
110f1d2b4d3SLarry Finger priv = hw->priv;
111f1d2b4d3SLarry Finger if (led->is_radio) {
112f1d2b4d3SLarry Finger if (brightness == LED_FULL) {
113f1d2b4d3SLarry Finger ieee80211_queue_delayed_work(hw, &priv->led_on, 0);
114f1d2b4d3SLarry Finger radio_on = true;
115f1d2b4d3SLarry Finger } else if (radio_on) {
116f1d2b4d3SLarry Finger radio_on = false;
117f1d2b4d3SLarry Finger cancel_delayed_work(&priv->led_on);
118f1d2b4d3SLarry Finger ieee80211_queue_delayed_work(hw, &priv->led_off, 0);
119f1d2b4d3SLarry Finger }
120f1d2b4d3SLarry Finger } else if (radio_on) {
121f1d2b4d3SLarry Finger if (brightness == LED_OFF) {
122f1d2b4d3SLarry Finger ieee80211_queue_delayed_work(hw, &priv->led_off, 0);
123f1d2b4d3SLarry Finger /* The LED is off for 1/20 sec - it just blinks. */
124f1d2b4d3SLarry Finger ieee80211_queue_delayed_work(hw, &priv->led_on,
125f1d2b4d3SLarry Finger HZ / 20);
126f1d2b4d3SLarry Finger } else
127f1d2b4d3SLarry Finger ieee80211_queue_delayed_work(hw, &priv->led_on, 0);
128f1d2b4d3SLarry Finger }
129f1d2b4d3SLarry Finger }
130f1d2b4d3SLarry Finger
rtl8187_register_led(struct ieee80211_hw * dev,struct rtl8187_led * led,const char * name,const char * default_trigger,u8 ledpin,bool is_radio)131f1d2b4d3SLarry Finger static int rtl8187_register_led(struct ieee80211_hw *dev,
132f1d2b4d3SLarry Finger struct rtl8187_led *led, const char *name,
133f1d2b4d3SLarry Finger const char *default_trigger, u8 ledpin,
134f1d2b4d3SLarry Finger bool is_radio)
135f1d2b4d3SLarry Finger {
136f1d2b4d3SLarry Finger int err;
137f1d2b4d3SLarry Finger struct rtl8187_priv *priv = dev->priv;
138f1d2b4d3SLarry Finger
139f1d2b4d3SLarry Finger if (led->dev)
140f1d2b4d3SLarry Finger return -EEXIST;
141f1d2b4d3SLarry Finger if (!default_trigger)
142f1d2b4d3SLarry Finger return -EINVAL;
143f1d2b4d3SLarry Finger led->dev = dev;
144f1d2b4d3SLarry Finger led->ledpin = ledpin;
145f1d2b4d3SLarry Finger led->is_radio = is_radio;
146*bf99f11dSWolfram Sang strscpy(led->name, name, sizeof(led->name));
147f1d2b4d3SLarry Finger
148f1d2b4d3SLarry Finger led->led_dev.name = led->name;
149f1d2b4d3SLarry Finger led->led_dev.default_trigger = default_trigger;
150f1d2b4d3SLarry Finger led->led_dev.brightness_set = rtl8187_led_brightness_set;
151f1d2b4d3SLarry Finger
152f1d2b4d3SLarry Finger err = led_classdev_register(&priv->udev->dev, &led->led_dev);
153f1d2b4d3SLarry Finger if (err) {
154f1d2b4d3SLarry Finger printk(KERN_INFO "LEDs: Failed to register %s\n", name);
155f1d2b4d3SLarry Finger led->dev = NULL;
156f1d2b4d3SLarry Finger return err;
157f1d2b4d3SLarry Finger }
158f1d2b4d3SLarry Finger return 0;
159f1d2b4d3SLarry Finger }
160f1d2b4d3SLarry Finger
rtl8187_unregister_led(struct rtl8187_led * led)161f1d2b4d3SLarry Finger static void rtl8187_unregister_led(struct rtl8187_led *led)
162f1d2b4d3SLarry Finger {
163f1d2b4d3SLarry Finger struct ieee80211_hw *hw = led->dev;
164f1d2b4d3SLarry Finger struct rtl8187_priv *priv = hw->priv;
165f1d2b4d3SLarry Finger
166f1d2b4d3SLarry Finger led_classdev_unregister(&led->led_dev);
167f1d2b4d3SLarry Finger flush_delayed_work(&priv->led_off);
168f1d2b4d3SLarry Finger led->dev = NULL;
169f1d2b4d3SLarry Finger }
170f1d2b4d3SLarry Finger
rtl8187_leds_init(struct ieee80211_hw * dev,u16 custid)171f1d2b4d3SLarry Finger void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid)
172f1d2b4d3SLarry Finger {
173f1d2b4d3SLarry Finger struct rtl8187_priv *priv = dev->priv;
174f1d2b4d3SLarry Finger char name[RTL8187_LED_MAX_NAME_LEN + 1];
175f1d2b4d3SLarry Finger u8 ledpin;
176f1d2b4d3SLarry Finger int err;
177f1d2b4d3SLarry Finger
178f1d2b4d3SLarry Finger /* According to the vendor driver, the LED operation depends on the
179f1d2b4d3SLarry Finger * customer ID encoded in the EEPROM
180f1d2b4d3SLarry Finger */
181f1d2b4d3SLarry Finger printk(KERN_INFO "rtl8187: Customer ID is 0x%02X\n", custid);
182f1d2b4d3SLarry Finger switch (custid) {
183f1d2b4d3SLarry Finger case EEPROM_CID_RSVD0:
184f1d2b4d3SLarry Finger case EEPROM_CID_RSVD1:
185f1d2b4d3SLarry Finger case EEPROM_CID_SERCOMM_PS:
186f1d2b4d3SLarry Finger case EEPROM_CID_QMI:
187f1d2b4d3SLarry Finger case EEPROM_CID_DELL:
188f1d2b4d3SLarry Finger case EEPROM_CID_TOSHIBA:
189f1d2b4d3SLarry Finger ledpin = LED_PIN_GPIO0;
190f1d2b4d3SLarry Finger break;
191f1d2b4d3SLarry Finger case EEPROM_CID_ALPHA0:
192f1d2b4d3SLarry Finger ledpin = LED_PIN_LED0;
193f1d2b4d3SLarry Finger break;
194f1d2b4d3SLarry Finger case EEPROM_CID_HW:
195f1d2b4d3SLarry Finger ledpin = LED_PIN_HW;
196f1d2b4d3SLarry Finger break;
197f1d2b4d3SLarry Finger default:
198f1d2b4d3SLarry Finger ledpin = LED_PIN_GPIO0;
199f1d2b4d3SLarry Finger }
200f1d2b4d3SLarry Finger
201f1d2b4d3SLarry Finger INIT_DELAYED_WORK(&priv->led_on, led_turn_on);
202f1d2b4d3SLarry Finger INIT_DELAYED_WORK(&priv->led_off, led_turn_off);
203f1d2b4d3SLarry Finger
204f1d2b4d3SLarry Finger snprintf(name, sizeof(name),
205f1d2b4d3SLarry Finger "rtl8187-%s::radio", wiphy_name(dev->wiphy));
206f1d2b4d3SLarry Finger err = rtl8187_register_led(dev, &priv->led_radio, name,
207f1d2b4d3SLarry Finger ieee80211_get_radio_led_name(dev), ledpin, true);
208f1d2b4d3SLarry Finger if (err)
209f1d2b4d3SLarry Finger return;
210f1d2b4d3SLarry Finger
211f1d2b4d3SLarry Finger snprintf(name, sizeof(name),
212f1d2b4d3SLarry Finger "rtl8187-%s::tx", wiphy_name(dev->wiphy));
213f1d2b4d3SLarry Finger err = rtl8187_register_led(dev, &priv->led_tx, name,
214f1d2b4d3SLarry Finger ieee80211_get_tx_led_name(dev), ledpin, false);
215f1d2b4d3SLarry Finger if (err)
216f1d2b4d3SLarry Finger goto err_tx;
217f1d2b4d3SLarry Finger
218f1d2b4d3SLarry Finger snprintf(name, sizeof(name),
219f1d2b4d3SLarry Finger "rtl8187-%s::rx", wiphy_name(dev->wiphy));
220f1d2b4d3SLarry Finger err = rtl8187_register_led(dev, &priv->led_rx, name,
221f1d2b4d3SLarry Finger ieee80211_get_rx_led_name(dev), ledpin, false);
222f1d2b4d3SLarry Finger if (!err)
223f1d2b4d3SLarry Finger return;
224f1d2b4d3SLarry Finger
225f1d2b4d3SLarry Finger /* registration of RX LED failed - unregister */
226f1d2b4d3SLarry Finger rtl8187_unregister_led(&priv->led_tx);
227f1d2b4d3SLarry Finger err_tx:
228f1d2b4d3SLarry Finger rtl8187_unregister_led(&priv->led_radio);
229f1d2b4d3SLarry Finger }
230f1d2b4d3SLarry Finger
rtl8187_leds_exit(struct ieee80211_hw * dev)231f1d2b4d3SLarry Finger void rtl8187_leds_exit(struct ieee80211_hw *dev)
232f1d2b4d3SLarry Finger {
233f1d2b4d3SLarry Finger struct rtl8187_priv *priv = dev->priv;
234f1d2b4d3SLarry Finger
235f1d2b4d3SLarry Finger rtl8187_unregister_led(&priv->led_radio);
236f1d2b4d3SLarry Finger rtl8187_unregister_led(&priv->led_rx);
237f1d2b4d3SLarry Finger rtl8187_unregister_led(&priv->led_tx);
238f1d2b4d3SLarry Finger cancel_delayed_work_sync(&priv->led_off);
239f1d2b4d3SLarry Finger cancel_delayed_work_sync(&priv->led_on);
240f1d2b4d3SLarry Finger }
241f1d2b4d3SLarry Finger #endif /* def CONFIG_RTL8187_LEDS */
242f1d2b4d3SLarry Finger
243