Revision de5604231ce4bc8db1bc1dcd27d8540cbedf1518 authored by Nick Piggin on 01 February 2010, 11:24:18 UTC, committed by Linus Torvalds on 02 February 2010, 20:50:47 UTC
RCU list walking of the per-cpu vmap cache was broken.  It did not use
RCU primitives, and also the union of free_list and rcu_head is
obviously wrong (because free_list is indeed the list we are RCU
walking).

While we are there, remove a couple of unused fields from an earlier
iteration.

These APIs aren't actually used anywhere, because of problems with the
XFS conversion.  Christoph has now verified that the problems are solved
with these patches.  Also it is an exported interface, so I think it
will be good to be merged now (and Christoph wants to get the XFS
changes into their local tree).

Cc: stable@kernel.org
Cc: linux-mm@kvack.org
Tested-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Nick Piggin <npiggin@suse.de>
--
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 489b24f
Raw File
au1000_db1x00.c
/*
 *
 * Alchemy Semi Db1x00 boards specific pcmcia routines.
 *
 * Copyright 2002 MontaVista Software Inc.
 * Author: MontaVista Software, Inc.
 *         	ppopov@mvista.com or source@mvista.com
 *
 * Copyright 2004 Pete Popov, updated the driver to 2.6.
 * Followed the sa11xx API and largely copied many of the hardware
 * independent functions.
 *
 * ########################################################################
 *
 *  This program is free software; you can distribute it and/or modify it
 *  under the terms of the GNU General Public License (Version 2) as
 *  published by the Free Software Foundation.
 *
 *  This program is distributed in the hope 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.
 *
 * ########################################################################
 *
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/init.h>

#include <asm/irq.h>
#include <asm/signal.h>
#include <asm/mach-au1x00/au1000.h>

#if defined(CONFIG_MIPS_DB1200)
	#include <db1200.h>
#elif defined(CONFIG_MIPS_PB1200)
	#include <pb1200.h>
#else
	#include <asm/mach-db1x00/db1x00.h>
	static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
#endif

#include "au1000_generic.h"

#if 0
#define debug(x,args...) printk(KERN_DEBUG "%s: " x, __func__ , ##args)
#else
#define debug(x,args...)
#endif


struct au1000_pcmcia_socket au1000_pcmcia_socket[PCMCIA_NUM_SOCKS];
extern int au1x00_pcmcia_socket_probe(struct device *, struct pcmcia_low_level *, int, int);

static int db1x00_pcmcia_hw_init(struct au1000_pcmcia_socket *skt)
{
#ifdef CONFIG_MIPS_DB1550
	skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_3;
#elif defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200)
	skt->irq = skt->nr ? BOARD_PC1_INT : BOARD_PC0_INT;
#else
	skt->irq = skt->nr ? AU1000_GPIO_5 : AU1000_GPIO_2;
#endif
	return 0;
}

static void db1x00_pcmcia_shutdown(struct au1000_pcmcia_socket *skt)
{
	bcsr->pcmcia = 0; /* turn off power */
	au_sync_delay(2);
}

static void
db1x00_pcmcia_socket_state(struct au1000_pcmcia_socket *skt, struct pcmcia_state *state)
{
	u32 inserted;
	unsigned char vs;

	state->ready = 0;
	state->vs_Xv = 0;
	state->vs_3v = 0;
	state->detect = 0;

	switch (skt->nr) {
	case 0:
		vs = bcsr->status & 0x3;
#if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200)
		inserted = BOARD_CARD_INSERTED(0);
#else
		inserted = !(bcsr->status & (1<<4));
#endif
		break;
	case 1:
		vs = (bcsr->status & 0xC)>>2;
#if defined(CONFIG_MIPS_DB1200) || defined(CONFIG_MIPS_PB1200)
		inserted = BOARD_CARD_INSERTED(1);
#else
		inserted = !(bcsr->status & (1<<5));
#endif
		break;
	default:/* should never happen */
		return;
	}

	if (inserted)
		debug("db1x00 socket %d: inserted %d, vs %d pcmcia %x\n",
				skt->nr, inserted, vs, bcsr->pcmcia);

	if (inserted) {
		switch (vs) {
			case 0:
			case 2:
				state->vs_3v=1;
				break;
			case 3: /* 5V */
				break;
			default:
				/* return without setting 'detect' */
				printk(KERN_ERR "db1x00 bad VS (%d)\n",
						vs);
		}
		state->detect = 1;
		state->ready = 1;
	}
	else {
		/* if the card was previously inserted and then ejected,
		 * we should turn off power to it
		 */
		if ((skt->nr == 0) && (bcsr->pcmcia & BCSR_PCMCIA_PC0RST)) {
			bcsr->pcmcia &= ~(BCSR_PCMCIA_PC0RST |
					BCSR_PCMCIA_PC0DRVEN |
					BCSR_PCMCIA_PC0VPP |
					BCSR_PCMCIA_PC0VCC);
			au_sync_delay(10);
		}
		else if ((skt->nr == 1) && bcsr->pcmcia & BCSR_PCMCIA_PC1RST) {
			bcsr->pcmcia &= ~(BCSR_PCMCIA_PC1RST |
					BCSR_PCMCIA_PC1DRVEN |
					BCSR_PCMCIA_PC1VPP |
					BCSR_PCMCIA_PC1VCC);
			au_sync_delay(10);
		}
	}

	state->bvd1=1;
	state->bvd2=1;
	state->wrprot=0;
}

