Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5cabeb3 authored by Alexandre Bounine's avatar Alexandre Bounine Committed by Linus Torvalds
Browse files

rapidio: add Port-Write handling for EM



Add RapidIO Port-Write message handling in the context of Error
   Management Extensions Specification Rev.1.3.

Signed-off-by: default avatarAlexandre Bounine <alexandre.bounine@idt.com>
Tested-by: default avatarThomas Moll <thomas.moll@sysgo.com>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 818a04a0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1057,7 +1057,7 @@ int fsl_rio_setup(struct of_device *dev)
	dev_info(&dev->dev, "LAW start 0x%016llx, size 0x%016llx.\n",
			law_start, law_size);

	ops = kmalloc(sizeof(struct rio_ops), GFP_KERNEL);
	ops = kzalloc(sizeof(struct rio_ops), GFP_KERNEL);
	if (!ops) {
		rc = -ENOMEM;
		goto err_ops;
+130 −36
Original line number Diff line number Diff line
@@ -4,6 +4,10 @@
 * Copyright 2005 MontaVista Software, Inc.
 * Matt Porter <mporter@kernel.crashing.org>
 *
 * Copyright 2009 Integrated Device Technology, Inc.
 * Alex Bounine <alexandre.bounine@idt.com>
 * - Added Port-Write/Error Management initialization and handling
 *
 * 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
@@ -31,15 +35,16 @@
LIST_HEAD(rio_devices);
static LIST_HEAD(rio_switches);

#define RIO_ENUM_CMPL_MAGIC	0xdeadbeef

static void rio_enum_timeout(unsigned long);

static void rio_init_em(struct rio_dev *rdev);

DEFINE_SPINLOCK(rio_global_list_lock);

static int next_destid = 0;
static int next_switchid = 0;
static int next_net = 0;
static int next_comptag;

static struct timer_list rio_enum_timer =
TIMER_INITIALIZER(rio_enum_timeout, 0, 0);
@@ -52,13 +57,6 @@ static int rio_mport_phys_table[] = {
	-1,
};

static int rio_sport_phys_table[] = {
	RIO_EFB_PAR_EP_FREE_ID,
	RIO_EFB_SER_EP_FREE_ID,
	RIO_EFB_SER_EP_FREC_ID,
	-1,
};

/**
 * rio_get_device_id - Get the base/extended device id for a device
 * @port: RIO master port
@@ -119,12 +117,26 @@ static int rio_clear_locks(struct rio_mport *port)
	u32 result;
	int ret = 0;

	/* Write component tag CSR magic complete value */
	rio_local_write_config_32(port, RIO_COMPONENT_TAG_CSR,
				  RIO_ENUM_CMPL_MAGIC);
	list_for_each_entry(rdev, &rio_devices, global_list)
	    rio_write_config_32(rdev, RIO_COMPONENT_TAG_CSR,
				RIO_ENUM_CMPL_MAGIC);
	/* Assign component tag to all devices */
	next_comptag = 1;
	rio_local_write_config_32(port, RIO_COMPONENT_TAG_CSR, next_comptag++);

	list_for_each_entry(rdev, &rio_devices, global_list) {
		/* Mark device as discovered */
		rio_read_config_32(rdev,
				   rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR,
				   &result);
		rio_write_config_32(rdev,
				    rdev->phys_efptr + RIO_PORT_GEN_CTL_CSR,
				    result | RIO_PORT_GEN_DISCOVERED);

		rio_write_config_32(rdev, RIO_COMPONENT_TAG_CSR, next_comptag);
		rdev->comp_tag = next_comptag++;
		if (next_comptag >= 0x10000) {
			pr_err("RIO: Component Tag Counter Overflow\n");
			break;
		}
	}

	/* Release host device id locks */
	rio_local_write_config_32(port, RIO_HOST_DID_LOCK_CSR,
@@ -266,6 +278,30 @@ static void rio_route_set_ops(struct rio_dev *rdev)
		       rio_name(rdev));
}

