~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

Viewing changes to drivers/net/wireless/iwlwifi/iwl-led.c

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/******************************************************************************
2
2
 *
3
 
 * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved.
 
3
 * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved.
4
4
 *
5
5
 * This program is free software; you can redistribute it and/or modify it
6
6
 * under the terms of version 2 of the GNU General Public License as
48
48
MODULE_PARM_DESC(led_mode, "0=system default, "
49
49
                "1=On(RF On)/Off(RF Off), 2=blinking");
50
50
 
51
 
static const struct {
52
 
        u16 tpt;        /* Mb/s */
53
 
        u8 on_time;
54
 
        u8 off_time;
55
 
} blink_tbl[] =
56
 
{
57
 
        {300, 25, 25},
58
 
        {200, 40, 40},
59
 
        {100, 55, 55},
60
 
        {70, 65, 65},
61
 
        {50, 75, 75},
62
 
        {20, 85, 85},
63
 
        {10, 95, 95},
64
 
        {5, 110, 110},
65
 
        {1, 130, 130},
66
 
        {0, 167, 167},
67
 
        /* SOLID_ON */
68
 
        {-1, IWL_LED_SOLID, 0}
 
51
/* Throughput           OFF time(ms)    ON time (ms)
 
52
 *      >300                    25              25
 
53
 *      >200 to 300             40              40
 
54
 *      >100 to 200             55              55
 
55
 *      >70 to 100              65              65
 
56
 *      >50 to 70               75              75
 
57
 *      >20 to 50               85              85
 
58
 *      >10 to 20               95              95
 
59
 *      >5 to 10                110             110
 
60
 *      >1 to 5                 130             130
 
61
 *      >0 to 1                 167             167
 
62
 *      <=0                                     SOLID ON
 
63
 */
 
64
static const struct ieee80211_tpt_blink iwl_blink[] = {
 
65
        { .throughput = 0, .blink_time = 334 },
 
66
        { .throughput = 1 * 1024 - 1, .blink_time = 260 },
 
67
        { .throughput = 5 * 1024 - 1, .blink_time = 220 },
 
68
        { .throughput = 10 * 1024 - 1, .blink_time = 190 },
 
69
        { .throughput = 20 * 1024 - 1, .blink_time = 170 },
 
70
        { .throughput = 50 * 1024 - 1, .blink_time = 150 },
 
71
        { .throughput = 70 * 1024 - 1, .blink_time = 130 },
 
72
        { .throughput = 100 * 1024 - 1, .blink_time = 110 },
 
73
        { .throughput = 200 * 1024 - 1, .blink_time = 80 },
 
74
        { .throughput = 300 * 1024 - 1, .blink_time = 50 },
69
75
};
70
76
 
71
 
#define IWL_1MB_RATE (128 * 1024)
72
 
#define IWL_LED_THRESHOLD (16)
73
 
#define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /* exclude SOLID_ON */
74
 
#define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1)
 
77
/* Set led register off */
 
78
void iwlagn_led_enable(struct iwl_priv *priv)
 
79
{
 
80
        iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON);
 
81
}
75
82
 
76
83
/*
77
84
 * Adjust led blink rate to compensate on a MAC Clock difference on every HW
78
 
 * Led blink rate analysis showed an average deviation of 0% on 3945,
79
 
 * 5% on 4965 HW and 20% on 5000 series and up.
 
85
 * Led blink rate analysis showed an average deviation of 20% on 5000 series
 
86
 * and up.
80
87
 * Need to compensate on the led on/off time per HW according to the deviation
81
88
 * to achieve the desired led frequency
82
89
 * The calculation is: (100-averageDeviation)/100 * blinkTime
96
103
        return (u8)((time * compensation) >> 6);
97
104
}
98
105
 
 
106
static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd)
 
107
{
 
108
        struct iwl_host_cmd cmd = {
 
109
                .id = REPLY_LEDS_CMD,
 
110
                .len = { sizeof(struct iwl_led_cmd), },
 
111
                .data = { led_cmd, },
 
112
                .flags = CMD_ASYNC,
 
113
                .callback = NULL,
 
114
        };
 
115
        u32 reg;
 
116
 
 
117
        reg = iwl_read32(priv, CSR_LED_REG);
 
118
        if (reg != (reg & CSR_LED_BSM_CTRL_MSK))
 
119
                iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK);
 
120
 
 
121
        return iwl_send_cmd(priv, &cmd);
 
122
}
 
123
 
99
124
/* Set led pattern command */
100
 
static int iwl_led_pattern(struct iwl_priv *priv, unsigned int idx)
 
