pxa2xx_sharpsl.c 7.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Sharp SL-C7xx Series PCMCIA routines
 *
 * Copyright (c) 2004-2005 Richard Purdie
 *
 * Based on Sharp's 2.4 kernel patches and pxa2xx_mainstone.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
19
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
20

21
#include <asm/mach-types.h>
L
Linus Torvalds 已提交
22 23 24 25 26 27 28
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/hardware/scoop.h>

#include "soc_common.h"

#define	NO_KEEP_VS 0x0001
29 30
#define SCOOP_DEV platform_scoop_config->devs

31
static void sharpsl_pcmcia_init_reset(struct soc_pcmcia_socket *skt)
L
Linus Torvalds 已提交
32
{
33 34
	struct scoop_pcmcia_dev *scoopdev = &SCOOP_DEV[skt->nr];

35
	reset_scoop(scoopdev->dev);
36 37 38 39 40 41 42

	/* Shared power controls need to be handled carefully */
	if (platform_scoop_config->power_ctrl)
		platform_scoop_config->power_ctrl(scoopdev->dev, 0x0000, skt->nr);
	else
		write_scoop_reg(scoopdev->dev, SCOOP_CPR, 0x0000);

43 44
	scoopdev->keep_vs = NO_KEEP_VS;
	scoopdev->keep_rd = 0;
L
Linus Torvalds 已提交
45 46 47 48 49 50
}

static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
	int ret;

51 52
	if (platform_scoop_config->pcmcia_init)
		platform_scoop_config->pcmcia_init();
L
Linus Torvalds 已提交
53 54

	/* Register interrupts */
55
	if (SCOOP_DEV[skt->nr].cd_irq >= 0) {
56 57 58
		struct pcmcia_irqs cd_irq;

		cd_irq.sock = skt->nr;
59 60
		cd_irq.irq  = SCOOP_DEV[skt->nr].cd_irq;
		cd_irq.str  = SCOOP_DEV[skt->nr].cd_irq_str;
61 62 63 64 65 66
		ret = soc_pcmcia_request_irqs(skt, &cd_irq, 1);

		if (ret) {
			printk(KERN_ERR "Request for Compact Flash IRQ failed\n");
			return ret;
		}
L
Linus Torvalds 已提交
67 68
	}

69
	skt->irq = SCOOP_DEV[skt->nr].irq;
L
Linus Torvalds 已提交
70 71 72 73 74 75

	return 0;
}

static void sharpsl_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
76
	if (SCOOP_DEV[skt->nr].cd_irq >= 0) {
77
		struct pcmcia_irqs cd_irq;
L
Linus Torvalds 已提交
78

79
		cd_irq.sock = skt->nr;
80 81
		cd_irq.irq  = SCOOP_DEV[skt->nr].cd_irq;
		cd_irq.str  = SCOOP_DEV[skt->nr].cd_irq_str;
82 83
		soc_pcmcia_free_irqs(skt, &cd_irq, 1);
	}
L
Linus Torvalds 已提交
84 85 86 87 88 89 90
}


static void sharpsl_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
				    struct pcmcia_state *state)
{
	unsigned short cpr, csr;
91
	struct device *scoop = SCOOP_DEV[skt->nr].dev;
L
Linus Torvalds 已提交
92

93
	cpr = read_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_CPR);
L
Linus Torvalds 已提交
94

95 96 97 98
	write_scoop_reg(scoop, SCOOP_IRM, 0x00FF);
	write_scoop_reg(scoop, SCOOP_ISR, 0x0000);
	write_scoop_reg(scoop, SCOOP_IRM, 0x0000);
	csr = read_scoop_reg(scoop, SCOOP_CSR);
L
Linus Torvalds 已提交
99 100
	if (csr & 0x0004) {
		/* card eject */
101
		write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
102
		SCOOP_DEV[skt->nr].keep_vs = NO_KEEP_VS;
L
Linus Torvalds 已提交
103
	}
