12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
233cab57aSLuciano Coelho /*
333cab57aSLuciano Coelho  * This file is part of wlcore
433cab57aSLuciano Coelho  *
533cab57aSLuciano Coelho  * Copyright (C) 2013 Texas Instruments Inc.
633cab57aSLuciano Coelho  */
733cab57aSLuciano Coelho 
8fa2648a3STony Lindgren #include <linux/pm_runtime.h>
9fa2648a3STony Lindgren 
10fa2648a3STony Lindgren #include "acx.h"
1133cab57aSLuciano Coelho #include "wlcore.h"
1233cab57aSLuciano Coelho #include "debug.h"
1333cab57aSLuciano Coelho #include "sysfs.h"
1433cab57aSLuciano Coelho 
bt_coex_state_show(struct device * dev,struct device_attribute * attr,char * buf)1586f1ea9dSYueHaibing static ssize_t bt_coex_state_show(struct device *dev,
1633cab57aSLuciano Coelho 				  struct device_attribute *attr,
1733cab57aSLuciano Coelho 				  char *buf)
1833cab57aSLuciano Coelho {
1933cab57aSLuciano Coelho 	struct wl1271 *wl = dev_get_drvdata(dev);
2033cab57aSLuciano Coelho 	ssize_t len;
2133cab57aSLuciano Coelho 
2233cab57aSLuciano Coelho 	len = PAGE_SIZE;
2333cab57aSLuciano Coelho 
2433cab57aSLuciano Coelho 	mutex_lock(&wl->mutex);
2533cab57aSLuciano Coelho 	len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
2633cab57aSLuciano Coelho 		       wl->sg_enabled);
2733cab57aSLuciano Coelho 	mutex_unlock(&wl->mutex);
2833cab57aSLuciano Coelho 
2933cab57aSLuciano Coelho 	return len;
3033cab57aSLuciano Coelho 
3133cab57aSLuciano Coelho }
3233cab57aSLuciano Coelho 
bt_coex_state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)3386f1ea9dSYueHaibing static ssize_t bt_coex_state_store(struct device *dev,
3433cab57aSLuciano Coelho 				   struct device_attribute *attr,
3533cab57aSLuciano Coelho 				   const char *buf, size_t count)
3633cab57aSLuciano Coelho {
3733cab57aSLuciano Coelho 	struct wl1271 *wl = dev_get_drvdata(dev);
3833cab57aSLuciano Coelho 	unsigned long res;
3933cab57aSLuciano Coelho 	int ret;
4033cab57aSLuciano Coelho 
4133cab57aSLuciano Coelho 	ret = kstrtoul(buf, 10, &res);
4233cab57aSLuciano Coelho 	if (ret < 0) {
4333cab57aSLuciano Coelho 		wl1271_warning("incorrect value written to bt_coex_mode");
4433cab57aSLuciano Coelho 		return count;
4533cab57aSLuciano Coelho 	}
4633cab57aSLuciano Coelho 
4733cab57aSLuciano Coelho 	mutex_lock(&wl->mutex);
4833cab57aSLuciano Coelho 
4933cab57aSLuciano Coelho 	res = !!res;
5033cab57aSLuciano Coelho 
5133cab57aSLuciano Coelho 	if (res == wl->sg_enabled)
5233cab57aSLuciano Coelho 		goto out;
5333cab57aSLuciano Coelho 
5433cab57aSLuciano Coelho 	wl->sg_enabled = res;
5533cab57aSLuciano Coelho 
5633cab57aSLuciano Coelho 	if (unlikely(wl->state != WLCORE_STATE_ON))
5733cab57aSLuciano Coelho 		goto out;
5833cab57aSLuciano Coelho 
59*da8e909cSMinghao Chi 	ret = pm_runtime_resume_and_get(wl->dev);
60*da8e909cSMinghao Chi 	if (ret < 0)
6133cab57aSLuciano Coelho 		goto out;
6233cab57aSLuciano Coelho 
6333cab57aSLuciano Coelho 	wl1271_acx_sg_enable(wl, wl->sg_enabled);
649b71578dSTony Lindgren 	pm_runtime_mark_last_busy(wl->dev);
659b71578dSTony Lindgren 	pm_runtime_put_autosuspend(wl->dev);
6633cab57aSLuciano Coelho 
6733cab57aSLuciano Coelho  out:
6833cab57aSLuciano Coelho 	mutex_unlock(&wl->mutex);
6933cab57aSLuciano Coelho 	return count;
7033cab57aSLuciano Coelho }
7133cab57aSLuciano Coelho 
7286f1ea9dSYueHaibing static DEVICE_ATTR_RW(bt_coex_state);
7333cab57aSLuciano Coelho 
hw_pg_ver_show(struct device * dev,struct device_attribute * attr,char * buf)7486f1ea9dSYueHaibing static ssize_t hw_pg_ver_show(struct device *dev,
7533cab57aSLuciano Coelho 			      struct device_attribute *attr,
7633cab57aSLuciano Coelho 			      char *buf)
7733cab57aSLuciano Coelho {
7833cab57aSLuciano Coelho 	struct wl1271 *wl = dev_get_drvdata(dev);
7933cab57aSLuciano Coelho 	ssize_t len;
8033cab57aSLuciano Coelho 
8133cab57aSLuciano Coelho 	len = PAGE_SIZE;
8233cab57aSLuciano Coelho 
8333cab57aSLuciano Coelho 	mutex_lock(&wl->mutex);
8433cab57aSLuciano Coelho 	if (wl->hw_pg_ver >= 0)
8533cab57aSLuciano Coelho 		len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
8633cab57aSLuciano Coelho 	else
8733cab57aSLuciano Coelho 		len = snprintf(buf, len, "n/a\n");
8833cab57aSLuciano Coelho 	mutex_unlock(&wl->mutex);
8933cab57aSLuciano Coelho 
9033cab57aSLuciano Coelho 	return len;
9133cab57aSLuciano Coelho }
9233cab57aSLuciano Coelho 
9386f1ea9dSYueHaibing static DEVICE_ATTR_RO(hw_pg_ver);
9433cab57aSLuciano Coelho 
wl1271_sysfs_read_fwlog(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buffer,loff_t pos,size_t count)9533cab57aSLuciano Coelho static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
9633cab57aSLuciano Coelho 				       struct bin_attribute *bin_attr,
9733cab57aSLuciano Coelho 				       char *buffer, loff_t pos, size_t count)
9833cab57aSLuciano Coelho {
993b52cf62STian Tao 	struct device *dev = kobj_to_dev(kobj);
10033cab57aSLuciano Coelho 	struct wl1271 *wl = dev_get_drvdata(dev);
10133cab57aSLuciano Coelho 	ssize_t len;
10233cab57aSLuciano Coelho 	int ret;
10333cab57aSLuciano Coelho 
10433cab57aSLuciano Coelho 	ret = mutex_lock_interruptible(&wl->mutex);
10533cab57aSLuciano Coelho 	if (ret < 0)
10633cab57aSLuciano Coelho 		return -ERESTARTSYS;
10733cab57aSLuciano Coelho 
10833cab57aSLuciano Coelho 	/* Check if the fwlog is still valid */
10933cab57aSLuciano Coelho 	if (wl->fwlog_size < 0) {
11033cab57aSLuciano Coelho 		mutex_unlock(&wl->mutex);
11133cab57aSLuciano Coelho 		return 0;
11233cab57aSLuciano Coelho 	}
11333cab57aSLuciano Coelho 
11433cab57aSLuciano Coelho 	/* Seeking is not supported - old logs are not kept. Disregard pos. */
115c8e49556SSilvan Jegen 	len = min_t(size_t, count, wl->fwlog_size);
11633cab57aSLuciano Coelho 	wl->fwlog_size -= len;
11733cab57aSLuciano Coelho 	memcpy(buffer, wl->fwlog, len);
11833cab57aSLuciano Coelho 
11933cab57aSLuciano Coelho 	/* Make room for new messages */
12033cab57aSLuciano Coelho 	memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
12133cab57aSLuciano Coelho 
12233cab57aSLuciano Coelho 	mutex_unlock(&wl->mutex);
12333cab57aSLuciano Coelho 
12433cab57aSLuciano Coelho 	return len;
12533cab57aSLuciano Coelho }
12633cab57aSLuciano Coelho 
1274f2949feSBhumika Goyal static const struct bin_attribute fwlog_attr = {
1282ef00c53SJoe Perches 	.attr = { .name = "fwlog", .mode = 0400 },
12933cab57aSLuciano Coelho 	.read = wl1271_sysfs_read_fwlog,
13033cab57aSLuciano Coelho };
13133cab57aSLuciano Coelho 
wlcore_sysfs_init(struct wl1271 * wl)13233cab57aSLuciano Coelho int wlcore_sysfs_init(struct wl1271 *wl)
13333cab57aSLuciano Coelho {
13433cab57aSLuciano Coelho 	int ret;
13533cab57aSLuciano Coelho 
13633cab57aSLuciano Coelho 	/* Create sysfs file to control bt coex state */
13733cab57aSLuciano Coelho 	ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
13833cab57aSLuciano Coelho 	if (ret < 0) {
13933cab57aSLuciano Coelho 		wl1271_error("failed to create sysfs file bt_coex_state");
14033cab57aSLuciano Coelho 		goto out;
14133cab57aSLuciano Coelho 	}
14233cab57aSLuciano Coelho 
14333cab57aSLuciano Coelho 	/* Create sysfs file to get HW PG version */
14433cab57aSLuciano Coelho 	ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
14533cab57aSLuciano Coelho 	if (ret < 0) {
14633cab57aSLuciano Coelho 		wl1271_error("failed to create sysfs file hw_pg_ver");
14733cab57aSLuciano Coelho 		goto out_bt_coex_state;
14833cab57aSLuciano Coelho 	}
14933cab57aSLuciano Coelho 
15033cab57aSLuciano Coelho 	/* Create sysfs file for the FW log */
15133cab57aSLuciano Coelho 	ret = device_create_bin_file(wl->dev, &fwlog_attr);
15233cab57aSLuciano Coelho 	if (ret < 0) {
15333cab57aSLuciano Coelho 		wl1271_error("failed to create sysfs file fwlog");
15433cab57aSLuciano Coelho 		goto out_hw_pg_ver;
15533cab57aSLuciano Coelho 	}
15633cab57aSLuciano Coelho 
15733cab57aSLuciano Coelho 	goto out;
15833cab57aSLuciano Coelho 
15933cab57aSLuciano Coelho out_hw_pg_ver:
16033cab57aSLuciano Coelho 	device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
16133cab57aSLuciano Coelho 
16233cab57aSLuciano Coelho out_bt_coex_state:
16333cab57aSLuciano Coelho 	device_remove_file(wl->dev, &dev_attr_bt_coex_state);
16433cab57aSLuciano Coelho 
16533cab57aSLuciano Coelho out:
16633cab57aSLuciano Coelho 	return ret;
16733cab57aSLuciano Coelho }
16833cab57aSLuciano Coelho 
wlcore_sysfs_free(struct wl1271 * wl)16933cab57aSLuciano Coelho void wlcore_sysfs_free(struct wl1271 *wl)
17033cab57aSLuciano Coelho {
17133cab57aSLuciano Coelho 	device_remove_bin_file(wl->dev, &fwlog_attr);
17233cab57aSLuciano Coelho 
17333cab57aSLuciano Coelho 	device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
17433cab57aSLuciano Coelho 
17533cab57aSLuciano Coelho 	device_remove_file(wl->dev, &dev_attr_bt_coex_state);
17633cab57aSLuciano Coelho }
177