[ixp1200] [Announce]: kernel module to reset IXP2400 from host

romit dasgupta romit at myrealbox.com
Tue Mar 16 05:50:38 EST 2004


Hello all,
               Many a times while developing kernel modules, I tend to 
hang my ENP-2611 radisys board. I thought it would be nice to develop an 
utility to reset the board from my host system. Attached is the source 
code for the same. It is easy to use. Once you have installed the kernel 
module in your host system, you can just issue
echo "" > /proc/ntb
and you can see the ENP board rebooting.  The caveats are on the 
beginning of the source code.  I hope it will make life easier for 
kernel programmers using radisys board.

Regards,
-Romit

-------------- next part --------------
/*
 * This software may be used and distributed according to the terms
 * of GNU General Public License, incorporated herein by reference.
 */

/*
 * Author Romit Dasgupta (romit at myrealbox.com)
 * Apropos Infotech Ltd. (www.aproposinfotech.com),
 * Future Systems, Inc. (www.future.co.kr).
 */

/*
 * 21555 non transparent bridge driver to reset ENP-2611 
 * board from the host CPU.
 * After installing this module, if you want to reset
 * ENP-2611 board, just type 
 * echo "" > /proc/ntb
 * Caveat: Load this module before invoking a reset 
 *         on ENP-2611 from redboot.
 * This has been compiled for 2.4.20-8
 * 
 * Version history : Released on 16th March 2004.
 *
 */

#include <linux/module.h> /* module_init, module_exit */
#include <linux/init.h> /* various macros */
#include <linux/pci.h> /* pci related fns */
#include <linux/proc_fs.h> /* proc related */
#include <linux/ioport.h> /* request_mem_region etc. */
#include <asm/io.h> /* ioremap, iounmap */

#ifdef DEBUG
#define Debug(message) printk message
#else
#define Debug(message)
#endif

#ifndef PCI_DEVICE_ID_INTEL_21555
#define PCI_DEVICE_ID_INTEL_21555 0xb555
#endif

#define DOWNSTREAM_XLAT1    0x98
#define CFG_ADDR_UCA_21555  0x88


static volatile char config_space[64*4];
static volatile char *cap_csr;

static struct pci_device_id bridge_tbl[] __devinitdata = {
	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_21555, 
		PCI_ANY_ID, PCI_ANY_ID, },
	{0,}
};

static struct pci_dev *pcidev_21555;

/*
 * Got the idea of interface checking from the mvnet source code. 
 */
static int
check_21555_interface(struct pci_dev *pdev)
{
    u32 readword = 0, readback = 0;
    int result;

    /*
     *  The upstream configuration address register can't be written
     *  from the primary interface.  Try to modify this register to
     *  determine which interface we are connected to.
     */

    result = pci_read_config_dword(pdev, CFG_ADDR_UCA_21555, &readword);
    if (result != PCIBIOS_SUCCESSFUL) {
        Debug(("21555: Error reading PCI configuration\n"));
        return -EIO;
    }

    result = pci_write_config_dword(pdev, CFG_ADDR_UCA_21555, ~readword);
    if (result != PCIBIOS_SUCCESSFUL) {
        Debug(("21555: Error writing PCI configuration\n"));
        return -EIO;
    }

    result = pci_read_config_dword(pdev, CFG_ADDR_UCA_21555, &readback);
    if (result != PCIBIOS_SUCCESSFUL) {
        Debug(("21555: Error reading back PCI configuration\n"));
        return -EIO;
    }

    /* Return 1 for primary, 0 for secondary interface */
    return (readword == readback) ? 1 : 0;
}

static int configure_21555_CSRs(struct pci_dev *pdev)
{

	int result;
	unsigned int value;

	/*
	 * The IXP CSR space is mapped from location 
	 * 0 of PCI address space.
	 */
	result = pci_write_config_dword(pdev, DOWNSTREAM_XLAT1, 0x00000000);
	if (result != PCIBIOS_SUCCESSFUL) {
		Debug(("21555: Error writing translation CSR for Downstream 1.\n"));
		return -EIO;
	}
	result = pci_read_config_dword(pdev, DOWNSTREAM_XLAT1, &value);
	if (result != PCIBIOS_SUCCESSFUL) {
		Debug(("21555: Error reading translation CSR for Downstream 1.\n"));
		return -EIO;
	} else 
		Debug(("21555: Translation Base for Downstream 1 is now 0x%x\n", value));

	return result;
}