/**
 * rio_em_set_ops- Sets Error Managment operations for a particular vendor switch
 * @rdev: RIO device
 *
 * Searches the RIO EM ops table for known switch types. If the vid
 * and did match a switch table entry, then set the em_init() and
 * em_handle() ops to the table entry values.
 */
static void rio_em_set_ops(struct rio_dev *rdev)
{
	struct rio_em_ops *cur = __start_rio_em_ops;
	struct rio_em_ops *end = __end_rio_em_ops;

	while (cur < end) {
		if ((cur->vid == rdev->vid) && (cur->did == rdev->did)) {
			pr_debug("RIO: adding EM ops for %s\n", rio_name(rdev));
			rdev->rswitch->em_init = cur->init_hook;
			rdev->rswitch->em_handle = cur->handler_hook;
			break;
		}
		cur++;
	}
}

/**
 * rio_add_device- Adds a RIO device to the device model
 * @rdev: RIO device
@@ -336,8 +372,14 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
	rdev->asm_rev = result >> 16;
	rio_mport_read_config_32(port, destid, hopcount, RIO_PEF_CAR,
				 &rdev->pef);
	if (rdev->pef & RIO_PEF_EXT_FEATURES)
	if (rdev->pef & RIO_PEF_EXT_FEATURES) {
		rdev->efptr = result & 0xffff;
		rdev->phys_efptr = rio_mport_get_physefb(port, 0, destid,
							 hopcount);

		rdev->em_efptr = rio_mport_get_feature(port, 0, destid,
						hopcount, RIO_EFB_ERR_MGMNT);
	}

	rio_mport_read_config_32(port, destid, hopcount, RIO_SRC_OPS_CAR,
				 &rdev->src_ops);
@@ -366,6 +408,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
		rswitch->switchid = next_switchid;
		rswitch->hopcount = hopcount;
		rswitch->destid = destid;
		rswitch->port_ok = 0;
		rswitch->route_table = kzalloc(sizeof(u8)*
					RIO_MAX_ROUTE_ENTRIES(port->sys_size),
					GFP_KERNEL);
@@ -379,6 +422,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
		dev_set_name(&rdev->dev, "%02x:s:%04x", rdev->net->id,
			     rdev->rswitch->switchid);
		rio_route_set_ops(rdev);
		rio_em_set_ops(rdev);

		if (do_enum && rdev->rswitch->clr_table)
			rdev->rswitch->clr_table(port, destid, hopcount,
@@ -429,23 +473,29 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net,
 *
 * Reads the port error status CSR for a particular switch port to
 * determine if the port has an active link.  Returns
 * %PORT_N_ERR_STS_PORT_OK if the port is active or %0 if it is
 * %RIO_PORT_N_ERR_STS_PORT_OK if the port is active or %0 if it is
 * inactive.
 */
static int
rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport)
{
	u32 result;
	u32 result = 0;
	u32 ext_ftr_ptr;

	int *entry = rio_sport_phys_table;

	do {
		if ((ext_ftr_ptr =
		     rio_mport_get_feature(port, 0, destid, hopcount, *entry)))
	ext_ftr_ptr = rio_mport_get_efb(port, 0, destid, hopcount, 0);

	while (ext_ftr_ptr) {
		rio_mport_read_config_32(port, destid, hopcount,
					 ext_ftr_ptr, &result);
		result = RIO_GET_BLOCK_ID(result);
		if ((result == RIO_EFB_SER_EP_FREE_ID) ||
		    (result == RIO_EFB_SER_EP_FREE_ID_V13P) ||
		    (result == RIO_EFB_SER_EP_FREC_ID))
			break;
	} while (*++entry >= 0);

		ext_ftr_ptr = rio_mport_get_efb(port, 0, destid, hopcount,
						ext_ftr_ptr);
	}

	if (ext_ftr_ptr)
		rio_mport_read_config_32(port, destid, hopcount,
@@ -453,7 +503,7 @@ rio_sport_is_active(struct rio_mport *port, u16 destid, u8 hopcount, int sport)
					 RIO_PORT_N_ERR_STS_CSR(sport),
					 &result);

	return (result & PORT_N_ERR_STS_PORT_OK);
	return result & RIO_PORT_N_ERR_STS_PORT_OK;
}

