Skip to content
Snippets Groups Projects
cmd_pcmcia.c 59.9 KiB
Newer Older
Wolfgang Denk's avatar
Wolfgang Denk committed
/*
 * (C) Copyright 2000-2002
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * 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; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *
 ********************************************************************
 *
 * Lots of code copied from:
 *
 * m8xx_pcmcia.c - Linux PCMCIA socket driver for the mpc8xx series.
 * (C) 1999-2000 Magnus Damm <damm@bitsmart.com>
 *
 * "The ExCA standard specifies that socket controllers should provide
 * two IO and five memory windows per socket, which can be independently
 * configured and positioned in the host address space and mapped to
 * arbitrary segments of card address space. " - David A Hinds. 1999
 *
 * This controller does _not_ meet the ExCA standard.
 *
 * m8xx pcmcia controller brief info:
 * + 8 windows (attrib, mem, i/o)
 * + up to two slots (SLOT_A and SLOT_B)
 * + inputpins, outputpins, event and mask registers.
 * - no offset register. sigh.
 *
 * Because of the lacking offset register we must map the whole card.
 * We assign each memory window PCMCIA_MEM_WIN_SIZE address space.
 * Make sure there is (PCMCIA_MEM_WIN_SIZE * PCMCIA_MEM_WIN_NO
 * * PCMCIA_SOCKETS_NO) bytes at PCMCIA_MEM_WIN_BASE.
 * The i/o windows are dynamically allocated at PCMCIA_IO_WIN_BASE.
 * They are maximum 64KByte each...
 */

/* #define DEBUG	1	*/

/*
 * PCMCIA support
 */
#include <common.h>
#include <command.h>
#include <config.h>
#include <pcmcia.h>
#include <cmd_pcmcia.h>
#if defined(CONFIG_IDE_8xx_PCCARD) && defined(CONFIG_8xx)
#include <mpc8xx.h>
#endif
#if defined(CONFIG_LWMON)
#include <i2c.h>
#endif

#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA) || \
    ((CONFIG_COMMANDS & CFG_CMD_IDE) && defined(CONFIG_IDE_8xx_PCCARD))

int pcmcia_on (void);

#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA)
static int  pcmcia_off (void);
#endif

#ifdef CONFIG_I82365

extern int i82365_init (void);
extern void i82365_exit (void);

#else /* ! CONFIG_I82365 */

#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA)
Wolfgang Denk's avatar
Wolfgang Denk committed
static int  hardware_disable(int slot);
#endif
static int  hardware_enable (int slot);
static int  voltage_set(int slot, int vcc, int vpp);

#ifndef	CONFIG_I82365
Wolfgang Denk's avatar
Wolfgang Denk committed
static u_int m8xx_get_graycode(u_int size);
#endif	/* CONFIG_I82365 */
Wolfgang Denk's avatar
Wolfgang Denk committed
#if 0
static u_int m8xx_get_speed(u_int ns, u_int is_io);
#endif

/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

/* look up table for pgcrx registers */

