mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 07:44:49 +01:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
380 lines
9.5 KiB
C
380 lines
9.5 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
// Copyright(c) 2017-2019 Intel Corporation.
|
|
|
|
#include <linux/cleanup.h>
|
|
#include <linux/device.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/soundwire/sdw.h>
|
|
#include <linux/soundwire/sdw_registers.h>
|
|
#include <linux/string_choices.h>
|
|
#include "bus.h"
|
|
|
|
static struct dentry *sdw_debugfs_root;
|
|
|
|
void sdw_bus_debugfs_init(struct sdw_bus *bus)
|
|
{
|
|
char name[16];
|
|
|
|
if (!sdw_debugfs_root)
|
|
return;
|
|
|
|
/* create the debugfs master-N */
|
|
snprintf(name, sizeof(name), "master-%d-%d", bus->controller_id, bus->link_id);
|
|
bus->debugfs = debugfs_create_dir(name, sdw_debugfs_root);
|
|
}
|
|
|
|
void sdw_bus_debugfs_exit(struct sdw_bus *bus)
|
|
{
|
|
debugfs_remove_recursive(bus->debugfs);
|
|
}
|
|
|
|
#define RD_BUF (3 * PAGE_SIZE)
|
|
|
|
static ssize_t sdw_sprintf(struct sdw_slave *slave,
|
|
char *buf, size_t pos, unsigned int reg)
|
|
{
|
|
int value;
|
|
|
|
value = sdw_read_no_pm(slave, reg);
|
|
|
|
if (value < 0)
|
|
return scnprintf(buf + pos, RD_BUF - pos, "%3x\tXX\n", reg);
|
|
else
|
|
return scnprintf(buf + pos, RD_BUF - pos,
|
|
"%3x\t%2x\n", reg, value);
|
|
}
|
|
|
|
static int sdw_slave_reg_show(struct seq_file *s_file, void *data)
|
|
{
|
|
struct sdw_slave *slave = s_file->private;
|
|
ssize_t ret;
|
|
int i, j;
|
|
|
|
char *buf __free(kfree) = kzalloc(RD_BUF, GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
ret = pm_runtime_get_sync(&slave->dev);
|
|
if (ret < 0 && ret != -EACCES) {
|
|
pm_runtime_put_noidle(&slave->dev);
|
|
return ret;
|
|
}
|
|
|
|
ret = scnprintf(buf, RD_BUF, "Register Value\n");
|
|
|
|
/* DP0 non-banked registers */
|
|
ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP0\n");
|
|
for (i = SDW_DP0_INT; i <= SDW_DP0_PREPARECTRL; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
|
|
/* DP0 Bank 0 registers */
|
|
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
|
|
ret += sdw_sprintf(slave, buf, ret, SDW_DP0_CHANNELEN);
|
|
for (i = SDW_DP0_SAMPLECTRL1; i <= SDW_DP0_LANECTRL; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
|
|
/* DP0 Bank 1 registers */
|
|
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
|
|
ret += sdw_sprintf(slave, buf, ret,
|
|
SDW_DP0_CHANNELEN + SDW_BANK1_OFFSET);
|
|
for (i = SDW_DP0_SAMPLECTRL1 + SDW_BANK1_OFFSET;
|
|
i <= SDW_DP0_LANECTRL + SDW_BANK1_OFFSET; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
|
|
/* SCP registers */
|
|
ret += scnprintf(buf + ret, RD_BUF - ret, "\nSCP\n");
|
|
for (i = SDW_SCP_INT1; i <= SDW_SCP_BUS_CLOCK_BASE; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
for (i = SDW_SCP_SDCA_INT1; i <= SDW_SCP_SDCA_INTMASK4; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
for (i = SDW_SCP_FRAMECTRL_B0; i <= SDW_SCP_BUSCLOCK_SCALE_B0; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
for (i = SDW_SCP_FRAMECTRL_B1; i <= SDW_SCP_BUSCLOCK_SCALE_B1; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
for (i = SDW_SCP_PHY_OUT_CTRL_0; i <= SDW_SCP_PHY_OUT_CTRL_7; i++)
|
|
ret += sdw_sprintf(slave, buf, ret, i);
|
|
|
|
|
|
/*
|
|
* SCP Bank 0/1 registers are read-only and cannot be
|
|
* retrieved from the Slave. The Master typically keeps track
|
|
* of the current frame size so the information can be found
|
|
* in other places
|
|
*/
|
|
|
|
/* DP1..14 registers */
|
|
for (i = 1; SDW_VALID_PORT_RANGE(i); i++) {
|
|
|
|
/* DPi registers */
|
|
ret += scnprintf(buf + ret, RD_BUF - ret, "\nDP%d\n", i);
|
|
for (j = SDW_DPN_INT(i); j <= SDW_DPN_PREPARECTRL(i); j++)
|
|
ret += sdw_sprintf(slave, buf, ret, j);
|
|
|
|
/* DPi Bank0 registers */
|
|
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank0\n");
|
|
for (j = SDW_DPN_CHANNELEN_B0(i);
|
|
j <= SDW_DPN_LANECTRL_B0(i); j++)
|
|
ret += sdw_sprintf(slave, buf, ret, j);
|
|
|
|
/* DPi Bank1 registers */
|
|
ret += scnprintf(buf + ret, RD_BUF - ret, "Bank1\n");
|
|
for (j = SDW_DPN_CHANNELEN_B1(i);
|
|
j <= SDW_DPN_LANECTRL_B1(i); j++)
|
|
ret += sdw_sprintf(slave, buf, ret, j);
|
|
}
|
|
|
|
seq_printf(s_file, "%s", buf);
|
|
|
|
pm_runtime_mark_last_busy(&slave->dev);
|
|
pm_runtime_put(&slave->dev);
|
|
|
|
return 0;
|
|
}
|
|
DEFINE_SHOW_ATTRIBUTE(sdw_slave_reg);
|
|
|
|
#define MAX_CMD_BYTES (1024 * 1024)
|
|
|
|
static int cmd;
|
|
static int cmd_type;
|
|
static u32 start_addr;
|
|
static size_t num_bytes;
|
|
static u8 read_buffer[MAX_CMD_BYTES];
|
|
static char *firmware_file;
|
|
|
|
static int set_command(void *data, u64 value)
|
|
{
|
|
struct sdw_slave *slave = data;
|
|
|
|
if (value > 1)
|
|
return -EINVAL;
|
|
|
|
/* Userspace changed the hardware state behind the kernel's back */
|
|
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
|
|
|
|
dev_dbg(&slave->dev, "command: %s\n", str_read_write(value));
|
|
cmd = value;
|
|
|
|
return 0;
|
|
}
|
|
DEFINE_DEBUGFS_ATTRIBUTE(set_command_fops, NULL,
|
|
set_command, "%llu\n");
|
|
|
|
static int set_command_type(void *data, u64 value)
|
|
{
|
|
struct sdw_slave *slave = data;
|
|
|
|
if (value > 1)
|
|
return -EINVAL;
|
|
|
|
/* Userspace changed the hardware state behind the kernel's back */
|
|
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
|
|
|
|
dev_dbg(&slave->dev, "command type: %s\n", value ? "BRA" : "Column0");
|
|
|
|
cmd_type = (int)value;
|
|
|
|
return 0;
|
|
}
|
|
DEFINE_DEBUGFS_ATTRIBUTE(set_command_type_fops, NULL,
|
|
set_command_type, "%llu\n");
|
|
|
|
static int set_start_address(void *data, u64 value)
|
|
{
|
|
struct sdw_slave *slave = data;
|
|
|
|
/* Userspace changed the hardware state behind the kernel's back */
|
|
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
|
|
|
|
dev_dbg(&slave->dev, "start address %#llx\n", value);
|
|
|
|
start_addr = value;
|
|
|
|
return 0;
|
|
}
|
|
DEFINE_DEBUGFS_ATTRIBUTE(set_start_address_fops, NULL,
|
|
set_start_address, "%llu\n");
|
|
|
|
static int set_num_bytes(void *data, u64 value)
|
|
{
|
|
struct sdw_slave *slave = data;
|
|
|
|
if (value == 0 || value > MAX_CMD_BYTES)
|
|
return -EINVAL;
|
|
|
|
/* Userspace changed the hardware state behind the kernel's back */
|
|
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
|
|
|
|
dev_dbg(&slave->dev, "number of bytes %lld\n", value);
|
|
|
|
num_bytes = value;
|
|
|
|
return 0;
|
|
}
|
|
DEFINE_DEBUGFS_ATTRIBUTE(set_num_bytes_fops, NULL,
|
|
set_num_bytes, "%llu\n");
|
|
|
|
static int do_bpt_sequence(struct sdw_slave *slave, bool write, u8 *buffer)
|
|
{
|
|
struct sdw_bpt_msg msg = {0};
|
|
struct sdw_bpt_section *sec;
|
|
|
|
sec = kzalloc_objs(*sec, 1);
|
|
if (!sec)
|
|
return -ENOMEM;
|
|
msg.sections = 1;
|
|
|
|
sec[0].addr = start_addr;
|
|
sec[0].len = num_bytes;
|
|
|
|
msg.sec = sec;
|
|
msg.dev_num = slave->dev_num;
|
|
if (write)
|
|
msg.flags = SDW_MSG_FLAG_WRITE;
|
|
else
|
|
msg.flags = SDW_MSG_FLAG_READ;
|
|
sec[0].buf = buffer;
|
|
|
|
return sdw_bpt_send_sync(slave->bus, slave, &msg);
|
|
}
|
|
|
|
static int cmd_go(void *data, u64 value)
|
|
{
|
|
const struct firmware *fw = NULL;
|
|
struct sdw_slave *slave = data;
|
|
ktime_t start_t;
|
|
ktime_t finish_t;
|
|
int ret;
|
|
|
|
if (value != 1)
|
|
return -EINVAL;
|
|
|
|
/* one last check */
|
|
if (start_addr > SDW_REG_MAX ||
|
|
num_bytes == 0 || num_bytes > MAX_CMD_BYTES)
|
|
return -EINVAL;
|
|
|
|
ret = pm_runtime_get_sync(&slave->dev);
|
|
if (ret < 0 && ret != -EACCES) {
|
|
pm_runtime_put_noidle(&slave->dev);
|
|
return ret;
|
|
}
|
|
|
|
if (cmd == 0) {
|
|
ret = request_firmware(&fw, firmware_file, &slave->dev);
|
|
if (ret < 0) {
|
|
dev_err(&slave->dev, "firmware %s not found\n", firmware_file);
|
|
goto out;
|
|
}
|
|
if (fw->size < num_bytes) {
|
|
dev_err(&slave->dev,
|
|
"firmware %s: firmware size %zd, desired %zd\n",
|
|
firmware_file, fw->size, num_bytes);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* Userspace changed the hardware state behind the kernel's back */
|
|
add_taint(TAINT_USER, LOCKDEP_STILL_OK);
|
|
|
|
dev_dbg(&slave->dev, "starting command\n");
|
|
start_t = ktime_get();
|
|
|
|
if (cmd == 0) {
|
|
if (cmd_type)
|
|
ret = do_bpt_sequence(slave, true, (u8 *)fw->data);
|
|
else
|
|
ret = sdw_nwrite_no_pm(slave, start_addr, num_bytes, fw->data);
|
|
} else {
|
|
memset(read_buffer, 0, sizeof(read_buffer));
|
|
|
|
if (cmd_type)
|
|
ret = do_bpt_sequence(slave, false, read_buffer);
|
|
else
|
|
ret = sdw_nread_no_pm(slave, start_addr, num_bytes, read_buffer);
|
|
}
|
|
|
|
finish_t = ktime_get();
|
|
|
|
dev_dbg(&slave->dev, "command completed, num_byte %zu status %d, time %lld ms\n",
|
|
num_bytes, ret, div_u64(finish_t - start_t, NSEC_PER_MSEC));
|
|
|
|
out:
|
|
if (fw)
|
|
release_firmware(fw);
|
|
|
|
pm_runtime_mark_last_busy(&slave->dev);
|
|
pm_runtime_put(&slave->dev);
|
|
|
|
return ret;
|
|
}
|
|
DEFINE_DEBUGFS_ATTRIBUTE(cmd_go_fops, NULL,
|
|
cmd_go, "%llu\n");
|
|
|
|
#define MAX_LINE_LEN 128
|
|
|
|
static int read_buffer_show(struct seq_file *s_file, void *data)
|
|
{
|
|
char buf[MAX_LINE_LEN];
|
|
int i;
|
|
|
|
if (num_bytes == 0 || num_bytes > MAX_CMD_BYTES)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < num_bytes; i++) {
|
|
scnprintf(buf, MAX_LINE_LEN, "address %#x val 0x%02x\n",
|
|
start_addr + i, read_buffer[i]);
|
|
seq_printf(s_file, "%s", buf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
DEFINE_SHOW_ATTRIBUTE(read_buffer);
|
|
|
|
void sdw_slave_debugfs_init(struct sdw_slave *slave)
|
|
{
|
|
struct dentry *master;
|
|
struct dentry *d;
|
|
char name[32];
|
|
|
|
master = slave->bus->debugfs;
|
|
|
|
/* create the debugfs slave-name */
|
|
snprintf(name, sizeof(name), "%s", dev_name(&slave->dev));
|
|
d = debugfs_create_dir(name, master);
|
|
|
|
debugfs_create_file("registers", 0400, d, slave, &sdw_slave_reg_fops);
|
|
|
|
/* interface to send arbitrary commands */
|
|
debugfs_create_file("command", 0200, d, slave, &set_command_fops);
|
|
debugfs_create_file("command_type", 0200, d, slave, &set_command_type_fops);
|
|
debugfs_create_file("start_address", 0200, d, slave, &set_start_address_fops);
|
|
debugfs_create_file("num_bytes", 0200, d, slave, &set_num_bytes_fops);
|
|
debugfs_create_file("go", 0200, d, slave, &cmd_go_fops);
|
|
|
|
debugfs_create_file("read_buffer", 0400, d, slave, &read_buffer_fops);
|
|
firmware_file = NULL;
|
|
debugfs_create_str("firmware_file", 0200, d, &firmware_file);
|
|
|
|
slave->debugfs = d;
|
|
}
|
|
|
|
void sdw_slave_debugfs_exit(struct sdw_slave *slave)
|
|
{
|
|
debugfs_remove_recursive(slave->debugfs);
|
|
}
|
|
|
|
void sdw_debugfs_init(void)
|
|
{
|
|
sdw_debugfs_root = debugfs_create_dir("soundwire", NULL);
|
|
}
|
|
|
|
void sdw_debugfs_exit(void)
|
|
{
|
|
debugfs_remove_recursive(sdw_debugfs_root);
|
|
}
|