/**
@@ -762,8 +812,10 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
		    rio_name(rdev), rdev->vid, rdev->did, num_ports);
		sw_destid = next_destid;
		for (port_num = 0; port_num < num_ports; port_num++) {
			if (sw_inport == port_num)
			if (sw_inport == port_num) {
				rdev->rswitch->port_ok |= (1 << port_num);
				continue;
			}

			cur_destid = next_destid;

@@ -773,6 +825,7 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
				pr_debug(
				    "RIO: scanning device on port %d\n",
				    port_num);
				rdev->rswitch->port_ok |= (1 << port_num);
				rio_route_add_entry(port, rdev->rswitch,
						RIO_GLOBAL_TABLE,
						RIO_ANY_DESTID(port->sys_size),
@@ -797,9 +850,28 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
						    port_num;
					}
				}
			} else {
				/* If switch supports Error Management,
				 * set PORT_LOCKOUT bit for unused port
				 */
				if (rdev->em_efptr)
					rio_set_port_lockout(rdev, port_num, 1);

				rdev->rswitch->port_ok &= ~(1 << port_num);
			}
		}

		/* Direct Port-write messages to the enumeratiing host */
		if ((rdev->src_ops & RIO_SRC_OPS_PORT_WRITE) &&
		    (rdev->em_efptr)) {
			rio_write_config_32(rdev,
					rdev->em_efptr + RIO_EM_PW_TGT_DEVID,
					(port->host_deviceid << 16) |
					(port->sys_size << 15));
		}

		rio_init_em(rdev);

		/* Check for empty switch */
		if (next_destid == sw_destid) {
			next_destid++;
@@ -819,21 +891,16 @@ static int __devinit rio_enum_peer(struct rio_net *net, struct rio_mport *port,
 * rio_enum_complete- Tests if enumeration of a network is complete
 * @port: Master port to send transaction
 *
 * Tests the Component Tag CSR for presence of the magic enumeration
 * complete flag. Return %1 if enumeration is complete or %0 if
 * Tests the Component Tag CSR for non-zero value (enumeration
 * complete flag). Return %1 if enumeration is complete or %0 if
 * enumeration is incomplete.
 */
static int rio_enum_complete(struct rio_mport *port)
{
	u32 tag_csr;
	int ret = 0;

	rio_local_read_config_32(port, RIO_COMPONENT_TAG_CSR, &tag_csr);

	if (tag_csr == RIO_ENUM_CMPL_MAGIC)
		ret = 1;

	return ret;
	return (tag_csr & 0xffff) ? 1 : 0;
}

/**
@@ -915,7 +982,7 @@ rio_disc_peer(struct rio_net *net, struct rio_mport *port, u16 destid,
 *
 * Reads the port error status CSR for the master port to
 * determine if the port has an active link.  Returns
 * %PORT_N_ERR_STS_PORT_OK if the  master port is active
 * %RIO_PORT_N_ERR_STS_PORT_OK if the  master port is active
 * or %0 if it is inactive.
 */
static int rio_mport_is_active(struct rio_mport *port)
@@ -936,7 +1003,7 @@ static int rio_mport_is_active(struct rio_mport *port)
					 RIO_PORT_N_ERR_STS_CSR(port->index),
					 &result);

	return (result & PORT_N_ERR_STS_PORT_OK);
	return result & RIO_PORT_N_ERR_STS_PORT_OK;
}

/**
@@ -1007,6 +1074,32 @@ static void rio_update_route_tables(struct rio_mport *port)
	}
}

/**
 * rio_init_em - Initializes RIO Error Management (for switches)
 * @port: Master port associated with the RIO network
 *
 * For each enumerated switch, call device-specific error management
 * initialization routine (if supplied by the switch driver).
 */