static u_int *pcmcia_pgcrx[2] = {
	&((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pgcra,
	&((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pgcrb,
};

#define PCMCIA_PGCRX(slot)	(*pcmcia_pgcrx[slot])

#endif /* CONFIG_I82365 */

#ifdef CONFIG_IDE_8xx_PCCARD
static void print_funcid (int func);
static void print_fixed  (volatile uchar *p);
static int  identify     (volatile uchar *p);
static int  check_ide_device (int slot);
#endif	/* CONFIG_IDE_8xx_PCCARD */

Wolfgang Denk's avatar
Wolfgang Denk committed
const char *indent = "\t   ";

/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA)

int do_pinit (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	int rcode = 0;

	if (argc != 2) {
		printf ("Usage: pinit {on | off}\n");
		return 1;
	}
	if (strcmp(argv[1],"on") == 0) {
	     	rcode = pcmcia_on ();
	} else if (strcmp(argv[1],"off") == 0) {
		rcode = pcmcia_off ();
	} else {
		printf ("Usage: pinit {on | off}\n");
		return 1;
	}

	return rcode;
}
#endif	/* CFG_CMD_PCMCIA */

/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

#ifdef CONFIG_I82365
int pcmcia_on (void)
{
	u_int rc;

	debug ("Enable PCMCIA " PCMCIA_SLOT_MSG "\n");

	rc = i82365_init();

	if (rc == 0)
	{
		rc = check_ide_device(0);
	}

	return (rc);
}
#else

Wolfgang Denk's avatar
Wolfgang Denk committed
#if defined(CONFIG_LWMON)
# define  CFG_PCMCIA_TIMING	(PCMCIA_SHT(9) | PCMCIA_SST(3) | PCMCIA_SL(12))
#else
# define  CFG_PCMCIA_TIMING	(PCMCIA_SHT(2) | PCMCIA_SST(4) | PCMCIA_SL(9))
#endif

int pcmcia_on (void)
{
	int i;
	u_long reg, base;
	pcmcia_win_t *win;
Wolfgang Denk's avatar
Wolfgang Denk committed

	debug ("Enable PCMCIA " PCMCIA_SLOT_MSG "\n");

	/* intialize the fixed memory windows */
	win = (pcmcia_win_t *)(&((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pbr0);
	base = CFG_PCMCIA_MEM_ADDR;

	if((reg = m8xx_get_graycode(CFG_PCMCIA_MEM_SIZE)) == -1) {
		printf ("Cannot set window size to 0x%08x\n",
			CFG_PCMCIA_MEM_SIZE);
		return (1);
	}

Wolfgang Denk's avatar
Wolfgang Denk committed
	for (i=0; i<PCMCIA_MEM_WIN_NO; ++i) {
		win->br = base;

#if (PCMCIA_SOCKETS_NO == 2)
		if (i == 4) /* Another slot starting from win 4 */
			slotbit = (slotbit ? PCMCIA_PSLOT_A : PCMCIA_PSLOT_B);
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
		switch (i) {
#ifdef CONFIG_IDE_8xx_PCCARD
Wolfgang Denk's avatar
Wolfgang Denk committed
		case 0:	{	/* map attribute memory */
			win->or = (	PCMCIA_BSIZE_64M
				|	PCMCIA_PPS_8
				|	PCMCIA_PRS_ATTR
Wolfgang Denk's avatar
Wolfgang Denk committed
				|	PCMCIA_PV
				|	CFG_PCMCIA_TIMING );
			break;
		    }
Wolfgang Denk's avatar
Wolfgang Denk committed
		case 1: {	/* map I/O window for data reg */
			win->or = (	PCMCIA_BSIZE_1K
				|	PCMCIA_PPS_16
				|	PCMCIA_PRS_IO
Wolfgang Denk's avatar
Wolfgang Denk committed
				|	PCMCIA_PV
				|	CFG_PCMCIA_TIMING );
			break;
		    }
		case 2: {	/* map I/O window for cmd/ctrl reg block */
Wolfgang Denk's avatar
Wolfgang Denk committed
			win->or = (	PCMCIA_BSIZE_1K
				|	PCMCIA_PPS_8
				|	PCMCIA_PRS_IO
Wolfgang Denk's avatar
Wolfgang Denk committed
				|	PCMCIA_PV
				|	CFG_PCMCIA_TIMING );
			break;
		    }
#endif	/* CONFIG_IDE_8xx_PCCARD */
		default:	/* set to not valid */
			win->or = 0;
			break;
		}

		debug ("MemWin %d: PBR 0x%08lX  POR %08lX\n",
			i, win->br, win->or);
		base += CFG_PCMCIA_MEM_SIZE;
		++win;
	}

	for (i=0, rc=0, slot=_slot_; i<PCMCIA_SOCKETS_NO; i++, slot = !slot) {
		/* turn off voltage */
		if ((rc = voltage_set(slot, 0, 0)))
			continue;
		/* Enable external hardware */
		if ((rc = hardware_enable(slot)))
			continue;
Wolfgang Denk's avatar
Wolfgang Denk committed
#ifdef CONFIG_IDE_8xx_PCCARD
		if ((rc = check_ide_device(i)))
			continue;
Wolfgang Denk's avatar
Wolfgang Denk committed
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed
}
#endif /* CONFIG_I82365 */
Wolfgang Denk's avatar
Wolfgang Denk committed

/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA)

#ifdef CONFIG_I82365
static int pcmcia_off (void)
{
	printf ("Disable PCMCIA " PCMCIA_SLOT_MSG "\n");

	i82365_exit();

	return 0;
}
#else
Wolfgang Denk's avatar
Wolfgang Denk committed
static int pcmcia_off (void)
{
	int i;
	pcmcia_win_t *win;

	printf ("Disable PCMCIA " PCMCIA_SLOT_MSG "\n");

	/* clear interrupt state, and disable interrupts */
	((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pscr =  PCMCIA_MASK(_slot_);
	((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_per &= ~PCMCIA_MASK(_slot_);

	/* turn off interrupt and disable CxOE */
	PCMCIA_PGCRX(_slot_) = __MY_PCMCIA_GCRX_CXOE;

	/* turn off memory windows */
	win = (pcmcia_win_t *)(&((immap_t *)CFG_IMMR)->im_pcmcia.pcmc_pbr0);

	for (i=0; i<PCMCIA_MEM_WIN_NO; ++i) {
		/* disable memory window */
		win->or = 0;
		++win;
	}

	/* turn off voltage */
	voltage_set(_slot_, 0, 0);

	/* disable external hardware */
	printf ("Shutdown and Poweroff " PCMCIA_SLOT_MSG "\n");
	hardware_disable(_slot_);
	return 0;
}
#endif /* CONFIG_I82365 */
Wolfgang Denk's avatar
Wolfgang Denk committed

#endif	/* CFG_CMD_PCMCIA */

/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

#ifdef CONFIG_IDE_8xx_PCCARD

#define	MAX_TUPEL_SZ	512
#define MAX_FEATURES	4

static int check_ide_device (int slot)
Wolfgang Denk's avatar
Wolfgang Denk committed
{
	volatile uchar *ident = NULL;
	volatile uchar *feature_p[MAX_FEATURES];
	volatile uchar *p, *start, *addr;
Wolfgang Denk's avatar
Wolfgang Denk committed
	int n_features = 0;
	uchar func_id = ~0;
	uchar code, len;
	ushort config_base = 0;
	int found = 0;
	int i;

	addr = (volatile uchar *)(CFG_PCMCIA_MEM_ADDR +
				  CFG_PCMCIA_MEM_SIZE * (slot * 4));
	debug ("PCMCIA MEM: %08lX\n", (ulong)addr);
Wolfgang Denk's avatar
Wolfgang Denk committed

	start = p = (volatile uchar *) addr;
Wolfgang Denk's avatar
Wolfgang Denk committed

	while ((p - start) < MAX_TUPEL_SZ) {

		code = *p; p += 2;

		if (code == 0xFF) { /* End of chain */
			break;
		}

		len = *p; p += 2;
#if defined(DEBUG) && (DEBUG > 1)
		{ volatile uchar *q = p;
			printf ("\nTuple code %02x  length %d\n\tData:",
				code, len);

			for (i = 0; i < len; ++i) {
				printf (" %02x", *q);
				q+= 2;
			}
		}
#endif	/* DEBUG */
		switch (code) {
		case CISTPL_VERS_1:
			ident = p + 4;
			break;
		case CISTPL_FUNCID:
			/* Fix for broken SanDisk which may have 0x80 bit set */
			func_id = *p & 0x7F;
			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));
			debug ("\n## Config_base = %04x ###\n", config_base);
		default:
			break;
		}
		p += 2 * len;
	}

	found = identify (ident);

	if (func_id != ((uchar)~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) {
		printf ("unknown card type\n");
		return (1);
	}

	ide_devices_found |= (1 << slot);

Wolfgang Denk's avatar
Wolfgang Denk committed
	/* set I/O area in config reg -> only valid for ARGOSY D5!!! */
	*((uchar *)(addr + config_base)) = 1;
Wolfgang Denk's avatar
Wolfgang Denk committed

	return (0);
}
#endif	/* CONFIG_IDE_8xx_PCCARD */

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
/* board specific stuff:						*/
/* voltage_set(), hardware_enable() and hardware_disable()		*/
/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

/* -------------------------------------------------------------------- */
/* RPX Boards from Embedded Planet					*/
/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

#if defined(CONFIG_RPXCLASSIC) || defined(CONFIG_RPXLITE)

/* The RPX boards seems to have it's bus monitor timeout set to 6*8 clocks.
 * SYPCR is write once only, therefore must the slowest memory be faster
 * than the bus monitor or we will get a machine check due to the bus timeout.
 */

#define PCMCIA_BOARD_MSG "RPX CLASSIC or RPX LITE"

#undef PCMCIA_BMT_LIMIT
#define PCMCIA_BMT_LIMIT (6*8)

static int voltage_set(int slot, int vcc, int vpp)
{
	u_long reg = 0;

	switch(vcc) {
	case 0: break;
	case 33: reg |= BCSR1_PCVCTL4; break;
	case 50: reg |= BCSR1_PCVCTL5; break;
	default: return 1;
	}

	switch(vpp) {
	case 0: break;
	case 33:
	case 50:
		if(vcc == vpp)
			reg |= BCSR1_PCVCTL6;
		else
			return 1;
		break;
	case 120:
		reg |= BCSR1_PCVCTL7;
	default: return 1;
	}

	if(vcc == 120)
	   return 1;

	/* first, turn off all power */

	*((uint *)RPX_CSR_ADDR) &= ~(BCSR1_PCVCTL4 | BCSR1_PCVCTL5
				     | BCSR1_PCVCTL6 | BCSR1_PCVCTL7);

	/* enable new powersettings */

	*((uint *)RPX_CSR_ADDR) |= reg;

	return 0;
}

#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V
static int hardware_enable (int slot)
{
	return 0;	/* No hardware to enable */
}
#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA)
static int hardware_disable(int slot)
{
	return 0;	/* No hardware to disable */
}
#endif	/* CFG_CMD_PCMCIA */
#endif	/* CONFIG_RPXCLASSIC */

/* -------------------------------------------------------------------- */
/* (F)ADS Boards from Motorola						*/
/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

#if defined(CONFIG_ADS) || defined(CONFIG_FADS)

#ifdef CONFIG_ADS
#define PCMCIA_BOARD_MSG "ADS"
#define PCMCIA_GLITCHY_CD  /* My ADS board needs this */
#else
#define PCMCIA_BOARD_MSG "FADS"
#endif

static int voltage_set(int slot, int vcc, int vpp)
{
	u_long reg = 0;

	switch(vpp) {
	case 0: reg = 0; break;
	case 50: reg = 1; break;
	case 120: reg = 2; break;
	default: return 1;
	}

	switch(vcc) {
	case 0: reg = 0; break;
#ifdef CONFIG_ADS
	case 50: reg = BCSR1_PCCVCCON; break;
#endif
#ifdef CONFIG_FADS
	case 33: reg = BCSR1_PCCVCC0 | BCSR1_PCCVCC1; break;
	case 50: reg = BCSR1_PCCVCC1; break;
#endif
	default: return 1;
	}

	/* first, turn off all power */

#ifdef CONFIG_ADS
	*((uint *)BCSR1) |= BCSR1_PCCVCCON;
#endif
#ifdef CONFIG_FADS
	*((uint *)BCSR1) &= ~(BCSR1_PCCVCC0 | BCSR1_PCCVCC1);
#endif
	*((uint *)BCSR1) &= ~BCSR1_PCCVPP_MASK;

	/* enable new powersettings */

#ifdef CONFIG_ADS
	*((uint *)BCSR1) &= ~reg;
#endif
#ifdef CONFIG_FADS
	*((uint *)BCSR1) |= reg;
#endif

 	*((uint *)BCSR1) |= reg << 20;

	return 0;
}

#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V

static int hardware_enable(int slot)
{
	*((uint *)BCSR1) &= ~BCSR1_PCCEN;
	return 0;
}

#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA)
static int hardware_disable(int slot)
{
	*((uint *)BCSR1) &= ~BCSR1_PCCEN;
	return 0;
}
#endif	/* CFG_CMD_PCMCIA */

#endif	/* (F)ADS */

/* -------------------------------------------------------------------- */
/* TQM8xxL Boards by TQ Components					*/
/* SC8xx   Boards by SinoVee Microsystems				*/
/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

#if defined(CONFIG_TQM8xxL) || defined(CONFIG_SVM_SC8xx)
Wolfgang Denk's avatar
Wolfgang Denk committed

#if defined(CONFIG_TQM8xxL)
Wolfgang Denk's avatar
Wolfgang Denk committed
#define PCMCIA_BOARD_MSG "TQM8xxL"
#endif
#if defined(CONFIG_SVM_SC8xx)
#define PCMCIA_BOARD_MSG "SC8xx"
#endif
Wolfgang Denk's avatar
Wolfgang Denk committed

static int hardware_enable(int slot)
{
	volatile immap_t	*immap;
	volatile cpm8xx_t	*cp;
	volatile pcmconf8xx_t	*pcmp;
	volatile sysconf8xx_t	*sysp;
	uint reg, mask;

	debug ("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot);

	udelay(10000);

	immap = (immap_t *)CFG_IMMR;
	sysp  = (sysconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_siu_conf));
	pcmp  = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia));
	cp    = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm));

	/*
	 * Configure SIUMCR to enable PCMCIA port B
	 * (VFLS[0:1] are not used for debugging, we connect FRZ# instead)
	 */
	sysp->sc_siumcr &= ~SIUMCR_DBGC11;	/* set DBGC to 00 */

	/* clear interrupt state, and disable interrupts */
	pcmp->pcmc_pscr =  PCMCIA_MASK(_slot_);
	pcmp->pcmc_per &= ~PCMCIA_MASK(_slot_);

	/*
	 * Disable interrupts, DMA, and PCMCIA buffers
	 * (isolate the interface) and assert RESET signal
Wolfgang Denk's avatar
Wolfgang Denk committed
	 */
	debug ("Disable PCMCIA buffers and assert RESET\n");
	reg  = 0;
	reg |= __MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg |= __MY_PCMCIA_GCRX_CXOE;		/* active low  */
Wolfgang Denk's avatar
Wolfgang Denk committed
	PCMCIA_PGCRX(_slot_) = reg;
	udelay(500);

	/*
	 * Configure Port C pins for
	 * 5 Volts Enable and 3 Volts enable
	 */
	immap->im_ioport.iop_pcpar &= ~(0x0002 | 0x0004);
	immap->im_ioport.iop_pcso  &= ~(0x0002 | 0x0004);
	/* remove all power */

	immap->im_ioport.iop_pcdat &= ~(0x0002 | 0x0004);

	/*
	 * Make sure there is a card in the slot, then configure the interface.
	 */
	udelay(10000);
	debug ("[%d] %s: PIPR(%p)=0x%x\n",
		__LINE__,__FUNCTION__,
		&(pcmp->pcmc_pipr),pcmp->pcmc_pipr);
	if (pcmp->pcmc_pipr & (0x18000000 >> (slot << 4))) {
Wolfgang Denk's avatar
Wolfgang Denk committed
		printf ("   No Card found\n");
		return (1);
	}

	/*
	 * Power On.
	 */
	mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot);
	reg  = pcmp->pcmc_pipr;
	debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n",
		reg,
		(reg&PCMCIA_VS1(slot))?"n":"ff",
		(reg&PCMCIA_VS2(slot))?"n":"ff");
	if ((reg & mask) == mask) {
		immap->im_ioport.iop_pcdat |= 0x0004;
		puts (" 5.0V card found: ");
	} else {
		immap->im_ioport.iop_pcdat |= 0x0002;
		puts (" 3.3V card found: ");
	}
	immap->im_ioport.iop_pcdir |= (0x0002 | 0x0004);
Wolfgang Denk's avatar
Wolfgang Denk committed
#if 0
	/*  VCC switch error flag, PCMCIA slot INPACK_ pin */
	cp->cp_pbdir &= ~(0x0020 | 0x0010);
	cp->cp_pbpar &= ~(0x0020 | 0x0010);
	udelay(500000);
#endif
	udelay(1000);
	debug ("Enable PCMCIA buffers and stop RESET\n");
	reg  =  PCMCIA_PGCRX(_slot_);
	reg &= ~__MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg &= ~__MY_PCMCIA_GCRX_CXOE;		/* active low  */
	PCMCIA_PGCRX(_slot_) = reg;

	udelay(250000);	/* some cards need >150 ms to come up :-( */

	debug ("# hardware_enable done\n");

	return (0);
}



#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA)
static int hardware_disable(int slot)
{
	volatile immap_t	*immap;
	volatile pcmconf8xx_t	*pcmp;
	u_long reg;

	debug ("hardware_disable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot);

	immap = (immap_t *)CFG_IMMR;
	pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia));

	/* remove all power */
	immap->im_ioport.iop_pcdat &= ~(0x0002 | 0x0004);

	debug ("Disable PCMCIA buffers and assert RESET\n");
Wolfgang Denk's avatar
Wolfgang Denk committed
	reg |= __MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg |= __MY_PCMCIA_GCRX_CXOE;		/* active low  */
	PCMCIA_PGCRX(_slot_) = reg;

	udelay(10000);

	return (0);
}
#endif	/* CFG_CMD_PCMCIA */



static int voltage_set(int slot, int vcc, int vpp)
{
	volatile immap_t	*immap;
	volatile pcmconf8xx_t	*pcmp;
	u_long reg;

	debug ("voltage_set: "
		PCMCIA_BOARD_MSG
		" Slot %c, Vcc=%d.%d, Vpp=%d.%d\n",
		'A'+slot, vcc/10, vcc%10, vpp/10, vcc%10);

	immap = (immap_t *)CFG_IMMR;
	pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia));
	/*
	 * Disable PCMCIA buffers (isolate the interface)
	 * and assert RESET signal
	 */
	debug ("Disable PCMCIA buffers and assert RESET\n");
	reg  = PCMCIA_PGCRX(_slot_);
	reg |= __MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg |= __MY_PCMCIA_GCRX_CXOE;		/* active low  */
