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: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Alexander Shishkin 2013-03-30 12:53:51 +02:00 committed by Greg Kroah-Hartman
parent 69b7e8d34f
commit 2d6512892c
4 changed files with 153 additions and 337 deletions

View File

@ -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)

View File

@ -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);
if (addr >= ci->hw_bank.size)
return -EINVAL;
/* align */
addr *= sizeof(u32);
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)
{
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;
}
/**
* 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 ci13xxx *ci = s->private;
struct usb_gadget *gadget = &ci->gadget;
int n = 0;
if (attr == NULL || buf == NULL) {
dev_err(ci->dev, "[%s] EINVAL\n", __func__);
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;
}
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 : ""));
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);
return n;
return 0;
}
static DEVICE_ATTR(device, S_IRUSR, show_device, NULL);
static int ci_device_open(struct inode *inode, struct file *file)
{
return single_open(file, ci_device_show, inode->i_private);
}
static const struct file_operations ci_device_fops = {
.open = ci_device_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/**
* show_driver: prints information about attached gadget (if any)
*
* Check "device.h" for details
* ci_port_test_show: reads port test mode
*/
static ssize_t show_driver(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 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);
/**
* show_port_test: reads port test mode
*
* Check "device.h" for details
*/
static ssize_t show_port_test(struct device *dev,
struct device_attribute *attr, char *buf)
{
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;
}
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)
{
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 0;
}
/**
* 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)
static int ci_qheads_open(struct inode *inode, struct file *file)
{
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;
return single_open(file, ci_qheads_show, inode->i_private);
}
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 DEVICE_ATTR(requests, S_IRUSR, show_requests, NULL);
static int ci_requests_open(struct inode *inode, struct file *file)
{
return single_open(file, ci_requests_show, inode->i_private);
}
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;
struct dentry *dent;
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;
ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL);
if (!ci->debugfs)
return -ENOMEM;
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;
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);
}

View File

@ -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

View File

@ -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));