This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM3894 PCIe rescan doesn't work

Dear All,


as they say here.

"Note-3: TI81XX PCIe hardware does not support hot plug and if an EP directly connected to the TI81XX RC goes down (e.g., powered down or disabled), the complete PCIe h/w initialization needs to be repeated and PCI enumeration re-triggered. This is NOT SUPPORTED by the RC driver and will require code modification to handle such cases."

but this is exactly our case. The EP goes down and we need to rescan. Well has anybody already solved this issue (i think need driver modification pcie-ti81xx.c).

From my test I see that when i do rescan command (using sysfs i.e.) evrything goes till the:

ti81xx_pci_read_config

that give us the following error:

The note above says "the complete PCIe h/w initialization needs to be repeated and PCI enumeration re-triggered." So I think that when i want to perform a rescan (maybe because with a HW gpio or similar i know that there is something new on the bus ) i should perform something similar to the

ti81xx_pcie_probe

Could be correct this kind of approach?

anybody else worked on this issue?


Thanks

Omar

  • The same problem , it could be a path to solve the issue:
    e2e.ti.com/.../154734

    I'm working on it.
    Omar
  • Omar,

    Can you check if there is something done regarding this in the latest version of the TI81xx linux kernel?

    arago-project.org/.../projects;a=shortlog;h=refs/heads/ti81xx-master

    BR
    Pavel
  • I'm checking right now...
    I will let you know,
    thanks
    Omar
  • well, it seem no...
    my version of pcie-ti81xx.c has some difference but nothing related to rescan fix...

    Omar
  • pcie-ti81xx.c
    /*
     * PCIe RC (Host) Driver for TI81XX PCIe Module (PCIESS) configured as Root
     * Complex.
     *
     * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
     *
     * 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 version 2.
     *
     * This program is distributed "as is" WITHOUT ANY WARRANTY of any
     * kind, whether express or implied; without even the implied warranty
     * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     */
    
    /*
     * General TODO:
     * - All of the register accesses are through raw_read/writes which eat up lines
     *   especially when needed to mask bits before writing. Better to add
     *   read-mask-write kind of wrappers.
     * - Lots of private data to maintain - group together inside a structure and
     *   provide accessor functions?
     * - Possibility of adding hw de-initialization sequence (also, re-enumeration /
     *   PM impact)
     */
    
    #include <linux/kernel.h>
    #include <linux/types.h>
    #include <linux/platform_device.h>
    #include <linux/interrupt.h>
    #include <linux/delay.h>
    #include <linux/clk.h>
    #include <linux/irq.h>
    #include <linux/slab.h>
    #include <linux/msi.h>
    #include <linux/pci.h>
    
    #include <asm/irq.h>
    #include <asm/signal.h>
    #include <asm/mach/pci.h>
    
    #include <plat/ti81xx.h>
    
    #include "pcie-ti81xx.h"
    #include "prm.h"
    #include "prm2xxx_3xxx.h"
    #include "prm-regbits-81xx.h"
    
    #define DRIVER_NAME		"ti81xx_pcie"
    
    static struct platform_device *pcie_pdev;
    static int msi_irq_base;
    static int msi_irq_num;
    static int force_x1;
    static int msi_inv;
    static unsigned short int device_id;
    
    /* Details for inbound access to RAM, passed from platform data */
    static u32 ram_base, ram_end;
    
    /* Used for BAR0 translation */
    static u32 reg_phys;
    
    /* Used for register accesses */
    static u32 reg_virt;
    
    /* Interrupt resources */
    static int legacy_irq;
    static int msi_irq;
    
    /*
     * Protect io accesses as it involves setting IOBASE register
     *
     * FIXME: We use differnet locks for IO and config accesses. This should be OK
     * as the h/w spec doesn't mention any restriction on config and io accesses to
     * be performed exclusively
     */
    static DEFINE_SPINLOCK(ti81xx_pci_io_lock);
    
    /* FIXME: per il rescan ... */
    static int ti81xx_pcie_rescan;
    
    
    /*
     *  Application Register Offsets
     */
    #define PCISTATSET                      0x010
    #define CMD_STATUS			0x004
    #define CFG_SETUP			0x008
    #define IOBASE				0x00c
    #define OB_SIZE				0x030
    #define MSI_IRQ				0x054
    #define OB_OFFSET_INDEX(n)              (0x200 + (8 * n))     /* 32 Registers */
    #define OB_OFFSET_HI(n)			(0x204 + (8 * n))     /* 32 Registers */
    #define IB_BAR0				0x300
    #define IB_START0_LO			0x304
    #define IB_START0_HI			0x308
    #define IB_OFFSET0			0x30c
    #define ERR_IRQ_STATUS_RAW		0x1c0
    #define ERR_IRQ_STATUS			0x1c4
    #define MSI0_IRQ_STATUS			0x104
    #define MSI0_IRQ_ENABLE_SET		0x108
    #define MSI0_IRQ_ENABLE_CLR		0x10c
    #define IRQ_ENABLE_SET			0x188
    #define IRQ_ENABLE_CLR			0x18c
    
    /*
     * PCIe Config Register Offsets (capabilities)
     */
    #define LINK_CAP			0x07c
    
    /*
     * PCIe Config Register Offsets (misc)
     */
    #define PL_LINK_CTRL			0x710
    #define DEBUG0				0x728
    #define PL_GEN2				0x80c
    
    /* Various regions in PCIESS address space */
    #define SPACE0_LOCAL_CFG_OFFSET		0x1000
    #define SPACE0_REMOTE_CFG_OFFSET	0x2000
    #define SPACE0_IO_OFFSET		0x3000
    
    /* Application command register values */
    #define DBI_CS2_EN_VAL			BIT(5)
    #define IB_XLAT_EN_VAL			BIT(2)
    #define OB_XLAT_EN_VAL			BIT(1)
    #define LTSSM_EN_VAL			BIT(0)
    
    /* Link training encodings as indicated in DEBUG0 register */
    #define LTSSM_STATE_MASK		0x1f
    #define LTSSM_STATE_L0			0x11
    
    /* Directed Speed Change */
    #define DIR_SPD				(1 << 17)
    
    /* Default value used for Command register */
    #define CFG_PCIM_CSR_VAL         (PCI_COMMAND_SERR			\
    					| PCI_COMMAND_PARITY		\
    					| PCI_COMMAND_INVALIDATE	\
    					| PCI_COMMAND_MASTER		\
    					| PCI_COMMAND_MEMORY		\
    					| PCI_COMMAND_INTX_DISABLE)
    
    /* Error mask for errors to check on CFG/IO */
    #define CFG_PCI_ERR_MASK	  ((0xf << 28) | (1 < 24))
    
    /* Outbound window size specified as power of 2 MB */
    #define CFG_PCIM_WIN_SZ_IDX	3
    #define CFG_PCIM_WIN_CNT	32
    
    /* Maximum MSIs supported by PCIESS */
    #define CFG_MAX_MSI_NUM		32
    
    static int get_and_clear_err(void)
    {
    	int status = __raw_readl(reg_virt + ERR_IRQ_STATUS_RAW);
    
    	if (status) {
    		/* The PCIESS interrupt status buts are "write 0 to clear" */
    		__raw_writel(~status, reg_virt + ERR_IRQ_STATUS);
    
    		/*
    		 * Clear all errors. We are not worried here about individual
    		 * status as no specific handling is required.
    		 */
    		__raw_writew(0xffff, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				PCI_STATUS);
    	}
    
    	return status;
    }
    
    /**
     * ti81xx_pcie_fault() - ARM abort handler for PCIe non-posted completion aborts
     * @addr: Address target on which the fault generated
     * @fsr: CP15 fault status register value
     * @regs: Pointer to register structure on abort
     *
     * Handles precise abort caused due to PCIe operation.
     *
     * Note that we are relying on virtual address filtering to determine if the
     * target of the precise aborts was a PCIe module access (i.e., config, I/O,
     * register) and only handle such aborts. We could check PCIe error status to
     * confirm if the abort was caused due to non-posted completion status received
     * by PCIESS, but this may not always be true and aborts from some downstream
     * devices, such as PCI-PCI bridges etc may not result into error status bit
     * getting set.
     *
     * Ignores and returns abort as unhandled otherwise.
     *
     * Also note that, using error status check (as was done in earlier
     * implementation) would also handle failed memory accesses (non-posted), but
     * address filerting based handling will cause aborts for memory accesses as the
     * addresses will be outside the PCIESS module space. This seems OK, as any
     * memory access after enumeration is sole responsibility of the driver and the
     * system integrator (e.g., access failures due to hotplug, suspend etc). If
     * using error check based handling, we also need to clear PCIe error status on
     * detecting errors.
     *
     * Note: Due to specific h/w implementation, we can't be sure of what kind of
     * error occurred (UR Completion, CA etc) and all we get is raw error IRQ status
     * and probably SERR which indicate 'some kind of' error - fatal or non-fatal is
     * received/happened.
     */
    static int
    ti81xx_pcie_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
    {
    	unsigned long instr = *(unsigned long *)regs->ARM_pc;
    
    	pr_debug(DRIVER_NAME ": Data abort: address = 0x%08lx "
    			"fsr = 0x%03x PC = 0x%08lx LR = 0x%08lx",
    			addr, fsr, regs->ARM_pc, regs->ARM_lr);
    
    #if 0
    	if (!get_and_clear_err())
    		return -1;
    #endif
    
    	/* Note: Only handle PCIESS module space access */
    	if ((addr < reg_virt) || (addr >= (reg_virt + SZ_16K)))
    		return -1;
    
    	/*
    	 * Mimic aborted read of all 1's as required to detect device/function
    	 * absence - check if the instruction that caused abort was a LOAD,
    	 */
    	if ((instr & 0x0c100000) == 0x04100000) {
    		int reg = (instr >> 12) & 15;
    		unsigned long val;
    
    		if (instr & 0x00400000)
    			val = 255;
    		else
    			val = -1;
    
    		regs->uregs[reg] = val;
    	}
    
    	regs->ARM_pc += 4;
    
    	pr_debug(DRIVER_NAME ": Handled PCIe abort\n");
    
    	return 0;
    }
    
    /**
     * set_outbound_trans() - Set PHYADDR <-> BUSADDR mapping for outbound
     */
    static void set_outbound_trans(u32 start, u32 end)
    {
    	int i, tr_size;
    
    	pr_debug(DRIVER_NAME ": Setting outbound translation for %#x-%#x\n",
    			start, end);
    
    	/* Set outbound translation size per window division */
    	__raw_writel(CFG_PCIM_WIN_SZ_IDX & 0x7, reg_virt + OB_SIZE);
    
    	tr_size = (1 << (CFG_PCIM_WIN_SZ_IDX & 0x7)) * SZ_1M;
    
    	/* Using Direct 1:1 mapping of RC <-> PCI memory space */
    	for (i = 0; (i < CFG_PCIM_WIN_CNT) && (start < end); i++) {
    		__raw_writel(start | 1, reg_virt + OB_OFFSET_INDEX(i));
    		__raw_writel(0,	reg_virt + OB_OFFSET_HI(i));
    		start += tr_size;
    	}
    
    	/* TODO: ensure unused translation regions are disabled */
    
    	/* Enable OB translation */
    	 __raw_writel(OB_XLAT_EN_VAL | __raw_readl(reg_virt + CMD_STATUS),
    			 reg_virt + CMD_STATUS);
    }
    
    /**
     * set_dbi_mode() - Set DBI mode to access overlaid BAR mask registers
     *
     * Since modification of dbi_cs2 involves different clock domain, read the
     * status back to ensure the transition is complete.
     */
    static inline void set_dbi_mode(void)
    {
    	 __raw_writel(DBI_CS2_EN_VAL | __raw_readl(reg_virt + CMD_STATUS),
    			 reg_virt + CMD_STATUS);
    
    	  /* FIXME: Need loop to check bit5 = 1? */
    	__raw_readl(reg_virt + CMD_STATUS);
    }
    
    /**
     * clear_dbi_mode() - Disable DBI mode
     *
     * Since modification of dbi_cs2 involves different clock domain, read the
     * status back to ensure the transition is complete.
     */
    static inline void clear_dbi_mode(void)
    {
    	__raw_writel(~DBI_CS2_EN_VAL & __raw_readl(reg_virt + CMD_STATUS),
    			reg_virt + CMD_STATUS);
    
    	  /* FIXME: Need loop to check bit5 = 1? */
    	__raw_readl(reg_virt + CMD_STATUS);
    }
    
    static void disable_bars(void)
    {
    	set_dbi_mode();
    
    	__raw_writel(0, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    			PCI_BASE_ADDRESS_0);
    	__raw_writel(0, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    			PCI_BASE_ADDRESS_1);
    
    	clear_dbi_mode();
    }
    
    /**
     * set_inbound_trans() - Setup inbound access
     *
     * Configure BAR0 and BAR1 for inbound access. BAR0 is set up in h/w to have
     * access to PCIESS application register space and just needs to set up inbound
     * address (mainly used for MSI). While BAR1 is set up to provide translation
     * into specified (SoC/Board level) internal address space.
     *
     * Note: 1:1 mapping for internal addresses is used.
     *
     * TODO: Add 64-bit support
     */
    static void set_inbound_trans(void)
    {
    	/* Configure and set up BAR0 */
    	set_dbi_mode();
    
    	/* Enable BAR0 */
    	__raw_writel(1, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    			PCI_BASE_ADDRESS_0);
    
    	__raw_writel(SZ_4K - 1, reg_virt +
    			SPACE0_LOCAL_CFG_OFFSET + PCI_BASE_ADDRESS_0);
    
    	clear_dbi_mode();
    
    	 /*
    	  * For BAR0, just setting bus address for inbound writes (MSI) should
    	  * be sufficient. Use physical address to avoid any conflicts.
    	  */
    	__raw_writel(reg_phys, reg_virt + SPACE0_LOCAL_CFG_OFFSET
    				+ PCI_BASE_ADDRESS_0);
    
    	/* Configure BAR1 only if inbound window is specified */
    	if (ram_base != ram_end) {
    		/*
    		 * Set Inbound translation. Skip BAR0 as it will have h/w
    		 * default set to open application register space.
    		 *
    		 * The value programmed in IB_STARTXX registers must be same as
    		 * the one set in corresponding BAR from PCI config space.
    		 *
    		 * We use translation 'offset' value to yield 1:1 mapping so as
    		 * to have physical address on RC side = Inbound PCIe link
    		 * address. This also ensures no overlapping with base/limit
    		 * regions (outbound).
    		 */
    		__raw_writel(ram_base, reg_virt + IB_START0_LO);
    		__raw_writel(0, reg_virt + IB_START0_HI);
    		__raw_writel(1, reg_virt + IB_BAR0);
    		__raw_writel(ram_base, reg_virt + IB_OFFSET0);
    
    		/*
    		 * Set BAR1 mask to accomodate inbound window
    		 */
    
    		set_dbi_mode();
    
    		__raw_writel(1, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				PCI_BASE_ADDRESS_1);
    
    		__raw_writel(ram_end - ram_base, reg_virt +
    			SPACE0_LOCAL_CFG_OFFSET + PCI_BASE_ADDRESS_1);
    
    		clear_dbi_mode();
    
    		/* Set BAR1 attributes and value in config space */
    		__raw_writel(ram_base | PCI_BASE_ADDRESS_MEM_PREFETCH,
    				reg_virt + SPACE0_LOCAL_CFG_OFFSET
    				+ PCI_BASE_ADDRESS_1);
    
    		/*
    		 * Enable IB translation only if BAR1 is set. BAR0 doesn't
    		 * require enabling IB translation as it is set up in h/w
    		 */
    		__raw_writel(IB_XLAT_EN_VAL | __raw_readl(reg_virt +
    					CMD_STATUS), reg_virt + CMD_STATUS);
    	}
    }
    
    static DECLARE_BITMAP(msi_irq_bits, CFG_MAX_MSI_NUM);
    
    /**
     * ti81xx_msi_handler() - Handle MSI interrupt
     * @irq: IRQ line for MSI interrupts
     * @desc: Pointer to irq descriptor
     *
     * Traverse through pending MSI interrupts and invoke handler for each. Also
     * takes care of interrupt controller level mask/ack operation.
     */
    static void ti81xx_msi_handler(unsigned int irq, struct irq_desc *desc)
    {
    	int bit = 0;
    	u32 status;
    
    	pr_debug(DRIVER_NAME ": Handling MSI irq %d\n", irq);
    
    	/*
    	 * The chained irq handler installation would have replaced normal
    	 * interrupt driver handler so we need to take care of mask/unmask and
    	 * ack operation.
    	 */
    	desc->chip->mask(irq);
    	if (desc->chip->ack)
    		desc->chip->ack(irq);
    
    	status = __raw_readl(reg_virt + MSI0_IRQ_STATUS);
    
    	/* FIXME: Use max loops count? */
    	while (status) {
    		bit = find_first_bit((unsigned long *)&status, 32);
    		generic_handle_irq(msi_irq_base + bit);
    		status = __raw_readl(reg_virt + MSI0_IRQ_STATUS);
    	}
    
    	desc->chip->unmask(irq);
    }
    
    static void ack_msi(unsigned int irq)
    {
    	unsigned int msi_num = irq - msi_irq_base;
    	__raw_writel((1 << (msi_num & 0x1f)), reg_virt + MSI0_IRQ_STATUS);
    }
    
    /*
     * Some TI81XX family devices (e.g., TI816X) PCIe h/w require invert ("write 0
     * to clear") operation to ack the MSI.
     */
    static void ack_msi_inv(unsigned int irq)
    {
    	unsigned int msi_num = irq - msi_irq_base;
    	__raw_writel(~(1 << (msi_num & 0x1f)), reg_virt + MSI0_IRQ_STATUS);
    }
    
    static void mask_msi(unsigned int irq)
    {
    	unsigned int msi_num = irq - msi_irq_base;
    	__raw_writel(1 << (msi_num & 0x1f), reg_virt + MSI0_IRQ_ENABLE_CLR);
    }
    
    static void unmask_msi(unsigned int irq)
    {
    	unsigned int msi_num = irq - msi_irq_base;
    	__raw_writel(1 << (msi_num & 0x1f), reg_virt + MSI0_IRQ_ENABLE_SET);
    }
    
    /*
     * TODO: Add support for mask/unmask on remote devices (mask_msi_irq and
     * unmask_msi_irq). Note that, this may not work always - requires endpoints to
     * support mask bits capability.
     */
    
    static struct irq_chip ti81xx_msi_chip = {
    	.name = "PCIe-MSI",
    	.ack = ack_msi,
    	.mask = mask_msi,
    	.unmask = unmask_msi,
    };
    
    /**
     * get_free_msi() - Get a free MSI number
     *
     * Checks for availability of MSI and returns the first available.
     */
    static int get_free_msi(void)
    {
    	int bit;
    
    	do {
    		bit = find_first_zero_bit(msi_irq_bits, msi_irq_num);
    
    		if (bit >= msi_irq_num)
    			return -ENOSPC;
    
    	} while (test_and_set_bit(bit, msi_irq_bits));
    
    	pr_debug(DRIVER_NAME ": MSI %d available\n", bit);
    
    	return bit;
    }
    
    /* Stub for legacy-interrupts-only mode. */
    #ifndef CONFIG_PCI_MSI
    void write_msi_msg(unsigned int irq, struct msi_msg *msg) {}
    #endif
    
    /**
     * arch_setup_msi_irq() - Set up an MSI for Endpoint
     * @pdev: Pointer to PCI device structure of requesting EP
     * @desc: Pointer to MSI descriptor data
     *
     * Assigns an MSI to endpoint and sets up corresponding irq. Also passes the MSI
     * information to the endpont.
     *
     * TODO: Add 64-bit addressing support
     */
    int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
    {
    	int ret, irq;
    	struct msi_msg msg;
    
    	if (msi_irq < 0) {
    		pr_err(DRIVER_NAME ": MSI irq pin not specified\n");
    		return msi_irq;
    	}
    
    	ret = get_free_msi();
    	if (ret < 0) {
    		pr_err(DRIVER_NAME ": Failed to get free MSI\n");
    	} else {
    		irq = msi_irq_base + ret;
    		msg.data = ret;
    
    		dynamic_irq_init(irq);
    		ret = set_irq_msi(irq, desc);
    
    		if (!ret) {
    			msg.address_hi = 0;
    			msg.address_lo = reg_phys + MSI_IRQ;
    
    			pr_debug(DRIVER_NAME ": MSI %d @%#x:%#x, irq = %d\n",
    					msg.data, msg.address_hi,
    					msg.address_lo, irq);
    
    			write_msi_msg(irq, &msg);
    
    			set_irq_chip_and_handler(irq, &ti81xx_msi_chip,
    						handle_level_irq);
    			set_irq_flags(irq, IRQF_VALID);
    		}
    	}
    
    	return ret;
    }
    
    void arch_teardown_msi_irq(unsigned int irq)
    {
    	int pos = irq - msi_irq_base;
    
    	dynamic_irq_cleanup(irq);
    
    	clear_bit(pos, msi_irq_bits);
    }
    
    /**
     * ti81xx_pcie_setup() - Perform PCIe system setup.
     * @nr: PCI controller index
     * @sys: PCI data for the controller
     *
     * Initializeand configure PCIe Root Complex h/w and fill up resource data to be
     * used by PCI core enumeration layer.
     *
     * H/W initializations consist mainly of:
     * - Setting up PCIESS module clock and getting out of reset.
     * - Establish link with downstream.
     * - Set up outbound access.
     * - Enable memory and IO access.
     *
     * Following resources are allotted for bios enumeration:
     * - Non-Prefetch memory
     * - 32-bit IO
     * - Legacy interrupt
     * - MSI (if enabled)
     *
     * TODO: Add
     * - Prefetchable memory
     * - 64-bit addressing support
     * - Additional delay/handshaking for EPs indulging in CRRS
     */
    static int ti81xx_pcie_setup(int nr, struct pci_sys_data *sys)
    {
    	struct resource *res, *plat_res;
    	struct clk *pcie_ck;
    
    	if (nr != 0)
    		return 0;
    
    	pr_info(DRIVER_NAME ": Setting up Host Controller...\n");
    
    	plat_res = platform_get_resource_byname(pcie_pdev,
    						IORESOURCE_MEM, "pcie-regs");
    	if (!plat_res) {
    		pr_err(DRIVER_NAME ": Failed to get 'regs' memory resource\n");
    		return -1;
    	}
    
    	reg_phys = plat_res->start;
    
    	/*
    	 * Substitute the resources which were set up by default by
    	 * pcibios_init_hw
    	 */
    	res = kzalloc(sizeof(*res) * 2, GFP_KERNEL);
    	if (res == NULL) {
    		pr_err(DRIVER_NAME ": resource structure allocation failed.\n");
    		return -1;
    	}
    
    	/*
    	 * Gather resources which will be used to assign BARs to targets during
    	 * scanning.
    	 */
    
    	plat_res = platform_get_resource_byname(pcie_pdev, IORESOURCE_MEM,
    						"pcie-nonprefetch");
    	if (!plat_res) {
    		pr_err(DRIVER_NAME ": no resource for non-prefetch memory\n");
    		goto err_memres;
    	}
    
    	res[0].start = plat_res->start;
    	res[0].end = plat_res->end;
    	res[0].name = "PCI Memory";
    	res[0].flags = IORESOURCE_MEM;
    
    #if 0
    	{
    		if (insert_resource(&iomem_resource, &res[0])) {
    			pr_err(DRIVER_NAME ": Failed to reserve memory res\n");
    			goto err_memres;
    	}
    #endif
    	/* Optional: io window */
    	plat_res = platform_get_resource_byname(pcie_pdev, IORESOURCE_IO,
    						"pcie-io");
    	if (!plat_res) {
    		pr_warning(DRIVER_NAME ": no resource for PCI I/O\n");
    	} else {
    		res[1].start = plat_res->start;
    		res[1].end = plat_res->end;
    		res[1].name = "PCI I/O";
    		res[1].flags = IORESOURCE_IO;
    
    		if (insert_resource(&ioport_resource, &res[1])) {
    			pr_err(DRIVER_NAME ": Failed to reserve io resource\n");
    			goto err_iores;
    		}
    	}
    
    	/*
    	 * Note: Only one inbound window can be considered as BAR0 is set up for
    	 * application register space in h/w.
    	 */
    	plat_res = platform_get_resource_byname(pcie_pdev, IORESOURCE_MEM,
    						"pcie-inbound0");
    	if (!plat_res) {
    		pr_warning(DRIVER_NAME ": no resource for inbound PCI\n");
    	} else {
    		ram_base = plat_res->start;
    		ram_end = plat_res->end;
    	}
    
    	sys->resource[0] = &res[0];
    	sys->resource[1] = &res[1];
    	sys->resource[2] = NULL;
    
    	/* 16KB region is sufficiant for reg(4KB) + configs(8KB) + IO(4KB) */
    	reg_virt = (u32)ioremap_nocache(reg_phys, SZ_16K);
    
    	if (!reg_virt) {
    		pr_err(DRIVER_NAME ": PCIESS register memory remap failed\n");
    		goto err_ioremap;
    	}
    
    	pr_info(DRIVER_NAME ": Register base mapped @0x%08x\n", (int)reg_virt);
    
    	pcie_ck = clk_get(NULL, "pcie_ck");
    	if (IS_ERR(pcie_ck)) {
    		pr_err(DRIVER_NAME ": Failed to get PCIESS clock\n");
    		goto err_clkget;
    	}
    
    	if (clk_enable(pcie_ck))
    		goto err_clken;
    
    #if 0
    	
    	/*
    	 * TI81xx devices do not support h/w autonomous link up-training to GEN2
    	 * form GEN1 in either EP/RC modes. The software needs to initiate speed
    	 * change.
    	 */
    	__raw_writel(DIR_SPD | __raw_readl(
    				reg_virt + SPACE0_LOCAL_CFG_OFFSET + PL_GEN2),
    			reg_virt + SPACE0_LOCAL_CFG_OFFSET + PL_GEN2);
    
    	/*
    	 * Check if we need to force the link to x1 lane. This is particularly
    	 * applicable for TI81XX devices which are single lane while the PCIe
    	 * module's registers show x2 as lane configuration (e.g., LINK_CAP
    	 * register shows x2 on TI814X devices while they actually support
    	 * single lane only). Setting force_x1 flag directs us to force x1 in
    	 * link configurations avoiding sending misleading information from
    	 * PCIe configuration dump (e.g., 'lspci -vv' output).
    	 */
    	if (force_x1) {
    		u32 val;
    
    		val = __raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				LINK_CAP);
    		val = (val & ~(0x3f << 4)) | (1 << 4);
    		__raw_writel(val, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				LINK_CAP);
    
    		val = __raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET + PL_GEN2);
    		val = (val & ~(0xff << 8)) | (1 << 8);
    		__raw_writel(val, reg_virt + SPACE0_LOCAL_CFG_OFFSET + PL_GEN2);
    
    		val = __raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				PL_LINK_CTRL);
    		val = (val & ~(0x3F << 16)) | (1 << 16);
    		__raw_writel(val, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				PL_LINK_CTRL);
    	}
    
    	/*
    	 * Override the default device ID if required - TI81XX devices generally
    	 * come up with ID 0x8888.
    	 */
    	if (device_id)
    		__raw_writew(device_id, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				PCI_DEVICE_ID);
    
    	/*
    	 * Initiate Link Training. We will delay for L0 as specified by
    	 * standard, but will still proceed and return success irrespective of
    	 * L0 status as this will be handled by explicit L0 state checks during
    	 * enumeration.
    	 */
    	 __raw_writel(LTSSM_EN_VAL | __raw_readl(reg_virt + CMD_STATUS),
    			 reg_virt + CMD_STATUS);
    
    	 /* 100ms */
    	 msleep(100);
    
    	/*
    	 * Identify ourselves as 'Bridge' for enumeration purpose. This also
    	 * avoids "Invalid class 0000 for header type 01" warnings from "lspci".
    	 *
    	 * If at all we want to restore the default class-subclass values, the
    	 * best place would be after returning from pci_common_init ().
    	 */
    	__raw_writew(PCI_CLASS_BRIDGE_PCI,
    			reg_virt + SPACE0_LOCAL_CFG_OFFSET + PCI_CLASS_DEVICE);
    
    	/*
    	 * Prevent the enumeration code from assigning resources to our BARs. We
    	 * will set up them after the scan is complete.
    	 */
    
    	disable_bars();
    
    	set_outbound_trans(res[0].start, res[0].end);
    
    	/* Enable 32-bit IO addressing support */
    	__raw_writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8),
    			reg_virt + SPACE0_LOCAL_CFG_OFFSET + PCI_IO_BASE);
    
    	/*
    	 * FIXME: The IO Decode size bits in IO base and limit registers are
    	 * writable from host any time and during enumeration, the Linux PCI
    	 * core clears the lower 4-bits of these registers while writing lower
    	 * IO address. This makes IO upper address and limit registers to show
    	 * value as '0' and not the actual value as configured by the core
    	 * during enumeration. We need to re-write bits 0 of IO limit and base
    	 * registers again. Need to find if a post configuration hook is
    	 * possible. An easier and clear but possibly inefficient WA is to snoop
    	 * each config write and restore 32-bit IO decode configuration.
    	 */
    
    	/*
    	 * Setup as PCI master, also clear any pending  status bits.
    	 * FIXME: Nolonger needed as post-scan fixup handles this (see below).
    	 */
    #if 0
    	__raw_writel((__raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET
    					+ PCI_COMMAND)
    			| CFG_PCIM_CSR_VAL),
    			reg_virt + SPACE0_LOCAL_CFG_OFFSET + PCI_COMMAND);
    #endif
    
    
    
    	legacy_irq = platform_get_irq_byname(pcie_pdev, "legacy_int");
    
    	if (legacy_irq >= 0) {
    		__raw_writel(0xf, reg_virt + IRQ_ENABLE_SET);
    	} else {
    		__raw_writel(0xf, reg_virt + IRQ_ENABLE_CLR);
    		pr_warning(DRIVER_NAME ": INTx disabled since no legacy IRQ\n");
    	}
    
    	msi_irq = platform_get_irq_byname(pcie_pdev, "msi_int");
    
    	if ((msi_irq >= 0) && msi_irq_num) {
    		if (msi_irq_num > CFG_MAX_MSI_NUM) {
    			msi_irq_num = CFG_MAX_MSI_NUM;
    			pr_warning(DRIVER_NAME
    				": Restricting MSI count to max supported (%d)",
    				msi_irq_num);
    		}
    
    		if (msi_inv)
    			ti81xx_msi_chip.ack = ack_msi_inv;
    
    		set_irq_chained_handler(msi_irq, ti81xx_msi_handler);
    	} else {
    		pr_warning(DRIVER_NAME ": MSI info not available, disabled\n");
    		msi_irq_num = 0;
    	}
    
    	get_and_clear_err();
    
    #endif
    	
    	/*
    	 * PCIe access errors that result into OCP errors are caught by ARM as
    	 * "External aborts" (Precise).
    	 */
    	hook_fault_code(8, ti81xx_pcie_fault, SIGBUS, 0,
    			"Precise External Abort on non-linefetch");
    
    		
    			
    	 return 1;
    
    err_clken:
    	 clk_put(pcie_ck);
    err_clkget:
    	 iounmap((void __iomem *)reg_virt);
    err_ioremap:
    	 if (res[1].flags == IORESOURCE_IO)
    		 release_resource(&res[1]);
    err_iores:
    	 release_resource(&res[0]);
    err_memres:
    	 kfree(res);
    
    	 return -1;
    }
    
    
    static int ti81xx_pcie_hw_setup(int nr, struct pci_sys_data *sys)
    {
    	struct resource *plat_res;
      
            /* reset pheripheral */
    #if 1  
    /*
            pr_err(DRIVER_NAME ": ti81xx_pcie_hw_setup\n");
    
            omap2_prm_set_mod_reg_bits(TI81XX_PCI_LRST_MASK,
    			TI81XX_PRM_DEFAULT_MOD,
    			TI81XX_RM_RSTCTRL);
    
    	 msleep(100);			
    			
    	omap2_prm_clear_mod_reg_bits(TI81XX_PCI_LRST_MASK,
    			TI81XX_PRM_DEFAULT_MOD,
    			TI81XX_RM_RSTCTRL);
    
    	 msleep(100);			
    */	
      
    /*
    	 * TI81xx devices do not support h/w autonomous link up-training to GEN2
    	 * form GEN1 in either EP/RC modes. The software needs to initiate speed
    	 * change.
    	 */
    	__raw_writel(DIR_SPD | __raw_readl(
    				reg_virt + SPACE0_LOCAL_CFG_OFFSET + PL_GEN2),
    			reg_virt + SPACE0_LOCAL_CFG_OFFSET + PL_GEN2);
    
    	/*
    	 * Check if we need to force the link to x1 lane. This is particularly
    	 * applicable for TI81XX devices which are single lane while the PCIe
    	 * module's registers show x2 as lane configuration (e.g., LINK_CAP
    	 * register shows x2 on TI814X devices while they actually support
    	 * single lane only). Setting force_x1 flag directs us to force x1 in
    	 * link configurations avoiding sending misleading information from
    	 * PCIe configuration dump (e.g., 'lspci -vv' output).
    	 */
    	if (force_x1) {
    		u32 val;
    
    		val = __raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				LINK_CAP);
    		val = (val & ~(0x3f << 4)) | (1 << 4);
    		__raw_writel(val, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				LINK_CAP);
    
    		val = __raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET + PL_GEN2);
    		val = (val & ~(0xff << 8)) | (1 << 8);
    		__raw_writel(val, reg_virt + SPACE0_LOCAL_CFG_OFFSET + PL_GEN2);
    
    		val = __raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				PL_LINK_CTRL);
    		val = (val & ~(0x3F << 16)) | (1 << 16);
    		__raw_writel(val, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				PL_LINK_CTRL);
    	}
    
    	/*
    	 * Override the default device ID if required - TI81XX devices generally
    	 * come up with ID 0x8888.
    	 */
    	if (device_id)
    		__raw_writew(device_id, reg_virt + SPACE0_LOCAL_CFG_OFFSET +
    				PCI_DEVICE_ID);
    
    	/*
    	 * Initiate Link Training. We will delay for L0 as specified by
    	 * standard, but will still proceed and return success irrespective of
    	 * L0 status as this will be handled by explicit L0 state checks during
    	 * enumeration.
    	 */
    	 __raw_writel(LTSSM_EN_VAL | __raw_readl(reg_virt + CMD_STATUS),
    			 reg_virt + CMD_STATUS);
    
    	 /* 100ms */
    	 msleep(100);
    
    	/*
    	 * Identify ourselves as 'Bridge' for enumeration purpose. This also
    	 * avoids "Invalid class 0000 for header type 01" warnings from "lspci".
    	 *
    	 * If at all we want to restore the default class-subclass values, the
    	 * best place would be after returning from pci_common_init ().
    	 */
    	__raw_writew(PCI_CLASS_BRIDGE_PCI,
    			reg_virt + SPACE0_LOCAL_CFG_OFFSET + PCI_CLASS_DEVICE);
    
    	/*
    	 * Prevent the enumeration code from assigning resources to our BARs. We
    	 * will set up them after the scan is complete.
    	 */
    	
    
    	
    	disable_bars();
    
    	plat_res = platform_get_resource_byname(pcie_pdev, IORESOURCE_MEM,
    						"pcie-nonprefetch");
    	if (!plat_res) {
    		pr_err(DRIVER_NAME ": no resource for non-prefetch memory\n");
    		goto err_mem;
    	}
    
    	set_outbound_trans(plat_res->start, plat_res->end);
    
    	/* Enable 32-bit IO addressing support */
    	__raw_writew(PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8),
    			reg_virt + SPACE0_LOCAL_CFG_OFFSET + PCI_IO_BASE);
    
    	/*
    	 * FIXME: The IO Decode size bits in IO base and limit registers are
    	 * writable from host any time and during enumeration, the Linux PCI
    	 * core clears the lower 4-bits of these registers while writing lower
    	 * IO address. This makes IO upper address and limit registers to show
    	 * value as '0' and not the actual value as configured by the core
    	 * during enumeration. We need to re-write bits 0 of IO limit and base
    	 * registers again. Need to find if a post configuration hook is
    	 * possible. An easier and clear but possibly inefficient WA is to snoop
    	 * each config write and restore 32-bit IO decode configuration.
    	 */
    
    	/*
    	 * Setup as PCI master, also clear any pending  status bits.
    	 * FIXME: Nolonger needed as post-scan fixup handles this (see below).
    	 */
    #if 0
    	__raw_writel((__raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET
    					+ PCI_COMMAND)
    			| CFG_PCIM_CSR_VAL),
    			reg_virt + SPACE0_LOCAL_CFG_OFFSET + PCI_COMMAND);
    #endif
    
    	legacy_irq = platform_get_irq_byname(pcie_pdev, "legacy_int");
    
    	if (legacy_irq >= 0) {
    		__raw_writel(0xf, reg_virt + IRQ_ENABLE_SET);
                    pr_debug(DRIVER_NAME ": legacy ints: %d\n",legacy_irq);		
    	} else {
    		__raw_writel(0xf, reg_virt + IRQ_ENABLE_CLR);
    		pr_warning(DRIVER_NAME ": INTx disabled since no legacy IRQ\n");
    	}
    
    	msi_irq = platform_get_irq_byname(pcie_pdev, "msi_int");
    
    	if ((msi_irq >= 0) && msi_irq_num) {
    
                    pr_debug(DRIVER_NAME ": MSI ints %d\n",msi_irq);			  
    	  
    	        if (msi_irq_num > CFG_MAX_MSI_NUM) {
    			msi_irq_num = CFG_MAX_MSI_NUM;
    			pr_warning(DRIVER_NAME
    				": Restricting MSI count to max supported (%d)",
    				msi_irq_num);
    		}
    
    		
    		
    		if (msi_inv)
    			ti81xx_msi_chip.ack = ack_msi_inv;
    
    		set_irq_chained_handler(msi_irq, ti81xx_msi_handler);
    	} else {
    		pr_warning(DRIVER_NAME ": MSI info not available, disabled\n");
    		msi_irq_num = 0;
    	}
    
    	get_and_clear_err();
    
    	/*
    	 * PCIe access errors that result into OCP errors are caught by ARM as
    	 * "External aborts" (Precise).
    	 */
    	//hook_fault_code(8, ti81xx_pcie_fault, SIGBUS, 0,
    	//		"Precise External Abort on non-linefetch");
    
    #endif			
            return 1;
    err_mem:
            return -1;
    	
      
    }
    
    
    
    
    
    
    
    /**
     * check_device() - Checks device availability
     * @bus: Pointer to bus to check the device availability on
     * @dev: Device number
     *
     * Checks for the possibility of device being present. Relies on following
     * logic to indicate success:
     * - downstream link must be established to traverse PCIe fabric
     * - treating RC as virtual PCI bridge, first (and only) device on bus 1 will be
     *   numbered as 0
     * - don't check device number beyond bus 1 as device on our secondary side may
     *   as well be a PCIe-PCI bridge
     */
    static int check_device(struct pci_bus *bus, int dev)
    {
    	//pr_debug(DRIVER_NAME ": check_device : %x ", (reg_virt + SPACE0_LOCAL_CFG_OFFSET + DEBUG0));
      
            if ((__raw_readl(reg_virt + SPACE0_LOCAL_CFG_OFFSET + DEBUG0) &
    				LTSSM_STATE_MASK) != LTSSM_STATE_L0)
    		return 0;
    
    	if (bus->number <= 1) {
    		if (dev == 0)
    			return 1;
    		else
    			return 0;
    	}
    
    	return 1;
    }
    
    /**
     * setup_config_addr() - Set up configuration space address for a device
     * @bus: Bus number the device is residing on
     * @device: Device number
     * @function: Function number in device
     * @where: Offset of configuration register
     *
     * Forms and returns the address of configuration space mapped in PCIESS
     * address space 0. Also configures CFG_SETUP for remote configuration space
     * access.
     *
     * The address space has two regions to access configuration - local and remote.
     * We access local region for bus 0 (as RC is attached on bus 0) and remote
     * region for others with TYPE 1 access when bus > 1. As for device on bus = 1,
     * we will do TYPE 0 access as it will be on our secondary bus (logical).
     * CFG_SETUP is needed only for remote configuration access.
     *
     * _NOTE_: Currently only supports device 0 on bus = 0 which is OK as PCIESS has
     * single root port.
     */
    static inline u32 setup_config_addr(u8 bus, u8 device, u8 function)
    {
    	u32 addr;
    
    	if (bus == 0) {
    		addr = reg_virt + SPACE0_LOCAL_CFG_OFFSET;
    	} else {
    		u32 regval = (bus << 16) | (device << 8) | function;
    
    		/*
    		 * Since Bus#1 will be a virtual bus, we need to have TYPE0
    		 * access only.
    		 */
    		/* TYPE 1 */
    		if (bus != 1)
    			regval |= BIT(24);
    
    		__raw_writel(regval, reg_virt + CFG_SETUP);
    
    		addr = reg_virt + SPACE0_REMOTE_CFG_OFFSET;
    	}
    
    	return addr;
    }
    
    /**
     * ti81xx_pci_io_read() - Perform PCI IO read from a device
     * @addr: IO address
     * @size: Number of bytes
     * @value: Pointer to hold the read value
     */
    int ti81xx_pci_io_read(u32 addr, int size, u32 *value)
    {
    	unsigned long flags;
    
    	if (!IS_ALIGNED(addr, size))
    		return -1;
    
    	pr_debug(DRIVER_NAME ": IO read @%#x = ", addr);
    
    	spin_lock_irqsave(&ti81xx_pci_io_lock, flags);
    
    	__raw_writel(addr & 0xfffff000, reg_virt + IOBASE);
    
    	/* Get the actual address in I/O space */
    	addr = reg_virt + SPACE0_IO_OFFSET + (addr & 0xffc);
    
    	*value = __raw_readl(addr);
    	*value >>= ((addr & 3)*8);
    
    	spin_unlock_irqrestore(&ti81xx_pci_io_lock, flags);
    
    	pr_debug("%#x\n", *value);
    
    	return 0;
    }
    EXPORT_SYMBOL(ti81xx_pci_io_read);
    
    /**
     * ti81xx_pci_io_write() - Perform PCI IO write to a device
     * @addr: IO address
     * @size: Number of bytes
     * @value: Value to write
     */
    int ti81xx_pci_io_write(u32 addr, int size, u32 value)
    {
    	unsigned long flags;
    	u32 iospace_addr;
    
    	if (!IS_ALIGNED(addr, size))
    		return -1;
    
    	pr_debug(DRIVER_NAME ": IO write @%#x = %#x\n", addr, value);
    
    	spin_lock_irqsave(&ti81xx_pci_io_lock, flags);
    
    	__raw_writel(addr & 0xfffff000, reg_virt + IOBASE);
    
    	/* Get the actual address in I/O space */
    	iospace_addr = reg_virt + SPACE0_IO_OFFSET + (addr & 0xffc);
    
    	if (size != 4) {
    		u32 shift = (addr & 3) * 8;
    		u32 mask = (size == 1 ? 0xff : 0xffff) << shift;
    		u32 readval = __raw_readl(iospace_addr);
    		value = ((value << shift) & mask) | (readval & ~mask);
    	}
    
    	__raw_writel(value, iospace_addr);
    
    	spin_unlock_irqrestore(&ti81xx_pci_io_lock, flags);
    
    	return 0;
    }
    EXPORT_SYMBOL(ti81xx_pci_io_write);
    
    /**
     * ti81xx_pci_read_config() - Perform PCI configuration read from a device
     * @bus: Pointer to bus to access device on
     * @devfn: Device number of the bus and function number within
     * @where: Configuration space register offset
     * @size: Width of the register in bytes
     * @value: Pointer to hold the read value
     *
     * Note: We skip alignment check and locking since it is taken care by PCI
     * access wrappers.
     */
    static int ti81xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
    				int where, int size, u32 *value)
    {
    	u8 bus_num = bus->number;
    
    	//pr_debug(DRIVER_NAME ": Reading config[%x] for device %04x:%02x:%02x..",
    	//		where, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn));
    
    	if (!check_device(bus, PCI_SLOT(devfn))) {
    		*value = ~0;
    		pr_debug("R failed. No link/device.\n");
    		return PCIBIOS_DEVICE_NOT_FOUND;
    	}
    
    	*value = __raw_readl(setup_config_addr(bus_num, PCI_SLOT(devfn),
    				PCI_FUNC(devfn)) + (where & ~3));
    
    	if (size == 1)
    		*value = (*value >> (8 * (where & 3))) & 0xff;
    	else if (size == 2)
    		*value = (*value >> (8 * (where & 3))) & 0xffff;
    
    	//pr_debug("done. value = %#x\n", *value);
    	return PCIBIOS_SUCCESSFUL;
    }
    
    /**
     * ti81xx_pci_write_config() - Perform PCI configuration write to a device
     * @bus: Pointer to bus to access device on
     * @devfn: Device number of the bus and function number within
     * @where: Configuration space register offset
     * @size: Width of the register in bytes
     * @value: Value to write
     *
     * Note: We skip alignment check and locking since it is taken care by PCI
     * access wrappers.
     */
    static int ti81xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
    				int where, int size, u32 value)
    {
    	u8 bus_num = bus->number;
    	u32 addr;
    
    	//pr_debug(DRIVER_NAME ": Writing config[%x] = %x "
    	//		"for device %04x:%02x:%02x ...", where, value,
    	//		bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn));
    
    	if (!check_device(bus, PCI_SLOT(devfn))) {
    		pr_debug("W failed. No link/device.\n");
    		return PCIBIOS_DEVICE_NOT_FOUND;
    	}
    
    	addr = setup_config_addr(bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn));
    
    	if (size == 4)
    		__raw_writel(value, addr + where);
    	else if (size == 2)
    		__raw_writew(value, addr + where);
    	else
    		__raw_writeb(value, addr + where);
    
    	/*
    	 * The h/w has a limitation where Config Writes don't signal aborts to
    	 * processor. Clear explicitly to avoid stale status.
    	 */
    	get_and_clear_err();
    
    	//pr_debug("done.\n");
    	return PCIBIOS_SUCCESSFUL;
    }
    
    static struct pci_ops ti81xx_pci_ops = {
    	.read	= ti81xx_pci_read_config,
    	.write	= ti81xx_pci_write_config,
    };
    
    static struct pci_bus *ti81xx_pcie_scan(int nr, struct pci_sys_data *sys)
    {
    	struct pci_bus *bus = NULL;
    
    	pr_info(DRIVER_NAME ": Starting PCI scan...\n");
    	if (nr == 0) {
    
                   ti81xx_pcie_hw_setup(nr,sys);	  
    	    
    	       if(ti81xx_pcie_rescan ==0)
    		{
    	          pr_info(DRIVER_NAME ": scan...\n");
    		  bus = pci_scan_bus(0, &ti81xx_pci_ops, sys);
    		  ti81xx_pcie_rescan = 1;
    		}
    		else
    		{
    	          pr_info(DRIVER_NAME ": rescan scan...\n");		  
    		  
    		  pci_rescan_bus(sys->bus);		  
    		  
    		}
    		
    		/* Post enumeration fixups */
    		  set_inbound_trans();
    
    		/* Bridges are not getting enabled by default! */
    		pci_assign_unassigned_resources();
    	}
    
    	return bus;
    }
    
    /**
     * ti81xx_pcie_map_irq() - Map a legacy interrupt to an IRQ
     * @dev: Device structure of End Point (EP) to assign IRQ to.
     * @slot: Device slot
     * @pin: Pin number for the function
     *
     * Note: Currently ignores all the parameters and only supports mapping to
     * single IRQ.
     */
    static int ti81xx_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
    {
    	pr_debug(DRIVER_NAME "Returning Legacy irq = %d\n", legacy_irq);
    	return (legacy_irq >= 0) ? legacy_irq : -1;
    }
    
    /* PCI controller setup and configuration data */
    static struct hw_pci ti81xx_pci = {
    	.nr_controllers     = 1,
    	.setup              = ti81xx_pcie_setup,
    	.scan               = ti81xx_pcie_scan,
    	.preinit            = NULL,
    	.postinit           = NULL,
    	.swizzle            = pci_std_swizzle,
    	.map_irq            = ti81xx_pcie_map_irq
    };
    
    /**
     * ti81xx_pcie_probe() - Invoke PCI BIOS to perrform enumeration.
     * @pdev: Contains platform data as supplied from board level code.
     *
     * Also stores reference to platform device structure for use during PCIe
     * module initialization and configuration.
     */
    static int ti81xx_pcie_probe(struct platform_device *pdev)
    {
    	struct ti81xx_pcie_data *pdata;
    
    	pcie_pdev = pdev;
            ti81xx_pcie_rescan = 0;
    
    	pdata = pdev->dev.platform_data;
    	if (!pdata) {
    		pr_err(DRIVER_NAME ": No platform data\n");
    		return -ENODEV;
    	}
    
    	msi_irq_base = pdata->msi_irq_base;
    	msi_irq_num = pdata->msi_irq_num;
    	force_x1 = pdata->force_x1;
    	msi_inv = pdata->msi_inv;
    	device_id = pdata->device_id;
    
    	pr_info(DRIVER_NAME ": Invoking PCI BIOS...\n");
    	pci_common_init(&ti81xx_pci);
    
    	return 0;
    }
    
    static struct platform_driver ti81xx_pcie_driver = {
    	.driver = {
    		.name	= DRIVER_NAME,
    		.owner	= THIS_MODULE,
    	},
    	.probe = ti81xx_pcie_probe,
    };
    
    /**
     * ti81xx_pcie_rc_init() - Register PCIe Root Complex node.
     *
     * Invoked as subsystem initialization.
     *
     * IMPORTANT NOTE: We are relying on SoC/Board level code to check PCIESS
     * mode setting (RC/EP) and register the RC device only in RC mode.
     */
    static int __init ti81xx_pcie_rc_init(void)
    {
    	platform_driver_register(&ti81xx_pcie_driver);
    	return 0;
    }
    subsys_initcall(ti81xx_pcie_rc_init);
    
    pci-sysfs.c
    /*
     * drivers/pci/pci-sysfs.c
     *
     * (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com>
     * (C) Copyright 2002-2004 IBM Corp.
     * (C) Copyright 2003 Matthew Wilcox
     * (C) Copyright 2003 Hewlett-Packard
     * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
     * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
     *
     * File attributes for PCI devices
     *
     * Modeled after usb's driverfs.c 
     *
     */
    
    
    #include <linux/kernel.h>
    #include <linux/sched.h>
    #include <linux/pci.h>
    #include <linux/stat.h>
    #include <linux/topology.h>
    #include <linux/mm.h>
    #include <linux/fs.h>
    #include <linux/capability.h>
    #include <linux/pci-aspm.h>
    #include <linux/slab.h>
    #include "pci.h"
    
    static int sysfs_initialized;	/* = 0 */
    
    /* show configuration fields */
    #define pci_config_attr(field, format_string)				\
    static ssize_t								\
    field##_show(struct device *dev, struct device_attribute *attr, char *buf)				\
    {									\
    	struct pci_dev *pdev;						\
    									\
    	pdev = to_pci_dev (dev);					\
    	return sprintf (buf, format_string, pdev->field);		\
    }
    
    pci_config_attr(vendor, "0x%04x\n");
    pci_config_attr(device, "0x%04x\n");
    pci_config_attr(subsystem_vendor, "0x%04x\n");
    pci_config_attr(subsystem_device, "0x%04x\n");
    pci_config_attr(class, "0x%06x\n");
    pci_config_attr(irq, "%u\n");
    
    static ssize_t broken_parity_status_show(struct device *dev,
    					 struct device_attribute *attr,
    					 char *buf)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    	return sprintf (buf, "%u\n", pdev->broken_parity_status);
    }
    
    static ssize_t broken_parity_status_store(struct device *dev,
    					  struct device_attribute *attr,
    					  const char *buf, size_t count)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    	unsigned long val;
    
    	if (strict_strtoul(buf, 0, &val) < 0)
    		return -EINVAL;
    
    	pdev->broken_parity_status = !!val;
    
    	return count;
    }
    
    static ssize_t local_cpus_show(struct device *dev,
    			struct device_attribute *attr, char *buf)
    {		
    	const struct cpumask *mask;
    	int len;
    
    #ifdef CONFIG_NUMA
    	mask = (dev_to_node(dev) == -1) ? cpu_online_mask :
    					  cpumask_of_node(dev_to_node(dev));
    #else
    	mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
    #endif
    	len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask);
    	buf[len++] = '\n';
    	buf[len] = '\0';
    	return len;
    }
    
    
    static ssize_t local_cpulist_show(struct device *dev,
    			struct device_attribute *attr, char *buf)
    {
    	const struct cpumask *mask;
    	int len;
    
    #ifdef CONFIG_NUMA
    	mask = (dev_to_node(dev) == -1) ? cpu_online_mask :
    					  cpumask_of_node(dev_to_node(dev));
    #else
    	mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
    #endif
    	len = cpulist_scnprintf(buf, PAGE_SIZE-2, mask);
    	buf[len++] = '\n';
    	buf[len] = '\0';
    	return len;
    }
    
    /* show resources */
    static ssize_t
    resource_show(struct device * dev, struct device_attribute *attr, char * buf)
    {
    	struct pci_dev * pci_dev = to_pci_dev(dev);
    	char * str = buf;
    	int i;
    	int max;
    	resource_size_t start, end;
    
    	if (pci_dev->subordinate)
    		max = DEVICE_COUNT_RESOURCE;
    	else
    		max = PCI_BRIDGE_RESOURCES;
    
    	for (i = 0; i < max; i++) {
    		struct resource *res =  &pci_dev->resource[i];
    		pci_resource_to_user(pci_dev, i, res, &start, &end);
    		str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx\n",
    			       (unsigned long long)start,
    			       (unsigned long long)end,
    			       (unsigned long long)res->flags);
    	}
    	return (str - buf);
    }
    
    static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
    {
    	struct pci_dev *pci_dev = to_pci_dev(dev);
    
    	return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x\n",
    		       pci_dev->vendor, pci_dev->device,
    		       pci_dev->subsystem_vendor, pci_dev->subsystem_device,
    		       (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
    		       (u8)(pci_dev->class));
    }
    
    static ssize_t is_enabled_store(struct device *dev,
    				struct device_attribute *attr, const char *buf,
    				size_t count)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    	unsigned long val;
    	ssize_t result = strict_strtoul(buf, 0, &val);
    
    	if (result < 0)
    		return result;
    
    	/* this can crash the machine when done on the "wrong" device */
    	if (!capable(CAP_SYS_ADMIN))
    		return -EPERM;
    
    	if (!val) {
    		if (pci_is_enabled(pdev))
    			pci_disable_device(pdev);
    		else
    			result = -EIO;
    	} else
    		result = pci_enable_device(pdev);
    
    	return result < 0 ? result : count;
    }
    
    static ssize_t is_enabled_show(struct device *dev,
    			       struct device_attribute *attr, char *buf)
    {
    	struct pci_dev *pdev;
    
    	pdev = to_pci_dev (dev);
    	return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt));
    }
    
    #ifdef CONFIG_NUMA
    static ssize_t
    numa_node_show(struct device *dev, struct device_attribute *attr, char *buf)
    {
    	return sprintf (buf, "%d\n", dev->numa_node);
    }
    #endif
    
    static ssize_t
    dma_mask_bits_show(struct device *dev, struct device_attribute *attr, char *buf)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    
    	return sprintf (buf, "%d\n", fls64(pdev->dma_mask));
    }
    
    static ssize_t
    consistent_dma_mask_bits_show(struct device *dev, struct device_attribute *attr,
    				 char *buf)
    {
    	return sprintf (buf, "%d\n", fls64(dev->coherent_dma_mask));
    }
    
    static ssize_t
    msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    
    	if (!pdev->subordinate)
    		return 0;
    
    	return sprintf (buf, "%u\n",
    			!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
    }
    
    static ssize_t
    msi_bus_store(struct device *dev, struct device_attribute *attr,
    	      const char *buf, size_t count)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    	unsigned long val;
    
    	if (strict_strtoul(buf, 0, &val) < 0)
    		return -EINVAL;
    
    	/* bad things may happen if the no_msi flag is changed
    	 * while some drivers are loaded */
    	if (!capable(CAP_SYS_ADMIN))
    		return -EPERM;
    
    	/* Maybe pci devices without subordinate busses shouldn't even have this
    	 * attribute in the first place?  */
    	if (!pdev->subordinate)
    		return count;
    
    	/* Is the flag going to change, or keep the value it already had? */
    	if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^
    	    !!val) {
    		pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI;
    
    		dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI,"
    			 " bad things could happen\n", val ? "" : " not");
    	}
    
    	return count;
    }
    
    #ifdef CONFIG_HOTPLUG
    static DEFINE_MUTEX(pci_remove_rescan_mutex);
    static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
    				size_t count)
    {
    	unsigned long val;
    	struct pci_bus *b = NULL;
    	int ret;
    	struct pci_sys_data *sys_data;
    	
    	printk(KERN_ERR "AAA %s: \n",__func__);
    	
    	if (strict_strtoul(buf, 0, &val) < 0)
    		return -EINVAL;
    
    	if (val) {
    		mutex_lock(&pci_remove_rescan_mutex);
    		while ((b = pci_find_next_bus(b)) != NULL)
    			/*pci_rescan_bus(b);*/
    			{
    			 sys_data = b->sysdata;
    		
    		         //ret = sys_data->hw->setup(0, sys_data);	 
    	                 //printk(KERN_ERR "%s: ret=%d\n",__func__,ret);	
    		         //if (ret>0)
    	                  sys_data->hw->scan(0, sys_data);
    			  
    			  
    			  
    			}
    		mutex_unlock(&pci_remove_rescan_mutex);
    	}
    	return count;
    }
    
    struct bus_attribute pci_bus_attrs[] = {
    	__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store),
    	__ATTR_NULL
    };
    
    static ssize_t
    dev_rescan_store(struct device *dev, struct device_attribute *attr,
    		 const char *buf, size_t count)
    {
    	unsigned long val;
    	struct pci_dev *pdev = to_pci_dev(dev);
    	struct pci_sys_data *sys_data;
    	int ret;
    	
    	printk(KERN_ERR "AAA %s: \n",__func__);	
    	
    	if (strict_strtoul(buf, 0, &val) < 0)
    		return -EINVAL;
    
    	/* 
    	 *  qui inserisco la mia reinit di basso livello
    	    
    	    ret = hw->setup(nr, sys);	 
    	 
    	    sys->bus = hw->scan(nr, sys);
    	 
    	 
    	 
    	 */
    
    	if (val) {
    		mutex_lock(&pci_remove_rescan_mutex);
    
    	        sys_data = pdev->bus->sysdata;
    		
    		//ret = sys_data->hw->setup(0, sys_data);	 
    	        //printk(KERN_ERR "%s: ret=%d\n",__func__,ret);	
    		//if (ret>0)
    	         sys_data->hw->scan(0, sys_data);
    		
    		mutex_unlock(&pci_remove_rescan_mutex);
    	}
    	
    	
    #if 0	
    	if (val) {
    		mutex_lock(&pci_remove_rescan_mutex);
    		pci_rescan_bus(pdev->bus);
    		mutex_unlock(&pci_remove_rescan_mutex);
    	}
    #endif	
    	return count;
    }
    
    static void remove_callback(struct device *dev)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    
    	mutex_lock(&pci_remove_rescan_mutex);
    	pci_remove_bus_device(pdev);
    	mutex_unlock(&pci_remove_rescan_mutex);
    }
    
    static ssize_t
    remove_store(struct device *dev, struct device_attribute *dummy,
    	     const char *buf, size_t count)
    {
    	int ret = 0;
    	unsigned long val;
    
    	if (strict_strtoul(buf, 0, &val) < 0)
    		return -EINVAL;
    
    	/* An attribute cannot be unregistered by one of its own methods,
    	 * so we have to use this roundabout approach.
    	 */
    	if (val)
    		ret = device_schedule_callback(dev, remove_callback);
    	if (ret)
    		count = ret;
    	return count;
    }
    #endif
    
    struct device_attribute pci_dev_attrs[] = {
    	__ATTR_RO(resource),
    	__ATTR_RO(vendor),
    	__ATTR_RO(device),
    	__ATTR_RO(subsystem_vendor),
    	__ATTR_RO(subsystem_device),
    	__ATTR_RO(class),
    	__ATTR_RO(irq),
    	__ATTR_RO(local_cpus),
    	__ATTR_RO(local_cpulist),
    	__ATTR_RO(modalias),
    #ifdef CONFIG_NUMA
    	__ATTR_RO(numa_node),
    #endif
    	__ATTR_RO(dma_mask_bits),
    	__ATTR_RO(consistent_dma_mask_bits),
    	__ATTR(enable, 0600, is_enabled_show, is_enabled_store),
    	__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
    		broken_parity_status_show,broken_parity_status_store),
    	__ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
    #ifdef CONFIG_HOTPLUG
    	__ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store),
    	__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store),
    #endif
    	__ATTR_NULL,
    };
    
    static ssize_t
    boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    
    	return sprintf(buf, "%u\n",
    		!!(pdev->resource[PCI_ROM_RESOURCE].flags &
    		   IORESOURCE_ROM_SHADOW));
    }
    struct device_attribute vga_attr = __ATTR_RO(boot_vga);
    
    static ssize_t
    pci_read_config(struct file *filp, struct kobject *kobj,
    		struct bin_attribute *bin_attr,
    		char *buf, loff_t off, size_t count)
    {
    	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
    	unsigned int size = 64;
    	loff_t init_off = off;
    	u8 *data = (u8*) buf;
    
    	/* Several chips lock up trying to read undefined config space */
    	if (cap_raised(filp->f_cred->cap_effective, CAP_SYS_ADMIN)) {
    		size = dev->cfg_size;
    	} else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
    		size = 128;
    	}
    
    	if (off > size)
    		return 0;
    	if (off + count > size) {
    		size -= off;
    		count = size;
    	} else {
    		size = count;
    	}
    
    	if ((off & 1) && size) {
    		u8 val;
    		pci_user_read_config_byte(dev, off, &val);
    		data[off - init_off] = val;
    		off++;
    		size--;
    	}
    
    	if ((off & 3) && size > 2) {
    		u16 val;
    		pci_user_read_config_word(dev, off, &val);
    		data[off - init_off] = val & 0xff;
    		data[off - init_off + 1] = (val >> 8) & 0xff;
    		off += 2;
    		size -= 2;
    	}
    
    	while (size > 3) {
    		u32 val;
    		pci_user_read_config_dword(dev, off, &val);
    		data[off - init_off] = val & 0xff;
    		data[off - init_off + 1] = (val >> 8) & 0xff;
    		data[off - init_off + 2] = (val >> 16) & 0xff;
    		data[off - init_off + 3] = (val >> 24) & 0xff;
    		off += 4;
    		size -= 4;
    	}
    
    	if (size >= 2) {
    		u16 val;
    		pci_user_read_config_word(dev, off, &val);
    		data[off - init_off] = val & 0xff;
    		data[off - init_off + 1] = (val >> 8) & 0xff;
    		off += 2;
    		size -= 2;
    	}
    
    	if (size > 0) {
    		u8 val;
    		pci_user_read_config_byte(dev, off, &val);
    		data[off - init_off] = val;
    		off++;
    		--size;
    	}
    
    	return count;
    }
    
    static ssize_t
    pci_write_config(struct file* filp, struct kobject *kobj,
    		 struct bin_attribute *bin_attr,
    		 char *buf, loff_t off, size_t count)
    {
    	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
    	unsigned int size = count;
    	loff_t init_off = off;
    	u8 *data = (u8*) buf;
    
    	if (off > dev->cfg_size)
    		return 0;
    	if (off + count > dev->cfg_size) {
    		size = dev->cfg_size - off;
    		count = size;
    	}
    	
    	if ((off & 1) && size) {
    		pci_user_write_config_byte(dev, off, data[off - init_off]);
    		off++;
    		size--;
    	}
    	
    	if ((off & 3) && size > 2) {
    		u16 val = data[off - init_off];
    		val |= (u16) data[off - init_off + 1] << 8;
                    pci_user_write_config_word(dev, off, val);
                    off += 2;
                    size -= 2;
            }
    
    	while (size > 3) {
    		u32 val = data[off - init_off];
    		val |= (u32) data[off - init_off + 1] << 8;
    		val |= (u32) data[off - init_off + 2] << 16;
    		val |= (u32) data[off - init_off + 3] << 24;
    		pci_user_write_config_dword(dev, off, val);
    		off += 4;
    		size -= 4;
    	}
    	
    	if (size >= 2) {
    		u16 val = data[off - init_off];
    		val |= (u16) data[off - init_off + 1] << 8;
    		pci_user_write_config_word(dev, off, val);
    		off += 2;
    		size -= 2;
    	}
    
    	if (size) {
    		pci_user_write_config_byte(dev, off, data[off - init_off]);
    		off++;
    		--size;
    	}
    
    	return count;
    }
    
    static ssize_t
    read_vpd_attr(struct file *filp, struct kobject *kobj,
    	      struct bin_attribute *bin_attr,
    	      char *buf, loff_t off, size_t count)
    {
    	struct pci_dev *dev =
    		to_pci_dev(container_of(kobj, struct device, kobj));
    
    	if (off > bin_attr->size)
    		count = 0;
    	else if (count > bin_attr->size - off)
    		count = bin_attr->size - off;
    
    	return pci_read_vpd(dev, off, count, buf);
    }
    
    static ssize_t
    write_vpd_attr(struct file *filp, struct kobject *kobj,
    	       struct bin_attribute *bin_attr,
    	       char *buf, loff_t off, size_t count)
    {
    	struct pci_dev *dev =
    		to_pci_dev(container_of(kobj, struct device, kobj));
    
    	if (off > bin_attr->size)
    		count = 0;
    	else if (count > bin_attr->size - off)
    		count = bin_attr->size - off;
    
    	return pci_write_vpd(dev, off, count, buf);
    }
    
    #ifdef HAVE_PCI_LEGACY
    /**
     * pci_read_legacy_io - read byte(s) from legacy I/O port space
     * @filp: open sysfs file
     * @kobj: kobject corresponding to file to read from
     * @bin_attr: struct bin_attribute for this file
     * @buf: buffer to store results
     * @off: offset into legacy I/O port space
     * @count: number of bytes to read
     *
     * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific
     * callback routine (pci_legacy_read).
     */
    static ssize_t
    pci_read_legacy_io(struct file *filp, struct kobject *kobj,
    		   struct bin_attribute *bin_attr,
    		   char *buf, loff_t off, size_t count)
    {
            struct pci_bus *bus = to_pci_bus(container_of(kobj,
                                                          struct device,
    						      kobj));
    
            /* Only support 1, 2 or 4 byte accesses */
            if (count != 1 && count != 2 && count != 4)
                    return -EINVAL;
    
            return pci_legacy_read(bus, off, (u32 *)buf, count);
    }
    
    /**
     * pci_write_legacy_io - write byte(s) to legacy I/O port space
     * @filp: open sysfs file
     * @kobj: kobject corresponding to file to read from
     * @bin_attr: struct bin_attribute for this file
     * @buf: buffer containing value to be written
     * @off: offset into legacy I/O port space
     * @count: number of bytes to write
     *
     * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific
     * callback routine (pci_legacy_write).
     */
    static ssize_t
    pci_write_legacy_io(struct file *filp, struct kobject *kobj,
    		    struct bin_attribute *bin_attr,
    		    char *buf, loff_t off, size_t count)
    {
            struct pci_bus *bus = to_pci_bus(container_of(kobj,
    						      struct device,
    						      kobj));
            /* Only support 1, 2 or 4 byte accesses */
            if (count != 1 && count != 2 && count != 4)
                    return -EINVAL;
    
            return pci_legacy_write(bus, off, *(u32 *)buf, count);
    }
    
    /**
     * pci_mmap_legacy_mem - map legacy PCI memory into user memory space
     * @filp: open sysfs file
     * @kobj: kobject corresponding to device to be mapped
     * @attr: struct bin_attribute for this file
     * @vma: struct vm_area_struct passed to mmap
     *
     * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap
     * legacy memory space (first meg of bus space) into application virtual
     * memory space.
     */
    static int
    pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj,
    		    struct bin_attribute *attr,
                        struct vm_area_struct *vma)
    {
            struct pci_bus *bus = to_pci_bus(container_of(kobj,
                                                          struct device,
    						      kobj));
    
            return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem);
    }
    
    /**
     * pci_mmap_legacy_io - map legacy PCI IO into user memory space
     * @filp: open sysfs file
     * @kobj: kobject corresponding to device to be mapped
     * @attr: struct bin_attribute for this file
     * @vma: struct vm_area_struct passed to mmap
     *
     * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap
     * legacy IO space (first meg of bus space) into application virtual
     * memory space. Returns -ENOSYS if the operation isn't supported
     */
    static int
    pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
    		   struct bin_attribute *attr,
    		   struct vm_area_struct *vma)
    {
            struct pci_bus *bus = to_pci_bus(container_of(kobj,
                                                          struct device,
    						      kobj));
    
            return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
    }
    
    /**
     * pci_adjust_legacy_attr - adjustment of legacy file attributes
     * @b: bus to create files under
     * @mmap_type: I/O port or memory
     *
     * Stub implementation. Can be overridden by arch if necessary.
     */
    void __weak
    pci_adjust_legacy_attr(struct pci_bus *b, enum pci_mmap_state mmap_type)
    {
    	return;
    }
    
    /**
     * pci_create_legacy_files - create legacy I/O port and memory files
     * @b: bus to create files under
     *
     * Some platforms allow access to legacy I/O port and ISA memory space on
     * a per-bus basis.  This routine creates the files and ties them into
     * their associated read, write and mmap files from pci-sysfs.c
     *
     * On error unwind, but don't propogate the error to the caller
     * as it is ok to set up the PCI bus without these files.
     */
    void pci_create_legacy_files(struct pci_bus *b)
    {
    	int error;
    
    	b->legacy_io = kzalloc(sizeof(struct bin_attribute) * 2,
    			       GFP_ATOMIC);
    	if (!b->legacy_io)
    		goto kzalloc_err;
    
    	sysfs_bin_attr_init(b->legacy_io);
    	b->legacy_io->attr.name = "legacy_io";
    	b->legacy_io->size = 0xffff;
    	b->legacy_io->attr.mode = S_IRUSR | S_IWUSR;
    	b->legacy_io->read = pci_read_legacy_io;
    	b->legacy_io->write = pci_write_legacy_io;
    	b->legacy_io->mmap = pci_mmap_legacy_io;
    	pci_adjust_legacy_attr(b, pci_mmap_io);
    	error = device_create_bin_file(&b->dev, b->legacy_io);
    	if (error)
    		goto legacy_io_err;
    
    	/* Allocated above after the legacy_io struct */
    	b->legacy_mem = b->legacy_io + 1;
    	sysfs_bin_attr_init(b->legacy_mem);
    	b->legacy_mem->attr.name = "legacy_mem";
    	b->legacy_mem->size = 1024*1024;
    	b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
    	b->legacy_mem->mmap = pci_mmap_legacy_mem;
    	pci_adjust_legacy_attr(b, pci_mmap_mem);
    	error = device_create_bin_file(&b->dev, b->legacy_mem);
    	if (error)
    		goto legacy_mem_err;
    
    	return;
    
    legacy_mem_err:
    	device_remove_bin_file(&b->dev, b->legacy_io);
    legacy_io_err:
    	kfree(b->legacy_io);
    	b->legacy_io = NULL;
    kzalloc_err:
    	printk(KERN_WARNING "pci: warning: could not create legacy I/O port "
    	       "and ISA memory resources to sysfs\n");
    	return;
    }
    
    void pci_remove_legacy_files(struct pci_bus *b)
    {
    	if (b->legacy_io) {
    		device_remove_bin_file(&b->dev, b->legacy_io);
    		device_remove_bin_file(&b->dev, b->legacy_mem);
    		kfree(b->legacy_io); /* both are allocated here */
    	}
    }
    #endif /* HAVE_PCI_LEGACY */
    
    #ifdef HAVE_PCI_MMAP
    
    int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
    		  enum pci_mmap_api mmap_api)
    {
    	unsigned long nr, start, size, pci_start;
    
    	if (pci_resource_len(pdev, resno) == 0)
    		return 0;
    	nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
    	start = vma->vm_pgoff;
    	size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
    	pci_start = (mmap_api == PCI_MMAP_PROCFS) ?
    			pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0;
    	if (start >= pci_start && start < pci_start + size &&
    			start + nr <= pci_start + size)
    		return 1;
    	return 0;
    }
    
    /**
     * pci_mmap_resource - map a PCI resource into user memory space
     * @kobj: kobject for mapping
     * @attr: struct bin_attribute for the file being mapped
     * @vma: struct vm_area_struct passed into the mmap
     * @write_combine: 1 for write_combine mapping
     *
     * Use the regular PCI mapping routines to map a PCI resource into userspace.
     */
    static int
    pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
    		  struct vm_area_struct *vma, int write_combine)
    {
    	struct pci_dev *pdev = to_pci_dev(container_of(kobj,
    						       struct device, kobj));
    	struct resource *res = attr->private;
    	enum pci_mmap_state mmap_type;
    	resource_size_t start, end;
    	int i;
    
    	for (i = 0; i < PCI_ROM_RESOURCE; i++)
    		if (res == &pdev->resource[i])
    			break;
    	if (i >= PCI_ROM_RESOURCE)
    		return -ENODEV;
    
    	if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
    		WARN(1, "process \"%s\" tried to map 0x%08lx bytes "
    			"at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
    			current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
    			pci_name(pdev), i,
    			(u64)pci_resource_start(pdev, i),
    			(u64)pci_resource_len(pdev, i));
    		return -EINVAL;
    	}
    
    	/* pci_mmap_page_range() expects the same kind of entry as coming
    	 * from /proc/bus/pci/ which is a "user visible" value. If this is
    	 * different from the resource itself, arch will do necessary fixup.
    	 */
    	pci_resource_to_user(pdev, i, res, &start, &end);
    	vma->vm_pgoff += start >> PAGE_SHIFT;
    	mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
    
    	if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start))
    		return -EINVAL;
    
    	return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
    }
    
    static int
    pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
    		     struct bin_attribute *attr,
    		     struct vm_area_struct *vma)
    {
    	return pci_mmap_resource(kobj, attr, vma, 0);
    }
    
    static int
    pci_mmap_resource_wc(struct file *filp, struct kobject *kobj,
    		     struct bin_attribute *attr,
    		     struct vm_area_struct *vma)
    {
    	return pci_mmap_resource(kobj, attr, vma, 1);
    }
    
    static ssize_t
    pci_resource_io(struct file *filp, struct kobject *kobj,
    		struct bin_attribute *attr, char *buf,
    		loff_t off, size_t count, bool write)
    {
    	struct pci_dev *pdev = to_pci_dev(container_of(kobj,
    						       struct device, kobj));
    	struct resource *res = attr->private;
    	unsigned long port = off;
    	int i;
    
    	for (i = 0; i < PCI_ROM_RESOURCE; i++)
    		if (res == &pdev->resource[i])
    			break;
    	if (i >= PCI_ROM_RESOURCE)
    		return -ENODEV;
    
    	port += pci_resource_start(pdev, i);
    
    	if (port > pci_resource_end(pdev, i))
    		return 0;
    
    	if (port + count - 1 > pci_resource_end(pdev, i))
    		return -EINVAL;
    
    	switch (count) {
    	case 1:
    		if (write)
    			outb(*(u8 *)buf, port);
    		else
    			*(u8 *)buf = inb(port);
    		return 1;
    	case 2:
    		if (write)
    			outw(*(u16 *)buf, port);
    		else
    			*(u16 *)buf = inw(port);
    		return 2;
    	case 4:
    		if (write)
    			outl(*(u32 *)buf, port);
    		else
    			*(u32 *)buf = inl(port);
    		return 4;
    	}
    	return -EINVAL;
    }
    
    static ssize_t
    pci_read_resource_io(struct file *filp, struct kobject *kobj,
    		     struct bin_attribute *attr, char *buf,
    		     loff_t off, size_t count)
    {
    	return pci_resource_io(filp, kobj, attr, buf, off, count, false);
    }
    
    static ssize_t
    pci_write_resource_io(struct file *filp, struct kobject *kobj,
    		      struct bin_attribute *attr, char *buf,
    		      loff_t off, size_t count)
    {
    	return pci_resource_io(filp, kobj, attr, buf, off, count, true);
    }
    
    /**
     * pci_remove_resource_files - cleanup resource files
     * @pdev: dev to cleanup
     *
     * If we created resource files for @pdev, remove them from sysfs and
     * free their resources.
     */
    static void
    pci_remove_resource_files(struct pci_dev *pdev)
    {
    	int i;
    
    	for (i = 0; i < PCI_ROM_RESOURCE; i++) {
    		struct bin_attribute *res_attr;
    
    		res_attr = pdev->res_attr[i];
    		if (res_attr) {
    			sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
    			kfree(res_attr);
    		}
    
    		res_attr = pdev->res_attr_wc[i];
    		if (res_attr) {
    			sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
    			kfree(res_attr);
    		}
    	}
    }
    
    static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
    {
    	/* allocate attribute structure, piggyback attribute name */
    	int name_len = write_combine ? 13 : 10;
    	struct bin_attribute *res_attr;
    	int retval;
    
    	res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
    	if (res_attr) {
    		char *res_attr_name = (char *)(res_attr + 1);
    
    		sysfs_bin_attr_init(res_attr);
    		if (write_combine) {
    			pdev->res_attr_wc[num] = res_attr;
    			sprintf(res_attr_name, "resource%d_wc", num);
    			res_attr->mmap = pci_mmap_resource_wc;
    		} else {
    			pdev->res_attr[num] = res_attr;
    			sprintf(res_attr_name, "resource%d", num);
    			res_attr->mmap = pci_mmap_resource_uc;
    		}
    		if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
    			res_attr->read = pci_read_resource_io;
    			res_attr->write = pci_write_resource_io;
    		}
    		res_attr->attr.name = res_attr_name;
    		res_attr->attr.mode = S_IRUSR | S_IWUSR;
    		res_attr->size = pci_resource_len(pdev, num);
    		res_attr->private = &pdev->resource[num];
    		retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
    	} else
    		retval = -ENOMEM;
    
    	return retval;
    }
    
    /**
     * pci_create_resource_files - create resource files in sysfs for @dev
     * @pdev: dev in question
     *
     * Walk the resources in @pdev creating files for each resource available.
     */
    static int pci_create_resource_files(struct pci_dev *pdev)
    {
    	int i;
    	int retval;
    
    	/* Expose the PCI resources from this device as files */
    	for (i = 0; i < PCI_ROM_RESOURCE; i++) {
    
    		/* skip empty resources */
    		if (!pci_resource_len(pdev, i))
    			continue;
    
    		retval = pci_create_attr(pdev, i, 0);
    		/* for prefetchable resources, create a WC mappable file */
    		if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH)
    			retval = pci_create_attr(pdev, i, 1);
    
    		if (retval) {
    			pci_remove_resource_files(pdev);
    			return retval;
    		}
    	}
    	return 0;
    }
    #else /* !HAVE_PCI_MMAP */
    int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; }
    void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
    #endif /* HAVE_PCI_MMAP */
    
    /**
     * pci_write_rom - used to enable access to the PCI ROM display
     * @filp: sysfs file
     * @kobj: kernel object handle
     * @bin_attr: struct bin_attribute for this file
     * @buf: user input
     * @off: file offset
     * @count: number of byte in input
     *
     * writing anything except 0 enables it
     */
    static ssize_t
    pci_write_rom(struct file *filp, struct kobject *kobj,
    	      struct bin_attribute *bin_attr,
    	      char *buf, loff_t off, size_t count)
    {
    	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
    
    	if ((off ==  0) && (*buf == '0') && (count == 2))
    		pdev->rom_attr_enabled = 0;
    	else
    		pdev->rom_attr_enabled = 1;
    
    	return count;
    }
    
    /**
     * pci_read_rom - read a PCI ROM
     * @filp: sysfs file
     * @kobj: kernel object handle
     * @bin_attr: struct bin_attribute for this file
     * @buf: where to put the data we read from the ROM
     * @off: file offset
     * @count: number of bytes to read
     *
     * Put @count bytes starting at @off into @buf from the ROM in the PCI
     * device corresponding to @kobj.
     */
    static ssize_t
    pci_read_rom(struct file *filp, struct kobject *kobj,
    	     struct bin_attribute *bin_attr,
    	     char *buf, loff_t off, size_t count)
    {
    	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
    	void __iomem *rom;
    	size_t size;
    
    	if (!pdev->rom_attr_enabled)
    		return -EINVAL;
    	
    	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
    	if (!rom || !size)
    		return -EIO;
    		
    	if (off >= size)
    		count = 0;
    	else {
    		if (off + count > size)
    			count = size - off;
    		
    		memcpy_fromio(buf, rom + off, count);
    	}
    	pci_unmap_rom(pdev, rom);
    		
    	return count;
    }
    
    static struct bin_attribute pci_config_attr = {
    	.attr =	{
    		.name = "config",
    		.mode = S_IRUGO | S_IWUSR,
    	},
    	.size = PCI_CFG_SPACE_SIZE,
    	.read = pci_read_config,
    	.write = pci_write_config,
    };
    
    static struct bin_attribute pcie_config_attr = {
    	.attr =	{
    		.name = "config",
    		.mode = S_IRUGO | S_IWUSR,
    	},
    	.size = PCI_CFG_SPACE_EXP_SIZE,
    	.read = pci_read_config,
    	.write = pci_write_config,
    };
    
    int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev)
    {
    	return 0;
    }
    
    static ssize_t reset_store(struct device *dev,
    			   struct device_attribute *attr, const char *buf,
    			   size_t count)
    {
    	struct pci_dev *pdev = to_pci_dev(dev);
    	unsigned long val;
    	ssize_t result = strict_strtoul(buf, 0, &val);
    
    	if (result < 0)
    		return result;
    
    	if (val != 1)
    		return -EINVAL;
    
    	result = pci_reset_function(pdev);
    	if (result < 0)
    		return result;
    
    	return count;
    }
    
    static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store);
    
    static int pci_create_capabilities_sysfs(struct pci_dev *dev)
    {
    	int retval;
    	struct bin_attribute *attr;
    
    	/* If the device has VPD, try to expose it in sysfs. */
    	if (dev->vpd) {
    		attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
    		if (!attr)
    			return -ENOMEM;
    
    		sysfs_bin_attr_init(attr);
    		attr->size = dev->vpd->len;
    		attr->attr.name = "vpd";
    		attr->attr.mode = S_IRUSR | S_IWUSR;
    		attr->read = read_vpd_attr;
    		attr->write = write_vpd_attr;
    		retval = sysfs_create_bin_file(&dev->dev.kobj, attr);
    		if (retval) {
    			kfree(dev->vpd->attr);
    			return retval;
    		}
    		dev->vpd->attr = attr;
    	}
    
    	/* Active State Power Management */
    	pcie_aspm_create_sysfs_dev_files(dev);
    
    	if (!pci_probe_reset_function(dev)) {
    		retval = device_create_file(&dev->dev, &reset_attr);
    		if (retval)
    			goto error;
    		dev->reset_fn = 1;
    	}
    	return 0;
    
    error:
    	pcie_aspm_remove_sysfs_dev_files(dev);
    	if (dev->vpd && dev->vpd->attr) {
    		sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
    		kfree(dev->vpd->attr);
    	}
    
    	return retval;
    }
    
    int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
    {
    	int retval;
    	int rom_size = 0;
    	struct bin_attribute *attr;
    
    	if (!sysfs_initialized)
    		return -EACCES;
    
    	if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
    		retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
    	else
    		retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
    	if (retval)
    		goto err;
    
    	retval = pci_create_resource_files(pdev);
    	if (retval)
    		goto err_config_file;
    
    	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
    		rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
    	else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
    		rom_size = 0x20000;
    
    	/* If the device has a ROM, try to expose it in sysfs. */
    	if (rom_size) {
    		attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
    		if (!attr) {
    			retval = -ENOMEM;
    			goto err_resource_files;
    		}
    		sysfs_bin_attr_init(attr);
    		attr->size = rom_size;
    		attr->attr.name = "rom";
    		attr->attr.mode = S_IRUSR;
    		attr->read = pci_read_rom;
    		attr->write = pci_write_rom;
    		retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
    		if (retval) {
    			kfree(attr);
    			goto err_resource_files;
    		}
    		pdev->rom_attr = attr;
    	}
    
    	if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
    		retval = device_create_file(&pdev->dev, &vga_attr);
    		if (retval)
    			goto err_rom_file;
    	}
    
    	/* add platform-specific attributes */
    	retval = pcibios_add_platform_entries(pdev);
    	if (retval)
    		goto err_vga_file;
    
    	/* add sysfs entries for various capabilities */
    	retval = pci_create_capabilities_sysfs(pdev);
    	if (retval)
    		goto err_vga_file;
    
    	pci_create_firmware_label_files(pdev);
    
    	return 0;
    
    err_vga_file:
    	if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
    		device_remove_file(&pdev->dev, &vga_attr);
    err_rom_file:
    	if (rom_size) {
    		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
    		kfree(pdev->rom_attr);
    		pdev->rom_attr = NULL;
    	}
    err_resource_files:
    	pci_remove_resource_files(pdev);
    err_config_file:
    	if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
    		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
    	else
    		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
    err:
    	return retval;
    }
    
    static void pci_remove_capabilities_sysfs(struct pci_dev *dev)
    {
    	if (dev->vpd && dev->vpd->attr) {
    		sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
    		kfree(dev->vpd->attr);
    	}
    
    	pcie_aspm_remove_sysfs_dev_files(dev);
    	if (dev->reset_fn) {
    		device_remove_file(&dev->dev, &reset_attr);
    		dev->reset_fn = 0;
    	}
    }
    
    /**
     * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
     * @pdev: device whose entries we should free
     *
     * Cleanup when @pdev is removed from sysfs.
     */
    void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
    {
    	int rom_size = 0;
    
    	if (!sysfs_initialized)
    		return;
    
    	pci_remove_capabilities_sysfs(pdev);
    
    	if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
    		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
    	else
    		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
    
    	pci_remove_resource_files(pdev);
    
    	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
    		rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
    	else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
    		rom_size = 0x20000;
    
    	if (rom_size && pdev->rom_attr) {
    		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
    		kfree(pdev->rom_attr);
    	}
    
    	pci_remove_firmware_label_files(pdev);
    
    }
    
    static int __init pci_sysfs_init(void)
    {
    	struct pci_dev *pdev = NULL;
    	int retval;
    
    	sysfs_initialized = 1;
    	for_each_pci_dev(pdev) {
    		retval = pci_create_sysfs_dev_files(pdev);
    		if (retval) {
    			pci_dev_put(pdev);
    			return retval;
    		}
    	}
    
    	return 0;
    }
    
    late_initcall(pci_sysfs_init);
    
    i attached the two files I've worked on...

  • I will check with the PCIe team, I will let you know when I have something.

    Regards,
    Pavel
  • thankyou so much!!!
  • if you need more detail on what i tried to do in my modification, just ask.
    Omar
  • Omar,

    I receive feedback that PCIe hot plug is still not supported. See if the below e2e threads will be in help:

    e2e.ti.com/.../153748
    e2e.ti.com/.../123972
    e2e.ti.com/.../343679

    BR
    Pavel
  • Thanks i will read them.
    Anyway I'm really close to make it working...
    It almost working fine, still have some error only in some cases..

    Omar