Subject: Re: [PATCH] MPC8xx PCMCIA driver

From: Jeff Garzik <[email-masked]>
Date: Mon, 29 Aug 2005 23:39:02 -0400

Marcelo Tosatti wrote:
> +static int voltage_set(int slot, int vcc, int vpp)
> +{
> +	u_int 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 == 50) || (vcc == 0)))
> +		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;

Should use bus read/write functions, such as foo_readl() or iowrite32().

Don't use weird types in kernel code such as 'uint'.  Use the more 
explicitly-sized u32.

> +	return 0;
> +}
> +
> +#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V
> +#define hardware_enable(_slot_)  /* No hardware to enable */
> +#define hardware_disable(_slot_) /* No hardware to disable */
> +
> +#endif /* CONFIG_RPXCLASSIC */
> +
> +/* FADS Boards from Motorola                                               */
> +
> +#if defined(CONFIG_FADS)
> +
> +#define PCMCIA_BOARD_MSG "FADS"
> +
> +static int voltage_set(int slot, int vcc, int vpp)
> +{
> +	uint reg = 0;
> +
> +	switch(vcc) {
> +		case 0:
> +			break;
> +		case 33:
> +			reg |= BCSR1_PCCVCC0;
> +			break;
> +		case 50:
> +			reg |= BCSR1_PCCVCC1;
> +			break;
> +		default:
> +			return 1;
> +	}
> +
> +	switch(vpp) {
> +		case 0:
> +			break;
> +		case 33:
> +		case 50:
> +			if(vcc == vpp)
> +				reg |= BCSR1_PCCVPP1;
> +			else
> +				return 1;
> +			break;
> +		case 120:
> +			if ((vcc == 33) || (vcc == 50))
> +				reg |= BCSR1_PCCVPP0;
> +			else
> +				return 1;
> +		default:
> +			return 1;
> +	}
> +
> +	/* first, turn off all power */
> +	*((uint *)BCSR1) &= ~(BCSR1_PCCVCC_MASK | BCSR1_PCCVPP_MASK);
> +
> +	/* enable new powersettings */
> +	*((uint *)BCSR1) |= reg;

ditto

> +	return 0;
> +}
> +
> +#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V
> +
> +static void hardware_enable(int slot)
> +{
> +	*((uint *)BCSR1) &= ~BCSR1_PCCEN;
> +}

ditto

> +static void hardware_disable(int slot)
> +{
> +	*((uint *)BCSR1) |=  BCSR1_PCCEN;
> +}

etc.