static int host_21555_pci_init(struct pci_dev *pdev, 
				const struct pci_device_id *ent)
{
	int result = -ENODEV;
	int primary;

	primary = check_21555_interface(pdev);
	if (unlikely(primary < 0))  goto err_out;
	Debug(("21555: %s interface\n", primary ? "host" : "local"));
	if (unlikely(primary == 0)) {
		printk("21555: Uhh! Trying to load this module on the secondary???\n");
		goto err_out;
	} else 
		result = 0;

	pcidev_21555 = pdev;

        if (check_mem_region(pci_resource_start(pdev, 2), 1024*1024)) {
                Debug(("21555: BAR 2 already mapped.\n"));
                result = -EBUSY;
                goto err_out1;
        } else
                request_mem_region(pci_resource_start(pdev, 2), 1024*1024,
                                "21555 downstream window 1");

	cap_csr = ioremap_nocache(pci_resource_start(pdev, 2), 1024*1024);
	if (cap_csr == NULL) {
	    result = -ENOMEM;
	    goto err_out;
	} else 
	    Debug(("21555: cap_csr at vaddr 0x%x\n", (unsigned int) cap_csr));

	if (pci_enable_device(pdev) < 0) {
		Debug(("21555: Couldn't enable 21555 master.\n"));
		goto err_out1;
	}

	pci_set_master(pdev);
	/* Smooth sailing so far. Load the configuration registers now. */
	result = configure_21555_CSRs(pdev);
	if (!result) return result;

err_out1:
	if (cap_csr) {
	    iounmap((void *)cap_csr);
	    cap_csr = NULL;
	}
err_out:
	return result;
}

static void host_21555_pci_remove(struct pci_dev *pdev)
{
	pci_disable_device(pdev);
	if (cap_csr) {
		iounmap((void *) cap_csr);
		cap_csr = NULL;
	}
        if (check_mem_region(pci_resource_start(pdev, 2), 1024*1024)) {
	    release_mem_region(pci_resource_start(pcidev_21555, 2), 1024*1024);
	}
	return;
}

static struct pci_driver host_21555_driver = {
	name: "host_21555",
	id_table: bridge_tbl,
	probe: host_21555_pci_init,
	remove: host_21555_pci_remove,
	/* TODO: Enable power management for this driver */
};

static int write_21555(struct file *file, const char *buffer, 
			unsigned long count, void *data)
{
	int i;

	MOD_INC_USE_COUNT;
	/*
	 * Take care of user forced resets on ENP
	 * by re-writing config space.
	 */
	for(i = 0; i < 256; i++) 
	    pci_write_config_byte(pcidev_21555, i, config_space[i]);

	/*
	 * Reset the IXP.
	 * Pg 332 of Intel IXP2400/2800 Network Processor, 
	 * Programmers reference manual (July 2003).
	 */
	*((int *)(cap_csr + 0x4a0c)) = (1<<16);

	MOD_DEC_USE_COUNT;

	return count;
}

static struct proc_dir_entry *write_21555_entry;

int __init host_21555_init(void)
{
	int result;
	int i;
	char value;
	printk("21555: Loading 21555 driver on the host CPU.\n");
	result = pci_module_init(&host_21555_driver);
	if (!result) {
	    write_21555_entry = create_proc_entry("ntb",
					0644,
					NULL);
	    if (write_21555_entry == NULL) {
		result = -ENOMEM;
		goto err_out;
	    }
	    write_21555_entry->owner = THIS_MODULE;
	    write_21555_entry->write_proc = write_21555;
	} else
	  return result;

	/* 
	 * save config space to handle
	 * resets of the ENP by user. 
	 */

	for(i = 0; i < 256; i++)  {
	    pci_read_config_byte(pcidev_21555, i, &value);
	    config_space[i] = value;
	}
	return result;
err_out:
	pci_unregister_driver(&host_21555_driver);
	return result;
}

void __exit host_21555_exit(void)
{
	int i;
	/* 
	 * handle cases where the module was 
	 * loaded and unloaded after a user
	 * reset of ENP. 
	 */
	for(i = 0; i < 256; i++) 
	    pci_write_config_byte(pcidev_21555, i, config_space[i]);
	printk("21555: Removing the 21555 driver on the host CPU.\n");
	pci_unregister_driver(&host_21555_driver);
	remove_proc_entry("ntb", NULL);
	return;
}

module_init(host_21555_init);
module_exit(host_21555_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel 21555 bridge driver used to reset Radisys board from host");
/*
 * assmuming /usr/src/linux -> <actual source tree of currently running kernel>
 * Compile with
 * gcc -Wall -DMODULE -D__KERNEL__ -I /usr/src/linux/include -c -O2 hostb.c
 */


More information about the ixp1200 mailing list