|  | /* | 
|  | *  linux/drivers/ide/ppc/ide-m8xx.c | 
|  | * | 
|  | *  Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de | 
|  | *  Modified for direct IDE interface | 
|  | *	by Thomas Lange, thomas@corelatus.com | 
|  | *  Modified for direct IDE interface on 8xx without using the PCMCIA | 
|  | *  controller | 
|  | *	by Steven.Scholz@imc-berlin.de | 
|  | *  Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups | 
|  | *	by Mathew Locke <mattl@mvista.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/config.h> | 
|  | #include <linux/errno.h> | 
|  | #include <linux/sched.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/mm.h> | 
|  | #include <linux/stddef.h> | 
|  | #include <linux/unistd.h> | 
|  | #include <linux/ptrace.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/user.h> | 
|  | #include <linux/a.out.h> | 
|  | #include <linux/tty.h> | 
|  | #include <linux/major.h> | 
|  | #include <linux/interrupt.h> | 
|  | #include <linux/reboot.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/ioport.h> | 
|  | #include <linux/ide.h> | 
|  | #include <linux/bootmem.h> | 
|  |  | 
|  | #include <asm/mpc8xx.h> | 
|  | #include <asm/mmu.h> | 
|  | #include <asm/processor.h> | 
|  | #include <asm/residual.h> | 
|  | #include <asm/io.h> | 
|  | #include <asm/pgtable.h> | 
|  | #include <asm/ide.h> | 
|  | #include <asm/8xx_immap.h> | 
|  | #include <asm/machdep.h> | 
|  | #include <asm/irq.h> | 
|  |  | 
|  | static int identify  (volatile u8 *p); | 
|  | static void print_fixed (volatile u8 *p); | 
|  | static void print_funcid (int func); | 
|  | static int check_ide_device (unsigned long base); | 
|  |  | 
|  | static void ide_interrupt_ack (void *dev); | 
|  | static void m8xx_ide_tuneproc(ide_drive_t *drive, u8 pio); | 
|  |  | 
|  | typedef	struct ide_ioport_desc { | 
|  | unsigned long	base_off;		/* Offset to PCMCIA memory	*/ | 
|  | unsigned long	reg_off[IDE_NR_PORTS];	/* controller register offsets	*/ | 
|  | int		irq;			/* IRQ				*/ | 
|  | } ide_ioport_desc_t; | 
|  |  | 
|  | ide_ioport_desc_t ioport_dsc[MAX_HWIFS] = { | 
|  | #ifdef IDE0_BASE_OFFSET | 
|  | { IDE0_BASE_OFFSET, | 
|  | { | 
|  | IDE0_DATA_REG_OFFSET, | 
|  | IDE0_ERROR_REG_OFFSET, | 
|  | IDE0_NSECTOR_REG_OFFSET, | 
|  | IDE0_SECTOR_REG_OFFSET, | 
|  | IDE0_LCYL_REG_OFFSET, | 
|  | IDE0_HCYL_REG_OFFSET, | 
|  | IDE0_SELECT_REG_OFFSET, | 
|  | IDE0_STATUS_REG_OFFSET, | 
|  | IDE0_CONTROL_REG_OFFSET, | 
|  | IDE0_IRQ_REG_OFFSET, | 
|  | }, | 
|  | IDE0_INTERRUPT, | 
|  | }, | 
|  | #ifdef IDE1_BASE_OFFSET | 
|  | { IDE1_BASE_OFFSET, | 
|  | { | 
|  | IDE1_DATA_REG_OFFSET, | 
|  | IDE1_ERROR_REG_OFFSET, | 
|  | IDE1_NSECTOR_REG_OFFSET, | 
|  | IDE1_SECTOR_REG_OFFSET, | 
|  | IDE1_LCYL_REG_OFFSET, | 
|  | IDE1_HCYL_REG_OFFSET, | 
|  | IDE1_SELECT_REG_OFFSET, | 
|  | IDE1_STATUS_REG_OFFSET, | 
|  | IDE1_CONTROL_REG_OFFSET, | 
|  | IDE1_IRQ_REG_OFFSET, | 
|  | }, | 
|  | IDE1_INTERRUPT, | 
|  | }, | 
|  | #endif /* IDE1_BASE_OFFSET */ | 
|  | #endif	/* IDE0_BASE_OFFSET */ | 
|  | }; | 
|  |  | 
|  | ide_pio_timings_t ide_pio_clocks[6]; | 
|  | int hold_time[6] =  {30, 20, 15, 10, 10, 10 };   /* PIO Mode 5 with IORDY (nonstandard) */ | 
|  |  | 
|  | /* | 
|  | * Warning: only 1 (ONE) PCMCIA slot supported here, | 
|  | * which must be correctly initialized by the firmware (PPCBoot). | 
|  | */ | 
|  | static int _slot_ = -1;			/* will be read from PCMCIA registers   */ | 
|  |  | 
|  | /* Make clock cycles and always round up */ | 
|  | #define PCMCIA_MK_CLKS( t, T ) (( (t) * ((T)/1000000) + 999U ) / 1000U ) | 
|  |  | 
|  |  | 
|  |  | 
|  | /* | 
|  | * IDE stuff. | 
|  | */ | 
|  | static int | 
|  | m8xx_ide_default_irq(unsigned long base) | 
|  | { | 
|  | #ifdef CONFIG_BLK_DEV_MPC8xx_IDE | 
|  | if (base >= MAX_HWIFS) | 
|  | return 0; | 
|  |  | 
|  | printk("[%d] m8xx_ide_default_irq %d\n",__LINE__,ioport_dsc[base].irq); | 
|  |  | 
|  | return (ioport_dsc[base].irq); | 
|  | #else | 
|  | return 9; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static unsigned long | 
|  | m8xx_ide_default_io_base(int index) | 
|  | { | 
|  | return index; | 
|  | } | 
|  |  | 
|  | #define M8XX_PCMCIA_CD2(slot)      (0x10000000 >> (slot << 4)) | 
|  | #define M8XX_PCMCIA_CD1(slot)      (0x08000000 >> (slot << 4)) | 
|  |  | 
|  | /* | 
|  | * The TQM850L hardware has two pins swapped! Grrrrgh! | 
|  | */ | 
|  | #ifdef	CONFIG_TQM850L | 
|  | #define __MY_PCMCIA_GCRX_CXRESET	PCMCIA_GCRX_CXOE | 
|  | #define __MY_PCMCIA_GCRX_CXOE		PCMCIA_GCRX_CXRESET | 
|  | #else | 
|  | #define __MY_PCMCIA_GCRX_CXRESET	PCMCIA_GCRX_CXRESET | 
|  | #define __MY_PCMCIA_GCRX_CXOE		PCMCIA_GCRX_CXOE | 
|  | #endif | 
|  |  | 
|  | #if defined(CONFIG_BLK_DEV_MPC8xx_IDE) && defined(CONFIG_IDE_8xx_PCCARD) | 
|  | #define PCMCIA_SCHLVL IDE0_INTERRUPT	/* Status Change Interrupt Level	*/ | 
|  | static int pcmcia_schlvl = PCMCIA_SCHLVL; | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | * See include/linux/ide.h for definition of hw_regs_t (p, base) | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * m8xx_ide_init_hwif_ports for a direct IDE interface _using_ | 
|  | */ | 
|  | #if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) | 
|  | static void | 
|  | m8xx_ide_init_hwif_ports(hw_regs_t *hw, unsigned long data_port, | 
|  | unsigned long ctrl_port, int *irq) | 
|  | { | 
|  | unsigned long *p = hw->io_ports; | 
|  | int i; | 
|  |  | 
|  | typedef struct { | 
|  | ulong br; | 
|  | ulong or; | 
|  | } pcmcia_win_t; | 
|  | volatile pcmcia_win_t *win; | 
|  | volatile pcmconf8xx_t *pcmp; | 
|  |  | 
|  | uint *pgcrx; | 
|  | u32 pcmcia_phy_base; | 
|  | u32 pcmcia_phy_end; | 
|  | static unsigned long pcmcia_base = 0; | 
|  | unsigned long base; | 
|  |  | 
|  | *p = 0; | 
|  | if (irq) | 
|  | *irq = 0; | 
|  |  | 
|  | pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia)); | 
|  |  | 
|  | if (!pcmcia_base) { | 
|  | /* | 
|  | * Read out PCMCIA registers. Since the reset values | 
|  | * are undefined, we sure hope that they have been | 
|  | * set up by firmware | 
|  | */ | 
|  |  | 
|  | /* Scan all registers for valid settings */ | 
|  | pcmcia_phy_base = 0xFFFFFFFF; | 
|  | pcmcia_phy_end = 0; | 
|  | /* br0 is start of brX and orX regs */ | 
|  | win = (pcmcia_win_t *) \ | 
|  | (&(((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0)); | 
|  | for (i = 0; i < 8; i++) { | 
|  | if (win->or & 1) {	/* This bank is marked as valid */ | 
|  | if (win->br < pcmcia_phy_base) { | 
|  | pcmcia_phy_base = win->br; | 
|  | } | 
|  | if ((win->br + PCMCIA_MEM_SIZE) > pcmcia_phy_end) { | 
|  | pcmcia_phy_end  = win->br + PCMCIA_MEM_SIZE; | 
|  | } | 
|  | /* Check which slot that has been defined */ | 
|  | _slot_ = (win->or >> 2) & 1; | 
|  |  | 
|  | }					/* Valid bank */ | 
|  | win++; | 
|  | }						/* for */ | 
|  |  | 
|  | printk ("PCMCIA slot %c: phys mem %08x...%08x (size %08x)\n", | 
|  | 'A' + _slot_, | 
|  | pcmcia_phy_base, pcmcia_phy_end, | 
|  | pcmcia_phy_end - pcmcia_phy_base); | 
|  |  | 
|  | pcmcia_base=(unsigned long)ioremap(pcmcia_phy_base, | 
|  | pcmcia_phy_end-pcmcia_phy_base); | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk ("PCMCIA virt base: %08lx\n", pcmcia_base); | 
|  | #endif | 
|  | /* Compute clock cycles for PIO timings */ | 
|  | for (i=0; i<6; ++i) { | 
|  | bd_t	*binfo = (bd_t *)__res; | 
|  |  | 
|  | hold_time[i]   = | 
|  | PCMCIA_MK_CLKS (hold_time[i], | 
|  | binfo->bi_busfreq); | 
|  | ide_pio_clocks[i].setup_time  = | 
|  | PCMCIA_MK_CLKS (ide_pio_timings[i].setup_time, | 
|  | binfo->bi_busfreq); | 
|  | ide_pio_clocks[i].active_time = | 
|  | PCMCIA_MK_CLKS (ide_pio_timings[i].active_time, | 
|  | binfo->bi_busfreq); | 
|  | ide_pio_clocks[i].cycle_time  = | 
|  | PCMCIA_MK_CLKS (ide_pio_timings[i].cycle_time, | 
|  | binfo->bi_busfreq); | 
|  | #if 0 | 
|  | printk ("PIO mode %d timings: %d/%d/%d => %d/%d/%d\n", | 
|  | i, | 
|  | ide_pio_clocks[i].setup_time, | 
|  | ide_pio_clocks[i].active_time, | 
|  | ide_pio_clocks[i].hold_time, | 
|  | ide_pio_clocks[i].cycle_time, | 
|  | ide_pio_timings[i].setup_time, | 
|  | ide_pio_timings[i].active_time, | 
|  | ide_pio_timings[i].hold_time, | 
|  | ide_pio_timings[i].cycle_time); | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | if (data_port >= MAX_HWIFS) | 
|  | return; | 
|  |  | 
|  | if (_slot_ == -1) { | 
|  | printk ("PCMCIA slot has not been defined! Using A as default\n"); | 
|  | _slot_ = 0; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_IDE_8xx_PCCARD | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk ("PIPR = 0x%08X  slot %c ==> mask = 0x%X\n", | 
|  | pcmp->pcmc_pipr, | 
|  | 'A' + _slot_, | 
|  | M8XX_PCMCIA_CD1(_slot_) | M8XX_PCMCIA_CD2(_slot_) ); | 
|  | #endif /* DEBUG */ | 
|  |  | 
|  | if (pcmp->pcmc_pipr & (M8XX_PCMCIA_CD1(_slot_)|M8XX_PCMCIA_CD2(_slot_))) { | 
|  | printk ("No card in slot %c: PIPR=%08x\n", | 
|  | 'A' + _slot_, (u32) pcmp->pcmc_pipr); | 
|  | return;		/* No card in slot */ | 
|  | } | 
|  |  | 
|  | check_ide_device (pcmcia_base); | 
|  |  | 
|  | #endif	/* CONFIG_IDE_8xx_PCCARD */ | 
|  |  | 
|  | base = pcmcia_base + ioport_dsc[data_port].base_off; | 
|  | #ifdef DEBUG | 
|  | printk ("base: %08x + %08x = %08x\n", | 
|  | pcmcia_base, ioport_dsc[data_port].base_off, base); | 
|  | #endif | 
|  |  | 
|  | for (i = 0; i < IDE_NR_PORTS; ++i) { | 
|  | #ifdef DEBUG | 
|  | printk ("port[%d]: %08x + %08x = %08x\n", | 
|  | i, | 
|  | base, | 
|  | ioport_dsc[data_port].reg_off[i], | 
|  | i, base + ioport_dsc[data_port].reg_off[i]); | 
|  | #endif | 
|  | *p++ = base + ioport_dsc[data_port].reg_off[i]; | 
|  | } | 
|  |  | 
|  | if (irq) { | 
|  | #ifdef CONFIG_IDE_8xx_PCCARD | 
|  | unsigned int reg; | 
|  |  | 
|  | *irq = ioport_dsc[data_port].irq; | 
|  | if (_slot_) | 
|  | pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcrb; | 
|  | else | 
|  | pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcra; | 
|  |  | 
|  | reg = *pgcrx; | 
|  | reg |= mk_int_int_mask (pcmcia_schlvl) << 24; | 
|  | reg |= mk_int_int_mask (pcmcia_schlvl) << 16; | 
|  | *pgcrx = reg; | 
|  | #else	/* direct connected IDE drive, i.e. external IRQ, not the PCMCIA irq */ | 
|  | *irq = ioport_dsc[data_port].irq; | 
|  | #endif	/* CONFIG_IDE_8xx_PCCARD */ | 
|  | } | 
|  |  | 
|  | /* register routine to tune PIO mode */ | 
|  | ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc; | 
|  |  | 
|  | hw->ack_intr = (ide_ack_intr_t *) ide_interrupt_ack; | 
|  | /* Enable Harddisk Interrupt, | 
|  | * and make it edge sensitive | 
|  | */ | 
|  | /* (11-18) Set edge detect for irq, no wakeup from low power mode */ | 
|  | ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |= | 
|  | (0x80000000 >> ioport_dsc[data_port].irq); | 
|  |  | 
|  | #ifdef CONFIG_IDE_8xx_PCCARD | 
|  | /* Make sure we don't get garbage irq */ | 
|  | ((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pscr = 0xFFFF; | 
|  |  | 
|  | /* Enable falling edge irq */ | 
|  | pcmp->pcmc_per = 0x100000 >> (16 * _slot_); | 
|  | #endif	/* CONFIG_IDE_8xx_PCCARD */ | 
|  | }	/* m8xx_ide_init_hwif_ports() using 8xx internal PCMCIA interface */ | 
|  | #endif /* CONFIG_IDE_8xx_PCCARD || CONFIG_IDE_8xx_DIRECT */ | 
|  |  | 
|  | /* | 
|  | * m8xx_ide_init_hwif_ports for a direct IDE interface _not_ using | 
|  | * MPC8xx's internal PCMCIA interface | 
|  | */ | 
|  | #if defined(CONFIG_IDE_EXT_DIRECT) | 
|  | void m8xx_ide_init_hwif_ports (hw_regs_t *hw, | 
|  | unsigned long data_port, unsigned long ctrl_port, int *irq) | 
|  | { | 
|  | unsigned long *p = hw->io_ports; | 
|  | int i; | 
|  |  | 
|  | u32 ide_phy_base; | 
|  | u32 ide_phy_end; | 
|  | static unsigned long ide_base = 0; | 
|  | unsigned long base; | 
|  |  | 
|  | *p = 0; | 
|  | if (irq) | 
|  | *irq = 0; | 
|  |  | 
|  | if (!ide_base) { | 
|  |  | 
|  | /* TODO: | 
|  | * - add code to read ORx, BRx | 
|  | */ | 
|  | ide_phy_base = CFG_ATA_BASE_ADDR; | 
|  | ide_phy_end  = CFG_ATA_BASE_ADDR + 0x200; | 
|  |  | 
|  | printk ("IDE phys mem : %08x...%08x (size %08x)\n", | 
|  | ide_phy_base, ide_phy_end, | 
|  | ide_phy_end - ide_phy_base); | 
|  |  | 
|  | ide_base=(unsigned long)ioremap(ide_phy_base, | 
|  | ide_phy_end-ide_phy_base); | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk ("IDE virt base: %08lx\n", ide_base); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (data_port >= MAX_HWIFS) | 
|  | return; | 
|  |  | 
|  | base = ide_base + ioport_dsc[data_port].base_off; | 
|  | #ifdef DEBUG | 
|  | printk ("base: %08x + %08x = %08x\n", | 
|  | ide_base, ioport_dsc[data_port].base_off, base); | 
|  | #endif | 
|  |  | 
|  | for (i = 0; i < IDE_NR_PORTS; ++i) { | 
|  | #ifdef DEBUG | 
|  | printk ("port[%d]: %08x + %08x = %08x\n", | 
|  | i, | 
|  | base, | 
|  | ioport_dsc[data_port].reg_off[i], | 
|  | i, base + ioport_dsc[data_port].reg_off[i]); | 
|  | #endif | 
|  | *p++ = base + ioport_dsc[data_port].reg_off[i]; | 
|  | } | 
|  |  | 
|  | if (irq) { | 
|  | /* direct connected IDE drive, i.e. external IRQ */ | 
|  | *irq = ioport_dsc[data_port].irq; | 
|  | } | 
|  |  | 
|  | /* register routine to tune PIO mode */ | 
|  | ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc; | 
|  |  | 
|  | hw->ack_intr = (ide_ack_intr_t *) ide_interrupt_ack; | 
|  | /* Enable Harddisk Interrupt, | 
|  | * and make it edge sensitive | 
|  | */ | 
|  | /* (11-18) Set edge detect for irq, no wakeup from low power mode */ | 
|  | ((immap_t *) IMAP_ADDR)->im_siu_conf.sc_siel |= | 
|  | (0x80000000 >> ioport_dsc[data_port].irq); | 
|  | }	/* m8xx_ide_init_hwif_ports() for CONFIG_IDE_8xx_DIRECT */ | 
|  |  | 
|  | #endif	/* CONFIG_IDE_8xx_DIRECT */ | 
|  |  | 
|  |  | 
|  | /* -------------------------------------------------------------------- */ | 
|  |  | 
|  |  | 
|  | /* PCMCIA Timing */ | 
|  | #ifndef	PCMCIA_SHT | 
|  | #define PCMCIA_SHT(t)	((t & 0x0F)<<16)	/* Strobe Hold  Time 	*/ | 
|  | #define PCMCIA_SST(t)	((t & 0x0F)<<12)	/* Strobe Setup Time	*/ | 
|  | #define PCMCIA_SL(t) ((t==32) ? 0 : ((t & 0x1F)<<7)) /* Strobe Length	*/ | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* Calculate PIO timings */ | 
|  | static void | 
|  | m8xx_ide_tuneproc(ide_drive_t *drive, u8 pio) | 
|  | { | 
|  | ide_pio_data_t d; | 
|  | #if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) | 
|  | volatile pcmconf8xx_t	*pcmp; | 
|  | ulong timing, mask, reg; | 
|  | #endif | 
|  |  | 
|  | pio = ide_get_best_pio_mode(drive, pio, 4, &d); | 
|  |  | 
|  | #if 1 | 
|  | printk("%s[%d] %s: best PIO mode: %d\n", | 
|  | __FILE__,__LINE__,__FUNCTION__, pio); | 
|  | #endif | 
|  |  | 
|  | #if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) | 
|  | pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia)); | 
|  |  | 
|  | mask = ~(PCMCIA_SHT(0xFF) | PCMCIA_SST(0xFF) | PCMCIA_SL(0xFF)); | 
|  |  | 
|  | timing  = PCMCIA_SHT(hold_time[pio]  ) | 
|  | | PCMCIA_SST(ide_pio_clocks[pio].setup_time ) | 
|  | | PCMCIA_SL (ide_pio_clocks[pio].active_time) | 
|  | ; | 
|  |  | 
|  | #if 1 | 
|  | printk ("Setting timing bits 0x%08lx in PCMCIA controller\n", timing); | 
|  | #endif | 
|  | if ((reg = pcmp->pcmc_por0 & mask) != 0) | 
|  | pcmp->pcmc_por0 = reg | timing; | 
|  |  | 
|  | if ((reg = pcmp->pcmc_por1 & mask) != 0) | 
|  | pcmp->pcmc_por1 = reg | timing; | 
|  |  | 
|  | if ((reg = pcmp->pcmc_por2 & mask) != 0) | 
|  | pcmp->pcmc_por2 = reg | timing; | 
|  |  | 
|  | if ((reg = pcmp->pcmc_por3 & mask) != 0) | 
|  | pcmp->pcmc_por3 = reg | timing; | 
|  |  | 
|  | if ((reg = pcmp->pcmc_por4 & mask) != 0) | 
|  | pcmp->pcmc_por4 = reg | timing; | 
|  |  | 
|  | if ((reg = pcmp->pcmc_por5 & mask) != 0) | 
|  | pcmp->pcmc_por5 = reg | timing; | 
|  |  | 
|  | if ((reg = pcmp->pcmc_por6 & mask) != 0) | 
|  | pcmp->pcmc_por6 = reg | timing; | 
|  |  | 
|  | if ((reg = pcmp->pcmc_por7 & mask) != 0) | 
|  | pcmp->pcmc_por7 = reg | timing; | 
|  |  | 
|  | #elif defined(CONFIG_IDE_EXT_DIRECT) | 
|  |  | 
|  | printk("%s[%d] %s: not implemented yet!\n", | 
|  | __FILE__,__LINE__,__FUNCTION__); | 
|  | #endif /* defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_PCMCIA */ | 
|  | } | 
|  |  | 
|  | static void | 
|  | ide_interrupt_ack (void *dev) | 
|  | { | 
|  | #ifdef CONFIG_IDE_8xx_PCCARD | 
|  | u_int pscr, pipr; | 
|  |  | 
|  | #if (PCMCIA_SOCKETS_NO == 2) | 
|  | u_int _slot_; | 
|  | #endif | 
|  |  | 
|  | /* get interrupt sources */ | 
|  |  | 
|  | pscr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr; | 
|  | pipr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr; | 
|  |  | 
|  | /* | 
|  | * report only if both card detect signals are the same | 
|  | * not too nice done, | 
|  | * we depend on that CD2 is the bit to the left of CD1... | 
|  | */ | 
|  |  | 
|  | if(_slot_==-1){ | 
|  | printk("PCMCIA slot has not been defined! Using A as default\n"); | 
|  | _slot_=0; | 
|  | } | 
|  |  | 
|  | if(((pipr & M8XX_PCMCIA_CD2(_slot_)) >> 1) ^ | 
|  | (pipr & M8XX_PCMCIA_CD1(_slot_))         ) { | 
|  | printk ("card detect interrupt\n"); | 
|  | } | 
|  | /* clear the interrupt sources */ | 
|  | ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr = pscr; | 
|  |  | 
|  | #else /* ! CONFIG_IDE_8xx_PCCARD */ | 
|  | /* | 
|  | * Only CONFIG_IDE_8xx_PCCARD is using the interrupt of the | 
|  | * MPC8xx's PCMCIA controller, so there is nothing to be done here | 
|  | * for CONFIG_IDE_8xx_DIRECT and CONFIG_IDE_EXT_DIRECT. | 
|  | * The interrupt is handled somewhere else.	-- Steven | 
|  | */ | 
|  | #endif /* CONFIG_IDE_8xx_PCCARD */ | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /* | 
|  | * CIS Tupel codes | 
|  | */ | 
|  | #define CISTPL_NULL		0x00 | 
|  | #define CISTPL_DEVICE		0x01 | 
|  | #define CISTPL_LONGLINK_CB	0x02 | 
|  | #define CISTPL_INDIRECT		0x03 | 
|  | #define CISTPL_CONFIG_CB	0x04 | 
|  | #define CISTPL_CFTABLE_ENTRY_CB 0x05 | 
|  | #define CISTPL_LONGLINK_MFC	0x06 | 
|  | #define CISTPL_BAR		0x07 | 
|  | #define CISTPL_PWR_MGMNT	0x08 | 
|  | #define CISTPL_EXTDEVICE	0x09 | 
|  | #define CISTPL_CHECKSUM		0x10 | 
|  | #define CISTPL_LONGLINK_A	0x11 | 
|  | #define CISTPL_LONGLINK_C	0x12 | 
|  | #define CISTPL_LINKTARGET	0x13 | 
|  | #define CISTPL_NO_LINK		0x14 | 
|  | #define CISTPL_VERS_1		0x15 | 
|  | #define CISTPL_ALTSTR		0x16 | 
|  | #define CISTPL_DEVICE_A		0x17 | 
|  | #define CISTPL_JEDEC_C		0x18 | 
|  | #define CISTPL_JEDEC_A		0x19 | 
|  | #define CISTPL_CONFIG		0x1a | 
|  | #define CISTPL_CFTABLE_ENTRY	0x1b | 
|  | #define CISTPL_DEVICE_OC	0x1c | 
|  | #define CISTPL_DEVICE_OA	0x1d | 
|  | #define CISTPL_DEVICE_GEO	0x1e | 
|  | #define CISTPL_DEVICE_GEO_A	0x1f | 
|  | #define CISTPL_MANFID		0x20 | 
|  | #define CISTPL_FUNCID		0x21 | 
|  | #define CISTPL_FUNCE		0x22 | 
|  | #define CISTPL_SWIL		0x23 | 
|  | #define CISTPL_END		0xff | 
|  |  | 
|  | /* | 
|  | * CIS Function ID codes | 
|  | */ | 
|  | #define CISTPL_FUNCID_MULTI	0x00 | 
|  | #define CISTPL_FUNCID_MEMORY	0x01 | 
|  | #define CISTPL_FUNCID_SERIAL	0x02 | 
|  | #define CISTPL_FUNCID_PARALLEL	0x03 | 
|  | #define CISTPL_FUNCID_FIXED	0x04 | 
|  | #define CISTPL_FUNCID_VIDEO	0x05 | 
|  | #define CISTPL_FUNCID_NETWORK	0x06 | 
|  | #define CISTPL_FUNCID_AIMS	0x07 | 
|  | #define CISTPL_FUNCID_SCSI	0x08 | 
|  |  | 
|  | /* | 
|  | * Fixed Disk FUNCE codes | 
|  | */ | 
|  | #define CISTPL_IDE_INTERFACE	0x01 | 
|  |  | 
|  | #define CISTPL_FUNCE_IDE_IFACE	0x01 | 
|  | #define CISTPL_FUNCE_IDE_MASTER	0x02 | 
|  | #define CISTPL_FUNCE_IDE_SLAVE	0x03 | 
|  |  | 
|  | /* First feature byte */ | 
|  | #define CISTPL_IDE_SILICON	0x04 | 
|  | #define CISTPL_IDE_UNIQUE	0x08 | 
|  | #define CISTPL_IDE_DUAL		0x10 | 
|  |  | 
|  | /* Second feature byte */ | 
|  | #define CISTPL_IDE_HAS_SLEEP	0x01 | 
|  | #define CISTPL_IDE_HAS_STANDBY	0x02 | 
|  | #define CISTPL_IDE_HAS_IDLE	0x04 | 
|  | #define CISTPL_IDE_LOW_POWER	0x08 | 
|  | #define CISTPL_IDE_REG_INHIBIT	0x10 | 
|  | #define CISTPL_IDE_HAS_INDEX	0x20 | 
|  | #define CISTPL_IDE_IOIS16	0x40 | 
|  |  | 
|  |  | 
|  | /* -------------------------------------------------------------------- */ | 
|  |  | 
|  |  | 
|  | #define	MAX_TUPEL_SZ	512 | 
|  | #define MAX_FEATURES	4 | 
|  |  | 
|  | static int check_ide_device (unsigned long base) | 
|  | { | 
|  | volatile u8 *ident = NULL; | 
|  | volatile u8 *feature_p[MAX_FEATURES]; | 
|  | volatile u8 *p, *start; | 
|  | int n_features = 0; | 
|  | u8 func_id = ~0; | 
|  | u8 code, len; | 
|  | unsigned short config_base = 0; | 
|  | int found = 0; | 
|  | int i; | 
|  |  | 
|  | #ifdef DEBUG | 
|  | printk ("PCMCIA MEM: %08lX\n", base); | 
|  | #endif | 
|  | start = p = (volatile u8 *) base; | 
|  |  | 
|  | while ((p - start) < MAX_TUPEL_SZ) { | 
|  |  | 
|  | code = *p; p += 2; | 
|  |  | 
|  | if (code == 0xFF) { /* End of chain */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | len = *p; p += 2; | 
|  | #ifdef	DEBUG_PCMCIA | 
|  | { volatile u8 *q = p; | 
|  | printk ("\nTuple code %02x  length %d\n\tData:", | 
|  | code, len); | 
|  |  | 
|  | for (i = 0; i < len; ++i) { | 
|  | printk (" %02x", *q); | 
|  | q+= 2; | 
|  | } | 
|  | } | 
|  | #endif	/* DEBUG_PCMCIA */ | 
|  | switch (code) { | 
|  | case CISTPL_VERS_1: | 
|  | ident = p + 4; | 
|  | break; | 
|  | case CISTPL_FUNCID: | 
|  | func_id = *p; | 
|  | break; | 
|  | case CISTPL_FUNCE: | 
|  | if (n_features < MAX_FEATURES) | 
|  | feature_p[n_features++] = p; | 
|  | break; | 
|  | case CISTPL_CONFIG: | 
|  | config_base = (*(p+6) << 8) + (*(p+4)); | 
|  | default: | 
|  | break; | 
|  | } | 
|  | p += 2 * len; | 
|  | } | 
|  |  | 
|  | found = identify (ident); | 
|  |  | 
|  | if (func_id != ((u8)~0)) { | 
|  | print_funcid (func_id); | 
|  |  | 
|  | if (func_id == CISTPL_FUNCID_FIXED) | 
|  | found = 1; | 
|  | else | 
|  | return (1);	/* no disk drive */ | 
|  | } | 
|  |  | 
|  | for (i=0; i<n_features; ++i) { | 
|  | print_fixed (feature_p[i]); | 
|  | } | 
|  |  | 
|  | if (!found) { | 
|  | printk ("unknown card type\n"); | 
|  | return (1); | 
|  | } | 
|  |  | 
|  | /* set level mode irq and I/O mapped device in config reg*/ | 
|  | *((u8 *)(base + config_base)) = 0x41; | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | /* ------------------------------------------------------------------------- */ | 
|  |  | 
|  | static void print_funcid (int func) | 
|  | { | 
|  | switch (func) { | 
|  | case CISTPL_FUNCID_MULTI: | 
|  | printk (" Multi-Function"); | 
|  | break; | 
|  | case CISTPL_FUNCID_MEMORY: | 
|  | printk (" Memory"); | 
|  | break; | 
|  | case CISTPL_FUNCID_SERIAL: | 
|  | printk (" Serial Port"); | 
|  | break; | 
|  | case CISTPL_FUNCID_PARALLEL: | 
|  | printk (" Parallel Port"); | 
|  | break; | 
|  | case CISTPL_FUNCID_FIXED: | 
|  | printk (" Fixed Disk"); | 
|  | break; | 
|  | case CISTPL_FUNCID_VIDEO: | 
|  | printk (" Video Adapter"); | 
|  | break; | 
|  | case CISTPL_FUNCID_NETWORK: | 
|  | printk (" Network Adapter"); | 
|  | break; | 
|  | case CISTPL_FUNCID_AIMS: | 
|  | printk (" AIMS Card"); | 
|  | break; | 
|  | case CISTPL_FUNCID_SCSI: | 
|  | printk (" SCSI Adapter"); | 
|  | break; | 
|  | default: | 
|  | printk (" Unknown"); | 
|  | break; | 
|  | } | 
|  | printk (" Card\n"); | 
|  | } | 
|  |  | 
|  | /* ------------------------------------------------------------------------- */ | 
|  |  | 
|  | static void print_fixed (volatile u8 *p) | 
|  | { | 
|  | if (p == NULL) | 
|  | return; | 
|  |  | 
|  | switch (*p) { | 
|  | case CISTPL_FUNCE_IDE_IFACE: | 
|  | {   u8 iface = *(p+2); | 
|  |  | 
|  | printk ((iface == CISTPL_IDE_INTERFACE) ? " IDE" : " unknown"); | 
|  | printk (" interface "); | 
|  | break; | 
|  | } | 
|  | case CISTPL_FUNCE_IDE_MASTER: | 
|  | case CISTPL_FUNCE_IDE_SLAVE: | 
|  | {   u8 f1 = *(p+2); | 
|  | u8 f2 = *(p+4); | 
|  |  | 
|  | printk ((f1 & CISTPL_IDE_SILICON) ? " [silicon]" : " [rotating]"); | 
|  |  | 
|  | if (f1 & CISTPL_IDE_UNIQUE) | 
|  | printk (" [unique]"); | 
|  |  | 
|  | printk ((f1 & CISTPL_IDE_DUAL) ? " [dual]" : " [single]"); | 
|  |  | 
|  | if (f2 & CISTPL_IDE_HAS_SLEEP) | 
|  | printk (" [sleep]"); | 
|  |  | 
|  | if (f2 & CISTPL_IDE_HAS_STANDBY) | 
|  | printk (" [standby]"); | 
|  |  | 
|  | if (f2 & CISTPL_IDE_HAS_IDLE) | 
|  | printk (" [idle]"); | 
|  |  | 
|  | if (f2 & CISTPL_IDE_LOW_POWER) | 
|  | printk (" [low power]"); | 
|  |  | 
|  | if (f2 & CISTPL_IDE_REG_INHIBIT) | 
|  | printk (" [reg inhibit]"); | 
|  |  | 
|  | if (f2 & CISTPL_IDE_HAS_INDEX) | 
|  | printk (" [index]"); | 
|  |  | 
|  | if (f2 & CISTPL_IDE_IOIS16) | 
|  | printk (" [IOis16]"); | 
|  |  | 
|  | break; | 
|  | } | 
|  | } | 
|  | printk ("\n"); | 
|  | } | 
|  |  | 
|  | /* ------------------------------------------------------------------------- */ | 
|  |  | 
|  |  | 
|  | #define MAX_IDENT_CHARS		64 | 
|  | #define	MAX_IDENT_FIELDS	4 | 
|  |  | 
|  | static u8 *known_cards[] = { | 
|  | "ARGOSY PnPIDE D5", | 
|  | NULL | 
|  | }; | 
|  |  | 
|  | static int identify  (volatile u8 *p) | 
|  | { | 
|  | u8 id_str[MAX_IDENT_CHARS]; | 
|  | u8 data; | 
|  | u8 *t; | 
|  | u8 **card; | 
|  | int i, done; | 
|  |  | 
|  | if (p == NULL) | 
|  | return (0);	/* Don't know */ | 
|  |  | 
|  | t = id_str; | 
|  | done =0; | 
|  |  | 
|  | for (i=0; i<=4 && !done; ++i, p+=2) { | 
|  | while ((data = *p) != '\0') { | 
|  | if (data == 0xFF) { | 
|  | done = 1; | 
|  | break; | 
|  | } | 
|  | *t++ = data; | 
|  | if (t == &id_str[MAX_IDENT_CHARS-1]) { | 
|  | done = 1; | 
|  | break; | 
|  | } | 
|  | p += 2; | 
|  | } | 
|  | if (!done) | 
|  | *t++ = ' '; | 
|  | } | 
|  | *t = '\0'; | 
|  | while (--t > id_str) { | 
|  | if (*t == ' ') | 
|  | *t = '\0'; | 
|  | else | 
|  | break; | 
|  | } | 
|  | printk ("Card ID: %s\n", id_str); | 
|  |  | 
|  | for (card=known_cards; *card; ++card) { | 
|  | if (strcmp(*card, id_str) == 0) {	/* found! */ | 
|  | return (1); | 
|  | } | 
|  | } | 
|  |  | 
|  | return (0);	/* don't know */ | 
|  | } | 
|  |  | 
|  | void m8xx_ide_init(void) | 
|  | { | 
|  | ppc_ide_md.default_irq          = m8xx_ide_default_irq; | 
|  | ppc_ide_md.default_io_base      = m8xx_ide_default_io_base; | 
|  | ppc_ide_md.ide_init_hwif        = m8xx_ide_init_hwif_ports; | 
|  | } |