mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
![Alexandre Bounine](/assets/img/avatar_default.png)
This patch removes an artificial RapidIO bus root device and establishes actual device hierarchy by providing reference to real parent devices. It also introduces device class for RapidIO controller devices (on-chip or an eternal bridge, known as "mport"). Existing implementation was sufficient for SoC-based platforms that have a single RapidIO controller. With introduction of devices using multiple RapidIO controllers and PCIe-to-RapidIO bridges the old scheme is very limiting or does not work at all. The implemented changes allow to properly reference platform's local RapidIO mport devices and provide device details needed for upper layers. This change to RapidIO device hierarchy does not break any known existing kernel or user space interfaces. Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com> Cc: Matt Porter <mporter@kernel.crashing.org> Cc: Li Yang <leoli@freescale.com> Cc: Kumar Gala <galak@kernel.crashing.org> Cc: Andre van Herk <andre.van.herk@prodrive-technologies.com> Cc: Stef van Os <stef.van.os@prodrive-technologies.com> Cc: Jerry Jacobs <jerry.jacobs@prodrive-technologies.com> Cc: Arno Tiemersma <arno.tiemersma@prodrive-technologies.com> Cc: Rob Landley <rob@landley.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
383 lines
8.4 KiB
C
383 lines
8.4 KiB
C
/*
|
|
* RapidIO sysfs attributes and support
|
|
*
|
|
* Copyright 2005 MontaVista Software, Inc.
|
|
* Matt Porter <mporter@kernel.crashing.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/rio.h>
|
|
#include <linux/rio_drv.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/capability.h>
|
|
|
|
#include "rio.h"
|
|
|
|
/* Sysfs support */
|
|
#define rio_config_attr(field, format_string) \
|
|
static ssize_t \
|
|
field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
|
|
{ \
|
|
struct rio_dev *rdev = to_rio_dev(dev); \
|
|
\
|
|
return sprintf(buf, format_string, rdev->field); \
|
|
} \
|
|
static DEVICE_ATTR_RO(field);
|
|
|
|
rio_config_attr(did, "0x%04x\n");
|
|
rio_config_attr(vid, "0x%04x\n");
|
|
rio_config_attr(device_rev, "0x%08x\n");
|
|
rio_config_attr(asm_did, "0x%04x\n");
|
|
rio_config_attr(asm_vid, "0x%04x\n");
|
|
rio_config_attr(asm_rev, "0x%04x\n");
|
|
rio_config_attr(destid, "0x%04x\n");
|
|
rio_config_attr(hopcount, "0x%02x\n");
|
|
|
|
static ssize_t routes_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|
{
|
|
struct rio_dev *rdev = to_rio_dev(dev);
|
|
char *str = buf;
|
|
int i;
|
|
|
|
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES(rdev->net->hport->sys_size);
|
|
i++) {
|
|
if (rdev->rswitch->route_table[i] == RIO_INVALID_ROUTE)
|
|
continue;
|
|
str +=
|
|
sprintf(str, "%04x %02x\n", i,
|
|
rdev->rswitch->route_table[i]);
|
|
}
|
|
|
|
return (str - buf);
|
|
}
|
|
static DEVICE_ATTR_RO(routes);
|
|
|
|
static ssize_t lprev_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct rio_dev *rdev = to_rio_dev(dev);
|
|
|
|
return sprintf(buf, "%s\n",
|
|
(rdev->prev) ? rio_name(rdev->prev) : "root");
|
|
}
|
|
static DEVICE_ATTR_RO(lprev);
|
|
|
|
static ssize_t lnext_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct rio_dev *rdev = to_rio_dev(dev);
|
|
char *str = buf;
|
|
int i;
|
|
|
|
if (rdev->pef & RIO_PEF_SWITCH) {
|
|
for (i = 0; i < RIO_GET_TOTAL_PORTS(rdev->swpinfo); i++) {
|
|
if (rdev->rswitch->nextdev[i])
|
|
str += sprintf(str, "%s\n",
|
|
rio_name(rdev->rswitch->nextdev[i]));
|
|
else
|
|
str += sprintf(str, "null\n");
|
|
}
|
|
}
|
|
|
|
return str - buf;
|
|
}
|
|
static DEVICE_ATTR_RO(lnext);
|
|
|
|
static ssize_t modalias_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct rio_dev *rdev = to_rio_dev(dev);
|
|
|
|
return sprintf(buf, "rapidio:v%04Xd%04Xav%04Xad%04X\n",
|
|
rdev->vid, rdev->did, rdev->asm_vid, rdev->asm_did);
|
|
}
|
|
static DEVICE_ATTR_RO(modalias);
|
|
|
|
static struct attribute *rio_dev_attrs[] = {
|
|
&dev_attr_did.attr,
|
|
&dev_attr_vid.attr,
|
|
&dev_attr_device_rev.attr,
|
|
&dev_attr_asm_did.attr,
|
|
&dev_attr_asm_vid.attr,
|
|
&dev_attr_asm_rev.attr,
|
|
&dev_attr_lprev.attr,
|
|
&dev_attr_destid.attr,
|
|
&dev_attr_modalias.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group rio_dev_group = {
|
|
.attrs = rio_dev_attrs,
|
|
};
|
|
|
|
const struct attribute_group *rio_dev_groups[] = {
|
|
&rio_dev_group,
|
|
NULL,
|
|
};
|
|
|
|
static ssize_t
|
|
rio_read_config(struct file *filp, struct kobject *kobj,
|
|
struct bin_attribute *bin_attr,
|
|
char *buf, loff_t off, size_t count)
|
|
{
|
|
struct rio_dev *dev =
|
|
to_rio_dev(container_of(kobj, struct device, kobj));
|
|
unsigned int size = 0x100;
|
|
loff_t init_off = off;
|
|
u8 *data = (u8 *) buf;
|
|
|
|
/* Several chips lock up trying to read undefined config space */
|
|
if (capable(CAP_SYS_ADMIN))
|
|
size = RIO_MAINT_SPACE_SZ;
|
|
|
|
if (off >= size)
|
|
return 0;
|
|
if (off + count > size) {
|
|
size -= off;
|
|
count = size;
|
|
} else {
|
|
size = count;
|
|
}
|
|
|
|
if ((off & 1) && size) {
|
|
u8 val;
|
|
rio_read_config_8(dev, off, &val);
|
|
data[off - init_off] = val;
|
|
off++;
|
|
size--;
|
|
}
|
|
|
|
if ((off & 3) && size > 2) {
|
|
u16 val;
|
|
rio_read_config_16(dev, off, &val);
|
|
data[off - init_off] = (val >> 8) & 0xff;
|
|
data[off - init_off + 1] = val & 0xff;
|
|
off += 2;
|
|
size -= 2;
|
|
}
|
|
|
|
while (size > 3) {
|
|
u32 val;
|
|
rio_read_config_32(dev, off, &val);
|
|
data[off - init_off] = (val >> 24) & 0xff;
|
|
data[off - init_off + 1] = (val >> 16) & 0xff;
|
|
data[off - init_off + 2] = (val >> 8) & 0xff;
|
|
data[off - init_off + 3] = val & 0xff;
|
|
off += 4;
|
|
size -= 4;
|
|
}
|
|
|
|
if (size >= 2) {
|
|
u16 val;
|
|
rio_read_config_16(dev, off, &val);
|
|
data[off - init_off] = (val >> 8) & 0xff;
|
|
data[off - init_off + 1] = val & 0xff;
|
|
off += 2;
|
|
size -= 2;
|
|
}
|
|
|
|
if (size > 0) {
|
|
u8 val;
|
|
rio_read_config_8(dev, off, &val);
|
|
data[off - init_off] = val;
|
|
off++;
|
|
--size;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static ssize_t
|
|
rio_write_config(struct file *filp, struct kobject *kobj,
|
|
struct bin_attribute *bin_attr,
|
|
char *buf, loff_t off, size_t count)
|
|
{
|
|
struct rio_dev *dev =
|
|
to_rio_dev(container_of(kobj, struct device, kobj));
|
|
unsigned int size = count;
|
|
loff_t init_off = off;
|
|
u8 *data = (u8 *) buf;
|
|
|
|
if (off >= RIO_MAINT_SPACE_SZ)
|
|
return 0;
|
|
if (off + count > RIO_MAINT_SPACE_SZ) {
|
|
size = RIO_MAINT_SPACE_SZ - off;
|
|
count = size;
|
|
}
|
|
|
|
if ((off & 1) && size) {
|
|
rio_write_config_8(dev, off, data[off - init_off]);
|
|
off++;
|
|
size--;
|
|
}
|
|
|
|
if ((off & 3) && (size > 2)) {
|
|
u16 val = data[off - init_off + 1];
|
|
val |= (u16) data[off - init_off] << 8;
|
|
rio_write_config_16(dev, off, val);
|
|
off += 2;
|
|
size -= 2;
|
|
}
|
|
|
|
while (size > 3) {
|
|
u32 val = data[off - init_off + 3];
|
|
val |= (u32) data[off - init_off + 2] << 8;
|
|
val |= (u32) data[off - init_off + 1] << 16;
|
|
val |= (u32) data[off - init_off] << 24;
|
|
rio_write_config_32(dev, off, val);
|
|
off += 4;
|
|
size -= 4;
|
|
}
|
|
|
|
if (size >= 2) {
|
|
u16 val = data[off - init_off + 1];
|
|
val |= (u16) data[off - init_off] << 8;
|
|
rio_write_config_16(dev, off, val);
|
|
off += 2;
|
|
size -= 2;
|
|
}
|
|
|
|
if (size) {
|
|
rio_write_config_8(dev, off, data[off - init_off]);
|
|
off++;
|
|
--size;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static struct bin_attribute rio_config_attr = {
|
|
.attr = {
|
|
.name = "config",
|
|
.mode = S_IRUGO | S_IWUSR,
|
|
},
|
|
.size = RIO_MAINT_SPACE_SZ,
|
|
.read = rio_read_config,
|
|
.write = rio_write_config,
|
|
};
|
|
|
|
/**
|
|
* rio_create_sysfs_dev_files - create RIO specific sysfs files
|
|
* @rdev: device whose entries should be created
|
|
*
|
|
* Create files when @rdev is added to sysfs.
|
|
*/
|
|
int rio_create_sysfs_dev_files(struct rio_dev *rdev)
|
|
{
|
|
int err = 0;
|
|
|
|
err = device_create_bin_file(&rdev->dev, &rio_config_attr);
|
|
|
|
if (!err && (rdev->pef & RIO_PEF_SWITCH)) {
|
|
err |= device_create_file(&rdev->dev, &dev_attr_routes);
|
|
err |= device_create_file(&rdev->dev, &dev_attr_lnext);
|
|
err |= device_create_file(&rdev->dev, &dev_attr_hopcount);
|
|
}
|
|
|
|
if (err)
|
|
pr_warning("RIO: Failed to create attribute file(s) for %s\n",
|
|
rio_name(rdev));
|
|
|
|
return err;
|
|
}
|
|
|
|
/**
|
|
* rio_remove_sysfs_dev_files - cleanup RIO specific sysfs files
|
|
* @rdev: device whose entries we should free
|
|
*
|
|
* Cleanup when @rdev is removed from sysfs.
|
|
*/
|
|
void rio_remove_sysfs_dev_files(struct rio_dev *rdev)
|
|
{
|
|
device_remove_bin_file(&rdev->dev, &rio_config_attr);
|
|
if (rdev->pef & RIO_PEF_SWITCH) {
|
|
device_remove_file(&rdev->dev, &dev_attr_routes);
|
|
device_remove_file(&rdev->dev, &dev_attr_lnext);
|
|
device_remove_file(&rdev->dev, &dev_attr_hopcount);
|
|
}
|
|
}
|
|
|
|
static ssize_t bus_scan_store(struct bus_type *bus, const char *buf,
|
|
size_t count)
|
|
{
|
|
long val;
|
|
int rc;
|
|
|
|
if (kstrtol(buf, 0, &val) < 0)
|
|
return -EINVAL;
|
|
|
|
if (val == RIO_MPORT_ANY) {
|
|
rc = rio_init_mports();
|
|
goto exit;
|
|
}
|
|
|
|
if (val < 0 || val >= RIO_MAX_MPORTS)
|
|
return -EINVAL;
|
|
|
|
rc = rio_mport_scan((int)val);
|
|
exit:
|
|
if (!rc)
|
|
rc = count;
|
|
|
|
return rc;
|
|
}
|
|
static BUS_ATTR(scan, (S_IWUSR|S_IWGRP), NULL, bus_scan_store);
|
|
|
|
static struct attribute *rio_bus_attrs[] = {
|
|
&bus_attr_scan.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group rio_bus_group = {
|
|
.attrs = rio_bus_attrs,
|
|
};
|
|
|
|
const struct attribute_group *rio_bus_groups[] = {
|
|
&rio_bus_group,
|
|
NULL,
|
|
};
|
|
|
|
static ssize_t
|
|
port_destid_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct rio_mport *mport = to_rio_mport(dev);
|
|
|
|
if (mport)
|
|
return sprintf(buf, "0x%04x\n", mport->host_deviceid);
|
|
else
|
|
return -ENODEV;
|
|
}
|
|
static DEVICE_ATTR_RO(port_destid);
|
|
|
|
static ssize_t sys_size_show(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct rio_mport *mport = to_rio_mport(dev);
|
|
|
|
if (mport)
|
|
return sprintf(buf, "%u\n", mport->sys_size);
|
|
else
|
|
return -ENODEV;
|
|
}
|
|
static DEVICE_ATTR_RO(sys_size);
|
|
|
|
static struct attribute *rio_mport_attrs[] = {
|
|
&dev_attr_port_destid.attr,
|
|
&dev_attr_sys_size.attr,
|
|
NULL,
|
|
};
|
|
|
|
static const struct attribute_group rio_mport_group = {
|
|
.attrs = rio_mport_attrs,
|
|
};
|
|
|
|
const struct attribute_group *rio_mport_groups[] = {
|
|
&rio_mport_group,
|
|
NULL,
|
|
};
|