Wolfgang Denk's avatar
Wolfgang Denk committed
	PCMCIA_PGCRX(_slot_) = reg;
	udelay(500);

	/*
	 * Configure Port C pins for
	 * 5 Volts Enable and 3 Volts enable,
	 * Turn off all power
	 */
	debug ("PCMCIA power OFF\n");
	immap->im_ioport.iop_pcpar &= ~(0x0002 | 0x0004);
	immap->im_ioport.iop_pcso  &= ~(0x0002 | 0x0004);
	immap->im_ioport.iop_pcdat &= ~(0x0002 | 0x0004);

	reg = 0;
	switch(vcc) {
	case  0: 		break;
	case 33: reg |= 0x0002;	break;
	case 50: reg |= 0x0004;	break;
	default: 		goto done;
	}

	/* Checking supported voltages */

	debug ("PIPR: 0x%x --> %s\n",
		pcmp->pcmc_pipr,
		(pcmp->pcmc_pipr & 0x00008000) ? "only 5 V" : "can do 3.3V");

	immap->im_ioport.iop_pcdat |= reg;
	immap->im_ioport.iop_pcdir |= (0x0002 | 0x0004);
Wolfgang Denk's avatar
Wolfgang Denk committed
	if (reg) {
		debug ("PCMCIA powered at %sV\n",
			(reg&0x0004) ? "5.0" : "3.3");
	} else {
		debug ("PCMCIA powered down\n");
	}