104
	else if (!(SCOOP_DEV[skt->nr].keep_vs & NO_KEEP_VS)) {
L
Linus Torvalds 已提交
105
		/* keep vs1,vs2 */
106
		write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
107
		csr |= SCOOP_DEV[skt->nr].keep_vs;
L
Linus Torvalds 已提交
108 109 110
	}
	else if (cpr & 0x0003) {
		/* power on */
111
		write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
112
		SCOOP_DEV[skt->nr].keep_vs = (csr & 0x00C0);
L
Linus Torvalds 已提交
113 114 115
	}
	else {
		/* card detect */
116 117 118 119 120
	        if ((machine_is_spitz() || machine_is_borzoi()) && skt->nr == 1) {
	                write_scoop_reg(scoop, SCOOP_CDR, 0x0000);
	        } else {
		        write_scoop_reg(scoop, SCOOP_CDR, 0x0002);
	        }
L
Linus Torvalds 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
	}

	state->detect = (csr & 0x0004) ? 0 : 1;
	state->ready  = (csr & 0x0002) ? 1 : 0;
	state->bvd1   = (csr & 0x0010) ? 1 : 0;
	state->bvd2   = (csr & 0x0020) ? 1 : 0;
	state->wrprot = (csr & 0x0008) ? 1 : 0;
	state->vs_3v  = (csr & 0x0040) ? 0 : 1;
	state->vs_Xv  = (csr & 0x0080) ? 0 : 1;

	if ((cpr & 0x0080) && ((cpr & 0x8040) != 0x8040)) {
		printk(KERN_ERR "sharpsl_pcmcia_socket_state(): CPR=%04X, Low voltage!\n", cpr);
	}
}


static int sharpsl_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
				       const socket_state_t *state)
{
	unsigned long flags;
141
	struct device *scoop = SCOOP_DEV[skt->nr].dev;
L
Linus Torvalds 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160

	unsigned short cpr, ncpr, ccr, nccr, mcr, nmcr, imr, nimr;

	switch (state->Vcc) {
	case	0:  	break;
	case 	33: 	break;
	case	50: 	break;
	default:
		 printk(KERN_ERR "sharpsl_pcmcia_configure_socket(): bad Vcc %u\n", state->Vcc);
		 return -1;
	}

	if ((state->Vpp!=state->Vcc) && (state->Vpp!=0)) {
		printk(KERN_ERR "CF slot cannot support Vpp %u\n", state->Vpp);
		return -1;
	}

	local_irq_save(flags);

161 162 163 164
	nmcr = (mcr = read_scoop_reg(scoop, SCOOP_MCR)) & ~0x0010;
	ncpr = (cpr = read_scoop_reg(scoop, SCOOP_CPR)) & ~0x0083;
	nccr = (ccr = read_scoop_reg(scoop, SCOOP_CCR)) & ~0x0080;
	nimr = (imr = read_scoop_reg(scoop, SCOOP_IMR)) & ~0x003E;
L
Linus Torvalds 已提交
165

166 167 168 169 170 171 172
	if ((machine_is_spitz() || machine_is_borzoi() || machine_is_akita()) && skt->nr == 0) {
	        ncpr |= (state->Vcc == 33) ? 0x0002 :
		        (state->Vcc == 50) ? 0x0002 : 0;
	} else {
	        ncpr |= (state->Vcc == 33) ? 0x0001 :
		        (state->Vcc == 50) ? 0x0002 : 0;
	}
L
Linus Torvalds 已提交
173 174 175 176 177 178 179 180 181 182 183
	nmcr |= (state->flags&SS_IOCARD) ? 0x0010 : 0;
	ncpr |= (state->flags&SS_OUTPUT_ENA) ? 0x0080 : 0;
	nccr |= (state->flags&SS_RESET)? 0x0080: 0;
	nimr |=	((skt->status&SS_DETECT) ? 0x0004 : 0)|
			((skt->status&SS_READY)  ? 0x0002 : 0)|
			((skt->status&SS_BATDEAD)? 0x0010 : 0)|
			((skt->status&SS_BATWARN)? 0x0020 : 0)|
			((skt->status&SS_STSCHG) ? 0x0010 : 0)|
			((skt->status&SS_WRPROT) ? 0x0008 : 0);

	if (!(ncpr & 0x0003)) {
184 185
		SCOOP_DEV[skt->nr].keep_rd = 0;
	} else if (!SCOOP_DEV[skt->nr].keep_rd) {
L
Linus Torvalds 已提交
186
		if (nccr & 0x0080)
187
			SCOOP_DEV[skt->nr].keep_rd = 1;
L
Linus Torvalds 已提交
188 189 190 191 192
		else
			nccr |= 0x0080;
	}

	if (mcr != nmcr)
193
		write_scoop_reg(scoop, SCOOP_MCR, nmcr);
194 195 196 197 198 199
	if (cpr != ncpr) {
		if (platform_scoop_config->power_ctrl)
			platform_scoop_config->power_ctrl(scoop, ncpr , skt->nr);
		else
		        write_scoop_reg(scoop, SCOOP_CPR, ncpr);
	}
L
Linus Torvalds 已提交
200
	if (ccr != nccr)
201
		write_scoop_reg(scoop, SCOOP_CCR, nccr);
L
Linus Torvalds 已提交
202
	if (imr != nimr)
203
		write_scoop_reg(scoop, SCOOP_IMR, nimr);
L
Linus Torvalds 已提交
204 205 206 207 208 209 210 211

	local_irq_restore(flags);

	return 0;
}