static void rio_init_em(struct rio_dev *rdev)
{
	if (rio_is_switch(rdev) && (rdev->em_efptr) &&
	    (rdev->rswitch->em_init)) {
		rdev->rswitch->em_init(rdev);
	}
}

/**
 * rio_pw_enable - Enables/disables port-write handling by a master port
 * @port: Master port associated with port-write handling
 * @enable:  1=enable,  0=disable
 */
static void rio_pw_enable(struct rio_mport *port, int enable)
{
	if (port->ops->pwenable)
		port->ops->pwenable(port, enable);
}

/**
 * rio_enum_mport- Start enumeration through a master port
 * @mport: Master port to send transactions
@@ -1050,6 +1143,7 @@ int __devinit rio_enum_mport(struct rio_mport *mport)
		}
		rio_update_route_tables(mport);
		rio_clear_locks(mport);
		rio_pw_enable(mport, 1);
	} else {
		printk(KERN_INFO "RIO: master port %d link inactive\n",
		       mport->id);
+328 −0
Original line number Diff line number Diff line
@@ -5,6 +5,10 @@
 * Copyright 2005 MontaVista Software, Inc.
 * Matt Porter <mporter@kernel.crashing.org>
 *
 * Copyright 2009 Integrated Device Technology, Inc.
 * Alex Bounine <alexandre.bounine@idt.com>
 * - Added Port-Write/Error Management initialization and handling
 *
 * 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
@@ -332,6 +336,329 @@ int rio_release_outb_dbell(struct rio_dev *rdev, struct resource *res)
	return rc;
}

/**
 * rio_request_inb_pwrite - request inbound port-write message service
 * @mport: RIO device to which register inbound port-write callback routine
 * @pwcback: Callback routine to execute when port-write is received
 *
 * Binds a port-write callback function to the RapidIO device.
 * Returns 0 if the request has been satisfied.
 */
int rio_request_inb_pwrite(struct rio_dev *rdev,
	int (*pwcback)(struct rio_dev *rdev, union rio_pw_msg *msg, int step))
{
	int rc = 0;

	spin_lock(&rio_global_list_lock);
	if (rdev->pwcback != NULL)
		rc = -ENOMEM;
	else
		rdev->pwcback = pwcback;

	spin_unlock(&rio_global_list_lock);
	return rc;
}
EXPORT_SYMBOL_GPL(rio_request_inb_pwrite);

/**
 * rio_release_inb_pwrite - release inbound port-write message service
 * @rdev: RIO device which registered for inbound port-write callback
 *
 * Removes callback from the rio_dev structure. Returns 0 if the request
 * has been satisfied.
 */
int rio_release_inb_pwrite(struct rio_dev *rdev)
{
	int rc = -ENOMEM;

	spin_lock(&rio_global_list_lock);
	if (rdev->pwcback) {
		rdev->pwcback = NULL;
		rc = 0;
	}

	spin_unlock(&rio_global_list_lock);
	return rc;
}
EXPORT_SYMBOL_GPL(rio_release_inb_pwrite);

/**
 * rio_mport_get_physefb - Helper function that returns register offset
 *                      for Physical Layer Extended Features Block.
 * @rdev: RIO device
 */
u32
rio_mport_get_physefb(struct rio_mport *port, int local,
		      u16 destid, u8 hopcount)
{
	u32 ext_ftr_ptr;
	u32 ftr_header;

	ext_ftr_ptr = rio_mport_get_efb(port, local, destid, hopcount, 0);

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

		ftr_header = RIO_GET_BLOCK_ID(ftr_header);
		switch (ftr_header) {

		case RIO_EFB_SER_EP_ID_V13P:
		case RIO_EFB_SER_EP_REC_ID_V13P:
		case RIO_EFB_SER_EP_FREE_ID_V13P:
		case RIO_EFB_SER_EP_ID:
		case RIO_EFB_SER_EP_REC_ID:
		case RIO_EFB_SER_EP_FREE_ID:
		case RIO_EFB_SER_EP_FREC_ID:

			return ext_ftr_ptr;

		default:
			break;
		}

		ext_ftr_ptr = rio_mport_get_efb(port, local, destid,
						hopcount, ext_ftr_ptr);
	}

	return ext_ftr_ptr;
}