done:
	debug ("Enable PCMCIA buffers and stop RESET\n");
	reg  =  PCMCIA_PGCRX(_slot_);
	reg &= ~__MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg &= ~__MY_PCMCIA_GCRX_CXOE;		/* active low  */
	PCMCIA_PGCRX(_slot_) = reg;
	udelay(500);

	debug ("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n",
		slot+'A');
	return (0);
}

#endif	/* TQM8xxL */


/* -------------------------------------------------------------------- */
/* LWMON Board								*/
/* -------------------------------------------------------------------- */
Wolfgang Denk's avatar
Wolfgang Denk committed

#if defined(CONFIG_LWMON)

#define PCMCIA_BOARD_MSG "LWMON"

/* #define's for MAX1604 Power Switch */
#define MAX1604_OP_SUS		0x80
#define MAX1604_VCCBON		0x40
#define MAX1604_VCC_35		0x20
#define MAX1604_VCCBHIZ		0x10
#define MAX1604_VPPBON		0x08
#define MAX1604_VPPBPBPGM	0x04
#define MAX1604_VPPBHIZ		0x02
/* reserved			0x01	*/

static int hardware_enable(int slot)
{
	volatile immap_t	*immap;
	volatile cpm8xx_t	*cp;
	volatile pcmconf8xx_t	*pcmp;
	volatile sysconf8xx_t	*sysp;
	uint reg, mask;
	uchar val;


	debug ("hardware_enable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot);

	/* Switch on PCMCIA port in PIC register 0x60 */
	reg = pic_read  (0x60);
	debug ("[%d] PIC read: reg_60 = 0x%02x\n", __LINE__, reg);
	reg &= ~0x10;
	/* reg |= 0x08; Vpp not needed */
Wolfgang Denk's avatar
Wolfgang Denk committed
	pic_write (0x60, reg);
#ifdef DEBUG
	reg = pic_read  (0x60);
	printf ("[%d] PIC read: reg_60 = 0x%02x\n", __LINE__, reg);
#endif
	udelay(10000);

	immap = (immap_t *)CFG_IMMR;
	sysp  = (sysconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_siu_conf));
	pcmp  = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia));
	cp    = (cpm8xx_t *)(&(((immap_t *)CFG_IMMR)->im_cpm));

	/*
	 * Configure SIUMCR to enable PCMCIA port B
	 * (VFLS[0:1] are not used for debugging, we connect FRZ# instead)
	 */
	sysp->sc_siumcr &= ~SIUMCR_DBGC11;	/* set DBGC to 00 */

	/* clear interrupt state, and disable interrupts */
	pcmp->pcmc_pscr =  PCMCIA_MASK(_slot_);
	pcmp->pcmc_per &= ~PCMCIA_MASK(_slot_);

	/*
	 * Disable interrupts, DMA, and PCMCIA buffers
	 * (isolate the interface) and assert RESET signal
Wolfgang Denk's avatar
Wolfgang Denk committed
	 */
	debug ("Disable PCMCIA buffers and assert RESET\n");
	reg  = 0;
	reg |= __MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg |= __MY_PCMCIA_GCRX_CXOE;		/* active low  */