static int
db1x00_pcmcia_configure_socket(struct au1000_pcmcia_socket *skt, struct socket_state_t *state)
{
	u16 pwr;
	int sock = skt->nr;

	debug("config_skt %d Vcc %dV Vpp %dV, reset %d\n",
			sock, state->Vcc, state->Vpp,
			state->flags & SS_RESET);

	/* pcmcia reg was set to zero at init time. Be careful when
	 * initializing a socket not to wipe out the settings of the
	 * other socket.
	 */
	pwr = bcsr->pcmcia;
	pwr &= ~(0xf << sock*8); /* clear voltage settings */

	state->Vpp = 0;
	switch(state->Vcc){
		case 0:  /* Vcc 0 */
			pwr |= SET_VCC_VPP(0,0,sock);
			break;
		case 50: /* Vcc 5V */
			switch(state->Vpp) {
				case 0:
					pwr |= SET_VCC_VPP(2,0,sock);
					break;
				case 50:
					pwr |= SET_VCC_VPP(2,1,sock);
					break;
				case 12:
					pwr |= SET_VCC_VPP(2,2,sock);
					break;
				case 33:
				default:
					pwr |= SET_VCC_VPP(0,0,sock);
					printk("%s: bad Vcc/Vpp (%d:%d)\n",
							__func__,
							state->Vcc,
							state->Vpp);
					break;
			}
			break;
		case 33: /* Vcc 3.3V */
			switch(state->Vpp) {
				case 0:
					pwr |= SET_VCC_VPP(1,0,sock);
					break;
				case 12:
					pwr |= SET_VCC_VPP(1,2,sock);
					break;
				case 33:
					pwr |= SET_VCC_VPP(1,1,sock);
					break;
				case 50:
				default:
					pwr |= SET_VCC_VPP(0,0,sock);
					printk("%s: bad Vcc/Vpp (%d:%d)\n",
							__func__,
							state->Vcc,
							state->Vpp);
					break;
			}
			break;
		default: /* what's this ? */
			pwr |= SET_VCC_VPP(0,0,sock);
			printk(KERN_ERR "%s: bad Vcc %d\n",
					__func__, state->Vcc);
			break;
	}

	bcsr->pcmcia = pwr;
	au_sync_delay(300);

	if (sock == 0) {
		if (!(state->flags & SS_RESET)) {
			pwr |= BCSR_PCMCIA_PC0DRVEN;
			bcsr->pcmcia = pwr;
			au_sync_delay(300);
			pwr |= BCSR_PCMCIA_PC0RST;
			bcsr->pcmcia = pwr;
			au_sync_delay(100);
		}
		else {
			pwr &= ~(BCSR_PCMCIA_PC0RST | BCSR_PCMCIA_PC0DRVEN);
			bcsr->pcmcia = pwr;
			au_sync_delay(100);
		}
	}
	else {
		if (!(state->flags & SS_RESET)) {
			pwr |= BCSR_PCMCIA_PC1DRVEN;
			bcsr->pcmcia = pwr;
			au_sync_delay(300);
			pwr |= BCSR_PCMCIA_PC1RST;
			bcsr->pcmcia = pwr;
			au_sync_delay(100);
		}
		else {
			pwr &= ~(BCSR_PCMCIA_PC1RST | BCSR_PCMCIA_PC1DRVEN);
			bcsr->pcmcia = pwr;
			au_sync_delay(100);
		}
	}
	return 0;
}

/*
 * Enable card status IRQs on (re-)initialisation.  This can
 * be called at initialisation, power management event, or
 * pcmcia event.
 */
void db1x00_socket_init(struct au1000_pcmcia_socket *skt)
{
	/* nothing to do for now */
}

/*
 * Disable card status IRQs and PCMCIA bus on suspend.
 */
void db1x00_socket_suspend(struct au1000_pcmcia_socket *skt)
{
	/* nothing to do for now */
}

struct pcmcia_low_level db1x00_pcmcia_ops = {
	.owner			= THIS_MODULE,

	.hw_init 		= db1x00_pcmcia_hw_init,
	.hw_shutdown		= db1x00_pcmcia_shutdown,

	.socket_state		= db1x00_pcmcia_socket_state,
	.configure_socket	= db1x00_pcmcia_configure_socket,

	.socket_init		= db1x00_socket_init,
	.socket_suspend		= db1x00_socket_suspend
};

int au1x_board_init(struct device *dev)
{
	int ret = -ENODEV;
	bcsr->pcmcia = 0; /* turn off power, if it's not already off */
	au_sync_delay(2);
	ret = au1x00_pcmcia_socket_probe(dev, &db1x00_pcmcia_ops, 0, 2);
	return ret;
}
back to top