/**
 * rio_get_comptag - Begin or continue searching for a RIO device by component tag
 * @comp_tag: RIO component tad to match
 * @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 @comp_tag, 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.
 */
static struct rio_dev *rio_get_comptag(u32 comp_tag, 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 (rdev->comp_tag == comp_tag)
			goto exit;
		n = n->next;
	}
	rdev = NULL;
exit:
	spin_unlock(&rio_global_list_lock);
	return rdev;
}

/**
 * rio_set_port_lockout - Sets/clears LOCKOUT bit (RIO EM 1.3) for a switch port.
 * @rdev: Pointer to RIO device control structure
 * @pnum: Switch port number to set LOCKOUT bit
 * @lock: Operation : set (=1) or clear (=0)
 */
int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock)
{
	u8 hopcount = 0xff;
	u16 destid = rdev->destid;
	u32 regval;

	if (rdev->rswitch) {
		destid = rdev->rswitch->destid;
		hopcount = rdev->rswitch->hopcount;
	}

	rio_mport_read_config_32(rdev->net->hport, destid, hopcount,
				 rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum),
				 &regval);
	if (lock)
		regval |= RIO_PORT_N_CTL_LOCKOUT;
	else
		regval &= ~RIO_PORT_N_CTL_LOCKOUT;

	rio_mport_write_config_32(rdev->net->hport, destid, hopcount,
				  rdev->phys_efptr + RIO_PORT_N_CTL_CSR(pnum),
				  regval);
	return 0;
}

/**
 * rio_inb_pwrite_handler - process inbound port-write message
 * @pw_msg: pointer to inbound port-write message
 *
 * Processes an inbound port-write message. Returns 0 if the request
 * has been satisfied.
 */