Wolfgang Denk's avatar
Wolfgang Denk committed
	PCMCIA_PGCRX(_slot_) = reg;
	udelay(500);

	/*
	 * Make sure there is a card in the slot, then configure the interface.
	 */
	udelay(10000);
	debug ("[%d] %s: PIPR(%p)=0x%x\n",
		__LINE__,__FUNCTION__,
		&(pcmp->pcmc_pipr),pcmp->pcmc_pipr);
	if (pcmp->pcmc_pipr & (0x18000000 >> (slot << 4))) {
Wolfgang Denk's avatar
Wolfgang Denk committed
		printf ("   No Card found\n");
		return (1);
	}

	/*
	 * Power On.
	 */
	mask = PCMCIA_VS1(slot) | PCMCIA_VS2(slot);
	reg  = pcmp->pcmc_pipr;
	debug ("PIPR: 0x%x ==> VS1=o%s, VS2=o%s\n",
		reg,
		(reg&PCMCIA_VS1(slot))?"n":"ff",
		(reg&PCMCIA_VS2(slot))?"n":"ff");
	if ((reg & mask) == mask) {
		val = 0;		/* VCCB3/5 = 0 ==> use Vx = 5.0 V */
		puts (" 5.0V card found: ");
	} else {
		val = MAX1604_VCC_35;	/* VCCB3/5 = 1 ==> use Vy = 3.3 V */
		puts (" 3.3V card found: ");
	}

	/*  switch VCC on */
	val |= MAX1604_OP_SUS | MAX1604_VCCBON;