125
static int iwl_led_cmd(struct iwl_priv *priv,
 
126
                       unsigned long on,
 
127
                       unsigned long off)
101
128
{
102
129
        struct iwl_led_cmd led_cmd = {
103
130
                .id = IWL_LED_LINK,
104
131
                .interval = IWL_DEF_LED_INTRVL
105
132
        };
106
 
 
107
 
        BUG_ON(idx > IWL_MAX_BLINK_TBL);
108
 
 
109
 
        IWL_DEBUG_LED(priv, "Led blink time compensation= %u\n",
 
133
        int ret;
 
134
 
 
135
        if (!test_bit(STATUS_READY, &priv->status))
 
136
                return -EBUSY;
 
137
 
 
138
        if (priv->blink_on == on && priv->blink_off == off)
 
139
                return 0;
 
140
 
 
141
        if (off == 0) {
 
142
                /* led is SOLID_ON */
 
143
                on = IWL_LED_SOLID;
 
144
        }
 
145
 
 
146
        IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n",
110
147
                        priv->cfg->base_params->led_compensation);
111
 
        led_cmd.on =
112
 
                iwl_blink_compensation(priv, blink_tbl[idx].on_time,
113
 
                                priv->cfg->base_params->led_compensation);
114
 
        led_cmd.off =
115
 
                iwl_blink_compensation(priv, blink_tbl[idx].off_time,
116
 
                                priv->cfg->base_params->led_compensation);
117
 
 
118
 
        return priv->cfg->ops->led->cmd(priv, &led_cmd);
119
 
}
120
 
 
121
 
int iwl_led_start(struct iwl_priv *priv)
122
 
{
123
 
        return priv->cfg->ops->led->on(priv);
124
 
}
125
 
EXPORT_SYMBOL(iwl_led_start);
126
 
 
127
 
int iwl_led_associate(struct iwl_priv *priv)
128
 
{
129
 
        IWL_DEBUG_LED(priv, "Associated\n");
130
 
        if (priv->cfg->led_mode == IWL_LED_BLINK)
131
 
                priv->allow_blinking = 1;
132
 
        priv->last_blink_time = jiffies;
133
 
 
134
 
        return 0;
135
 
}
136
 
EXPORT_SYMBOL(iwl_led_associate);
137
 
 
138
 
int iwl_led_disassociate(struct iwl_priv *priv)
139
 
{
140
 
        priv->allow_blinking = 0;
141
 
 
142
 
        return 0;
143
 
}
144
 
EXPORT_SYMBOL(iwl_led_disassociate);
145
 
 
146
 
/*
147
 
 * calculate blink rate according to last second Tx/Rx activities
148
 
 */
149
 
static int iwl_get_blink_rate(struct iwl_priv *priv)
150
 
{
151
 
        int i;
152
 
        /* count both tx and rx traffic to be able to
153
 
         * handle traffic in either direction
154
 
         */
155
 
        u64 current_tpt = priv->tx_stats.data_bytes +
156
 
                          priv->rx_stats.data_bytes;
157
 
        s64 tpt = current_tpt - priv->led_tpt;
158
 
 
159
 
        if (tpt < 0) /* wraparound */
160
 
                tpt = -tpt;
161
 
 
162
 
        IWL_DEBUG_LED(priv, "tpt %lld current_tpt %llu\n",
163
 
                (long long)tpt,
164
 
                (unsigned long long)current_tpt);
165
 
        priv->led_tpt = current_tpt;
166
 
 
167
 
        if (!priv->allow_blinking)
168
 
                i = IWL_MAX_BLINK_TBL;
169
 
        else
170
 
                for (i = 0; i < IWL_MAX_BLINK_TBL; i++)
171
 
                        if (tpt > (blink_tbl[i].tpt * IWL_1MB_RATE))
172
 
                                break;
173
 
 
174
 
        IWL_DEBUG_LED(priv, "LED BLINK IDX=%d\n", i);
175
 
        return i;
176
 
}
177
 
 
178
 
/*
179
 
 * this function called from handler. Since setting Led command can
180
 
 * happen very frequent we postpone led command to be called from
181
 
 * REPLY handler so we know ucode is up
182
 
 */
183
 
void iwl_leds_background(struct iwl_priv *priv)
184
 
{
185
 
        u8 blink_idx;
186
 
 
187
 
        if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
188
 
                priv->last_blink_time = 0;
189
 
                return;
190
 
        }
191
 
        if (iwl_is_rfkill(priv)) {
192
 
                priv->last_blink_time = 0;
193
 
                return;
194
 
        }
