1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * This file is part of wlcore
4 *
5 * Copyright (C) 2013 Texas Instruments Inc.
6 */
7
8#include <linux/pm_runtime.h>
9
10#include "acx.h"
11#include "wlcore.h"
12#include "debug.h"
13#include "sysfs.h"
14
15static ssize_t bt_coex_state_show(struct device *dev,
16				  struct device_attribute *attr,
17				  char *buf)
18{
19	struct wl1271 *wl = dev_get_drvdata(dev);
20	ssize_t len;
21
22	len = PAGE_SIZE;
23
24	mutex_lock(&wl->mutex);
25	len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
26		       wl->sg_enabled);
27	mutex_unlock(&wl->mutex);
28
29	return len;
30
31}
32
33static ssize_t bt_coex_state_store(struct device *dev,
34				   struct device_attribute *attr,
35				   const char *buf, size_t count)
36{
37	struct wl1271 *wl = dev_get_drvdata(dev);
38	unsigned long res;
39	int ret;
40
41	ret = kstrtoul(buf, 10, &res);
42	if (ret < 0) {
43		wl1271_warning("incorrect value written to bt_coex_mode");
44		return count;
45	}
46
47	mutex_lock(&wl->mutex);
48
49	res = !!res;
50
51	if (res == wl->sg_enabled)
52		goto out;
53
54	wl->sg_enabled = res;
55
56	if (unlikely(wl->state != WLCORE_STATE_ON))
57		goto out;
58
59	ret = pm_runtime_resume_and_get(wl->dev);
60	if (ret < 0)
61		goto out;
62
63	wl1271_acx_sg_enable(wl, wl->sg_enabled);
64	pm_runtime_mark_last_busy(wl->dev);
65	pm_runtime_put_autosuspend(wl->dev);
66
67 out:
68	mutex_unlock(&wl->mutex);
69	return count;
70}
71
72static DEVICE_ATTR_RW(bt_coex_state);
73
74static ssize_t hw_pg_ver_show(struct device *dev,
75			      struct device_attribute *attr,
76			      char *buf)
77{
78	struct wl1271 *wl = dev_get_drvdata(dev);
79	ssize_t len;
80
81	len = PAGE_SIZE;
82
83	mutex_lock(&wl->mutex);
84	if (wl->hw_pg_ver >= 0)
85		len = snprintf(buf, len, "%d\n", wl->hw_pg_ver);
86	else
87		len = snprintf(buf, len, "n/a\n");
88	mutex_unlock(&wl->mutex);
89
90	return len;
91}
92
93static DEVICE_ATTR_RO(hw_pg_ver);
94
95static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj,
96				       struct bin_attribute *bin_attr,
97				       char *buffer, loff_t pos, size_t count)
98{
99	struct device *dev = kobj_to_dev(kobj);
100	struct wl1271 *wl = dev_get_drvdata(dev);
101	ssize_t len;
102	int ret;
103
104	ret = mutex_lock_interruptible(&wl->mutex);
105	if (ret < 0)
106		return -ERESTARTSYS;
107
108	/* Check if the fwlog is still valid */
109	if (wl->fwlog_size < 0) {
110		mutex_unlock(&wl->mutex);
111		return 0;
112	}
113
114	/* Seeking is not supported - old logs are not kept. Disregard pos. */
115	len = min_t(size_t, count, wl->fwlog_size);
116	wl->fwlog_size -= len;
117	memcpy(buffer, wl->fwlog, len);
118
119	/* Make room for new messages */
120	memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size);
121
122	mutex_unlock(&wl->mutex);
123
124	return len;
125}
126
127static const struct bin_attribute fwlog_attr = {
128	.attr = { .name = "fwlog", .mode = 0400 },
129	.read = wl1271_sysfs_read_fwlog,
130};
131
132int wlcore_sysfs_init(struct wl1271 *wl)
133{
134	int ret;
135
136	/* Create sysfs file to control bt coex state */
137	ret = device_create_file(wl->dev, &dev_attr_bt_coex_state);
138	if (ret < 0) {
139		wl1271_error("failed to create sysfs file bt_coex_state");
140		goto out;
141	}
142
143	/* Create sysfs file to get HW PG version */
144	ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver);
145	if (ret < 0) {
146		wl1271_error("failed to create sysfs file hw_pg_ver");
147		goto out_bt_coex_state;
148	}
149
150	/* Create sysfs file for the FW log */
151	ret = device_create_bin_file(wl->dev, &fwlog_attr);
152	if (ret < 0) {
153		wl1271_error("failed to create sysfs file fwlog");
154		goto out_hw_pg_ver;
155	}
156
157	goto out;
158
159out_hw_pg_ver:
160	device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
161
162out_bt_coex_state:
163	device_remove_file(wl->dev, &dev_attr_bt_coex_state);
164
165out:
166	return ret;
167}
168
169void wlcore_sysfs_free(struct wl1271 *wl)
170{
171	device_remove_bin_file(wl->dev, &fwlog_attr);
172
173	device_remove_file(wl->dev, &dev_attr_hw_pg_ver);
174
175	device_remove_file(wl->dev, &dev_attr_bt_coex_state);
176}
177