Wolfgang Denk's avatar
Wolfgang Denk committed
	i2c_init  (CFG_I2C_SPEED, CFG_I2C_SLAVE);
	i2c_write (CFG_I2C_POWER_A_ADDR, 0, 0, &val, 1);

	udelay(500000);

	debug ("Enable PCMCIA buffers and stop RESET\n");
	reg  =  PCMCIA_PGCRX(_slot_);
	reg &= ~__MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg &= ~__MY_PCMCIA_GCRX_CXOE;		/* active low  */
	PCMCIA_PGCRX(_slot_) = reg;

	udelay(250000);	/* some cards need >150 ms to come up :-( */

	debug ("# hardware_enable done\n");

	return (0);
}



#if (CONFIG_COMMANDS & CFG_CMD_PCMCIA)
static int hardware_disable(int slot)
{
	volatile immap_t	*immap;
	volatile pcmconf8xx_t	*pcmp;
	u_long reg;
	uchar val;

	debug ("hardware_disable: " PCMCIA_BOARD_MSG " Slot %c\n", 'A'+slot);

	immap = (immap_t *)CFG_IMMR;
	pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia));

	/* remove all power, put output in high impedance state */
	val  = MAX1604_VCCBHIZ | MAX1604_VPPBHIZ;
	i2c_init  (CFG_I2C_SPEED, CFG_I2C_SLAVE);
	i2c_write (CFG_I2C_POWER_A_ADDR, 0, 0, &val, 1);

	/* Configure PCMCIA General Control Register */
	debug ("Disable PCMCIA buffers and assert RESET\n");