195
 
 
196
 
        if (!priv->allow_blinking) {
197
 
                priv->last_blink_time = 0;
198
 
                if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) {
199
 
                        priv->last_blink_rate = IWL_SOLID_BLINK_IDX;
200
 
                        iwl_led_pattern(priv, IWL_SOLID_BLINK_IDX);
201
 
                }
202
 
                return;
203
 
        }
204
 
        if (!priv->last_blink_time ||
205
 
            !time_after(jiffies, priv->last_blink_time +
206
 
                        msecs_to_jiffies(1000)))
207
 
                return;
208
 
 
209
 
        blink_idx = iwl_get_blink_rate(priv);
210
 
 
211
 
        /* call only if blink rate change */
212
 
        if (blink_idx != priv->last_blink_rate)
213
 
                iwl_led_pattern(priv, blink_idx);
214
 
 
215
 
        priv->last_blink_time = jiffies;
216
 
        priv->last_blink_rate = blink_idx;
217
 
}
218
 
EXPORT_SYMBOL(iwl_leds_background);
 
148
        led_cmd.on = iwl_blink_compensation(priv, on,
 
149
                                priv->cfg->base_params->led_compensation);
 
150
        led_cmd.off = iwl_blink_compensation(priv, off,
 
151
                                priv->cfg->base_params->led_compensation);
 
152
 
 
153
        ret = iwl_send_led_cmd(priv, &led_cmd);
 
154
        if (!ret) {
 
155
                priv->blink_on = on;
 
156
                priv->blink_off = off;
 
157
        }
 
158
        return ret;
 
159
}
 
160
 
 
161
static void iwl_led_brightness_set(struct led_classdev *led_cdev,
 
162
                                   enum led_brightness brightness)
 
163
{
 
164
        struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led);
 
165
        unsigned long on = 0;
 
166
 
 
167
        if (brightness > 0)
 
168
                on = IWL_LED_SOLID;
 
169
 
 
170
        iwl_led_cmd(priv, on, 0);
 
171
}
 
172
 
 
173
static int iwl_led_blink_set(struct led_classdev *led_cdev,
 
174
                             unsigned long *delay_on,
 
175
                             unsigned long *delay_off)
 
176
{
 
177
        struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led);
 
178
 
 
179
        return iwl_led_cmd(priv, *delay_on, *delay_off);
 
180
}
219
181
 
220
182
void iwl_leds_init(struct iwl_priv *priv)
221
183
{
222
 
        priv->last_blink_rate = 0;
223
 
        priv->last_blink_time = 0;
224
 
        priv->allow_blinking = 0;
225
 
        if (led_mode != IWL_LED_DEFAULT &&
226
 
            led_mode != priv->cfg->led_mode)
227
 
                priv->cfg->led_mode = led_mode;
228
 
}
229
 
EXPORT_SYMBOL(iwl_leds_init);
 
184
        int mode = led_mode;
 
185
        int ret;
 
186
 
 
187
        if (mode == IWL_LED_DEFAULT)
 
188
                mode = priv->cfg->led_mode;
 
189
 
 
190
        priv->led.name = kasprintf(GFP_KERNEL, "%s-led",
 
191
                                   wiphy_name(priv->hw->wiphy));
 
192
        priv->led.brightness_set = iwl_led_brightness_set;
 
193
        priv->led.blink_set = iwl_led_blink_set;
 
194
        priv->led.max_brightness = 1;
 
195
 
 
196
        switch (mode) {
 
197
        case IWL_LED_DEFAULT:
 
198
                WARN_ON(1);
 
199
                break;
 
200
        case IWL_LED_BLINK:
 
201
                priv->led.default_trigger =
 
202
                        ieee80211_create_tpt_led_trigger(priv->hw,
 
203
                                        IEEE80211_TPT_LEDTRIG_FL_CONNECTED,
 
204
                                        iwl_blink, ARRAY_SIZE(iwl_blink));
 
205
                break;
 
206
        case IWL_LED_RF_STATE:
 
207
                priv->led.default_trigger =
 
208
                        ieee80211_get_radio_led_name(priv->hw);
 
209
                break;
 
210
        }
 
211
 
 
212
        ret = led_classdev_register(&priv->pci_dev->dev, &priv->led);
 
213
        if (ret) {
 
214
                kfree(priv->led.name);
 
215
                return;
 
216
        }
 
217
 
 
218
        priv->led_registered = true;
 
219
}
 
220
 
 
221
void iwl_leds_exit(struct iwl_priv *priv)
 
222
{
 
223
        if (!priv->led_registered)
 
224
                return;
 
225
 
 
226
        led_classdev_unregister(&priv->led);
 
227
        kfree(priv->led.name);
 
228
}