Path: blob/master/arch/arm/mach-ixp2000/ixdp2x00.c
10817 views
/*1* arch/arm/mach-ixp2000/ixdp2x00.c2*3* Code common to IXDP2400 and IXDP2800 platforms.4*5* Original Author: Naeem Afzal <[email protected]>6* Maintainer: Deepak Saxena <[email protected]>7*8* Copyright (C) 2002 Intel Corp.9* Copyright (C) 2003-2004 MontaVista Software, Inc.10*11* This program is free software; you can redistribute it and/or modify it12* under the terms of the GNU General Public License as published by the13* Free Software Foundation; either version 2 of the License, or (at your14* option) any later version.15*/16#include <linux/kernel.h>17#include <linux/init.h>18#include <linux/mm.h>19#include <linux/sched.h>20#include <linux/interrupt.h>21#include <linux/platform_device.h>22#include <linux/bitops.h>23#include <linux/pci.h>24#include <linux/ioport.h>25#include <linux/delay.h>26#include <linux/io.h>2728#include <asm/irq.h>29#include <asm/pgtable.h>30#include <asm/page.h>31#include <asm/system.h>32#include <mach/hardware.h>33#include <asm/mach-types.h>3435#include <asm/mach/pci.h>36#include <asm/mach/map.h>37#include <asm/mach/irq.h>38#include <asm/mach/time.h>39#include <asm/mach/flash.h>40#include <asm/mach/arch.h>4142#include <mach/gpio.h>434445/*************************************************************************46* IXDP2x00 IRQ Initialization47*************************************************************************/48static volatile unsigned long *board_irq_mask;49static volatile unsigned long *board_irq_stat;50static unsigned long board_irq_count;5152#ifdef CONFIG_ARCH_IXDP240053/*54* Slowport configuration for accessing CPLD registers on IXDP2x0055*/56static struct slowport_cfg slowport_cpld_cfg = {57.CCR = SLOWPORT_CCR_DIV_2,58.WTC = 0x00000070,59.RTC = 0x00000070,60.PCR = SLOWPORT_MODE_FLASH,61.ADC = SLOWPORT_ADDR_WIDTH_24 | SLOWPORT_DATA_WIDTH_862};63#endif6465static void ixdp2x00_irq_mask(struct irq_data *d)66{67unsigned long dummy;68static struct slowport_cfg old_cfg;6970/*71* This is ugly in common code but really don't know72* of a better way to handle it. :(73*/74#ifdef CONFIG_ARCH_IXDP240075if (machine_is_ixdp2400())76ixp2000_acquire_slowport(&slowport_cpld_cfg, &old_cfg);77#endif7879dummy = *board_irq_mask;80dummy |= IXP2000_BOARD_IRQ_MASK(d->irq);81ixp2000_reg_wrb(board_irq_mask, dummy);8283#ifdef CONFIG_ARCH_IXDP240084if (machine_is_ixdp2400())85ixp2000_release_slowport(&old_cfg);86#endif87}8889static void ixdp2x00_irq_unmask(struct irq_data *d)90{91unsigned long dummy;92static struct slowport_cfg old_cfg;9394#ifdef CONFIG_ARCH_IXDP240095if (machine_is_ixdp2400())96ixp2000_acquire_slowport(&slowport_cpld_cfg, &old_cfg);97#endif9899dummy = *board_irq_mask;100dummy &= ~IXP2000_BOARD_IRQ_MASK(d->irq);101ixp2000_reg_wrb(board_irq_mask, dummy);102103if (machine_is_ixdp2400())104ixp2000_release_slowport(&old_cfg);105}106107static void ixdp2x00_irq_handler(unsigned int irq, struct irq_desc *desc)108{109volatile u32 ex_interrupt = 0;110static struct slowport_cfg old_cfg;111int i;112113desc->irq_data.chip->irq_mask(&desc->irq_data);114115#ifdef CONFIG_ARCH_IXDP2400116if (machine_is_ixdp2400())117ixp2000_acquire_slowport(&slowport_cpld_cfg, &old_cfg);118#endif119ex_interrupt = *board_irq_stat & 0xff;120if (machine_is_ixdp2400())121ixp2000_release_slowport(&old_cfg);122123if(!ex_interrupt) {124printk(KERN_ERR "Spurious IXDP2x00 CPLD interrupt!\n");125return;126}127128for(i = 0; i < board_irq_count; i++) {129if(ex_interrupt & (1 << i)) {130int cpld_irq = IXP2000_BOARD_IRQ(0) + i;131generic_handle_irq(cpld_irq);132}133}134135desc->irq_data.chip->irq_unmask(&desc->irq_data);136}137138static struct irq_chip ixdp2x00_cpld_irq_chip = {139.irq_ack = ixdp2x00_irq_mask,140.irq_mask = ixdp2x00_irq_mask,141.irq_unmask = ixdp2x00_irq_unmask142};143144void __init ixdp2x00_init_irq(volatile unsigned long *stat_reg, volatile unsigned long *mask_reg, unsigned long nr_of_irqs)145{146unsigned int irq;147148ixp2000_init_irq();149150if (!ixdp2x00_master_npu())151return;152153board_irq_stat = stat_reg;154board_irq_mask = mask_reg;155board_irq_count = nr_of_irqs;156157*board_irq_mask = 0xffffffff;158159for(irq = IXP2000_BOARD_IRQ(0); irq < IXP2000_BOARD_IRQ(board_irq_count); irq++) {160irq_set_chip_and_handler(irq, &ixdp2x00_cpld_irq_chip,161handle_level_irq);162set_irq_flags(irq, IRQF_VALID);163}164165/* Hook into PCI interrupt */166irq_set_chained_handler(IRQ_IXP2000_PCIB, ixdp2x00_irq_handler);167}168169/*************************************************************************170* IXDP2x00 memory map171*************************************************************************/172static struct map_desc ixdp2x00_io_desc __initdata = {173.virtual = IXDP2X00_VIRT_CPLD_BASE,174.pfn = __phys_to_pfn(IXDP2X00_PHYS_CPLD_BASE),175.length = IXDP2X00_CPLD_SIZE,176.type = MT_DEVICE177};178179void __init ixdp2x00_map_io(void)180{181ixp2000_map_io();182183iotable_init(&ixdp2x00_io_desc, 1);184}185186/*************************************************************************187* IXDP2x00-common PCI init188*189* The IXDP2[48]00 has a horrid PCI bus layout. Basically the board190* contains two NPUs (ingress and egress) connected over PCI, both running191* instances of the kernel. So far so good. Peers on the PCI bus running192* Linux is a common design in telecom systems. The problem is that instead193* of all the devices being controlled by a single host, different194* devices are controlled by different NPUs on the same bus, leading to195* multiple hosts on the bus. The exact bus layout looks like:196*197* Bus 0198* Master NPU <-------------------+-------------------> Slave NPU199* |200* |201* P2P202* |203*204* Bus 1 |205* <--+------+---------+---------+------+-->206* | | | | |207* | | | | |208* ... Dev PMC Media Eth0 Eth1 ...209*210* The master controls all but Eth1, which is controlled by the211* slave. What this means is that the both the master and the slave212* have to scan the bus, but only one of them can enumerate the bus.213* In addition, after the bus is scanned, each kernel must remove214* the device(s) it does not control from the PCI dev list otherwise215* a driver on each NPU will try to manage it and we will have horrible216* conflicts. Oh..and the slave NPU needs to see the master NPU217* for Intel's drivers to work properly. Closed source drivers...218*219* The way we deal with this is fairly simple but ugly:220*221* 1) Let master scan and enumerate the bus completely.222* 2) Master deletes Eth1 from device list.223* 3) Slave scans bus and then deletes all but Eth1 (Eth0 on slave)224* from device list.225* 4) Find HW designers and LART them.226*227* The boards also do not do normal PCI IRQ routing, or any sort of228* sensical swizzling, so we just need to check where on the bus a229* device sits and figure out to which CPLD pin the interrupt is routed.230* See ixdp2[48]00.c files.231*232*************************************************************************/233void ixdp2x00_slave_pci_postinit(void)234{235struct pci_dev *dev;236237/*238* Remove PMC device is there is one239*/240if((dev = pci_get_bus_and_slot(1, IXDP2X00_PMC_DEVFN))) {241pci_remove_bus_device(dev);242pci_dev_put(dev);243}244245dev = pci_get_bus_and_slot(0, IXDP2X00_21555_DEVFN);246pci_remove_bus_device(dev);247pci_dev_put(dev);248}249250/**************************************************************************251* IXDP2x00 Machine Setup252*************************************************************************/253static struct flash_platform_data ixdp2x00_platform_data = {254.map_name = "cfi_probe",255.width = 1,256};257258static struct ixp2000_flash_data ixdp2x00_flash_data = {259.platform_data = &ixdp2x00_platform_data,260.nr_banks = 1261};262263static struct resource ixdp2x00_flash_resource = {264.start = 0xc4000000,265.end = 0xc4000000 + 0x00ffffff,266.flags = IORESOURCE_MEM,267};268269static struct platform_device ixdp2x00_flash = {270.name = "IXP2000-Flash",271.id = 0,272.dev = {273.platform_data = &ixdp2x00_flash_data,274},275.num_resources = 1,276.resource = &ixdp2x00_flash_resource,277};278279static struct ixp2000_i2c_pins ixdp2x00_i2c_gpio_pins = {280.sda_pin = IXDP2X00_GPIO_SDA,281.scl_pin = IXDP2X00_GPIO_SCL,282};283284static struct platform_device ixdp2x00_i2c_controller = {285.name = "IXP2000-I2C",286.id = 0,287.dev = {288.platform_data = &ixdp2x00_i2c_gpio_pins,289},290.num_resources = 0291};292293static struct platform_device *ixdp2x00_devices[] __initdata = {294&ixdp2x00_flash,295&ixdp2x00_i2c_controller296};297298void __init ixdp2x00_init_machine(void)299{300gpio_line_set(IXDP2X00_GPIO_I2C_ENABLE, 1);301gpio_line_config(IXDP2X00_GPIO_I2C_ENABLE, GPIO_OUT);302303platform_add_devices(ixdp2x00_devices, ARRAY_SIZE(ixdp2x00_devices));304ixp2000_uart_init();305}306307308309