static void sharpsl_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
{
212
	sharpsl_pcmcia_init_reset(skt);
213 214

	/* Enable interrupt */
215 216 217
	write_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_IMR, 0x00C0);
	write_scoop_reg(SCOOP_DEV[skt->nr].dev, SCOOP_MCR, 0x0101);
	SCOOP_DEV[skt->nr].keep_vs = NO_KEEP_VS;
L
Linus Torvalds 已提交
218 219 220 221
}

static void sharpsl_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
222
	sharpsl_pcmcia_init_reset(skt);
L
Linus Torvalds 已提交
223 224 225
}

static struct pcmcia_low_level sharpsl_pcmcia_ops = {
226 227 228 229 230 231 232 233 234
	.owner                  = THIS_MODULE,
	.hw_init                = sharpsl_pcmcia_hw_init,
	.hw_shutdown            = sharpsl_pcmcia_hw_shutdown,
	.socket_state           = sharpsl_pcmcia_socket_state,
	.configure_socket       = sharpsl_pcmcia_configure_socket,
	.socket_init            = sharpsl_pcmcia_socket_init,
	.socket_suspend         = sharpsl_pcmcia_socket_suspend,
	.first                  = 0,
	.nr                     = 0,
L
Linus Torvalds 已提交
235 236
};

237
#ifdef CONFIG_SA1100_COLLIE
238 239
#include "sa11xx_base.h"

240 241 242 243 244 245 246 247 248 249 250 251
int __init pcmcia_collie_init(struct device *dev)
{
       int ret = -ENODEV;

       if (machine_is_collie())
               ret = sa11xx_drv_pcmcia_probe(dev, &sharpsl_pcmcia_ops, 0, 1);

       return ret;
}

#else

252 253
static struct platform_device *sharpsl_pcmcia_device;

L
Linus Torvalds 已提交
254 255 256 257
static int __init sharpsl_pcmcia_init(void)
{
	int ret;

258 259 260
	sharpsl_pcmcia_ops.nr = platform_scoop_config->num_devs;
	sharpsl_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1);

L
Linus Torvalds 已提交
261 262
	if (!sharpsl_pcmcia_device)
		return -ENOMEM;
263

L
Linus Torvalds 已提交
264
	sharpsl_pcmcia_device->dev.platform_data = &sharpsl_pcmcia_ops;
265 266 267
	sharpsl_pcmcia_device->dev.parent = platform_scoop_config->devs[0].dev;

	ret = platform_device_add(sharpsl_pcmcia_device);
L
Linus Torvalds 已提交
268 269

	if (ret)
270
		platform_device_put(sharpsl_pcmcia_device);
L
Linus Torvalds 已提交
271 272 273 274 275 276 277 278 279

	return ret;
}

static void __exit sharpsl_pcmcia_exit(void)
{
	platform_device_unregister(sharpsl_pcmcia_device);
}

280
fs_initcall(sharpsl_pcmcia_init);
L
Linus Torvalds 已提交
281
module_exit(sharpsl_pcmcia_exit);
282
#endif
L
Linus Torvalds 已提交
283 284 285

MODULE_DESCRIPTION("Sharp SL Series PCMCIA Support");
MODULE_LICENSE("GPL");
286
MODULE_ALIAS("platform:pxa2xx-pcmcia");