int rio_inb_pwrite_handler(union rio_pw_msg *pw_msg)
{
	struct rio_dev *rdev;
	struct rio_mport *mport;
	u8 hopcount;
	u16 destid;
	u32 err_status;
	int rc, portnum;

	rdev = rio_get_comptag(pw_msg->em.comptag, NULL);
	if (rdev == NULL) {
		/* Someting bad here (probably enumeration error) */
		pr_err("RIO: %s No matching device for CTag 0x%08x\n",
			__func__, pw_msg->em.comptag);
		return -EIO;
	}

	pr_debug("RIO: Port-Write message from %s\n", rio_name(rdev));

#ifdef DEBUG_PW
	{
	u32 i;
	for (i = 0; i < RIO_PW_MSG_SIZE/sizeof(u32);) {
			pr_debug("0x%02x: %08x %08x %08x %08x",
				 i*4, pw_msg->raw[i], pw_msg->raw[i + 1],
				 pw_msg->raw[i + 2], pw_msg->raw[i + 3]);
			i += 4;
	}
	pr_debug("\n");
	}
#endif

	/* Call an external service function (if such is registered
	 * for this device). This may be the service for endpoints that send
	 * device-specific port-write messages. End-point messages expected
	 * to be handled completely by EP specific device driver.
	 * For switches rc==0 signals that no standard processing required.
	 */
	if (rdev->pwcback != NULL) {
		rc = rdev->pwcback(rdev, pw_msg, 0);
		if (rc == 0)
			return 0;
	}

	/* For End-point devices processing stops here */
	if (!(rdev->pef & RIO_PEF_SWITCH))
		return 0;

	if (rdev->phys_efptr == 0) {
		pr_err("RIO_PW: Bad switch initialization for %s\n",
			rio_name(rdev));
		return 0;
	}

	mport = rdev->net->hport;
	destid = rdev->rswitch->destid;
	hopcount = rdev->rswitch->hopcount;

	/*
	 * Process the port-write notification from switch
	 */

	portnum = pw_msg->em.is_port & 0xFF;

	if (rdev->rswitch->em_handle)
		rdev->rswitch->em_handle(rdev, portnum);

	rio_mport_read_config_32(mport, destid, hopcount,
			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
			&err_status);
	pr_debug("RIO_PW: SP%d_ERR_STS_CSR=0x%08x\n", portnum, err_status);

	if (pw_msg->em.errdetect) {
		pr_debug("RIO_PW: RIO_EM_P%d_ERR_DETECT=0x%08x\n",
			 portnum, pw_msg->em.errdetect);
		/* Clear EM Port N Error Detect CSR */
		rio_mport_write_config_32(mport, destid, hopcount,
			rdev->em_efptr + RIO_EM_PN_ERR_DETECT(portnum), 0);
	}

	if (pw_msg->em.ltlerrdet) {
		pr_debug("RIO_PW: RIO_EM_LTL_ERR_DETECT=0x%08x\n",
			 pw_msg->em.ltlerrdet);
		/* Clear EM L/T Layer Error Detect CSR */
		rio_mport_write_config_32(mport, destid, hopcount,
			rdev->em_efptr + RIO_EM_LTL_ERR_DETECT, 0);
	}

	/* Clear Port Errors */
	rio_mport_write_config_32(mport, destid, hopcount,
			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
			err_status & RIO_PORT_N_ERR_STS_CLR_MASK);

	if (rdev->rswitch->port_ok & (1 << portnum)) {
		if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) {
			rdev->rswitch->port_ok &= ~(1 << portnum);
			rio_set_port_lockout(rdev, portnum, 1);

			rio_mport_write_config_32(mport, destid, hopcount,
				rdev->phys_efptr +
					RIO_PORT_N_ACK_STS_CSR(portnum),
				RIO_PORT_N_ACK_CLEAR);

			/* Schedule Extraction Service */
			pr_debug("RIO_PW: Device Extraction on [%s]-P%d\n",
			       rio_name(rdev), portnum);
		}
	} else {
		if (err_status & RIO_PORT_N_ERR_STS_PORT_OK) {
			rdev->rswitch->port_ok |= (1 << portnum);
			rio_set_port_lockout(rdev, portnum, 0);

			/* Schedule Insertion Service */
			pr_debug("RIO_PW: Device Insertion on [%s]-P%d\n",
			       rio_name(rdev), portnum);
		}
	}

	/* Clear Port-Write Pending bit */
	rio_mport_write_config_32(mport, destid, hopcount,
			rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum),
			RIO_PORT_N_ERR_STS_PW_PEND);

	return 0;
}
EXPORT_SYMBOL_GPL(rio_inb_pwrite_handler);

/**
 * rio_mport_get_efb - get pointer to next extended features block
 * @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
 * @from: Offset of  current Extended Feature block header (if 0 starts
 * from	ExtFeaturePtr)
 */
u32
rio_mport_get_efb(struct rio_mport *port, int local, u16 destid,
		      u8 hopcount, u32 from)
{
	u32 reg_val;

	if (from == 0) {
		if (local)
			rio_local_read_config_32(port, RIO_ASM_INFO_CAR,
						 &reg_val);
		else
			rio_mport_read_config_32(port, destid, hopcount,
						 RIO_ASM_INFO_CAR, &reg_val);
		return reg_val & RIO_EXT_FTR_PTR_MASK;
	} else {
		if (local)
			rio_local_read_config_32(port, from, &reg_val);
		else
			rio_mport_read_config_32(port, destid, hopcount,
						 from, &reg_val);
		return RIO_GET_BLOCK_ID(reg_val);
	}
}

