| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * This file is part of wlcore |
| * |
| * Copyright (C) 2013 Texas Instruments Inc. |
| */ |
| |
| #include <linux/pm_runtime.h> |
| |
| #include "acx.h" |
| #include "wlcore.h" |
| #include "debug.h" |
| #include "sysfs.h" |
| |
| static ssize_t bt_coex_state_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct wl1271 *wl = dev_get_drvdata(dev); |
| ssize_t len; |
| |
| mutex_lock(&wl->mutex); |
| len = sysfs_emit(buf, "%d\n\n0 - off\n1 - on\n", wl->sg_enabled); |
| mutex_unlock(&wl->mutex); |
| |
| return len; |
| |
| } |
| |
| static ssize_t bt_coex_state_store(struct device *dev, |
| struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| struct wl1271 *wl = dev_get_drvdata(dev); |
| unsigned long res; |
| int ret; |
| |
| ret = kstrtoul(buf, 10, &res); |
| if (ret < 0) { |
| wl1271_warning("incorrect value written to bt_coex_mode"); |
| return count; |
| } |
| |
| mutex_lock(&wl->mutex); |
| |
| res = !!res; |
| |
| if (res == wl->sg_enabled) |
| goto out; |
| |
| wl->sg_enabled = res; |
| |
| if (unlikely(wl->state != WLCORE_STATE_ON)) |
| goto out; |
| |
| ret = pm_runtime_resume_and_get(wl->dev); |
| if (ret < 0) |
| goto out; |
| |
| wl1271_acx_sg_enable(wl, wl->sg_enabled); |
| pm_runtime_mark_last_busy(wl->dev); |
| pm_runtime_put_autosuspend(wl->dev); |
| |
| out: |
| mutex_unlock(&wl->mutex); |
| return count; |
| } |
| |
| static DEVICE_ATTR_RW(bt_coex_state); |
| |
| static ssize_t hw_pg_ver_show(struct device *dev, |
| struct device_attribute *attr, |
| char *buf) |
| { |
| struct wl1271 *wl = dev_get_drvdata(dev); |
| ssize_t len; |
| |
| mutex_lock(&wl->mutex); |
| if (wl->hw_pg_ver >= 0) |
| len = sysfs_emit(buf, "%d\n", wl->hw_pg_ver); |
| else |
| len = sysfs_emit(buf, "n/a\n"); |
| mutex_unlock(&wl->mutex); |
| |
| return len; |
| } |
| |
| static DEVICE_ATTR_RO(hw_pg_ver); |
| |
| static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, |
| struct bin_attribute *bin_attr, |
| char *buffer, loff_t pos, size_t count) |
| { |
| struct device *dev = kobj_to_dev(kobj); |
| struct wl1271 *wl = dev_get_drvdata(dev); |
| ssize_t len; |
| int ret; |
| |
| ret = mutex_lock_interruptible(&wl->mutex); |
| if (ret < 0) |
| return -ERESTARTSYS; |
| |
| /* Check if the fwlog is still valid */ |
| if (wl->fwlog_size < 0) { |
| mutex_unlock(&wl->mutex); |
| return 0; |
| } |
| |
| /* Seeking is not supported - old logs are not kept. Disregard pos. */ |
| len = min_t(size_t, count, wl->fwlog_size); |
| wl->fwlog_size -= len; |
| memcpy(buffer, wl->fwlog, len); |
| |
| /* Make room for new messages */ |
| memmove(wl->fwlog, wl->fwlog + len, wl->fwlog_size); |
| |
| mutex_unlock(&wl->mutex); |
| |
| return len; |
| } |
| |
| static const struct bin_attribute fwlog_attr = { |
| .attr = { .name = "fwlog", .mode = 0400 }, |
| .read = wl1271_sysfs_read_fwlog, |
| }; |
| |
| int wlcore_sysfs_init(struct wl1271 *wl) |
| { |
| int ret; |
| |
| /* Create sysfs file to control bt coex state */ |
| ret = device_create_file(wl->dev, &dev_attr_bt_coex_state); |
| if (ret < 0) { |
| wl1271_error("failed to create sysfs file bt_coex_state"); |
| goto out; |
| } |
| |
| /* Create sysfs file to get HW PG version */ |
| ret = device_create_file(wl->dev, &dev_attr_hw_pg_ver); |
| if (ret < 0) { |
| wl1271_error("failed to create sysfs file hw_pg_ver"); |
| goto out_bt_coex_state; |
| } |
| |
| /* Create sysfs file for the FW log */ |
| ret = device_create_bin_file(wl->dev, &fwlog_attr); |
| if (ret < 0) { |
| wl1271_error("failed to create sysfs file fwlog"); |
| goto out_hw_pg_ver; |
| } |
| |
| goto out; |
| |
| out_hw_pg_ver: |
| device_remove_file(wl->dev, &dev_attr_hw_pg_ver); |
| |
| out_bt_coex_state: |
| device_remove_file(wl->dev, &dev_attr_bt_coex_state); |
| |
| out: |
| return ret; |
| } |
| |
| void wlcore_sysfs_free(struct wl1271 *wl) |
| { |
| device_remove_bin_file(wl->dev, &fwlog_attr); |
| |
| device_remove_file(wl->dev, &dev_attr_hw_pg_ver); |
| |
| device_remove_file(wl->dev, &dev_attr_bt_coex_state); |
| } |