Commit 10d599ad authored by Maya Erez's avatar Maya Erez Committed by Kalle Valo

wil6210: add support for device led configuration

Add the ability to configure the device led to be used for notifying
the AP activity (60G device supports leds 0-2).
The host can also configure the blinking frequency of the led in
three states.
Signed-off-by: default avatarMaya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 41842dc1
......@@ -1441,6 +1441,118 @@ static const struct file_operations fops_sta = {
.llseek = seq_lseek,
};
static ssize_t wil_read_file_led_cfg(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
char buf[80];
int n;
n = snprintf(buf, sizeof(buf),
"led_id is set to %d, echo 1 to enable, 0 to disable\n",
led_id);
n = min_t(int, n, sizeof(buf));
return simple_read_from_buffer(user_buf, count, ppos,
buf, n);
}
static ssize_t wil_write_file_led_cfg(struct file *file,
const char __user *buf_,
size_t count, loff_t *ppos)
{
struct wil6210_priv *wil = file->private_data;
int val;
int rc;
rc = kstrtoint_from_user(buf_, count, 0, &val);
if (rc) {
wil_err(wil, "Invalid argument\n");
return rc;
}
wil_info(wil, "%s led %d\n", val ? "Enabling" : "Disabling", led_id);
rc = wmi_led_cfg(wil, val);
if (rc) {
wil_info(wil, "%s led %d failed\n",
val ? "Enabling" : "Disabling", led_id);
return rc;
}
return count;
}
static const struct file_operations fops_led_cfg = {
.read = wil_read_file_led_cfg,
.write = wil_write_file_led_cfg,
.open = simple_open,
};
/* led_blink_time, write:
* "<blink_on_slow> <blink_off_slow> <blink_on_med> <blink_off_med> <blink_on_fast> <blink_off_fast>
*/
static ssize_t wil_write_led_blink_time(struct file *file,
const char __user *buf,
size_t len, loff_t *ppos)
{
int rc;
char *kbuf = kmalloc(len + 1, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
if (rc != len) {
kfree(kbuf);
return rc >= 0 ? -EIO : rc;
}
kbuf[len] = '\0';
rc = sscanf(kbuf, "%d %d %d %d %d %d",
&led_blink_time[WIL_LED_TIME_SLOW].on_ms,
&led_blink_time[WIL_LED_TIME_SLOW].off_ms,
&led_blink_time[WIL_LED_TIME_MED].on_ms,
&led_blink_time[WIL_LED_TIME_MED].off_ms,
&led_blink_time[WIL_LED_TIME_FAST].on_ms,
&led_blink_time[WIL_LED_TIME_FAST].off_ms);
kfree(kbuf);
if (rc < 0)
return rc;
if (rc < 6)
return -EINVAL;
return len;
}
static ssize_t wil_read_led_blink_time(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
static char text[400];
snprintf(text, sizeof(text),
"To set led blink on/off time variables write:\n"
"<blink_on_slow> <blink_off_slow> <blink_on_med> "
"<blink_off_med> <blink_on_fast> <blink_off_fast>\n"
"The current values are:\n"
"%d %d %d %d %d %d\n",
led_blink_time[WIL_LED_TIME_SLOW].on_ms,
led_blink_time[WIL_LED_TIME_SLOW].off_ms,
led_blink_time[WIL_LED_TIME_MED].on_ms,
led_blink_time[WIL_LED_TIME_MED].off_ms,
led_blink_time[WIL_LED_TIME_FAST].on_ms,
led_blink_time[WIL_LED_TIME_FAST].off_ms);
return simple_read_from_buffer(user_buf, count, ppos, text,
sizeof(text));
}
static const struct file_operations fops_led_blink_time = {
.read = wil_read_led_blink_time,
.write = wil_write_led_blink_time,
.open = simple_open,
};
/*----------------*/
static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
struct dentry *dbg)
......@@ -1489,6 +1601,8 @@ static const struct {
{"link", S_IRUGO, &fops_link},
{"info", S_IRUGO, &fops_info},
{"recovery", S_IRUGO | S_IWUSR, &fops_recovery},
{"led_cfg", S_IRUGO | S_IWUSR, &fops_led_cfg},
{"led_blink_time", S_IRUGO | S_IWUSR, &fops_led_blink_time},
};
static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
......@@ -1551,6 +1665,7 @@ static const struct dbg_off dbg_statics[] = {
{"mem_addr", S_IRUGO | S_IWUSR, (ulong)&mem_addr, doff_u32},
{"vring_idle_trsh", S_IRUGO | S_IWUSR, (ulong)&vring_idle_trsh,
doff_u32},
{"led_polarity", S_IRUGO | S_IWUSR, (ulong)&led_polarity, doff_u8},
{},
};
......
......@@ -841,6 +841,9 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
wil_bcast_fini(wil);
/* Disable device led before reset*/
wmi_led_cfg(wil, false);
/* prevent NAPI from being scheduled and prevent wmi commands */
mutex_lock(&wil->wmi_mutex);
bitmap_zero(wil->status, wil_status_last);
......
......@@ -546,6 +546,30 @@ struct wil_blob_wrapper {
struct debugfs_blob_wrapper blob;
};
#define WIL_LED_MAX_ID (2)
#define WIL_LED_INVALID_ID (0xF)
#define WIL_LED_BLINK_ON_SLOW_MS (300)
#define WIL_LED_BLINK_OFF_SLOW_MS (300)
#define WIL_LED_BLINK_ON_MED_MS (200)
#define WIL_LED_BLINK_OFF_MED_MS (200)
#define WIL_LED_BLINK_ON_FAST_MS (100)
#define WIL_LED_BLINK_OFF_FAST_MS (100)
enum {
WIL_LED_TIME_SLOW = 0,
WIL_LED_TIME_MED,
WIL_LED_TIME_FAST,
WIL_LED_TIME_LAST,
};
struct blink_on_off_time {
u32 on_ms;
u32 off_ms;
};
extern struct blink_on_off_time led_blink_time[WIL_LED_TIME_LAST];
extern u8 led_id;
extern u8 led_polarity;
struct wil6210_priv {
struct pci_dev *pdev;
struct wireless_dev *wdev;
......@@ -834,6 +858,7 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr);
int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
u8 chan, u8 hidden_ssid, u8 is_go);
int wmi_pcp_stop(struct wil6210_priv *wil);
int wmi_led_cfg(struct wil6210_priv *wil, bool enable);
void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid,
u16 reason_code, bool from_event);
void wil_probe_client_flush(struct wil6210_priv *wil);
......
......@@ -32,6 +32,11 @@ module_param(agg_wsize, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(agg_wsize, " Window size for Tx Block Ack after connect;"
" 0 - use default; < 0 - don't auto-establish");
u8 led_id = WIL_LED_INVALID_ID;
module_param(led_id, byte, S_IRUGO);
MODULE_PARM_DESC(led_id,
" 60G device led enablement. Set the led ID (0-2) to enable");
/**
* WMI event receiving - theory of operations
*
......@@ -94,6 +99,14 @@ const struct fw_map fw_mapping[] = {
*/
};
struct blink_on_off_time led_blink_time[] = {
{WIL_LED_BLINK_ON_SLOW_MS, WIL_LED_BLINK_OFF_SLOW_MS},
{WIL_LED_BLINK_ON_MED_MS, WIL_LED_BLINK_OFF_MED_MS},
{WIL_LED_BLINK_ON_FAST_MS, WIL_LED_BLINK_OFF_FAST_MS},
};
u8 led_polarity = LED_POLARITY_LOW_ACTIVE;
/**
* return AHB address for given firmware/ucode internal (linker) address
* @x - internal address
......@@ -971,6 +984,60 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr)
return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd));
}
int wmi_led_cfg(struct wil6210_priv *wil, bool enable)
{
int rc = 0;
struct wmi_led_cfg_cmd cmd = {
.led_mode = enable,
.id = led_id,
.slow_blink_cfg.blink_on =
cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].on_ms),
.slow_blink_cfg.blink_off =
cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].off_ms),
.medium_blink_cfg.blink_on =
cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].on_ms),
.medium_blink_cfg.blink_off =
cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].off_ms),
.fast_blink_cfg.blink_on =
cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].on_ms),
.fast_blink_cfg.blink_off =
cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].off_ms),
.led_polarity = led_polarity,
};
struct {
struct wmi_cmd_hdr wmi;
struct wmi_led_cfg_done_event evt;
} __packed reply;
if (led_id == WIL_LED_INVALID_ID)
goto out;
if (led_id > WIL_LED_MAX_ID) {
wil_err(wil, "Invalid led id %d\n", led_id);
rc = -EINVAL;
goto out;
}
wil_dbg_wmi(wil,
"%s led %d\n",
enable ? "enabling" : "disabling", led_id);
rc = wmi_call(wil, WMI_LED_CFG_CMDID, &cmd, sizeof(cmd),
WMI_LED_CFG_DONE_EVENTID, &reply, sizeof(reply),
100);
if (rc)
goto out;
if (reply.evt.status) {
wil_err(wil, "led %d cfg failed with status %d\n",
led_id, le32_to_cpu(reply.evt.status));
rc = -EINVAL;
}
out:
return rc;
}
int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
u8 chan, u8 hidden_ssid, u8 is_go)
{
......@@ -1013,11 +1080,21 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype,
if (reply.evt.status != WMI_FW_STATUS_SUCCESS)
rc = -EINVAL;
if (wmi_nettype != WMI_NETTYPE_P2P)
/* Don't fail due to error in the led configuration */
wmi_led_cfg(wil, true);
return rc;
}
int wmi_pcp_stop(struct wil6210_priv *wil)
{
int rc;
rc = wmi_led_cfg(wil, false);
if (rc)
return rc;
return wmi_call(wil, WMI_PCP_STOP_CMDID, NULL, 0,
WMI_PCP_STOPPED_EVENTID, NULL, 0, 20);
}
......
......@@ -129,6 +129,7 @@ enum wmi_command_id {
WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x855,
WMI_OTP_READ_CMDID = 0x856,
WMI_OTP_WRITE_CMDID = 0x857,
WMI_LED_CFG_CMDID = 0x858,
/* Performance monitoring commands */
WMI_BF_CTRL_CMDID = 0x862,
WMI_NOTIFY_REQ_CMDID = 0x863,
......@@ -868,6 +869,7 @@ enum wmi_event_id {
WMI_RX_MGMT_PACKET_EVENTID = 0x1840,
WMI_TX_MGMT_PACKET_EVENTID = 0x1841,
WMI_OTP_READ_RESULT_EVENTID = 0x1856,
WMI_LED_CFG_DONE_EVENTID = 0x1858,
/* Performance monitoring events */
WMI_DATA_PORT_OPEN_EVENTID = 0x1860,
WMI_WBE_LINK_DOWN_EVENTID = 0x1861,
......@@ -1349,4 +1351,63 @@ enum wmi_hidden_ssid {
WMI_HIDDEN_SSID_CLEAR = 0xFE,
};
/* WMI_LED_CFG_CMDID
*
* Configure LED On\Off\Blinking operation
*
* Returned events:
* - WMI_LED_CFG_DONE_EVENTID
*/
enum led_mode {
LED_DISABLE = 0x00,
LED_ENABLE = 0x01,
};
/* The names of the led as
* described on HW schemes.
*/
enum wmi_led_id {
WMI_LED_WLAN = 0x00,
WMI_LED_WPAN = 0x01,
WMI_LED_WWAN = 0x02,
};
/* Led polarity mode. */
enum wmi_led_polarity {
LED_POLARITY_HIGH_ACTIVE = 0x00,
LED_POLARITY_LOW_ACTIVE = 0x01,
};
/* Combination of on and off
* creates the blinking period
*/
struct wmi_led_blink_mode {
__le32 blink_on;
__le32 blink_off;
} __packed;
/* WMI_LED_CFG_CMDID */
struct wmi_led_cfg_cmd {
/* enum led_mode_e */
u8 led_mode;
/* enum wmi_led_id_e */
u8 id;
/* slow speed blinking combination */
struct wmi_led_blink_mode slow_blink_cfg;
/* medium speed blinking combination */
struct wmi_led_blink_mode medium_blink_cfg;
/* high speed blinking combination */
struct wmi_led_blink_mode fast_blink_cfg;
/* polarity of the led */
u8 led_polarity;
/* reserved */
u8 reserved;
} __packed;
/* WMI_LED_CFG_DONE_EVENTID */
struct wmi_led_cfg_done_event {
/* led config status */
__le32 status;
} __packed;
#endif /* __WILOCITY_WMI_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment