[PATCH] MPC8xx PCMCIA driver
Jeff Garzik
jgarzik at pobox.com
Tue Aug 30 13:39:02 EST 2005
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.
More information about the Linuxppc-embedded
mailing list