> +/* ------------------------------------------------------------------------- */
> +/* Motorola MBX860                                                           */
> +
> +#if defined(CONFIG_MBX)
> +
> +#define PCMCIA_BOARD_MSG "MBX"
> +
> +static int voltage_set(int slot, int vcc, int vpp)
> +{
> +	unsigned char reg = 0;
> +
> +	switch(vcc) {
> +		case 0:
> +			break;
> +		case 33:
> +			reg |= CSR2_VCC_33;
> +			break;
> +		case 50:
> +			reg |= CSR2_VCC_50;
> +			break;
> +		default:
> +			return 1;
> +	}
> +
> +	switch(vpp) {
> +		case 0:
> +			break;
> +		case 33:
> +		case 50:
> +			if(vcc == vpp)
> +				reg |= CSR2_VPP_VCC;
> +			else
> +				return 1;
> +			break;
> +		case 120:
> +			if ((vcc == 33) || (vcc == 50))
> +				reg |= CSR2_VPP_12;
> +			else
> +				return 1;
> +		default:
> +			return 1;
> +	}
> +
> +	/* first, turn off all power */
> +	*((unsigned char *)MBX_CSR2_ADDR) &= ~(CSR2_VCC_MASK | CSR2_VPP_MASK);
> +
> +	/* enable new powersettings */
> +	*((unsigned char *)MBX_CSR2_ADDR) |= reg;

ditto.

also, use u8 not unsigned char.

> +	return 0;
> +}
> +
> +#define socket_get(_slot_) PCMCIA_SOCKET_KEY_5V
> +#define hardware_enable(_slot_)  /* No hardware to enable */
> +#define hardware_disable(_slot_) /* No hardware to disable */
> +
> +#endif /* CONFIG_MBX */
> +
> +#if defined(CONFIG_PRxK)
> +#include <asm/cpld.h>
> +extern volatile fpga_pc_regs *fpga_pc;
> +
> +#define PCMCIA_BOARD_MSG "MPC855T"
> +
> +static int voltage_set(int slot, int vcc, int vpp)
> +{
> +	unsigned char reg = 0;
> +	unsigned char regread;
> +	cpld_regs *ccpld = get_cpld();
> +
> +	switch(vcc) {
> +		case 0:
> +			break;
> +		case 33:
> +			reg |= PCMCIA_VCC_33;
> +			break;
> +		case 50:
> +			reg |= PCMCIA_VCC_50;
> +			break;
> +		default:
> +			return 1;
> +	}
> +
> +	switch(vpp) {
> +		case 0:
> +			break;
> +		case 33:
> +		case 50:
> +			if(vcc == vpp)
> +				reg |= PCMCIA_VPP_VCC;
> +			else
> +				return 1;
> +			break;
> +		case 120:
> +			if ((vcc == 33) || (vcc == 50))
> +				reg |= PCMCIA_VPP_12;
> +			else
> +				return 1;
> +		default:
> +			return 1;
> +	}
> +
> +	reg = reg >> (slot << 2);
> +	regread = ccpld->fpga_pc_ctl;
> +	if (reg != (regread & ((PCMCIA_VCC_MASK | PCMCIA_VPP_MASK) >> (slot << 2)))) {
> +		/* enable new powersettings */
> +		regread = regread & ~((PCMCIA_VCC_MASK | PCMCIA_VPP_MASK) >> (slot << 2));
> +		ccpld->fpga_pc_ctl = reg | regread;
> +		mdelay(100);

should be msleep() AFAICS

> +	return 0;
> +}
> +
> +#define socket_get(_slot_) PCMCIA_SOCKET_KEY_LV
> +#define hardware_enable(_slot_)  /* No hardware to enable */
> +#define hardware_disable(_slot_) /* No hardware to disable */
> +
> +#endif /* CONFIG_PRxK */
> +
> +static void m8xx_shutdown(void)
> +{
> +	u_int m, i;
> +	pcmcia_win_t *w;
> +
> +	for(i = 0; i < PCMCIA_SOCKETS_NO; i++){
> +		w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0;
> +
> +		((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr = 
> +			M8XX_PCMCIA_MASK(i); 
> +		((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per 
> +			&= ~M8XX_PCMCIA_MASK(i);
> + 
> +		/* turn off interrupt and disable CxOE */
> +		M8XX_PGCRX(i) = M8XX_PGCRX_CXOE;
> +		
> +		/* turn off memory windows */
> +		for(m = 0; m < PCMCIA_MEM_WIN_NO; m++) {
> +			w->or = 0;  /* set to not valid */
> +			w++;
> +		}
> +		
> +		/* turn off voltage */
> +		voltage_set(i, 0, 0);
> +		
> +		/* disable external hardware */
> +		hardware_disable(i);
> +	}
> +
> +	free_irq(pcmcia_schlvl, NULL);

don't you want to free_irq() first?

> +}
> +
> +/* copied from tcic.c */
> +
> +static int m8xx_drv_suspend(struct device *dev, u32 state, u32 level)
> +{
> +        int ret = 0;
> +        if (level == SUSPEND_SAVE_STATE)
> +                ret = pcmcia_socket_dev_suspend(dev, state);
> +        return ret;
> +}
> +                                                                                       
> +static int m8xx_drv_resume(struct device *dev, u32 level)
> +{
> +        int ret = 0;
> +        if (level == RESUME_RESTORE_STATE)
> +                ret = pcmcia_socket_dev_resume(dev);
> +        return ret;
> +}
> +                                                                                       
> +static struct device_driver m8xx_driver = {
> +        .name = "m8xx-pcmcia",
> +        .bus = &platform_bus_type,
> +        .suspend = m8xx_drv_suspend,
> +        .resume = m8xx_drv_resume,
> +};
> +
> +static struct platform_device m8xx_device = {
> +        .name = "m8xx-pcmcia",
> +        .id = 0,
> +};
> +
> +static u_int pending_events[PCMCIA_SOCKETS_NO];
> +static spinlock_t pending_event_lock = SPIN_LOCK_UNLOCKED;
> +
> +static irqreturn_t m8xx_interrupt(int irq, void *dev, struct pt_regs *regs)
> +{
> +	socket_info_t *s;
> +	event_table_t *e;
> +	u_int i, events, pscr, pipr, per;
> +
> +	dprintk("Interrupt!\n");
> +	/* get interrupt sources */
> +
> +	pscr = ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr;
> +	pipr = ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr;
> +	per = ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per;
> +
> +	for(i = 0; i < PCMCIA_SOCKETS_NO; i++) {
> +		s = &socket[i];
> +		e = &s->events[0]; 
> +		events = 0;
> +
> +		while(e->regbit) {
> +			if(pscr & e->regbit)
> +				events |= e->eventbit;
> +	    
> +				e++;
> +		}
> +
> +		/* 
> +		 * 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(events & SS_DETECT)
> +			if(((pipr & M8XX_PCMCIA_CD2(i)) >> 1) ^
> +				(pipr & M8XX_PCMCIA_CD1(i)))
> +			{
> +				events &= ~SS_DETECT;
> +			}
> +
> +#ifdef PCMCIA_GLITCHY_CD
> +		/*
> +		 * I've experienced CD problems with my ADS board.
> +		 * We make an extra check to see if there was a
> +		 * real change of Card detection.
> +		 */
> +	  
> +		if((events & SS_DETECT) && 
> +		   ((pipr &
> +		     (M8XX_PCMCIA_CD2(i) | M8XX_PCMCIA_CD1(i))) == 0) &&
> +		   (s->state.Vcc | s->state.Vpp)) {
> +			events &= ~SS_DETECT;
> +			/*printk( "CD glitch workaround - CD = 0x%08x!\n",
> +				(pipr & (M8XX_PCMCIA_CD2(i) 
> +					 | M8XX_PCMCIA_CD1(i))));*/
> +		}
> +#endif
> +	  
> +		/* call the handler */
> +
> +		dprintk("slot %u: events = 0x%02x, pscr = 0x%08x, "
> +			"pipr = 0x%08x\n", 
> +			i, events, pscr, pipr);
> +  
> +		if(events) {
> +			spin_lock(&pending_event_lock);
> +			pending_events[i] |= events;
> +			spin_unlock(&pending_event_lock);
> +			/* 
> +			 * Turn off RDY_L bits in the PER mask on 
> +			 * CD interrupt receival.
> +			 *
> +			 * They can generate bad interrupts on the
> +			 * ACS4,8,16,32.   - marcelo
> +			 */
> +			per &= ~M8XX_PCMCIA_RDY_L(0);
> +			per &= ~M8XX_PCMCIA_RDY_L(1);
> +
> +			((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per = per;
> +
> +			if (events)
> +				pcmcia_parse_events(&socket[i].socket, events);
> +		}
> +	}
> +	
> +	/* clear the interrupt sources */
> +	((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr = pscr;

more direct memory writes which should be foo_writel()

> +	dprintk("Interrupt done.\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int m8xx_suspend(struct pcmcia_socket *sock)
> +{
> +	return m8xx_set_socket(sock, &dead_socket);
> +}
> +
> +static struct pccard_operations m8xx_services = {
> +	.init	= m8xx_sock_init,	
> +	.suspend = m8xx_suspend,
> +	.get_status = m8xx_get_status,
> +	.get_socket = m8xx_get_socket,
> +	.set_socket = m8xx_set_socket,
> +	.set_io_map = m8xx_set_io_map,
> +	.set_mem_map = m8xx_set_mem_map,
> +};
> +
> +static int __init m8xx_init(void)
> +{
> +	pcmcia_win_t *w;
> +	u_int i,m;
> +
> +	pcmcia_info("%s\n", version);
> +
> +	if (driver_register(&m8xx_driver))
> +		return -1;
> +
> +	pcmcia_info(PCMCIA_BOARD_MSG " using " PCMCIA_SLOT_MSG 
> +		    " with IRQ %u.\n", pcmcia_schlvl); 
> +
> +	/* Configure Status change interrupt */
> +
> +	if(request_irq(pcmcia_schlvl, m8xx_interrupt, 0, 
> +			  "m8xx_pcmcia", NULL)) {
> +		pcmcia_error("Cannot allocate IRQ %u for SCHLVL!\n", 
> +			     pcmcia_schlvl);
> +		return -1;
> +	}
> +
> +	w = (void *) &((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0;
> +
> +	((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr = 
> +		M8XX_PCMCIA_MASK(0)| M8XX_PCMCIA_MASK(1); 
> +	((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_per 
> +		&= ~(M8XX_PCMCIA_MASK(0)| M8XX_PCMCIA_MASK(1));

ditto etc.