mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-09 07:23:14 +00:00
[PATCH] RapidIO support: core base
Adds a RapidIO subsystem to the kernel. RIO is a switched fabric interconnect used in higher-end embedded applications. The curious can look at the specs over at http://www.rapidio.org The core code implements enumeration/discovery, management of devices/resources, and interfaces for RIO drivers. There's a lot more to do to take advantages of all the hardware features. However, this should provide a good base for folks with RIO hardware to start contributing. Signed-off-by: Matt Porter <mporter@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
d217d5450f
commit
394b701ce4
@ -10,7 +10,7 @@ DOCBOOKS := wanbook.xml z8530book.xml mcabook.xml videobook.xml \
|
|||||||
kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
|
kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
|
||||||
procfs-guide.xml writing_usb_driver.xml \
|
procfs-guide.xml writing_usb_driver.xml \
|
||||||
sis900.xml kernel-api.xml journal-api.xml lsm.xml usb.xml \
|
sis900.xml kernel-api.xml journal-api.xml lsm.xml usb.xml \
|
||||||
gadget.xml libata.xml mtdnand.xml librs.xml
|
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml
|
||||||
|
|
||||||
###
|
###
|
||||||
# The build process is as follows (targets):
|
# The build process is as follows (targets):
|
||||||
|
160
Documentation/DocBook/rapidio.tmpl
Normal file
160
Documentation/DocBook/rapidio.tmpl
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
|
||||||
|
"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
|
||||||
|
<!ENTITY rapidio SYSTEM "rapidio.xml">
|
||||||
|
]>
|
||||||
|
|
||||||
|
<book id="RapidIO-Guide">
|
||||||
|
<bookinfo>
|
||||||
|
<title>RapidIO Subsystem Guide</title>
|
||||||
|
|
||||||
|
<authorgroup>
|
||||||
|
<author>
|
||||||
|
<firstname>Matt</firstname>
|
||||||
|
<surname>Porter</surname>
|
||||||
|
<affiliation>
|
||||||
|
<address>
|
||||||
|
<email>mporter@kernel.crashing.org</email>
|
||||||
|
<email>mporter@mvista.com</email>
|
||||||
|
</address>
|
||||||
|
</affiliation>
|
||||||
|
</author>
|
||||||
|
</authorgroup>
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
<year>2005</year>
|
||||||
|
<holder>MontaVista Software, Inc.</holder>
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<legalnotice>
|
||||||
|
<para>
|
||||||
|
This documentation is free software; you can redistribute
|
||||||
|
it and/or modify it under the terms of the GNU General Public
|
||||||
|
License version 2 as published by the Free Software Foundation.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This program is distributed in the hope that it will be
|
||||||
|
useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
See the GNU General Public License for more details.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
You should have received a copy of the GNU General Public
|
||||||
|
License along with this program; if not, write to the Free
|
||||||
|
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||||||
|
MA 02111-1307 USA
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
For more details see the file COPYING in the source
|
||||||
|
distribution of Linux.
|
||||||
|
</para>
|
||||||
|
</legalnotice>
|
||||||
|
</bookinfo>
|
||||||
|
|
||||||
|
<toc></toc>
|
||||||
|
|
||||||
|
<chapter id="intro">
|
||||||
|
<title>Introduction</title>
|
||||||
|
<para>
|
||||||
|
RapidIO is a high speed switched fabric interconnect with
|
||||||
|
features aimed at the embedded market. RapidIO provides
|
||||||
|
support for memory-mapped I/O as well as message-based
|
||||||
|
transactions over the switched fabric network. RapidIO has
|
||||||
|
a standardized discovery mechanism not unlike the PCI bus
|
||||||
|
standard that allows simple detection of devices in a
|
||||||
|
network.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
This documentation is provided for developers intending
|
||||||
|
to support RapidIO on new architectures, write new drivers,
|
||||||
|
or to understand the subsystem internals.
|
||||||
|
</para>
|
||||||
|
</chapter>
|
||||||
|
|
||||||
|
<chapter id="bugs">
|
||||||
|
<title>Known Bugs and Limitations</title>
|
||||||
|
|
||||||
|
<sect1>
|
||||||
|
<title>Bugs</title>
|
||||||
|
<para>None. ;)</para>
|
||||||
|
</sect1>
|
||||||
|
<sect1>
|
||||||
|
<title>Limitations</title>
|
||||||
|
<para>
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>Access/management of RapidIO memory regions is not supported</para></listitem>
|
||||||
|
<listitem><para>Multiple host enumeration is not supported</para></listitem>
|
||||||
|
</orderedlist>
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
</chapter>
|
||||||
|
|
||||||
|
<chapter id="drivers">
|
||||||
|
<title>RapidIO driver interface</title>
|
||||||
|
<para>
|
||||||
|
Drivers are provided a set of calls in order
|
||||||
|
to interface with the subsystem to gather info
|
||||||
|
on devices, request/map memory region resources,
|
||||||
|
and manage mailboxes/doorbells.
|
||||||
|
</para>
|
||||||
|
<sect1>
|
||||||
|
<title>Functions</title>
|
||||||
|
!Iinclude/linux/rio_drv.h
|
||||||
|
!Edrivers/rapidio/rio-driver.c
|
||||||
|
!Edrivers/rapidio/rio.c
|
||||||
|
</sect1>
|
||||||
|
</chapter>
|
||||||
|
|
||||||
|
<chapter id="internals">
|
||||||
|
<title>Internals</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This chapter contains the autogenerated documentation of the RapidIO
|
||||||
|
subsystem.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<sect1><title>Structures</title>
|
||||||
|
!Iinclude/linux/rio.h
|
||||||
|
</sect1>
|
||||||
|
<sect1><title>Enumeration and Discovery</title>
|
||||||
|
!Idrivers/rapidio/rio-scan.c
|
||||||
|
</sect1>
|
||||||
|
<sect1><title>Driver functionality</title>
|
||||||
|
!Idrivers/rapidio/rio.c
|
||||||
|
!Idrivers/rapidio/rio-access.c
|
||||||
|
</sect1>
|
||||||
|
<sect1><title>Device model support</title>
|
||||||
|
!Idrivers/rapidio/rio-driver.c
|
||||||
|
</sect1>
|
||||||
|
<sect1><title>Sysfs support</title>
|
||||||
|
!Idrivers/rapidio/rio-sysfs.c
|
||||||
|
</sect1>
|
||||||
|
<sect1><title>PPC32 support</title>
|
||||||
|
!Iarch/ppc/kernel/rio.c
|
||||||
|
!Earch/ppc/syslib/ppc85xx_rio.c
|
||||||
|
!Iarch/ppc/syslib/ppc85xx_rio.c
|
||||||
|
</sect1>
|
||||||
|
</chapter>
|
||||||
|
|
||||||
|
<chapter id="credits">
|
||||||
|
<title>Credits</title>
|
||||||
|
<para>
|
||||||
|
The following people have contributed to the RapidIO
|
||||||
|
subsystem directly or indirectly:
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>Matt Porter<email>mporter@kernel.crashing.org</email></para></listitem>
|
||||||
|
<listitem><para>Randy Vinson<email>rvinson@mvista.com</email></para></listitem>
|
||||||
|
<listitem><para>Dan Malek<email>dan@embeddedalley.com</email></para></listitem>
|
||||||
|
</orderedlist>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The following people have contributed to this document:
|
||||||
|
<orderedlist>
|
||||||
|
<listitem><para>Matt Porter<email>mporter@kernel.crashing.org</email></para></listitem>
|
||||||
|
</orderedlist>
|
||||||
|
</para>
|
||||||
|
</chapter>
|
||||||
|
</book>
|
@ -2071,6 +2071,12 @@ P: Matt Mackall
|
|||||||
M: mpm@selenic.com
|
M: mpm@selenic.com
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
||||||
|
RAPIDIO SUBSYSTEM
|
||||||
|
P: Matt Porter
|
||||||
|
M: mporter@kernel.crashing.org
|
||||||
|
L: linux-kernel@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
|
||||||
REAL TIME CLOCK DRIVER
|
REAL TIME CLOCK DRIVER
|
||||||
P: Paul Gortmaker
|
P: Paul Gortmaker
|
||||||
M: p_gortmaker@yahoo.com
|
M: p_gortmaker@yahoo.com
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
obj-$(CONFIG_PCI) += pci/ usb/
|
obj-$(CONFIG_PCI) += pci/ usb/
|
||||||
obj-$(CONFIG_PARISC) += parisc/
|
obj-$(CONFIG_PARISC) += parisc/
|
||||||
|
obj-$(CONFIG_RAPIDIO) += rapidio/
|
||||||
obj-y += video/
|
obj-y += video/
|
||||||
obj-$(CONFIG_ACPI) += acpi/
|
obj-$(CONFIG_ACPI) += acpi/
|
||||||
# PnP must come after ACPI since it will eventually need to check if acpi
|
# PnP must come after ACPI since it will eventually need to check if acpi
|
||||||
|
18
drivers/rapidio/Kconfig
Normal file
18
drivers/rapidio/Kconfig
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# RapidIO configuration
|
||||||
|
#
|
||||||
|
config RAPIDIO_8_BIT_TRANSPORT
|
||||||
|
bool "8-bit transport addressing"
|
||||||
|
depends on RAPIDIO
|
||||||
|
---help---
|
||||||
|
By default, the kernel assumes a 16-bit addressed RapidIO
|
||||||
|
network. By selecting this option, the kernel will support
|
||||||
|
an 8-bit addressed network.
|
||||||
|
|
||||||
|
config RAPIDIO_DISC_TIMEOUT
|
||||||
|
int "Discovery timeout duration (seconds)"
|
||||||
|
depends on RAPIDIO
|
||||||
|
default "30"
|
||||||
|
---help---
|
||||||
|
Amount of time a discovery node waits for a host to complete
|
||||||
|
enumeration beforing giving up.
|
6
drivers/rapidio/Makefile
Normal file
6
drivers/rapidio/Makefile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#
|
||||||
|
# Makefile for RapidIO interconnect services
|
||||||
|
#
|
||||||
|
obj-y += rio.o rio-access.o rio-driver.o rio-scan.o rio-sysfs.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_RAPIDIO) += switches/
|
175
drivers/rapidio/rio-access.c
Normal file
175
drivers/rapidio/rio-access.c
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* RapidIO configuration space access 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/rio.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These interrupt-safe spinlocks protect all accesses to RIO
|
||||||
|
* configuration space and doorbell access.
|
||||||
|
*/
|
||||||
|
static spinlock_t rio_config_lock = SPIN_LOCK_UNLOCKED;
|
||||||
|
static spinlock_t rio_doorbell_lock = SPIN_LOCK_UNLOCKED;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wrappers for all RIO configuration access functions. They just check
|
||||||
|
* alignment, do locking and call the low-level functions pointed to
|
||||||
|
* by rio_mport->ops.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define RIO_8_BAD 0
|
||||||
|
#define RIO_16_BAD (offset & 1)
|
||||||
|
#define RIO_32_BAD (offset & 3)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RIO_LOP_READ - Generate rio_local_read_config_* functions
|
||||||
|
* @size: Size of configuration space read (8, 16, 32 bits)
|
||||||
|
* @type: C type of value argument
|
||||||
|
* @len: Length of configuration space read (1, 2, 4 bytes)
|
||||||
|
*
|
||||||
|
* Generates rio_local_read_config_* functions used to access
|
||||||
|
* configuration space registers on the local device.
|
||||||
|
*/
|
||||||
|
#define RIO_LOP_READ(size,type,len) \
|
||||||
|
int __rio_local_read_config_##size \
|
||||||
|
(struct rio_mport *mport, u32 offset, type *value) \
|
||||||
|
{ \
|
||||||
|
int res; \
|
||||||
|
unsigned long flags; \
|
||||||
|
u32 data = 0; \
|
||||||
|
if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
|
||||||
|
spin_lock_irqsave(&rio_config_lock, flags); \
|
||||||
|
res = mport->ops->lcread(mport->id, offset, len, &data); \
|
||||||
|
*value = (type)data; \
|
||||||
|
spin_unlock_irqrestore(&rio_config_lock, flags); \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RIO_LOP_WRITE - Generate rio_local_write_config_* functions
|
||||||
|
* @size: Size of configuration space write (8, 16, 32 bits)
|
||||||
|
* @type: C type of value argument
|
||||||
|
* @len: Length of configuration space write (1, 2, 4 bytes)
|
||||||
|
*
|
||||||
|
* Generates rio_local_write_config_* functions used to access
|
||||||
|
* configuration space registers on the local device.
|
||||||
|
*/
|
||||||
|
#define RIO_LOP_WRITE(size,type,len) \
|
||||||
|
int __rio_local_write_config_##size \
|
||||||
|
(struct rio_mport *mport, u32 offset, type value) \
|
||||||
|
{ \
|
||||||
|
int res; \
|
||||||
|
unsigned long flags; \
|
||||||
|
if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
|
||||||
|
spin_lock_irqsave(&rio_config_lock, flags); \
|
||||||
|
res = mport->ops->lcwrite(mport->id, offset, len, value); \
|
||||||
|
spin_unlock_irqrestore(&rio_config_lock, flags); \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
RIO_LOP_READ(8, u8, 1)
|
||||||
|
RIO_LOP_READ(16, u16, 2)
|
||||||
|
RIO_LOP_READ(32, u32, 4)
|
||||||
|
RIO_LOP_WRITE(8, u8, 1)
|
||||||
|
RIO_LOP_WRITE(16, u16, 2)
|
||||||
|
RIO_LOP_WRITE(32, u32, 4)
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(__rio_local_read_config_8);
|
||||||
|
EXPORT_SYMBOL_GPL(__rio_local_read_config_16);
|
||||||
|
EXPORT_SYMBOL_GPL(__rio_local_read_config_32);
|
||||||
|
EXPORT_SYMBOL_GPL(__rio_local_write_config_8);
|
||||||
|
EXPORT_SYMBOL_GPL(__rio_local_write_config_16);
|
||||||
|
EXPORT_SYMBOL_GPL(__rio_local_write_config_32);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RIO_OP_READ - Generate rio_mport_read_config_* functions
|
||||||
|
* @size: Size of configuration space read (8, 16, 32 bits)
|
||||||
|
* @type: C type of value argument
|
||||||
|
* @len: Length of configuration space read (1, 2, 4 bytes)
|
||||||
|
*
|
||||||
|
* Generates rio_mport_read_config_* functions used to access
|
||||||
|
* configuration space registers on the local device.
|
||||||
|
*/
|
||||||
|
#define RIO_OP_READ(size,type,len) \
|
||||||
|
int rio_mport_read_config_##size \
|
||||||
|
(struct rio_mport *mport, u16 destid, u8 hopcount, u32 offset, type *value) \
|
||||||
|
{ \
|
||||||
|
int res; \
|
||||||
|
unsigned long flags; \
|
||||||
|
u32 data = 0; \
|
||||||
|
if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
|
||||||
|
spin_lock_irqsave(&rio_config_lock, flags); \
|
||||||
|
res = mport->ops->cread(mport->id, destid, hopcount, offset, len, &data); \
|
||||||
|
*value = (type)data; \
|
||||||
|
spin_unlock_irqrestore(&rio_config_lock, flags); \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RIO_OP_WRITE - Generate rio_mport_write_config_* functions
|
||||||
|
* @size: Size of configuration space write (8, 16, 32 bits)
|
||||||
|
* @type: C type of value argument
|
||||||
|
* @len: Length of configuration space write (1, 2, 4 bytes)
|
||||||
|
*
|
||||||
|
* Generates rio_mport_write_config_* functions used to access
|
||||||
|
* configuration space registers on the local device.
|
||||||
|
*/
|
||||||
|
#define RIO_OP_WRITE(size,type,len) \
|
||||||
|
int rio_mport_write_config_##size \
|
||||||
|
(struct rio_mport *mport, u16 destid, u8 hopcount, u32 offset, type value) \
|
||||||
|
{ \
|
||||||
|
int res; \
|
||||||
|
unsigned long flags; \
|
||||||
|
if (RIO_##size##_BAD) return RIO_BAD_SIZE; \
|
||||||
|
spin_lock_irqsave(&rio_config_lock, flags); \
|
||||||
|
res = mport->ops->cwrite(mport->id, destid, hopcount, offset, len, value); \
|
||||||
|
spin_unlock_irqrestore(&rio_config_lock, flags); \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
|
RIO_OP_READ(8, u8, 1)
|
||||||
|
RIO_OP_READ(16, u16, 2)
|
||||||
|
RIO_OP_READ(32, u32, 4)
|
||||||
|
RIO_OP_WRITE(8, u8, 1)
|
||||||
|
RIO_OP_WRITE(16, u16, 2)
|
||||||
|
RIO_OP_WRITE(32, u32, 4)
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(rio_mport_read_config_8);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_mport_read_config_16);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_mport_read_config_32);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_mport_write_config_8);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_mport_write_config_16);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_mport_write_config_32);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_mport_send_doorbell - Send a doorbell message
|
||||||
|
*
|
||||||
|
* @mport: RIO master port
|
||||||
|
* @destid: RIO device destination ID
|
||||||
|
* @data: Doorbell message data
|
||||||
|
*
|
||||||
|
* Send a doorbell message to a RIO device. The doorbell message
|
||||||
|
* has a 16-bit info field provided by the data argument.
|
||||||
|
*/
|
||||||
|
int rio_mport_send_doorbell(struct rio_mport *mport, u16 destid, u16 data)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&rio_doorbell_lock, flags);
|
||||||
|
res = mport->ops->dsend(mport->id, destid, data);
|
||||||
|
spin_unlock_irqrestore(&rio_doorbell_lock, flags);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(rio_mport_send_doorbell);
|
229
drivers/rapidio/rio-driver.c
Normal file
229
drivers/rapidio/rio-driver.c
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* RapidIO driver 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/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/rio.h>
|
||||||
|
#include <linux/rio_ids.h>
|
||||||
|
|
||||||
|
#include "rio.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_match_device - Tell if a RIO device has a matching RIO device id structure
|
||||||
|
* @id: the RIO device id structure to match against
|
||||||
|
* @rdev: the RIO device structure to match against
|
||||||
|
*
|
||||||
|
* Used from driver probe and bus matching to check whether a RIO device
|
||||||
|
* matches a device id structure provided by a RIO driver. Returns the
|
||||||
|
* matching &struct rio_device_id or %NULL if there is no match.
|
||||||
|
*/
|
||||||
|
static const struct rio_device_id *rio_match_device(const struct rio_device_id
|
||||||
|
*id,
|
||||||
|
const struct rio_dev *rdev)
|
||||||
|
{
|
||||||
|
while (id->vid || id->asm_vid) {
|
||||||
|
if (((id->vid == RIO_ANY_ID) || (id->vid == rdev->vid)) &&
|
||||||
|
((id->did == RIO_ANY_ID) || (id->did == rdev->did)) &&
|
||||||
|
((id->asm_vid == RIO_ANY_ID)
|
||||||
|
|| (id->asm_vid == rdev->asm_vid))
|
||||||
|
&& ((id->asm_did == RIO_ANY_ID)
|
||||||
|
|| (id->asm_did == rdev->asm_did)))
|
||||||
|
return id;
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_dev_get - Increments the reference count of the RIO device structure
|
||||||
|
*
|
||||||
|
* @rdev: RIO device being referenced
|
||||||
|
*
|
||||||
|
* Each live reference to a device should be refcounted.
|
||||||
|
*
|
||||||
|
* Drivers for RIO devices should normally record such references in
|
||||||
|
* their probe() methods, when they bind to a device, and release
|
||||||
|
* them by calling rio_dev_put(), in their disconnect() methods.
|
||||||
|
*/
|
||||||
|
struct rio_dev *rio_dev_get(struct rio_dev *rdev)
|
||||||
|
{
|
||||||
|
if (rdev)
|
||||||
|
get_device(&rdev->dev);
|
||||||
|
|
||||||
|
return rdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_dev_put - Release a use of the RIO device structure
|
||||||
|
*
|
||||||
|
* @rdev: RIO device being disconnected
|
||||||
|
*
|
||||||
|
* Must be called when a user of a device is finished with it.
|
||||||
|
* When the last user of the device calls this function, the
|
||||||
|
* memory of the device is freed.
|
||||||
|
*/
|
||||||
|
void rio_dev_put(struct rio_dev *rdev)
|
||||||
|
{
|
||||||
|
if (rdev)
|
||||||
|
put_device(&rdev->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_device_probe - Tell if a RIO device structure has a matching RIO
|
||||||
|
* device id structure
|
||||||
|
* @id: the RIO device id structure to match against
|
||||||
|
* @dev: the RIO device structure to match against
|
||||||
|
*
|
||||||
|
* return 0 and set rio_dev->driver when drv claims rio_dev, else error
|
||||||
|
*/
|
||||||
|
static int rio_device_probe(struct device *dev)
|
||||||
|
{
|
||||||
|
struct rio_driver *rdrv = to_rio_driver(dev->driver);
|
||||||
|
struct rio_dev *rdev = to_rio_dev(dev);
|
||||||
|
int error = -ENODEV;
|
||||||
|
const struct rio_device_id *id;
|
||||||
|
|
||||||
|
if (!rdev->driver && rdrv->probe) {
|
||||||
|
if (!rdrv->id_table)
|
||||||
|
return error;
|
||||||
|
id = rio_match_device(rdrv->id_table, rdev);
|
||||||
|
rio_dev_get(rdev);
|
||||||
|
if (id)
|
||||||
|
error = rdrv->probe(rdev, id);
|
||||||
|
if (error >= 0) {
|
||||||
|
rdev->driver = rdrv;
|
||||||
|
error = 0;
|
||||||
|
rio_dev_put(rdev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_device_remove - Remove a RIO device from the system
|
||||||
|
*
|
||||||
|
* @dev: the RIO device structure to match against
|
||||||
|
*
|
||||||
|
* Remove a RIO device from the system. If it has an associated
|
||||||
|
* driver, then run the driver remove() method. Then update
|
||||||
|
* the reference count.
|
||||||
|
*/
|
||||||
|
static int rio_device_remove(struct device *dev)
|
||||||
|
{
|
||||||
|
struct rio_dev *rdev = to_rio_dev(dev);
|
||||||
|
struct rio_driver *rdrv = rdev->driver;
|
||||||
|
|
||||||
|
if (rdrv) {
|
||||||
|
if (rdrv->remove)
|
||||||
|
rdrv->remove(rdev);
|
||||||
|
rdev->driver = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rio_dev_put(rdev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_register_driver - register a new RIO driver
|
||||||
|
* @rdrv: the RIO driver structure to register
|
||||||
|
*
|
||||||
|
* Adds a &struct rio_driver to the list of registered drivers
|
||||||
|
* Returns a negative value on error, otherwise 0. If no error
|
||||||
|
* occurred, the driver remains registered even if no device
|
||||||
|
* was claimed during registration.
|
||||||
|
*/
|
||||||
|
int rio_register_driver(struct rio_driver *rdrv)
|
||||||
|
{
|
||||||
|
/* initialize common driver fields */
|
||||||
|
rdrv->driver.name = rdrv->name;
|
||||||
|
rdrv->driver.bus = &rio_bus_type;
|
||||||
|
rdrv->driver.probe = rio_device_probe;
|
||||||
|
rdrv->driver.remove = rio_device_remove;
|
||||||
|
|
||||||
|
/* register with core */
|
||||||
|
return driver_register(&rdrv->driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_unregister_driver - unregister a RIO driver
|
||||||
|
* @rdrv: the RIO driver structure to unregister
|
||||||
|
*
|
||||||
|
* Deletes the &struct rio_driver from the list of registered RIO
|
||||||
|
* drivers, gives it a chance to clean up by calling its remove()
|
||||||
|
* function for each device it was responsible for, and marks those
|
||||||
|
* devices as driverless.
|
||||||
|
*/
|
||||||
|
void rio_unregister_driver(struct rio_driver *rdrv)
|
||||||
|
{
|
||||||
|
driver_unregister(&rdrv->driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_match_bus - Tell if a RIO device structure has a matching RIO
|
||||||
|
* driver device id structure
|
||||||
|
* @dev: the standard device structure to match against
|
||||||
|
* @drv: the standard driver structure containing the ids to match against
|
||||||
|
*
|
||||||
|
* Used by a driver to check whether a RIO device present in the
|
||||||
|
* system is in its list of supported devices. Returns 1 if
|
||||||
|
* there is a matching &struct rio_device_id or 0 if there is
|
||||||
|
* no match.
|
||||||
|
*/
|
||||||
|
static int rio_match_bus(struct device *dev, struct device_driver *drv)
|
||||||
|
{
|
||||||
|
struct rio_dev *rdev = to_rio_dev(dev);
|
||||||
|
struct rio_driver *rdrv = to_rio_driver(drv);
|
||||||
|
const struct rio_device_id *id = rdrv->id_table;
|
||||||
|
const struct rio_device_id *found_id;
|
||||||
|
|
||||||
|
if (!id)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
found_id = rio_match_device(id, rdev);
|
||||||
|
|
||||||
|
if (found_id)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
out:return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device rio_bus = {
|
||||||
|
.bus_id = "rapidio",
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bus_type rio_bus_type = {
|
||||||
|
.name = "rapidio",
|
||||||
|
.match = rio_match_bus,
|
||||||
|
.dev_attrs = rio_dev_attrs
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_bus_init - Register the RapidIO bus with the device model
|
||||||
|
*
|
||||||
|
* Registers the RIO bus device and RIO bus type with the Linux
|
||||||
|
* device model.
|
||||||
|
*/
|
||||||
|
static int __init rio_bus_init(void)
|
||||||
|
{
|
||||||
|
if (device_register(&rio_bus) < 0)
|
||||||
|
printk("RIO: failed to register RIO bus device\n");
|
||||||
|
return bus_register(&rio_bus_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
postcore_initcall(rio_bus_init);
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(rio_register_driver);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_unregister_driver);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_bus_type);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_dev_get);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_dev_put);
|
230
drivers/rapidio/rio-sysfs.c
Normal file
230
drivers/rapidio/rio-sysfs.c
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* 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/config.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/rio.h>
|
||||||
|
#include <linux/rio_drv.h>
|
||||||
|
#include <linux/stat.h>
|
||||||
|
|
||||||
|
#include "rio.h"
|
||||||
|
|
||||||
|
/* Sysfs support */
|
||||||
|
#define rio_config_attr(field, format_string) \
|
||||||
|
static ssize_t \
|
||||||
|
field##_show(struct device *dev, char *buf) \
|
||||||
|
{ \
|
||||||
|
struct rio_dev *rdev = to_rio_dev(dev); \
|
||||||
|
\
|
||||||
|
return sprintf(buf, format_string, rdev->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");
|
||||||
|
|
||||||
|
static ssize_t routes_show(struct device *dev, char *buf)
|
||||||
|
{
|
||||||
|
struct rio_dev *rdev = to_rio_dev(dev);
|
||||||
|
char *str = buf;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!rdev->rswitch)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (i = 0; i < RIO_MAX_ROUTE_ENTRIES; i++) {
|
||||||
|
if (rdev->rswitch->route_table[i] == RIO_INVALID_ROUTE)
|
||||||
|
continue;
|
||||||
|
str +=
|
||||||
|
sprintf(str, "%04x %02x\n", i,
|
||||||
|
rdev->rswitch->route_table[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return (str - buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct device_attribute rio_dev_attrs[] = {
|
||||||
|
__ATTR_RO(did),
|
||||||
|
__ATTR_RO(vid),
|
||||||
|
__ATTR_RO(device_rev),
|
||||||
|
__ATTR_RO(asm_did),
|
||||||
|
__ATTR_RO(asm_vid),
|
||||||
|
__ATTR_RO(asm_rev),
|
||||||
|
__ATTR_RO(routes),
|
||||||
|
__ATTR_NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
rio_read_config(struct kobject *kobj, 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 = 0x200000;
|
||||||
|
|
||||||
|
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 kobject *kobj, 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 > 0x200000)
|
||||||
|
return 0;
|
||||||
|
if (off + count > 0x200000) {
|
||||||
|
size = 0x200000 - 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,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.size = 0x200000,
|
||||||
|
.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)
|
||||||
|
{
|
||||||
|
sysfs_create_bin_file(&rdev->dev.kobj, &rio_config_attr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
sysfs_remove_bin_file(&rdev->dev.kobj, &rio_config_attr);
|
||||||
|
}
|
503
drivers/rapidio/rio.c
Normal file
503
drivers/rapidio/rio.c
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
/*
|
||||||
|
* RapidIO interconnect services
|
||||||
|
* (RapidIO Interconnect Specification, http://www.rapidio.org)
|
||||||
|
*
|
||||||
|
* 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/config.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/rio.h>
|
||||||
|
#include <linux/rio_drv.h>
|
||||||
|
#include <linux/rio_ids.h>
|
||||||
|
#include <linux/rio_regs.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
|
#include "rio.h"
|
||||||
|
|
||||||
|
static LIST_HEAD(rio_mports);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_local_get_device_id - Get the base/extended device id for a port
|
||||||
|
* @port: RIO master port from which to get the deviceid
|
||||||
|
*
|
||||||
|
* Reads the base/extended device id from the local device
|
||||||
|
* implementing the master port. Returns the 8/16-bit device
|
||||||
|
* id.
|
||||||
|
*/
|
||||||
|
u16 rio_local_get_device_id(struct rio_mport *port)
|
||||||
|
{
|
||||||
|
u32 result;
|
||||||
|
|
||||||
|
rio_local_read_config_32(port, RIO_DID_CSR, &result);
|
||||||
|
|
||||||
|
return (RIO_GET_DID(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_request_inb_mbox - request inbound mailbox service
|
||||||
|
* @mport: RIO master port from which to allocate the mailbox resource
|
||||||
|
* @mbox: Mailbox number to claim
|
||||||
|
* @entries: Number of entries in inbound mailbox queue
|
||||||
|
* @minb: Callback to execute when inbound message is received
|
||||||
|
*
|
||||||
|
* Requests ownership of an inbound mailbox resource and binds
|
||||||
|
* a callback function to the resource. Returns %0 on success.
|
||||||
|
*/
|
||||||
|
int rio_request_inb_mbox(struct rio_mport *mport,
|
||||||
|
int mbox,
|
||||||
|
int entries,
|
||||||
|
void (*minb) (struct rio_mport * mport, int mbox,
|
||||||
|
int slot))
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
rio_init_mbox_res(res, mbox, mbox);
|
||||||
|
|
||||||
|
/* Make sure this mailbox isn't in use */
|
||||||
|
if ((rc =
|
||||||
|
request_resource(&mport->riores[RIO_INB_MBOX_RESOURCE],
|
||||||
|
res)) < 0) {
|
||||||
|
kfree(res);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mport->inb_msg[mbox].res = res;
|
||||||
|
|
||||||
|
/* Hook the inbound message callback */
|
||||||
|
mport->inb_msg[mbox].mcback = minb;
|
||||||
|
|
||||||
|
rc = rio_open_inb_mbox(mport, mbox, entries);
|
||||||
|
} else
|
||||||
|
rc = -ENOMEM;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_release_inb_mbox - release inbound mailbox message service
|
||||||
|
* @mport: RIO master port from which to release the mailbox resource
|
||||||
|
* @mbox: Mailbox number to release
|
||||||
|
*
|
||||||
|
* Releases ownership of an inbound mailbox resource. Returns 0
|
||||||
|
* if the request has been satisfied.
|
||||||
|
*/
|
||||||
|
int rio_release_inb_mbox(struct rio_mport *mport, int mbox)
|
||||||
|
{
|
||||||
|
rio_close_inb_mbox(mport, mbox);
|
||||||
|
|
||||||
|
/* Release the mailbox resource */
|
||||||
|
return release_resource(mport->inb_msg[mbox].res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_request_outb_mbox - request outbound mailbox service
|
||||||
|
* @mport: RIO master port from which to allocate the mailbox resource
|
||||||
|
* @mbox: Mailbox number to claim
|
||||||
|
* @entries: Number of entries in outbound mailbox queue
|
||||||
|
* @moutb: Callback to execute when outbound message is sent
|
||||||
|
*
|
||||||
|
* Requests ownership of an outbound mailbox resource and binds
|
||||||
|
* a callback function to the resource. Returns 0 on success.
|
||||||
|
*/
|
||||||
|
int rio_request_outb_mbox(struct rio_mport *mport,
|
||||||
|
int mbox,
|
||||||
|
int entries,
|
||||||
|
void (*moutb) (struct rio_mport * mport, int mbox,
|
||||||
|
int slot))
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
rio_init_mbox_res(res, mbox, mbox);
|
||||||
|
|
||||||
|
/* Make sure this outbound mailbox isn't in use */
|
||||||
|
if ((rc =
|
||||||
|
request_resource(&mport->riores[RIO_OUTB_MBOX_RESOURCE],
|
||||||
|
res)) < 0) {
|
||||||
|
kfree(res);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
mport->outb_msg[mbox].res = res;
|
||||||
|
|
||||||
|
/* Hook the inbound message callback */
|
||||||
|
mport->outb_msg[mbox].mcback = moutb;
|
||||||
|
|
||||||
|
rc = rio_open_outb_mbox(mport, mbox, entries);
|
||||||
|
} else
|
||||||
|
rc = -ENOMEM;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_release_outb_mbox - release outbound mailbox message service
|
||||||
|
* @mport: RIO master port from which to release the mailbox resource
|
||||||
|
* @mbox: Mailbox number to release
|
||||||
|
*
|
||||||
|
* Releases ownership of an inbound mailbox resource. Returns 0
|
||||||
|
* if the request has been satisfied.
|
||||||
|
*/
|
||||||
|
int rio_release_outb_mbox(struct rio_mport *mport, int mbox)
|
||||||
|
{
|
||||||
|
rio_close_outb_mbox(mport, mbox);
|
||||||
|
|
||||||
|
/* Release the mailbox resource */
|
||||||
|
return release_resource(mport->outb_msg[mbox].res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_setup_inb_dbell - bind inbound doorbell callback
|
||||||
|
* @mport: RIO master port to bind the doorbell callback
|
||||||
|
* @res: Doorbell message resource
|
||||||
|
* @dinb: Callback to execute when doorbell is received
|
||||||
|
*
|
||||||
|
* Adds a doorbell resource/callback pair into a port's
|
||||||
|
* doorbell event list. Returns 0 if the request has been
|
||||||
|
* satisfied.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
rio_setup_inb_dbell(struct rio_mport *mport, struct resource *res,
|
||||||
|
void (*dinb) (struct rio_mport * mport, u16 src, u16 dst,
|
||||||
|
u16 info))
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
struct rio_dbell *dbell;
|
||||||
|
|
||||||
|
if (!(dbell = kmalloc(sizeof(struct rio_dbell), GFP_KERNEL))) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbell->res = res;
|
||||||
|
dbell->dinb = dinb;
|
||||||
|
|
||||||
|
list_add_tail(&dbell->node, &mport->dbells);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_request_inb_dbell - request inbound doorbell message service
|
||||||
|
* @mport: RIO master port from which to allocate the doorbell resource
|
||||||
|
* @start: Doorbell info range start
|
||||||
|
* @end: Doorbell info range end
|
||||||
|
* @dinb: Callback to execute when doorbell is received
|
||||||
|
*
|
||||||
|
* Requests ownership of an inbound doorbell resource and binds
|
||||||
|
* a callback function to the resource. Returns 0 if the request
|
||||||
|
* has been satisfied.
|
||||||
|
*/
|
||||||
|
int rio_request_inb_dbell(struct rio_mport *mport,
|
||||||
|
u16 start,
|
||||||
|
u16 end,
|
||||||
|
void (*dinb) (struct rio_mport * mport, u16 src,
|
||||||
|
u16 dst, u16 info))
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
|
||||||
|
struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
rio_init_dbell_res(res, start, end);
|
||||||
|
|
||||||
|
/* Make sure these doorbells aren't in use */
|
||||||
|
if ((rc =
|
||||||
|
request_resource(&mport->riores[RIO_DOORBELL_RESOURCE],
|
||||||
|
res)) < 0) {
|
||||||
|
kfree(res);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hook the doorbell callback */
|
||||||
|
rc = rio_setup_inb_dbell(mport, res, dinb);
|
||||||
|
} else
|
||||||
|
rc = -ENOMEM;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_release_inb_dbell - release inbound doorbell message service
|
||||||
|
* @mport: RIO master port from which to release the doorbell resource
|
||||||
|
* @start: Doorbell info range start
|
||||||
|
* @end: Doorbell info range end
|
||||||
|
*
|
||||||
|
* Releases ownership of an inbound doorbell resource and removes
|
||||||
|
* callback from the doorbell event list. Returns 0 if the request
|
||||||
|
* has been satisfied.
|
||||||
|
*/
|
||||||
|
int rio_release_inb_dbell(struct rio_mport *mport, u16 start, u16 end)
|
||||||
|
{
|
||||||
|
int rc = 0, found = 0;
|
||||||
|
struct rio_dbell *dbell;
|
||||||
|
|
||||||
|
list_for_each_entry(dbell, &mport->dbells, node) {
|
||||||
|
if ((dbell->res->start == start) && (dbell->res->end == end)) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we can't find an exact match, fail */
|
||||||
|
if (!found) {
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete from list */
|
||||||
|
list_del(&dbell->node);
|
||||||
|
|
||||||
|
/* Release the doorbell resource */
|
||||||
|
rc = release_resource(dbell->res);
|
||||||
|
|
||||||
|
/* Free the doorbell event */
|
||||||
|
kfree(dbell);
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_request_outb_dbell - request outbound doorbell message range
|
||||||
|
* @rdev: RIO device from which to allocate the doorbell resource
|
||||||
|
* @start: Doorbell message range start
|
||||||
|
* @end: Doorbell message range end
|
||||||
|
*
|
||||||
|
* Requests ownership of a doorbell message range. Returns a resource
|
||||||
|
* if the request has been satisfied or %NULL on failure.
|
||||||
|
*/
|
||||||
|
struct resource *rio_request_outb_dbell(struct rio_dev *rdev, u16 start,
|
||||||
|
u16 end)
|
||||||
|
{
|
||||||
|
struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
rio_init_dbell_res(res, start, end);
|
||||||
|
|
||||||
|
/* Make sure these doorbells aren't in use */
|
||||||
|
if (request_resource(&rdev->riores[RIO_DOORBELL_RESOURCE], res)
|
||||||
|
< 0) {
|
||||||
|
kfree(res);
|
||||||
|
res = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_release_outb_dbell - release outbound doorbell message range
|
||||||
|
* @rdev: RIO device from which to release the doorbell resource
|
||||||
|
* @res: Doorbell resource to be freed
|
||||||
|
*
|
||||||
|
* Releases ownership of a doorbell message range. Returns 0 if the
|
||||||
|
* request has been satisfied.
|
||||||
|
*/
|
||||||
|
int rio_release_outb_dbell(struct rio_dev *rdev, struct resource *res)
|
||||||
|
{
|
||||||
|
int rc = release_resource(res);
|
||||||
|
|
||||||
|
kfree(res);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_mport_get_feature - query for devices' extended features
|
||||||
|
* @port: Master port to issue transaction
|
||||||
|
* @local: Indicate a local master port or remote device access
|
||||||
|
* @destid: Destination ID of the device
|
||||||
|
* @hopcount: Number of switch hops to the device
|
||||||
|
* @ftr: Extended feature code
|
||||||
|
*
|
||||||
|
* Tell if a device supports a given RapidIO capability.
|
||||||
|
* Returns the offset of the requested extended feature
|
||||||
|
* block within the device's RIO configuration space or
|
||||||
|
* 0 in case the device does not support it. Possible
|
||||||
|
* values for @ftr:
|
||||||
|
*
|
||||||
|
* %RIO_EFB_PAR_EP_ID LP/LVDS EP Devices
|
||||||
|
*
|
||||||
|
* %RIO_EFB_PAR_EP_REC_ID LP/LVDS EP Recovery Devices
|
||||||
|
*
|
||||||
|
* %RIO_EFB_PAR_EP_FREE_ID LP/LVDS EP Free Devices
|
||||||
|
*
|
||||||
|
* %RIO_EFB_SER_EP_ID LP/Serial EP Devices
|
||||||
|
*
|
||||||
|
* %RIO_EFB_SER_EP_REC_ID LP/Serial EP Recovery Devices
|
||||||
|
*
|
||||||
|
* %RIO_EFB_SER_EP_FREE_ID LP/Serial EP Free Devices
|
||||||
|
*/
|
||||||
|
u32
|
||||||
|
rio_mport_get_feature(struct rio_mport * port, int local, u16 destid,
|
||||||
|
u8 hopcount, int ftr)
|
||||||
|
{
|
||||||
|
u32 asm_info, ext_ftr_ptr, ftr_header;
|
||||||
|
|
||||||
|
if (local)
|
||||||
|
rio_local_read_config_32(port, RIO_ASM_INFO_CAR, &asm_info);
|
||||||
|
else
|
||||||
|
rio_mport_read_config_32(port, destid, hopcount,
|
||||||
|
RIO_ASM_INFO_CAR, &asm_info);
|
||||||
|
|
||||||
|
ext_ftr_ptr = asm_info & RIO_EXT_FTR_PTR_MASK;
|
||||||
|
|
||||||
|
while (ext_ftr_ptr) {
|
||||||
|
if (local)
|
||||||
|
rio_local_read_config_32(port, ext_ftr_ptr,
|
||||||
|
&ftr_header);
|
||||||
|
else
|
||||||
|
rio_mport_read_config_32(port, destid, hopcount,
|
||||||
|
ext_ftr_ptr, &ftr_header);
|
||||||
|
if (RIO_GET_BLOCK_ID(ftr_header) == ftr)
|
||||||
|
return ext_ftr_ptr;
|
||||||
|
if (!(ext_ftr_ptr = RIO_GET_BLOCK_PTR(ftr_header)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_get_asm - Begin or continue searching for a RIO device by vid/did/asm_vid/asm_did
|
||||||
|
* @vid: RIO vid to match or %RIO_ANY_ID to match all vids
|
||||||
|
* @did: RIO did to match or %RIO_ANY_ID to match all dids
|
||||||
|
* @asm_vid: RIO asm_vid to match or %RIO_ANY_ID to match all asm_vids
|
||||||
|
* @asm_did: RIO asm_did to match or %RIO_ANY_ID to match all asm_dids
|
||||||
|
* @from: Previous RIO device found in search, or %NULL for new search
|
||||||
|
*
|
||||||
|
* Iterates through the list of known RIO devices. If a RIO device is
|
||||||
|
* found with a matching @vid, @did, @asm_vid, @asm_did, the reference
|
||||||
|
* count to the device is incrememted and a pointer to its device
|
||||||
|
* structure is returned. Otherwise, %NULL is returned. A new search
|
||||||
|
* is initiated by passing %NULL to the @from argument. Otherwise, if
|
||||||
|
* @from is not %NULL, searches continue from next device on the global
|
||||||
|
* list. The reference count for @from is always decremented if it is
|
||||||
|
* not %NULL.
|
||||||
|
*/
|
||||||
|
struct rio_dev *rio_get_asm(u16 vid, u16 did,
|
||||||
|
u16 asm_vid, u16 asm_did, struct rio_dev *from)
|
||||||
|
{
|
||||||
|
struct list_head *n;
|
||||||
|
struct rio_dev *rdev;
|
||||||
|
|
||||||
|
WARN_ON(in_interrupt());
|
||||||
|
spin_lock(&rio_global_list_lock);
|
||||||
|
n = from ? from->global_list.next : rio_devices.next;
|
||||||
|
|
||||||
|
while (n && (n != &rio_devices)) {
|
||||||
|
rdev = rio_dev_g(n);
|
||||||
|
if ((vid == RIO_ANY_ID || rdev->vid == vid) &&
|
||||||
|
(did == RIO_ANY_ID || rdev->did == did) &&
|
||||||
|
(asm_vid == RIO_ANY_ID || rdev->asm_vid == asm_vid) &&
|
||||||
|
(asm_did == RIO_ANY_ID || rdev->asm_did == asm_did))
|
||||||
|
goto exit;
|
||||||
|
n = n->next;
|
||||||
|
}
|
||||||
|
rdev = NULL;
|
||||||
|
exit:
|
||||||
|
rio_dev_put(from);
|
||||||
|
rdev = rio_dev_get(rdev);
|
||||||
|
spin_unlock(&rio_global_list_lock);
|
||||||
|
return rdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rio_get_device - Begin or continue searching for a RIO device by vid/did
|
||||||
|
* @vid: RIO vid to match or %RIO_ANY_ID to match all vids
|
||||||
|
* @did: RIO did to match or %RIO_ANY_ID to match all dids
|
||||||
|
* @from: Previous RIO device found in search, or %NULL for new search
|
||||||
|
*
|
||||||
|
* Iterates through the list of known RIO devices. If a RIO device is
|
||||||
|
* found with a matching @vid and @did, the reference count to the
|
||||||
|
* device is incrememted and a pointer to its device structure is returned.
|
||||||
|
* Otherwise, %NULL is returned. A new search is initiated by passing %NULL
|
||||||
|
* to the @from argument. Otherwise, if @from is not %NULL, searches
|
||||||
|
* continue from next device on the global list. The reference count for
|
||||||
|
* @from is always decremented if it is not %NULL.
|
||||||
|
*/
|
||||||
|
struct rio_dev *rio_get_device(u16 vid, u16 did, struct rio_dev *from)
|
||||||
|
{
|
||||||
|
return rio_get_asm(vid, did, RIO_ANY_ID, RIO_ANY_ID, from);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rio_fixup_device(struct rio_dev *dev)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit rio_init(void)
|
||||||
|
{
|
||||||
|
struct rio_dev *dev = NULL;
|
||||||
|
|
||||||
|
while ((dev = rio_get_device(RIO_ANY_ID, RIO_ANY_ID, dev)) != NULL) {
|
||||||
|
rio_fixup_device(dev);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
device_initcall(rio_init);
|
||||||
|
|
||||||
|
int rio_init_mports(void)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
struct rio_mport *port;
|
||||||
|
|
||||||
|
list_for_each_entry(port, &rio_mports, node) {
|
||||||
|
if (!request_mem_region(port->iores.start,
|
||||||
|
port->iores.end - port->iores.start,
|
||||||
|
port->name)) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"RIO: Error requesting master port region %8.8lx-%8.8lx\n",
|
||||||
|
port->iores.start, port->iores.end - 1);
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port->host_deviceid >= 0)
|
||||||
|
rio_enum_mport(port);
|
||||||
|
else
|
||||||
|
rio_disc_mport(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rio_register_mport(struct rio_mport *port)
|
||||||
|
{
|
||||||
|
list_add_tail(&port->node, &rio_mports);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(rio_local_get_device_id);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_get_device);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_get_asm);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_request_inb_dbell);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_release_inb_dbell);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_request_outb_dbell);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_release_outb_dbell);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_request_inb_mbox);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_release_inb_mbox);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_request_outb_mbox);
|
||||||
|
EXPORT_SYMBOL_GPL(rio_release_outb_mbox);
|
57
drivers/rapidio/rio.h
Normal file
57
drivers/rapidio/rio.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* RapidIO interconnect services
|
||||||
|
*
|
||||||
|
* 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/device.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/rio.h>
|
||||||
|
|
||||||
|
/* Functions internal to the RIO core code */
|
||||||
|
|
||||||
|
extern u32 rio_mport_get_feature(struct rio_mport *mport, int local, u16 destid,
|
||||||
|
u8 hopcount, int ftr);
|
||||||
|
extern int rio_create_sysfs_dev_files(struct rio_dev *rdev);
|
||||||
|
extern int rio_enum_mport(struct rio_mport *mport);
|
||||||
|
extern int rio_disc_mport(struct rio_mport *mport);
|
||||||
|
|
||||||
|
/* Structures internal to the RIO core code */
|
||||||
|
extern struct device_attribute rio_dev_attrs[];
|
||||||
|
extern spinlock_t rio_global_list_lock;
|
||||||
|
|
||||||
|
/* Helpers internal to the RIO core code */
|
||||||
|
#define DECLARE_RIO_ROUTE_SECTION(section, vid, did, add_hook, get_hook) \
|
||||||
|
static struct rio_route_ops __rio_route_ops __attribute_used__ \
|
||||||
|
__attribute__((__section__(#section))) = { vid, did, add_hook, get_hook };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DECLARE_RIO_ROUTE_OPS - Registers switch routing operations
|
||||||
|
* @vid: RIO vendor ID
|
||||||
|
* @did: RIO device ID
|
||||||
|
* @add_hook: Callback that adds a route entry
|
||||||
|
* @get_hook: Callback that gets a route entry
|
||||||
|
*
|
||||||
|
* Manipulating switch route tables in RIO is switch specific. This
|
||||||
|
* registers a switch by vendor and device ID with two callbacks for
|
||||||
|
* modifying and retrieving route entries in a switch. A &struct
|
||||||
|
* rio_route_ops is initialized with the ops and placed into a
|
||||||
|
* RIO-specific kernel section.
|
||||||
|
*/
|
||||||
|
#define DECLARE_RIO_ROUTE_OPS(vid, did, add_hook, get_hook) \
|
||||||
|
DECLARE_RIO_ROUTE_SECTION(.rio_route_ops, \
|
||||||
|
vid, did, add_hook, get_hook)
|
||||||
|
|
||||||
|
#ifdef CONFIG_RAPIDIO_8_BIT_TRANSPORT
|
||||||
|
#define RIO_GET_DID(x) ((x & 0x00ff0000) >> 16)
|
||||||
|
#define RIO_SET_DID(x) ((x & 0x000000ff) << 16)
|
||||||
|
#else
|
||||||
|
#define RIO_GET_DID(x) (x & 0xffff)
|
||||||
|
#define RIO_SET_DID(x) (x & 0xffff)
|
||||||
|
#endif
|
@ -35,6 +35,13 @@
|
|||||||
VMLINUX_SYMBOL(__end_pci_fixups_enable) = .; \
|
VMLINUX_SYMBOL(__end_pci_fixups_enable) = .; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
/* RapidIO route ops */ \
|
||||||
|
.rio_route : AT(ADDR(.rio_route) - LOAD_OFFSET) { \
|
||||||
|
VMLINUX_SYMBOL(__start_rio_route_ops) = .; \
|
||||||
|
*(.rio_route_ops) \
|
||||||
|
VMLINUX_SYMBOL(__end_rio_route_ops) = .; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
/* Kernel symbol table: Normal symbols */ \
|
/* Kernel symbol table: Normal symbols */ \
|
||||||
__ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
|
__ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
|
||||||
VMLINUX_SYMBOL(__start___ksymtab) = .; \
|
VMLINUX_SYMBOL(__start___ksymtab) = .; \
|
||||||
|
Loading…
Reference in New Issue
Block a user