Implementing PCI & Super I/O Firmware For Linux
From RCSWiki
Early device configuration
Before Linux kernel is uncompressed, onboard PCI devices including Super I/O devices need to be configured correctly:
- Implement configuration function ml410_init() :
- Modify linux-2.6.10/arch/ppc/boot/simple/Makefile
- Add ml410.o right after boot-$(CONFIG_40x) += embed_config.o
boot-$(CONFIG_40x) += embed_config.o ml410.o
- Call extern function in linux-2.6.10/arch/ppc/boot/simple/misc-embedded.c
- Declare ml410_init() right after the line extern void embed_config(bd_t **bp);
extern void embed_config(bd_t **bp);
/* Early PCI device configuration on ML-410*/ extern void ml410_init();
- Call ml410_init() right after the piece of code com_port = serial_init(0, bp); #endif
#if defined(CONFIG_SERIAL_CPM_CONSOLE) || defined(CONFIG_SERIAL_8250_CONSOLE)
com_port = serial_init(0, bp);
#endif
/* Early PCI device configuration on ML-410*/
#ifdef CONFIG_PCI
ml410_init();
#endif
Map PCI I/O, Memory, Configuration Space parameters
Once the kernel is uncompressed to the memory, Linux requires developer to define several names in order to talk to PCI devices.
- The reader can download virtex-ii_pro.h and just replace the original.
- Or add definition in linux-2.6.10/arch/ppc/platforms/4xx/virtex-ii_pro.h right after the line #include <asm/ibm405.h>
#include <asm/ibm405.h>
#ifdef CONFIG_PCI #include "./xparameters/xparameters_ml300.h" /* PCI memory space */ #define PPC405_PCI_MEM_BASE XPAR_PCI_0_MEM_BASEADDR #define PPC405_PCI_LOWER_MEM XPAR_PCI_0_MEM_BASEADDR #define PPC405_PCI_UPPER_MEM XPAR_PCI_0_MEM_HIGHADDR #define PPC405_PCI_PHY_MEM_BASE XPAR_PCI_0_MEM_BASEADDR /* PCI I/O space parameters for io_block_mapping. */ #define PPC4xx_PCI_IO_PADDR ((uint)XPAR_PCI_0_IO_BASEADDR) #define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR #define PPC4xx_PCI_IO_SIZE 0x10000 /* Hardcoded size from ppc405_pci.c */ /* PCI I/O space processor address */ #define PPC405_PCI_PHY_IO_BASE XPAR_PCI_0_IO_BASEADDR /* PCI I/O space PCI address */ #define PPC405_PCI_LOWER_IO 0x00000000 #define PPC405_PCI_UPPER_IO (PPC405_PCI_LOWER_IO + PPC4xx_PCI_IO_SIZE - 1) /* PCI Configuration space parameters for io_block_mapping. */ #define PPC4xx_PCI_CFG_PADDR ((uint)XPAR_PCI_0_CONFIG_ADDR) #define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR #define PPC4xx_PCI_CFG_SIZE 8u /* size of two registers */ /* PCI Configuration space address and data registers. */ #define PPC405_PCI_CONFIG_ADDR XPAR_PCI_0_CONFIG_ADDR #define PPC405_PCI_CONFIG_DATA XPAR_PCI_0_CONFIG_DATA /* PCI Local configuration space parameters for io_block_mapping. */ #define PPC4xx_PCI_LCFG_PADDR ((uint)XPAR_PCI_0_LCONFIG_ADDR) #define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR #define PPC4xx_PCI_LCFG_SIZE 256u /* PCI configuration address space size */ #define _IO_BASE isa_io_base #define _ISA_MEM_BASE isa_mem_base #define PCI_DRAM_OFFSET pci_dram_offset #endif /* CONFIG_PCI */
Add OPB2PCI parameters for Linux
In xparameters_ml40x.h automatically generated by EDK8.2i (EDK9.2 or higher has solved this problem), some PCI parameters have to be added manually.
- In linux-2.6.10/arch/ppc/platforms/xparameters/xparameters_ml300.h (we have overlay it by xparameters_ml40x.h before )
- Comment the line #define XPAR_PCI_0_CLOCK_FREQ_HZ
//#define XPAR_PCI_0_CLOCK_FREQ_HZ
- Add PCI parameters:
#define XPAR_XPCI_CLOCK_HZ 33333333 #define XPAR_PCI32_BRIDGE_CONFIG_ADDR 0x42600000+0x10c #define XPAR_PCI32_BRIDGE_CONFIG_DATA 0x42600000+0x110 #define XPAR_PCI32_BRIDGE_LCONFIG_ADDR 0x42600000 #define XPAR_PCI32_BRIDGE_MEM_BASEADDR 0x20000000 #define XPAR_PCI32_BRIDGE_MEM_HIGHADDR 0x3fffffff #define XPAR_PCI32_BRIDGE_IO_BASEADDR 0xe8000000 #define XPAR_PCI32_BRIDGE_IO_HIGHADDR 0xebffffff /******************************************************************/ #define XPAR_PCI_0_BASEADDR XPAR_PCI32_BRIDGE_BASEADDR #define XPAR_PCI_0_HIGHADDR XPAR_PCI32_BRIDGE_HIGHADDR #define XPAR_PCI_0_CONFIG_ADDR XPAR_PCI32_BRIDGE_CONFIG_ADDR #define XPAR_PCI_0_CONFIG_DATA XPAR_PCI32_BRIDGE_CONFIG_DATA #define XPAR_PCI_0_LCONFIG_ADDR XPAR_PCI32_BRIDGE_LCONFIG_ADDR #define XPAR_PCI_0_MEM_BASEADDR XPAR_PCI32_BRIDGE_MEM_BASEADDR #define XPAR_PCI_0_MEM_HIGHADDR XPAR_PCI32_BRIDGE_MEM_HIGHADDR #define XPAR_PCI_0_IO_BASEADDR XPAR_PCI32_BRIDGE_IO_BASEADDR #define XPAR_PCI_0_IO_HIGHADDR XPAR_PCI32_BRIDGE_IO_HIGHADDR #define XPAR_PCI_0_CLOCK_FREQ_HZ XPAR_XPCI_CLOCK_HZ #define XPAR_PCI_0_DEVICE_ID XPAR_PCI32_BRIDGE_DEVICE_ID #define XPAR_INTC_0_PCI_0_VEC_ID_SBR XPAR_OPB_INTC_0_SYSTEM_FPGA_0_PCI32_BRIDGE_PCI_SBR_INT_INTR
Define PCI device IRQ Lookup Table
Linux also needs to allocate its IRQ resources to PCI devices when probing PCI bus.
- The reader can download xilinx_ml300.c and just replace the original.
- Or add IRQ Lookup Table in linux-2.6.10/arch/ppc/platforms/4xx/xilinx_ml300.c right after head files:
#include <linux/config.h> #include <linux/init.h> #include <linux/irq.h> #include <linux/tty.h> #include <linux/serial.h> #include <linux/serial_core.h> #include <linux/serialP.h> #include <asm/io.h> #include <asm/machdep.h> #include <asm/ocp.h> #include <platforms/4xx/virtex-ii_pro.h> /* for NR_SER_PORTS */
#ifdef CONFIG_PCI
#include <asm/pci-bridge.h>
#define PCI_INTA (XPAR_INTC_0_PCI_0_VEC_ID_A)
#define PCI_INTB (XPAR_INTC_0_PCI_0_VEC_ID_B)
#define PCI_INTC (XPAR_INTC_0_PCI_0_VEC_ID_C)
#define PCI_INTD (XPAR_INTC_0_PCI_0_VEC_ID_D)
/* changes for ML410 */
#define PCI_SBR (XPAR_INTC_0_PCI_0_VEC_ID_SBR)
int __init
ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin)
{
static signed char pci_irq_table[][32] =
{
{ PCI_SBR, -1, -1, -1 }, /* ALi Audio 1 */
{ PCI_SBR, -1, -1, -1 }, /* ALi South Bridge 2 */
{ PCI_SBR, -1, -1, -1 }, /* ALi Modem 3 */
{ -1, -1, -1, -1 }, /* Other 4 */
{ PCI_INTB, PCI_INTC, PCI_INTD, PCI_INTA }, /* Pri. slot 5 5 */
{ PCI_INTC, PCI_INTD, PCI_INTA, PCI_INTB }, /* Pri. slot 3 6 */
{ -1, -1, -1, -1 }, /* Other 7 */
{ -1, -1, -1, -1 }, /* Other 8 */
{ PCI_INTB, PCI_INTC, PCI_INTD, PCI_INTA }, /* PCI 2 PCI Bridge 9 */
{ PCI_SBR, -1, -1, -1 }, /* ALi USB #2 10 */
{ PCI_SBR, -1, -1, -1 }, /* ALi IDE 11 */
{ PCI_SBR, -1, -1, -1 }, /* ALi Pwr Mgmt 12 */
{ -1, -1, -1, -1 }, /* Other 13 */
{ -1, -1, -1, -1 }, /* Other 14 */
{ PCI_SBR, -1, -1, -1 }, /* ALi USB #1 15 */
};
const long min_idsel = 1, max_idsel = 15, irqs_per_slot = 4;
int res = PCI_IRQ_TABLE_LOOKUP;
int bus = dev->bus->number;
printk("ppc405_map_irq: bus %d idsel %d pin %d, res = %d\n", bus, idsel, pin, res);
return res;
};
#endif
Hook up 8259 and Xilinx interrupt controller in Linux
On Ali 15x3 South Bridge, there is an embedded interrupt controller 8259 which the developer needs to hook up with Xilinx interrupt controller in Linux.
- In linux-2.6.10/arch/ppc/syslib/, download xilinx_pic.c and replace the original.
Setup PCI bridge memory ranges in Linux
- In linux-2.6.10/arch/ppc/syslib/, download ppc405_pci.c and replace the original.
Assign IRQ to South bridge
- In linux-2.6.10/driver/ide/pci , download alim15x3.c and replace the original.
Compile and create Linux image
Clean old Linux image:
- make ARCH=ppc CROSS_COMPILE=powerpc-405-linux-gnu- clean
Create Linux Image combined with initial ram disk:
- make ARCH=ppc CROSS_COMPILE=powerpc-405-linux-gnu- zImage.initrd
Continue on with Example: How to mount IDE hard drive on ML410
