162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * pcmciamtd.c - MTD driver for PCMCIA flash memory cards
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Author: Simon Evans <spse@secret.org.uk>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2002 Simon Evans
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Licence: GPL
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/timer.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <asm/io.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <pcmcia/cistpl.h>
1962306a36Sopenharmony_ci#include <pcmcia/ds.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/mtd/map.h>
2262306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define info(format, arg...) printk(KERN_INFO "pcmciamtd: " format "\n" , ## arg)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define DRIVER_DESC	"PCMCIA Flash memory card driver"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* Size of the PCMCIA address space: 26 bits = 64 MB */
2962306a36Sopenharmony_ci#define MAX_PCMCIA_ADDR	0x4000000
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct pcmciamtd_dev {
3262306a36Sopenharmony_ci	struct pcmcia_device	*p_dev;
3362306a36Sopenharmony_ci	void __iomem	*win_base;	/* ioremapped address of PCMCIA window */
3462306a36Sopenharmony_ci	unsigned int	win_size;	/* size of window */
3562306a36Sopenharmony_ci	unsigned int	offset;		/* offset into card the window currently points at */
3662306a36Sopenharmony_ci	struct map_info	pcmcia_map;
3762306a36Sopenharmony_ci	struct mtd_info	*mtd_info;
3862306a36Sopenharmony_ci	int		vpp;
3962306a36Sopenharmony_ci	char		mtd_name[sizeof(struct cistpl_vers_1_t)];
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* Module parameters */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/* 2 = do 16-bit transfers, 1 = do 8-bit transfers */
4662306a36Sopenharmony_cistatic int bankwidth = 2;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Speed of memory accesses, in ns */
4962306a36Sopenharmony_cistatic int mem_speed;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* Force the size of an SRAM card */
5262306a36Sopenharmony_cistatic int force_size;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* Force Vpp */
5562306a36Sopenharmony_cistatic int vpp;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* Set Vpp */
5862306a36Sopenharmony_cistatic int setvpp;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* Force card to be treated as FLASH, ROM or RAM */
6162306a36Sopenharmony_cistatic int mem_type;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
6462306a36Sopenharmony_ciMODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
6562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
6662306a36Sopenharmony_cimodule_param(bankwidth, int, 0);
6762306a36Sopenharmony_ciMODULE_PARM_DESC(bankwidth, "Set bankwidth (1=8 bit, 2=16 bit, default=2)");
6862306a36Sopenharmony_cimodule_param(mem_speed, int, 0);
6962306a36Sopenharmony_ciMODULE_PARM_DESC(mem_speed, "Set memory access speed in ns");
7062306a36Sopenharmony_cimodule_param(force_size, int, 0);
7162306a36Sopenharmony_ciMODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)");
7262306a36Sopenharmony_cimodule_param(setvpp, int, 0);
7362306a36Sopenharmony_ciMODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)");
7462306a36Sopenharmony_cimodule_param(vpp, int, 0);
7562306a36Sopenharmony_ciMODULE_PARM_DESC(vpp, "Vpp value in 1/10ths eg 33=3.3V 120=12V (Dangerous)");
7662306a36Sopenharmony_cimodule_param(mem_type, int, 0);
7762306a36Sopenharmony_ciMODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)");
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* read/write{8,16} copy_{from,to} routines with window remapping
8162306a36Sopenharmony_ci * to access whole card
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_cistatic void __iomem *remap_window(struct map_info *map, unsigned long to)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
8662306a36Sopenharmony_ci	struct resource *win = (struct resource *) map->map_priv_2;
8762306a36Sopenharmony_ci	unsigned int offset;
8862306a36Sopenharmony_ci	int ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (!pcmcia_dev_present(dev->p_dev)) {
9162306a36Sopenharmony_ci		pr_debug("device removed\n");
9262306a36Sopenharmony_ci		return NULL;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	offset = to & ~(dev->win_size-1);
9662306a36Sopenharmony_ci	if (offset != dev->offset) {
9762306a36Sopenharmony_ci		pr_debug("Remapping window from 0x%8.8x to 0x%8.8x\n",
9862306a36Sopenharmony_ci		      dev->offset, offset);
9962306a36Sopenharmony_ci		ret = pcmcia_map_mem_page(dev->p_dev, win, offset);
10062306a36Sopenharmony_ci		if (ret != 0)
10162306a36Sopenharmony_ci			return NULL;
10262306a36Sopenharmony_ci		dev->offset = offset;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	return dev->win_base + (to & (dev->win_size-1));
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	void __iomem *addr;
11162306a36Sopenharmony_ci	map_word d = {{0}};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	addr = remap_window(map, ofs);
11462306a36Sopenharmony_ci	if(!addr)
11562306a36Sopenharmony_ci		return d;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	d.x[0] = readb(addr);
11862306a36Sopenharmony_ci	pr_debug("ofs = 0x%08lx (%p) data = 0x%02lx\n", ofs, addr, d.x[0]);
11962306a36Sopenharmony_ci	return d;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	void __iomem *addr;
12662306a36Sopenharmony_ci	map_word d = {{0}};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	addr = remap_window(map, ofs);
12962306a36Sopenharmony_ci	if(!addr)
13062306a36Sopenharmony_ci		return d;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	d.x[0] = readw(addr);
13362306a36Sopenharmony_ci	pr_debug("ofs = 0x%08lx (%p) data = 0x%04lx\n", ofs, addr, d.x[0]);
13462306a36Sopenharmony_ci	return d;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long from, ssize_t len)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
14162306a36Sopenharmony_ci	unsigned long win_size = dev->win_size;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	pr_debug("to = %p from = %lu len = %zd\n", to, from, len);
14462306a36Sopenharmony_ci	while(len) {
14562306a36Sopenharmony_ci		int toread = win_size - (from & (win_size-1));
14662306a36Sopenharmony_ci		void __iomem *addr;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci		if(toread > len)
14962306a36Sopenharmony_ci			toread = len;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		addr = remap_window(map, from);
15262306a36Sopenharmony_ci		if(!addr)
15362306a36Sopenharmony_ci			return;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		pr_debug("memcpy from %p to %p len = %d\n", addr, to, toread);
15662306a36Sopenharmony_ci		memcpy_fromio(to, addr, toread);
15762306a36Sopenharmony_ci		len -= toread;
15862306a36Sopenharmony_ci		to += toread;
15962306a36Sopenharmony_ci		from += toread;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long adr)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	void __iomem *addr = remap_window(map, adr);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if(!addr)
16962306a36Sopenharmony_ci		return;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	pr_debug("adr = 0x%08lx (%p)  data = 0x%02lx\n", adr, addr, d.x[0]);
17262306a36Sopenharmony_ci	writeb(d.x[0], addr);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_cistatic void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long adr)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	void __iomem *addr = remap_window(map, adr);
17962306a36Sopenharmony_ci	if(!addr)
18062306a36Sopenharmony_ci		return;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	pr_debug("adr = 0x%08lx (%p)  data = 0x%04lx\n", adr, addr, d.x[0]);
18362306a36Sopenharmony_ci	writew(d.x[0], addr);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const void *from, ssize_t len)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
19062306a36Sopenharmony_ci	unsigned long win_size = dev->win_size;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	pr_debug("to = %lu from = %p len = %zd\n", to, from, len);
19362306a36Sopenharmony_ci	while(len) {
19462306a36Sopenharmony_ci		int towrite = win_size - (to & (win_size-1));
19562306a36Sopenharmony_ci		void __iomem *addr;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		if(towrite > len)
19862306a36Sopenharmony_ci			towrite = len;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		addr = remap_window(map, to);
20162306a36Sopenharmony_ci		if(!addr)
20262306a36Sopenharmony_ci			return;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci		pr_debug("memcpy from %p to %p len = %d\n", from, addr, towrite);
20562306a36Sopenharmony_ci		memcpy_toio(addr, from, towrite);
20662306a36Sopenharmony_ci		len -= towrite;
20762306a36Sopenharmony_ci		to += towrite;
20862306a36Sopenharmony_ci		from += towrite;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/* read/write{8,16} copy_{from,to} routines with direct access */
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci#define DEV_REMOVED(x)  (!(pcmcia_dev_present(((struct pcmciamtd_dev *)map->map_priv_1)->p_dev)))
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic map_word pcmcia_read8(struct map_info *map, unsigned long ofs)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	void __iomem *win_base = (void __iomem *)map->map_priv_2;
22062306a36Sopenharmony_ci	map_word d = {{0}};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if(DEV_REMOVED(map))
22362306a36Sopenharmony_ci		return d;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	d.x[0] = readb(win_base + ofs);
22662306a36Sopenharmony_ci	pr_debug("ofs = 0x%08lx (%p) data = 0x%02lx\n",
22762306a36Sopenharmony_ci	      ofs, win_base + ofs, d.x[0]);
22862306a36Sopenharmony_ci	return d;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic map_word pcmcia_read16(struct map_info *map, unsigned long ofs)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	void __iomem *win_base = (void __iomem *)map->map_priv_2;
23562306a36Sopenharmony_ci	map_word d = {{0}};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if(DEV_REMOVED(map))
23862306a36Sopenharmony_ci		return d;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	d.x[0] = readw(win_base + ofs);
24162306a36Sopenharmony_ci	pr_debug("ofs = 0x%08lx (%p) data = 0x%04lx\n",
24262306a36Sopenharmony_ci	      ofs, win_base + ofs, d.x[0]);
24362306a36Sopenharmony_ci	return d;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	void __iomem *win_base = (void __iomem *)map->map_priv_2;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if(DEV_REMOVED(map))
25262306a36Sopenharmony_ci		return;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	pr_debug("to = %p from = %lu len = %zd\n", to, from, len);
25562306a36Sopenharmony_ci	memcpy_fromio(to, win_base + from, len);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	void __iomem *win_base = (void __iomem *)map->map_priv_2;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	if(DEV_REMOVED(map))
26462306a36Sopenharmony_ci		return;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	pr_debug("adr = 0x%08lx (%p)  data = 0x%02lx\n",
26762306a36Sopenharmony_ci	      adr, win_base + adr, d.x[0]);
26862306a36Sopenharmony_ci	writeb(d.x[0], win_base + adr);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	void __iomem *win_base = (void __iomem *)map->map_priv_2;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	if(DEV_REMOVED(map))
27762306a36Sopenharmony_ci		return;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	pr_debug("adr = 0x%08lx (%p)  data = 0x%04lx\n",
28062306a36Sopenharmony_ci	      adr, win_base + adr, d.x[0]);
28162306a36Sopenharmony_ci	writew(d.x[0], win_base + adr);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	void __iomem *win_base = (void __iomem *)map->map_priv_2;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if(DEV_REMOVED(map))
29062306a36Sopenharmony_ci		return;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	pr_debug("to = %lu from = %p len = %zd\n", to, from, len);
29362306a36Sopenharmony_ci	memcpy_toio(win_base + to, from, len);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic DEFINE_MUTEX(pcmcia_vpp_lock);
29862306a36Sopenharmony_cistatic int pcmcia_vpp_refcnt;
29962306a36Sopenharmony_cistatic void pcmciamtd_set_vpp(struct map_info *map, int on)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
30262306a36Sopenharmony_ci	struct pcmcia_device *link = dev->p_dev;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	pr_debug("dev = %p on = %d vpp = %d\n\n", dev, on, dev->vpp);
30562306a36Sopenharmony_ci	mutex_lock(&pcmcia_vpp_lock);
30662306a36Sopenharmony_ci	if (on) {
30762306a36Sopenharmony_ci		if (++pcmcia_vpp_refcnt == 1)   /* first nested 'on' */
30862306a36Sopenharmony_ci			pcmcia_fixup_vpp(link, dev->vpp);
30962306a36Sopenharmony_ci	} else {
31062306a36Sopenharmony_ci		if (--pcmcia_vpp_refcnt == 0)   /* last nested 'off' */
31162306a36Sopenharmony_ci			pcmcia_fixup_vpp(link, 0);
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci	mutex_unlock(&pcmcia_vpp_lock);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic void pcmciamtd_release(struct pcmcia_device *link)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = link->priv;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	pr_debug("link = 0x%p\n", link);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (link->resource[2]->end) {
32462306a36Sopenharmony_ci		if(dev->win_base) {
32562306a36Sopenharmony_ci			iounmap(dev->win_base);
32662306a36Sopenharmony_ci			dev->win_base = NULL;
32762306a36Sopenharmony_ci		}
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	pcmcia_disable_device(link);
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic int pcmciamtd_cistpl_format(struct pcmcia_device *p_dev,
33462306a36Sopenharmony_ci				tuple_t *tuple,
33562306a36Sopenharmony_ci				void *priv_data)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	cisparse_t parse;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (!pcmcia_parse_tuple(tuple, &parse)) {
34062306a36Sopenharmony_ci		cistpl_format_t *t = &parse.format;
34162306a36Sopenharmony_ci		(void)t; /* Shut up, gcc */
34262306a36Sopenharmony_ci		pr_debug("Format type: %u, Error Detection: %u, offset = %u, length =%u\n",
34362306a36Sopenharmony_ci			t->type, t->edc, t->offset, t->length);
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci	return -ENOSPC;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev,
34962306a36Sopenharmony_ci				tuple_t *tuple,
35062306a36Sopenharmony_ci				void *priv_data)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	cisparse_t parse;
35362306a36Sopenharmony_ci	int i;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!pcmcia_parse_tuple(tuple, &parse)) {
35662306a36Sopenharmony_ci		cistpl_jedec_t *t = &parse.jedec;
35762306a36Sopenharmony_ci		for (i = 0; i < t->nid; i++)
35862306a36Sopenharmony_ci			pr_debug("JEDEC: 0x%02x 0x%02x\n",
35962306a36Sopenharmony_ci			      t->id[i].mfr, t->id[i].info);
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci	return -ENOSPC;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic int pcmciamtd_cistpl_device(struct pcmcia_device *p_dev,
36562306a36Sopenharmony_ci				tuple_t *tuple,
36662306a36Sopenharmony_ci				void *priv_data)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = priv_data;
36962306a36Sopenharmony_ci	cisparse_t parse;
37062306a36Sopenharmony_ci	cistpl_device_t *t = &parse.device;
37162306a36Sopenharmony_ci	int i;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (pcmcia_parse_tuple(tuple, &parse))
37462306a36Sopenharmony_ci		return -EINVAL;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	pr_debug("Common memory:\n");
37762306a36Sopenharmony_ci	dev->pcmcia_map.size = t->dev[0].size;
37862306a36Sopenharmony_ci	/* from here on: DEBUG only */
37962306a36Sopenharmony_ci	for (i = 0; i < t->ndev; i++) {
38062306a36Sopenharmony_ci		pr_debug("Region %d, type = %u\n", i, t->dev[i].type);
38162306a36Sopenharmony_ci		pr_debug("Region %d, wp = %u\n", i, t->dev[i].wp);
38262306a36Sopenharmony_ci		pr_debug("Region %d, speed = %u ns\n", i, t->dev[i].speed);
38362306a36Sopenharmony_ci		pr_debug("Region %d, size = %u bytes\n", i, t->dev[i].size);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	return 0;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev,
38962306a36Sopenharmony_ci				tuple_t *tuple,
39062306a36Sopenharmony_ci				void *priv_data)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = priv_data;
39362306a36Sopenharmony_ci	cisparse_t parse;
39462306a36Sopenharmony_ci	cistpl_device_geo_t *t = &parse.device_geo;
39562306a36Sopenharmony_ci	int i;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (pcmcia_parse_tuple(tuple, &parse))
39862306a36Sopenharmony_ci		return -EINVAL;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	dev->pcmcia_map.bankwidth = t->geo[0].buswidth;
40162306a36Sopenharmony_ci	/* from here on: DEBUG only */
40262306a36Sopenharmony_ci	for (i = 0; i < t->ngeo; i++) {
40362306a36Sopenharmony_ci		pr_debug("region: %d bankwidth = %u\n", i, t->geo[i].buswidth);
40462306a36Sopenharmony_ci		pr_debug("region: %d erase_block = %u\n", i, t->geo[i].erase_block);
40562306a36Sopenharmony_ci		pr_debug("region: %d read_block = %u\n", i, t->geo[i].read_block);
40662306a36Sopenharmony_ci		pr_debug("region: %d write_block = %u\n", i, t->geo[i].write_block);
40762306a36Sopenharmony_ci		pr_debug("region: %d partition = %u\n", i, t->geo[i].partition);
40862306a36Sopenharmony_ci		pr_debug("region: %d interleave = %u\n", i, t->geo[i].interleave);
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev, int *new_name)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	int i;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (p_dev->prod_id[0]) {
41962306a36Sopenharmony_ci		dev->mtd_name[0] = '\0';
42062306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
42162306a36Sopenharmony_ci			if (i)
42262306a36Sopenharmony_ci				strcat(dev->mtd_name, " ");
42362306a36Sopenharmony_ci			if (p_dev->prod_id[i])
42462306a36Sopenharmony_ci				strcat(dev->mtd_name, p_dev->prod_id[i]);
42562306a36Sopenharmony_ci		}
42662306a36Sopenharmony_ci		pr_debug("Found name: %s\n", dev->mtd_name);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	pcmcia_loop_tuple(p_dev, CISTPL_FORMAT, pcmciamtd_cistpl_format, NULL);
43062306a36Sopenharmony_ci	pcmcia_loop_tuple(p_dev, CISTPL_JEDEC_C, pcmciamtd_cistpl_jedec, NULL);
43162306a36Sopenharmony_ci	pcmcia_loop_tuple(p_dev, CISTPL_DEVICE, pcmciamtd_cistpl_device, dev);
43262306a36Sopenharmony_ci	pcmcia_loop_tuple(p_dev, CISTPL_DEVICE_GEO, pcmciamtd_cistpl_geo, dev);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if(!dev->pcmcia_map.size)
43562306a36Sopenharmony_ci		dev->pcmcia_map.size = MAX_PCMCIA_ADDR;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if(!dev->pcmcia_map.bankwidth)
43862306a36Sopenharmony_ci		dev->pcmcia_map.bankwidth = 2;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if(force_size) {
44162306a36Sopenharmony_ci		dev->pcmcia_map.size = force_size << 20;
44262306a36Sopenharmony_ci		pr_debug("size forced to %dM\n", force_size);
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if(bankwidth) {
44662306a36Sopenharmony_ci		dev->pcmcia_map.bankwidth = bankwidth;
44762306a36Sopenharmony_ci		pr_debug("bankwidth forced to %d\n", bankwidth);
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	dev->pcmcia_map.name = dev->mtd_name;
45162306a36Sopenharmony_ci	if(!dev->mtd_name[0]) {
45262306a36Sopenharmony_ci		strcpy(dev->mtd_name, "PCMCIA Memory card");
45362306a36Sopenharmony_ci		*new_name = 1;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	pr_debug("Device: Size: %lu Width:%d Name: %s\n",
45762306a36Sopenharmony_ci	      dev->pcmcia_map.size,
45862306a36Sopenharmony_ci	      dev->pcmcia_map.bankwidth << 3, dev->mtd_name);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int pcmciamtd_config(struct pcmcia_device *link)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = link->priv;
46562306a36Sopenharmony_ci	struct mtd_info *mtd = NULL;
46662306a36Sopenharmony_ci	int ret;
46762306a36Sopenharmony_ci	int i, j = 0;
46862306a36Sopenharmony_ci	static char *probes[] = { "jedec_probe", "cfi_probe" };
46962306a36Sopenharmony_ci	int new_name = 0;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	pr_debug("link=0x%p\n", link);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	card_settings(dev, link, &new_name);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	dev->pcmcia_map.phys = NO_XIP;
47662306a36Sopenharmony_ci	dev->pcmcia_map.copy_from = pcmcia_copy_from_remap;
47762306a36Sopenharmony_ci	dev->pcmcia_map.copy_to = pcmcia_copy_to_remap;
47862306a36Sopenharmony_ci	if (dev->pcmcia_map.bankwidth == 1) {
47962306a36Sopenharmony_ci		dev->pcmcia_map.read = pcmcia_read8_remap;
48062306a36Sopenharmony_ci		dev->pcmcia_map.write = pcmcia_write8_remap;
48162306a36Sopenharmony_ci	} else {
48262306a36Sopenharmony_ci		dev->pcmcia_map.read = pcmcia_read16_remap;
48362306a36Sopenharmony_ci		dev->pcmcia_map.write = pcmcia_write16_remap;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	if(setvpp == 1)
48662306a36Sopenharmony_ci		dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	/* Request a memory window for PCMCIA. Some architeures can map windows
48962306a36Sopenharmony_ci	 * up to the maximum that PCMCIA can support (64MiB) - this is ideal and
49062306a36Sopenharmony_ci	 * we aim for a window the size of the whole card - otherwise we try
49162306a36Sopenharmony_ci	 * smaller windows until we succeed
49262306a36Sopenharmony_ci	 */
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	link->resource[2]->flags |=  WIN_MEMORY_TYPE_CM | WIN_ENABLE;
49562306a36Sopenharmony_ci	link->resource[2]->flags |= (dev->pcmcia_map.bankwidth == 1) ?
49662306a36Sopenharmony_ci					WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16;
49762306a36Sopenharmony_ci	link->resource[2]->start = 0;
49862306a36Sopenharmony_ci	link->resource[2]->end = (force_size) ? force_size << 20 :
49962306a36Sopenharmony_ci					MAX_PCMCIA_ADDR;
50062306a36Sopenharmony_ci	dev->win_size = 0;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	do {
50362306a36Sopenharmony_ci		int ret;
50462306a36Sopenharmony_ci		pr_debug("requesting window with size = %luKiB memspeed = %d\n",
50562306a36Sopenharmony_ci			(unsigned long) resource_size(link->resource[2]) >> 10,
50662306a36Sopenharmony_ci			mem_speed);
50762306a36Sopenharmony_ci		ret = pcmcia_request_window(link, link->resource[2], mem_speed);
50862306a36Sopenharmony_ci		pr_debug("ret = %d dev->win_size = %d\n", ret, dev->win_size);
50962306a36Sopenharmony_ci		if(ret) {
51062306a36Sopenharmony_ci			j++;
51162306a36Sopenharmony_ci			link->resource[2]->start = 0;
51262306a36Sopenharmony_ci			link->resource[2]->end = (force_size) ?
51362306a36Sopenharmony_ci					force_size << 20 : MAX_PCMCIA_ADDR;
51462306a36Sopenharmony_ci			link->resource[2]->end >>= j;
51562306a36Sopenharmony_ci		} else {
51662306a36Sopenharmony_ci			pr_debug("Got window of size %luKiB\n", (unsigned long)
51762306a36Sopenharmony_ci				resource_size(link->resource[2]) >> 10);
51862306a36Sopenharmony_ci			dev->win_size = resource_size(link->resource[2]);
51962306a36Sopenharmony_ci			break;
52062306a36Sopenharmony_ci		}
52162306a36Sopenharmony_ci	} while (link->resource[2]->end >= 0x1000);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	pr_debug("dev->win_size = %d\n", dev->win_size);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if(!dev->win_size) {
52662306a36Sopenharmony_ci		dev_err(&dev->p_dev->dev, "Cannot allocate memory window\n");
52762306a36Sopenharmony_ci		pcmciamtd_release(link);
52862306a36Sopenharmony_ci		return -ENODEV;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci	pr_debug("Allocated a window of %dKiB\n", dev->win_size >> 10);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Get write protect status */
53362306a36Sopenharmony_ci	dev->win_base = ioremap(link->resource[2]->start,
53462306a36Sopenharmony_ci				resource_size(link->resource[2]));
53562306a36Sopenharmony_ci	if(!dev->win_base) {
53662306a36Sopenharmony_ci		dev_err(&dev->p_dev->dev, "ioremap(%pR) failed\n",
53762306a36Sopenharmony_ci			link->resource[2]);
53862306a36Sopenharmony_ci		pcmciamtd_release(link);
53962306a36Sopenharmony_ci		return -ENODEV;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci	pr_debug("mapped window dev = %p @ %pR, base = %p\n",
54262306a36Sopenharmony_ci	      dev, link->resource[2], dev->win_base);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	dev->offset = 0;
54562306a36Sopenharmony_ci	dev->pcmcia_map.map_priv_1 = (unsigned long)dev;
54662306a36Sopenharmony_ci	dev->pcmcia_map.map_priv_2 = (unsigned long)link->resource[2];
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	dev->vpp = (vpp) ? vpp : link->socket->socket.Vpp;
54962306a36Sopenharmony_ci	if(setvpp == 2) {
55062306a36Sopenharmony_ci		link->vpp = dev->vpp;
55162306a36Sopenharmony_ci	} else {
55262306a36Sopenharmony_ci		link->vpp = 0;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	link->config_index = 0;
55662306a36Sopenharmony_ci	pr_debug("Setting Configuration\n");
55762306a36Sopenharmony_ci	ret = pcmcia_enable_device(link);
55862306a36Sopenharmony_ci	if (ret != 0) {
55962306a36Sopenharmony_ci		if (dev->win_base) {
56062306a36Sopenharmony_ci			iounmap(dev->win_base);
56162306a36Sopenharmony_ci			dev->win_base = NULL;
56262306a36Sopenharmony_ci		}
56362306a36Sopenharmony_ci		return -ENODEV;
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if(mem_type == 1) {
56762306a36Sopenharmony_ci		mtd = do_map_probe("map_ram", &dev->pcmcia_map);
56862306a36Sopenharmony_ci	} else if(mem_type == 2) {
56962306a36Sopenharmony_ci		mtd = do_map_probe("map_rom", &dev->pcmcia_map);
57062306a36Sopenharmony_ci	} else {
57162306a36Sopenharmony_ci		for(i = 0; i < ARRAY_SIZE(probes); i++) {
57262306a36Sopenharmony_ci			pr_debug("Trying %s\n", probes[i]);
57362306a36Sopenharmony_ci			mtd = do_map_probe(probes[i], &dev->pcmcia_map);
57462306a36Sopenharmony_ci			if(mtd)
57562306a36Sopenharmony_ci				break;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci			pr_debug("FAILED: %s\n", probes[i]);
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if(!mtd) {
58262306a36Sopenharmony_ci		pr_debug("Can not find an MTD\n");
58362306a36Sopenharmony_ci		pcmciamtd_release(link);
58462306a36Sopenharmony_ci		return -ENODEV;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	dev->mtd_info = mtd;
58862306a36Sopenharmony_ci	mtd->owner = THIS_MODULE;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if(new_name) {
59162306a36Sopenharmony_ci		int size = 0;
59262306a36Sopenharmony_ci		char unit = ' ';
59362306a36Sopenharmony_ci		/* Since we are using a default name, make it better by adding
59462306a36Sopenharmony_ci		 * in the size
59562306a36Sopenharmony_ci		 */
59662306a36Sopenharmony_ci		if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */
59762306a36Sopenharmony_ci			size = mtd->size >> 10;
59862306a36Sopenharmony_ci			unit = 'K';
59962306a36Sopenharmony_ci		} else {
60062306a36Sopenharmony_ci			size = mtd->size >> 20;
60162306a36Sopenharmony_ci			unit = 'M';
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci		snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %s", size, unit, "PCMCIA Memory card");
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* If the memory found is fits completely into the mapped PCMCIA window,
60762306a36Sopenharmony_ci	   use the faster non-remapping read/write functions */
60862306a36Sopenharmony_ci	if(mtd->size <= dev->win_size) {
60962306a36Sopenharmony_ci		pr_debug("Using non remapping memory functions\n");
61062306a36Sopenharmony_ci		dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base;
61162306a36Sopenharmony_ci		if (dev->pcmcia_map.bankwidth == 1) {
61262306a36Sopenharmony_ci			dev->pcmcia_map.read = pcmcia_read8;
61362306a36Sopenharmony_ci			dev->pcmcia_map.write = pcmcia_write8;
61462306a36Sopenharmony_ci		} else {
61562306a36Sopenharmony_ci			dev->pcmcia_map.read = pcmcia_read16;
61662306a36Sopenharmony_ci			dev->pcmcia_map.write = pcmcia_write16;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci		dev->pcmcia_map.copy_from = pcmcia_copy_from;
61962306a36Sopenharmony_ci		dev->pcmcia_map.copy_to = pcmcia_copy_to;
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (mtd_device_register(mtd, NULL, 0)) {
62362306a36Sopenharmony_ci		map_destroy(mtd);
62462306a36Sopenharmony_ci		dev->mtd_info = NULL;
62562306a36Sopenharmony_ci		dev_err(&dev->p_dev->dev,
62662306a36Sopenharmony_ci			"Could not register the MTD device\n");
62762306a36Sopenharmony_ci		pcmciamtd_release(link);
62862306a36Sopenharmony_ci		return -ENODEV;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci	dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name);
63162306a36Sopenharmony_ci	return 0;
63262306a36Sopenharmony_ci}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int pcmciamtd_suspend(struct pcmcia_device *dev)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	pr_debug("EVENT_PM_RESUME\n");
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	/* get_lock(link); */
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return 0;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int pcmciamtd_resume(struct pcmcia_device *dev)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	pr_debug("EVENT_PM_SUSPEND\n");
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	/* free_lock(link); */
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	return 0;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic void pcmciamtd_detach(struct pcmcia_device *link)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct pcmciamtd_dev *dev = link->priv;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	pr_debug("link=0x%p\n", link);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	if(dev->mtd_info) {
66162306a36Sopenharmony_ci		mtd_device_unregister(dev->mtd_info);
66262306a36Sopenharmony_ci		dev_info(&dev->p_dev->dev, "mtd%d: Removing\n",
66362306a36Sopenharmony_ci			 dev->mtd_info->index);
66462306a36Sopenharmony_ci		map_destroy(dev->mtd_info);
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	pcmciamtd_release(link);
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int pcmciamtd_probe(struct pcmcia_device *link)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	struct pcmciamtd_dev *dev;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* Create new memory card device */
67662306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
67762306a36Sopenharmony_ci	if (!dev) return -ENOMEM;
67862306a36Sopenharmony_ci	pr_debug("dev=0x%p\n", dev);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	dev->p_dev = link;
68162306a36Sopenharmony_ci	link->priv = dev;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	return pcmciamtd_config(link);
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic const struct pcmcia_device_id pcmciamtd_ids[] = {
68762306a36Sopenharmony_ci	PCMCIA_DEVICE_FUNC_ID(1),
68862306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCS-2M", "2MB SRAM", 0x547e66dc, 0x1fed36cd, 0x36eadd21),
68962306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("IBM", "2MB SRAM", 0xb569a6e5, 0x36eadd21),
69062306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("IBM", "4MB FLASH", 0xb569a6e5, 0x8bc54d2a),
69162306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("IBM", "8MB FLASH", 0xb569a6e5, 0x6df1be3e),
69262306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Intel", "S2E20SW", 0x816cc815, 0xd14c9dcf),
69362306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Intel", "S2E8 SW", 0x816cc815, 0xa2d7dedb),
69462306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-02 ", 0x40ade711, 0x145cea5c),
69562306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-04 ", 0x40ade711, 0x42064dda),
69662306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-20 ", 0x40ade711, 0x25ee5cb0),
69762306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("intel", "VALUE SERIES 100 ", 0x40ade711, 0xdf8506d8),
69862306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("KINGMAX TECHNOLOGY INC.", "SRAM 256K Bytes", 0x54d0c69c, 0xad12c29c),
69962306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0),
70062306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965),
70162306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("PRETEC", "  2MB SRAM CARD", 0xebf91155, 0x805360ca),
70262306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("PRETEC", "  4MB SRAM CARD", 0xebf91155, 0x20b6bf17),
70362306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b),
70462306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad),
70562306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05),
70662306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-3000", 0x05ddca47, 0xe7d67bca),
70762306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-4100", 0x05ddca47, 0x7bc32944),
70862306a36Sopenharmony_ci	/* the following was commented out in pcmcia-cs-3.2.7 */
70962306a36Sopenharmony_ci	/* PCMCIA_DEVICE_PROD_ID12("RATOC Systems,Inc.", "SmartMedia ADAPTER PC Card", 0xf4a2fefe, 0x5885b2ae), */
71062306a36Sopenharmony_ci#ifdef CONFIG_MTD_PCMCIA_ANONYMOUS
71162306a36Sopenharmony_ci	{ .match_flags = PCMCIA_DEV_ID_MATCH_ANONYMOUS, },
71262306a36Sopenharmony_ci#endif
71362306a36Sopenharmony_ci	PCMCIA_DEVICE_NULL
71462306a36Sopenharmony_ci};
71562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pcmcia, pcmciamtd_ids);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic struct pcmcia_driver pcmciamtd_driver = {
71862306a36Sopenharmony_ci	.name		= "pcmciamtd",
71962306a36Sopenharmony_ci	.probe		= pcmciamtd_probe,
72062306a36Sopenharmony_ci	.remove		= pcmciamtd_detach,
72162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
72262306a36Sopenharmony_ci	.id_table	= pcmciamtd_ids,
72362306a36Sopenharmony_ci	.suspend	= pcmciamtd_suspend,
72462306a36Sopenharmony_ci	.resume		= pcmciamtd_resume,
72562306a36Sopenharmony_ci};
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic int __init init_pcmciamtd(void)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	if(bankwidth && bankwidth != 1 && bankwidth != 2) {
73162306a36Sopenharmony_ci		info("bad bankwidth (%d), using default", bankwidth);
73262306a36Sopenharmony_ci		bankwidth = 2;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci	if(force_size && (force_size < 1 || force_size > 64)) {
73562306a36Sopenharmony_ci		info("bad force_size (%d), using default", force_size);
73662306a36Sopenharmony_ci		force_size = 0;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci	if(mem_type && mem_type != 1 && mem_type != 2) {
73962306a36Sopenharmony_ci		info("bad mem_type (%d), using default", mem_type);
74062306a36Sopenharmony_ci		mem_type = 0;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci	return pcmcia_register_driver(&pcmciamtd_driver);
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_cistatic void __exit exit_pcmciamtd(void)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	pr_debug(DRIVER_DESC " unloading");
74962306a36Sopenharmony_ci	pcmcia_unregister_driver(&pcmciamtd_driver);
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cimodule_init(init_pcmciamtd);
75362306a36Sopenharmony_cimodule_exit(exit_pcmciamtd);
754