Commit 2d651289 authored by Alexander Shishkin's avatar Alexander Shishkin Committed by Greg Kroah-Hartman

usb: chipidea: convert debug entries in sysfs to debugfs

Currently, we have a bunch of files in sysfs that display all sorts of
debugging information for the device controller, so they have to move to
debugfs where they belong. The "registers" interface have been removed,
since it doesn't fit into the current driver design as is and it's hardly
a good idea to touch the registers from userspace anyway.
Signed-off-by: default avatarAlexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 69b7e8d3
......@@ -129,6 +129,7 @@ struct hw_bank {
* @vbus_active: is VBUS active
* @transceiver: pointer to USB PHY, if any
* @hcd: pointer to usb_hcd for ehci host driver
* @debugfs: root dentry for this controller in debugfs
*/
struct ci13xxx {
struct device *dev;
......@@ -164,6 +165,7 @@ struct ci13xxx {
bool global_phy;
struct usb_phy *transceiver;
struct usb_hcd *hcd;
struct dentry *debugfs;
};
static inline struct ci_role_driver *ci_role(struct ci13xxx *ci)
......
......@@ -2,6 +2,9 @@
#include <linux/device.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
......@@ -11,223 +14,113 @@
#include "debug.h"
/**
* hw_register_read: reads all device registers (execute without interruption)
* @buf: destination buffer
* @size: buffer size
*
* This function returns number of registers read
* ci_device_show: prints information about device capabilities and status
*/
static size_t hw_register_read(struct ci13xxx *ci, u32 *buf, size_t size)
static int ci_device_show(struct seq_file *s, void *data)
{
unsigned i;
if (size > ci->hw_bank.size)
size = ci->hw_bank.size;
for (i = 0; i < size; i++)
buf[i] = hw_read(ci, i * sizeof(u32), ~0);
return size;
}
/**
* hw_register_write: writes to register
* @addr: register address
* @data: register value
*
* This function returns an error code
*/
static int hw_register_write(struct ci13xxx *ci, u16 addr, u32 data)
{
/* align */
addr /= sizeof(u32);
struct ci13xxx *ci = s->private;
struct usb_gadget *gadget = &ci->gadget;
if (addr >= ci->hw_bank.size)
return -EINVAL;
seq_printf(s, "speed = %d\n", gadget->speed);
seq_printf(s, "max_speed = %d\n", gadget->max_speed);
seq_printf(s, "is_otg = %d\n", gadget->is_otg);
seq_printf(s, "is_a_peripheral = %d\n", gadget->is_a_peripheral);
seq_printf(s, "b_hnp_enable = %d\n", gadget->b_hnp_enable);
seq_printf(s, "a_hnp_support = %d\n", gadget->a_hnp_support);
seq_printf(s, "a_alt_hnp_support = %d\n", gadget->a_alt_hnp_support);
seq_printf(s, "name = %s\n",
(gadget->name ? gadget->name : ""));
if (!ci->driver)
return 0;
/* align */
addr *= sizeof(u32);
seq_printf(s, "gadget function = %s\n",
(ci->driver->function ? ci->driver->function : ""));
seq_printf(s, "gadget max speed = %d\n", ci->driver->max_speed);
hw_write(ci, addr, ~0, data);
return 0;
}
/**
* hw_intr_clear: disables interrupt & clears interrupt status (execute without
* interruption)
* @n: interrupt bit
*
* This function returns an error code
*/
static int hw_intr_clear(struct ci13xxx *ci, int n)
static int ci_device_open(struct inode *inode, struct file *file)
{
if (n >= REG_BITS)
return -EINVAL;
hw_write(ci, OP_USBINTR, BIT(n), 0);
hw_write(ci, OP_USBSTS, BIT(n), BIT(n));
return 0;
return single_open(file, ci_device_show, inode->i_private);
}
/**
* hw_intr_force: enables interrupt & forces interrupt status (execute without
* interruption)
* @n: interrupt bit
*
* This function returns an error code
*/
static int hw_intr_force(struct ci13xxx *ci, int n)
{
if (n >= REG_BITS)
return -EINVAL;
hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, TESTMODE_FORCE);
hw_write(ci, OP_USBINTR, BIT(n), BIT(n));
hw_write(ci, OP_USBSTS, BIT(n), BIT(n));
hw_write(ci, CAP_TESTMODE, TESTMODE_FORCE, 0);
return 0;
}
/**
* show_device: prints information about device capabilities and status
*
* Check "device.h" for details
*/
static ssize_t show_device(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
struct usb_gadget *gadget = &ci->gadget;
int n = 0;
if (attr == NULL || buf == NULL) {
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
return 0;
}
n += scnprintf(buf + n, PAGE_SIZE - n, "speed = %d\n",
gadget->speed);
n += scnprintf(buf + n, PAGE_SIZE - n, "max_speed = %d\n",
gadget->max_speed);
n += scnprintf(buf + n, PAGE_SIZE - n, "is_otg = %d\n",
gadget->is_otg);
n += scnprintf(buf + n, PAGE_SIZE - n, "is_a_peripheral = %d\n",
gadget->is_a_peripheral);
n += scnprintf(buf + n, PAGE_SIZE - n, "b_hnp_enable = %d\n",
gadget->b_hnp_enable);
n += scnprintf(buf + n, PAGE_SIZE - n, "a_hnp_support = %d\n",
gadget->a_hnp_support);
n += scnprintf(buf + n, PAGE_SIZE - n, "a_alt_hnp_support = %d\n",
gadget->a_alt_hnp_support);
n += scnprintf(buf + n, PAGE_SIZE - n, "name = %s\n",
(gadget->name ? gadget->name : ""));
return n;
}
static DEVICE_ATTR(device, S_IRUSR, show_device, NULL);
/**
* show_driver: prints information about attached gadget (if any)
*
* Check "device.h" for details
*/
static ssize_t show_driver(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
struct usb_gadget_driver *driver = ci->driver;
int n = 0;
if (attr == NULL || buf == NULL) {
dev_err(dev, "[%s] EINVAL\n", __func__);
return 0;
}
if (driver == NULL)
return scnprintf(buf, PAGE_SIZE,
"There is no gadget attached!\n");
n += scnprintf(buf + n, PAGE_SIZE - n, "function = %s\n",
(driver->function ? driver->function : ""));
n += scnprintf(buf + n, PAGE_SIZE - n, "max speed = %d\n",
driver->max_speed);
return n;
}
static DEVICE_ATTR(driver, S_IRUSR, show_driver, NULL);
static const struct file_operations ci_device_fops = {
.open = ci_device_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* show_port_test: reads port test mode
*
* Check "device.h" for details
* ci_port_test_show: reads port test mode
*/
static ssize_t show_port_test(struct device *dev,
struct device_attribute *attr, char *buf)
static int ci_port_test_show(struct seq_file *s, void *data)
{
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
struct ci13xxx *ci = s->private;
unsigned long flags;
unsigned mode;
if (attr == NULL || buf == NULL) {
dev_err(ci->dev, "EINVAL\n");
return 0;
}
spin_lock_irqsave(&ci->lock, flags);
mode = hw_port_test_get(ci);
spin_unlock_irqrestore(&ci->lock, flags);
return scnprintf(buf, PAGE_SIZE, "mode = %u\n", mode);
seq_printf(s, "mode = %u\n", mode);
return 0;
}
/**
* store_port_test: writes port test mode
*
* Check "device.h" for details
* ci_port_test_write: writes port test mode
*/
static ssize_t store_port_test(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
struct seq_file *s = file->private_data;
struct ci13xxx *ci = s->private;
unsigned long flags;
unsigned mode;
char buf[32];
int ret;
if (attr == NULL || buf == NULL) {
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
goto done;
}
if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (sscanf(buf, "%u", &mode) != 1) {
dev_err(ci->dev, "<mode>: set port test mode");
goto done;
}
if (sscanf(buf, "%u", &mode) != 1)
return -EINVAL;
spin_lock_irqsave(&ci->lock, flags);
if (hw_port_test_set(ci, mode))
dev_err(ci->dev, "invalid mode\n");
ret = hw_port_test_set(ci, mode);
spin_unlock_irqrestore(&ci->lock, flags);
done:
return count;
return ret ? ret : count;
}
static DEVICE_ATTR(port_test, S_IRUSR | S_IWUSR,
show_port_test, store_port_test);
static int ci_port_test_open(struct inode *inode, struct file *file)
{
return single_open(file, ci_port_test_show, inode->i_private);
}
static const struct file_operations ci_port_test_fops = {
.open = ci_port_test_open,
.write = ci_port_test_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* show_qheads: DMA contents of all queue heads
*
* Check "device.h" for details
* ci_qheads_show: DMA contents of all queue heads
*/
static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
char *buf)
static int ci_qheads_show(struct seq_file *s, void *data)
{
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
struct ci13xxx *ci = s->private;
unsigned long flags;
unsigned i, j, n = 0;
unsigned i, j;
if (attr == NULL || buf == NULL) {
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
if (ci->role != CI_ROLE_GADGET) {
seq_printf(s, "not in gadget mode\n");
return 0;
}
......@@ -236,197 +129,119 @@ static ssize_t show_qheads(struct device *dev, struct device_attribute *attr,
struct ci13xxx_ep *mEpRx = &ci->ci13xxx_ep[i];
struct ci13xxx_ep *mEpTx =
&ci->ci13xxx_ep[i + ci->hw_ep_max/2];
n += scnprintf(buf + n, PAGE_SIZE - n,
"EP=%02i: RX=%08X TX=%08X\n",
i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++) {
n += scnprintf(buf + n, PAGE_SIZE - n,
" %04X: %08X %08X\n", j,
*((u32 *)mEpRx->qh.ptr + j),
*((u32 *)mEpTx->qh.ptr + j));
}
seq_printf(s, "EP=%02i: RX=%08X TX=%08X\n",
i, (u32)mEpRx->qh.dma, (u32)mEpTx->qh.dma);
for (j = 0; j < (sizeof(struct ci13xxx_qh)/sizeof(u32)); j++)
seq_printf(s, " %04X: %08X %08X\n", j,
*((u32 *)mEpRx->qh.ptr + j),
*((u32 *)mEpTx->qh.ptr + j));
}
spin_unlock_irqrestore(&ci->lock, flags);
return n;
return 0;
}
static DEVICE_ATTR(qheads, S_IRUSR, show_qheads, NULL);
/**
* show_registers: dumps all registers
*
* Check "device.h" for details
*/
#define DUMP_ENTRIES 512
static ssize_t show_registers(struct device *dev,
struct device_attribute *attr, char *buf)
static int ci_qheads_open(struct inode *inode, struct file *file)
{
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long flags;
u32 *dump;
unsigned i, k, n = 0;
if (attr == NULL || buf == NULL) {
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
return 0;
}
dump = kmalloc(sizeof(u32) * DUMP_ENTRIES, GFP_KERNEL);
if (!dump) {
dev_err(ci->dev, "%s: out of memory\n", __func__);
return 0;
}
spin_lock_irqsave(&ci->lock, flags);
k = hw_register_read(ci, dump, DUMP_ENTRIES);
spin_unlock_irqrestore(&ci->lock, flags);
for (i = 0; i < k; i++) {
n += scnprintf(buf + n, PAGE_SIZE - n,
"reg[0x%04X] = 0x%08X\n",
i * (unsigned)sizeof(u32), dump[i]);
}
kfree(dump);
return n;
return single_open(file, ci_qheads_show, inode->i_private);
}
/**
* store_registers: writes value to register address
*
* Check "device.h" for details
*/
static ssize_t store_registers(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
unsigned long addr, data, flags;
if (attr == NULL || buf == NULL) {
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
goto done;
}
if (sscanf(buf, "%li %li", &addr, &data) != 2) {
dev_err(ci->dev,
"<addr> <data>: write data to register address\n");
goto done;
}
spin_lock_irqsave(&ci->lock, flags);
if (hw_register_write(ci, addr, data))
dev_err(ci->dev, "invalid address range\n");
spin_unlock_irqrestore(&ci->lock, flags);
done:
return count;
}
static DEVICE_ATTR(registers, S_IRUSR | S_IWUSR,
show_registers, store_registers);
static const struct file_operations ci_qheads_fops = {
.open = ci_qheads_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* show_requests: DMA contents of all requests currently queued (all endpts)
*
* Check "device.h" for details
* ci_requests_show: DMA contents of all requests currently queued (all endpts)
*/
static ssize_t show_requests(struct device *dev, struct device_attribute *attr,
char *buf)
static int ci_requests_show(struct seq_file *s, void *data)
{
struct ci13xxx *ci = container_of(dev, struct ci13xxx, gadget.dev);
struct ci13xxx *ci = s->private;
unsigned long flags;
struct list_head *ptr = NULL;
struct ci13xxx_req *req = NULL;
unsigned i, j, n = 0, qSize = sizeof(struct ci13xxx_td)/sizeof(u32);
unsigned i, j, qsize = sizeof(struct ci13xxx_td)/sizeof(u32);
if (attr == NULL || buf == NULL) {
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
if (ci->role != CI_ROLE_GADGET) {
seq_printf(s, "not in gadget mode\n");
return 0;
}
spin_lock_irqsave(&ci->lock, flags);
for (i = 0; i < ci->hw_ep_max; i++)
list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue)
{
list_for_each(ptr, &ci->ci13xxx_ep[i].qh.queue) {
req = list_entry(ptr, struct ci13xxx_req, queue);
n += scnprintf(buf + n, PAGE_SIZE - n,
"EP=%02i: TD=%08X %s\n",
i % ci->hw_ep_max/2, (u32)req->dma,
((i < ci->hw_ep_max/2) ? "RX" : "TX"));
seq_printf(s, "EP=%02i: TD=%08X %s\n",
i % ci->hw_ep_max/2, (u32)req->dma,
((i < ci->hw_ep_max/2) ? "RX" : "TX"));
for (j = 0; j < qSize; j++)
n += scnprintf(buf + n, PAGE_SIZE - n,
" %04X: %08X\n", j,
*((u32 *)req->ptr + j));
for (j = 0; j < qsize; j++)
seq_printf(s, " %04X: %08X\n", j,
*((u32 *)req->ptr + j));
}
spin_unlock_irqrestore(&ci->lock, flags);
return n;
return 0;
}
static int ci_requests_open(struct inode *inode, struct file *file)
{
return single_open(file, ci_requests_show, inode->i_private);
}
static DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL);
static const struct file_operations ci_requests_fops = {
.open = ci_requests_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* dbg_create_files: initializes the attribute interface
* @dev: device
* @ci: device
*
* This function returns an error code
*/
int dbg_create_files(struct device *dev)
int dbg_create_files(struct ci13xxx *ci)
{
int retval = 0;
if (dev == NULL)
return -EINVAL;
retval = device_create_file(dev, &dev_attr_device);
if (retval)
goto done;
retval = device_create_file(dev, &dev_attr_driver);
if (retval)
goto rm_device;
retval = device_create_file(dev, &dev_attr_port_test);
if (retval)
goto rm_driver;
retval = device_create_file(dev, &dev_attr_qheads);
if (retval)
goto rm_port_test;
retval = device_create_file(dev, &dev_attr_registers);
if (retval)
goto rm_qheads;
retval = device_create_file(dev, &dev_attr_requests);
if (retval)
goto rm_registers;
return 0;
rm_registers:
device_remove_file(dev, &dev_attr_registers);
rm_qheads:
device_remove_file(dev, &dev_attr_qheads);
rm_port_test:
device_remove_file(dev, &dev_attr_port_test);
rm_driver:
device_remove_file(dev, &dev_attr_driver);
rm_device:
device_remove_file(dev, &dev_attr_device);
done:
return retval;
struct dentry *dent;
ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL);
if (!ci->debugfs)
return -ENOMEM;
dent = debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
&ci_device_fops);
if (!dent)
goto err;
dent = debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs,
ci, &ci_port_test_fops);
if (!dent)
goto err;
dent = debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci,
&ci_qheads_fops);
if (!dent)
goto err;
dent = debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci,
&ci_requests_fops);
if (dent)
return 0;
err:
debugfs_remove_recursive(ci->debugfs);
return -ENOMEM;
}
/**
* dbg_remove_files: destroys the attribute interface
* @dev: device
*
* This function returns an error code
* @ci: device
*/
int dbg_remove_files(struct device *dev)
void dbg_remove_files(struct ci13xxx *ci)
{
if (dev == NULL)
return -EINVAL;
device_remove_file(dev, &dev_attr_requests);
device_remove_file(dev, &dev_attr_registers);
device_remove_file(dev, &dev_attr_qheads);
device_remove_file(dev, &dev_attr_port_test);
device_remove_file(dev, &dev_attr_driver);
device_remove_file(dev, &dev_attr_device);
return 0;
debugfs_remove_recursive(ci->debugfs);
}
......@@ -14,17 +14,16 @@
#define __DRIVERS_USB_CHIPIDEA_DEBUG_H
#ifdef CONFIG_USB_CHIPIDEA_DEBUG
int dbg_create_files(struct device *dev);
int dbg_remove_files(struct device *dev);
int dbg_create_files(struct ci13xxx *ci);
void dbg_remove_files(struct ci13xxx *ci);
#else
static inline int dbg_create_files(struct device *dev)
static inline int dbg_create_files(struct ci13xxx *ci)
{
return 0;
}
static inline int dbg_remove_files(struct device *dev)
static inline void dbg_remove_files(struct ci13xxx *ci)
{
return 0;
}
#endif
......
......@@ -1697,7 +1697,7 @@ static int udc_start(struct ci13xxx *ci)
goto put_transceiver;
}
retval = dbg_create_files(ci->dev);
retval = dbg_create_files(ci);
if (retval)
goto unreg_device;
......@@ -1726,7 +1726,7 @@ static int udc_start(struct ci13xxx *ci)
dev_err(dev, "error = %i\n", retval);
remove_dbg:
dbg_remove_files(ci->dev);
dbg_remove_files(ci);
unreg_device:
device_unregister(&ci->gadget.dev);
put_transceiver:
......@@ -1763,7 +1763,7 @@ static void udc_stop(struct ci13xxx *ci)
if (ci->global_phy)
usb_put_phy(ci->transceiver);
}
dbg_remove_files(ci->dev);
dbg_remove_files(ci);
device_unregister(&ci->gadget.dev);
/* my kobject is dynamic, I swear! */
memset(&ci->gadget, 0, sizeof(ci->gadget));
......
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