/**
 * rio_mport_get_feature - query for devices' extended features
 * @port: Master port to issue transaction
@@ -472,6 +799,7 @@ int rio_std_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
				RIO_STD_RTE_CONF_PORT_SEL_CSR,
				(u32)route_port);
	}

	udelay(10);
	return 0;
}
+30 −0
Original line number Diff line number Diff line
@@ -18,6 +18,10 @@

extern u32 rio_mport_get_feature(struct rio_mport *mport, int local, u16 destid,
				 u8 hopcount, int ftr);
extern u32 rio_mport_get_physefb(struct rio_mport *port, int local,
				 u16 destid, u8 hopcount);
extern u32 rio_mport_get_efb(struct rio_mport *port, int local, u16 destid,
			     u8 hopcount, u32 from);
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);
@@ -29,6 +33,7 @@ extern int rio_std_route_get_entry(struct rio_mport *mport, u16 destid,
				   u8 *route_port);
extern int rio_std_route_clr_table(struct rio_mport *mport, u16 destid,
				   u8 hopcount, u16 table);
extern int rio_set_port_lockout(struct rio_dev *rdev, u32 pnum, int lock);

/* Structures internal to the RIO core code */
extern struct device_attribute rio_dev_attrs[];
@@ -61,3 +66,28 @@ extern struct rio_route_ops __end_rio_route_ops[];

#define RIO_GET_DID(size, x)	(size ? (x & 0xffff) : ((x & 0x00ff0000) >> 16))
#define RIO_SET_DID(size, x)	(size ? (x & 0xffff) : ((x & 0x000000ff) << 16))

/*
 *   RapidIO Error Management
 */
extern struct rio_em_ops __start_rio_em_ops[];
extern struct rio_em_ops __end_rio_em_ops[];

/* Helpers internal to the RIO core code */
#define DECLARE_RIO_EM_SECTION(section, name, vid, did, init_hook, em_hook)  \
	static const struct rio_em_ops __rio_em_##name __used   \
	__section(section) = { vid, did, init_hook, em_hook };

/**
 * DECLARE_RIO_EM_OPS - Registers switch EM operations
 * @vid: RIO vendor ID
 * @did: RIO device ID
 * @init_hook: Callback that initializes device specific EM
 * @em_hook: Callback that handles device specific EM
 *
 * A &struct rio_em_ops is initialized with the ops and placed into a
 * RIO-specific kernel section.
 */
#define DECLARE_RIO_EM_OPS(vid, did, init_hook, em_hook)	\
	DECLARE_RIO_EM_SECTION(.rio_em_ops, vid##did,		\
			vid, did, init_hook, em_hook)
+24 −0
Original line number Diff line number Diff line
@@ -25,6 +25,9 @@
#define SPP_ROUTE_CFG_DESTID(n)	(0x11070 + 0x100*n)
#define SPP_ROUTE_CFG_PORT(n)	(0x11074 + 0x100*n)

#define TSI568_SP_MODE_BC	0x10004
#define  TSI568_SP_MODE_PW_DIS	0x08000000

static int
tsi568_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount,
		       u16 table, u16 route_destid, u8 route_port)
@@ -104,3 +107,24 @@ tsi568_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
}

DECLARE_RIO_ROUTE_OPS(RIO_VID_TUNDRA, RIO_DID_TSI568, tsi568_route_add_entry, tsi568_route_get_entry, tsi568_route_clr_table);

static int
tsi568_em_init(struct rio_dev *rdev)
{
	struct rio_mport *mport = rdev->net->hport;
	u16 destid = rdev->rswitch->destid;
	u8 hopcount = rdev->rswitch->hopcount;
	u32 regval;

	pr_debug("TSI568 %s [%d:%d]\n", __func__, destid, hopcount);

	/* Make sure that Port-Writes are disabled (for all ports) */
	rio_mport_read_config_32(mport, destid, hopcount,
			TSI568_SP_MODE_BC, &regval);
	rio_mport_write_config_32(mport, destid, hopcount,
			TSI568_SP_MODE_BC, regval | TSI568_SP_MODE_PW_DIS);

	return 0;
}

DECLARE_RIO_EM_OPS(RIO_VID_TUNDRA, RIO_DID_TSI568, tsi568_em_init, NULL);
Loading