Wolfgang Denk's avatar
Wolfgang Denk committed
	reg |= __MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg |= __MY_PCMCIA_GCRX_CXOE;		/* active low  */
	PCMCIA_PGCRX(_slot_) = reg;

	/* Switch off PCMCIA port in PIC register 0x60 */
	reg = pic_read  (0x60);
	debug ("[%d] PIC read: reg_60 = 0x%02x\n", __LINE__, reg);
	reg |=  0x10;
	reg &= ~0x08;
	pic_write (0x60, reg);
#ifdef DEBUG
	reg = pic_read  (0x60);
	printf ("[%d] PIC read: reg_60 = 0x%02x\n", __LINE__, reg);
#endif
	udelay(10000);

	return (0);
}
#endif	/* CFG_CMD_PCMCIA */



static int voltage_set(int slot, int vcc, int vpp)
{
	volatile immap_t	*immap;
	volatile pcmconf8xx_t	*pcmp;
	u_long reg;
	uchar val;

	debug ("voltage_set: "
		PCMCIA_BOARD_MSG
		" Slot %c, Vcc=%d.%d, Vpp=%d.%d\n",
		'A'+slot, vcc/10, vcc%10, vpp/10, vcc%10);

	immap = (immap_t *)CFG_IMMR;
	pcmp = (pcmconf8xx_t *)(&(((immap_t *)CFG_IMMR)->im_pcmcia));
	/*
	 * Disable PCMCIA buffers (isolate the interface)
	 * and assert RESET signal
	 */
	debug ("Disable PCMCIA buffers and assert RESET\n");
	reg  = PCMCIA_PGCRX(_slot_);
	reg |= __MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg |= __MY_PCMCIA_GCRX_CXOE;		/* active low  */
Wolfgang Denk's avatar
Wolfgang Denk committed
	PCMCIA_PGCRX(_slot_) = reg;
	udelay(500);

	/*
	 * Turn off all power (switch to high impedance)
	 */
	debug ("PCMCIA power OFF\n");
	val  = MAX1604_VCCBHIZ | MAX1604_VPPBHIZ;
	i2c_init  (CFG_I2C_SPEED, CFG_I2C_SLAVE);
	i2c_write (CFG_I2C_POWER_A_ADDR, 0, 0, &val, 1);

	val = 0;
	switch(vcc) {
	case  0: 			break;
	case 33: val = MAX1604_VCC_35;	break;
	case 50: 			break;
	default: 			goto done;
	}

	/* Checking supported voltages */

	debug ("PIPR: 0x%x --> %s\n",
		pcmp->pcmc_pipr,
		(pcmp->pcmc_pipr & 0x00008000) ? "only 5 V" : "can do 3.3V");

	i2c_write (CFG_I2C_POWER_A_ADDR, 0, 0, &val, 1);
	if (val) {
		debug ("PCMCIA powered at %sV\n",
			(val & MAX1604_VCC_35) ? "3.3" : "5.0");
	} else {
		debug ("PCMCIA powered down\n");
	}

done:
	debug ("Enable PCMCIA buffers and stop RESET\n");
	reg  =  PCMCIA_PGCRX(_slot_);
	reg &= ~__MY_PCMCIA_GCRX_CXRESET;	/* active high */
	reg &= ~__MY_PCMCIA_GCRX_CXOE;		/* active low  */
	PCMCIA_PGCRX(_slot_) = reg;
	udelay(500);

	debug ("voltage_set: " PCMCIA_BOARD_MSG " Slot %c, DONE\n",