18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCMCIA socket code for the Alchemy Db1xxx/Pb1xxx boards.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2009 Manuel Lauss <manuel.lauss@gmail.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/* This is a fairly generic PCMCIA socket driver suitable for the
108c2ecf20Sopenharmony_ci * following Alchemy Development boards:
118c2ecf20Sopenharmony_ci *  Db1000, Db/Pb1500, Db/Pb1100, Db/Pb1550, Db/Pb1200, Db1300
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * The Db1000 is used as a reference:  Per-socket card-, carddetect- and
148c2ecf20Sopenharmony_ci *  statuschange IRQs connected to SoC GPIOs, control and status register
158c2ecf20Sopenharmony_ci *  bits arranged in per-socket groups in an external PLD.  All boards
168c2ecf20Sopenharmony_ci *  listed here use this layout, including bit positions and meanings.
178c2ecf20Sopenharmony_ci *  Of course there are exceptions in later boards:
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *	- Pb1100/Pb1500:  single socket only; voltage key bits VS are
208c2ecf20Sopenharmony_ci *			  at STATUS[5:4] (instead of STATUS[1:0]).
218c2ecf20Sopenharmony_ci *	- Au1200-based:	  additional card-eject irqs, irqs not gpios!
228c2ecf20Sopenharmony_ci *	- Db1300:	  Db1200-like, no pwr ctrl, single socket (#1).
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/delay.h>
268c2ecf20Sopenharmony_ci#include <linux/gpio.h>
278c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
288c2ecf20Sopenharmony_ci#include <linux/pm.h>
298c2ecf20Sopenharmony_ci#include <linux/module.h>
308c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
318c2ecf20Sopenharmony_ci#include <linux/resource.h>
328c2ecf20Sopenharmony_ci#include <linux/slab.h>
338c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <pcmcia/ss.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include <asm/mach-au1x00/au1000.h>
388c2ecf20Sopenharmony_ci#include <asm/mach-db1x00/bcsr.h>
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MEM_MAP_SIZE	0x400000
418c2ecf20Sopenharmony_ci#define IO_MAP_SIZE	0x1000
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct db1x_pcmcia_sock {
448c2ecf20Sopenharmony_ci	struct pcmcia_socket	socket;
458c2ecf20Sopenharmony_ci	int		nr;		/* socket number */
468c2ecf20Sopenharmony_ci	void		*virt_io;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	phys_addr_t	phys_io;
498c2ecf20Sopenharmony_ci	phys_addr_t	phys_attr;
508c2ecf20Sopenharmony_ci	phys_addr_t	phys_mem;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* previous flags for set_socket() */
538c2ecf20Sopenharmony_ci	unsigned int old_flags;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	/* interrupt sources: linux irq numbers! */
568c2ecf20Sopenharmony_ci	int	insert_irq;	/* default carddetect irq */
578c2ecf20Sopenharmony_ci	int	stschg_irq;	/* card-status-change irq */
588c2ecf20Sopenharmony_ci	int	card_irq;	/* card irq */
598c2ecf20Sopenharmony_ci	int	eject_irq;	/* db1200/pb1200 have these */
608c2ecf20Sopenharmony_ci	int	insert_gpio;	/* db1000 carddetect gpio */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define BOARD_TYPE_DEFAULT	0	/* most boards */
638c2ecf20Sopenharmony_ci#define BOARD_TYPE_DB1200	1	/* IRQs aren't gpios */
648c2ecf20Sopenharmony_ci#define BOARD_TYPE_PB1100	2	/* VS bits slightly different */
658c2ecf20Sopenharmony_ci#define BOARD_TYPE_DB1300	3	/* no power control */
668c2ecf20Sopenharmony_ci	int	board_type;
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define to_db1x_socket(x) container_of(x, struct db1x_pcmcia_sock, socket)
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int db1300_card_inserted(struct db1x_pcmcia_sock *sock)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	return bcsr_read(BCSR_SIGSTAT) & (1 << 8);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/* DB/PB1200: check CPLD SIGSTATUS register bit 10/12 */
778c2ecf20Sopenharmony_cistatic int db1200_card_inserted(struct db1x_pcmcia_sock *sock)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	unsigned short sigstat;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	sigstat = bcsr_read(BCSR_SIGSTAT);
828c2ecf20Sopenharmony_ci	return sigstat & 1 << (8 + 2 * sock->nr);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* carddetect gpio: low-active */
868c2ecf20Sopenharmony_cistatic int db1000_card_inserted(struct db1x_pcmcia_sock *sock)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	return !gpio_get_value(sock->insert_gpio);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int db1x_card_inserted(struct db1x_pcmcia_sock *sock)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	switch (sock->board_type) {
948c2ecf20Sopenharmony_ci	case BOARD_TYPE_DB1200:
958c2ecf20Sopenharmony_ci		return db1200_card_inserted(sock);
968c2ecf20Sopenharmony_ci	case BOARD_TYPE_DB1300:
978c2ecf20Sopenharmony_ci		return db1300_card_inserted(sock);
988c2ecf20Sopenharmony_ci	default:
998c2ecf20Sopenharmony_ci		return db1000_card_inserted(sock);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/* STSCHG tends to bounce heavily when cards are inserted/ejected.
1048c2ecf20Sopenharmony_ci * To avoid this, the interrupt is normally disabled and only enabled
1058c2ecf20Sopenharmony_ci * after reset to a card has been de-asserted.
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_cistatic inline void set_stschg(struct db1x_pcmcia_sock *sock, int en)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	if (sock->stschg_irq != -1) {
1108c2ecf20Sopenharmony_ci		if (en)
1118c2ecf20Sopenharmony_ci			enable_irq(sock->stschg_irq);
1128c2ecf20Sopenharmony_ci		else
1138c2ecf20Sopenharmony_ci			disable_irq(sock->stschg_irq);
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic irqreturn_t db1000_pcmcia_cdirq(int irq, void *data)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock = data;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	pcmcia_parse_events(&sock->socket, SS_DETECT);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic irqreturn_t db1000_pcmcia_stschgirq(int irq, void *data)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock = data;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	pcmcia_parse_events(&sock->socket, SS_STSCHG);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci/* Db/Pb1200 have separate per-socket insertion and ejection
1368c2ecf20Sopenharmony_ci * interrupts which stay asserted as long as the card is
1378c2ecf20Sopenharmony_ci * inserted/missing.  The one which caused us to be called
1388c2ecf20Sopenharmony_ci * needs to be disabled and the other one enabled.
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_cistatic irqreturn_t db1200_pcmcia_cdirq(int irq, void *data)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	disable_irq_nosync(irq);
1438c2ecf20Sopenharmony_ci	return IRQ_WAKE_THREAD;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic irqreturn_t db1200_pcmcia_cdirq_fn(int irq, void *data)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock = data;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* Wait a bit for the signals to stop bouncing. */
1518c2ecf20Sopenharmony_ci	msleep(100);
1528c2ecf20Sopenharmony_ci	if (irq == sock->insert_irq)
1538c2ecf20Sopenharmony_ci		enable_irq(sock->eject_irq);
1548c2ecf20Sopenharmony_ci	else
1558c2ecf20Sopenharmony_ci		enable_irq(sock->insert_irq);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	pcmcia_parse_events(&sock->socket, SS_DETECT);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int db1x_pcmcia_setup_irqs(struct db1x_pcmcia_sock *sock)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int ret;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (sock->stschg_irq != -1) {
1678c2ecf20Sopenharmony_ci		ret = request_irq(sock->stschg_irq, db1000_pcmcia_stschgirq,
1688c2ecf20Sopenharmony_ci				  0, "pcmcia_stschg", sock);
1698c2ecf20Sopenharmony_ci		if (ret)
1708c2ecf20Sopenharmony_ci			return ret;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/* Db/Pb1200 have separate per-socket insertion and ejection
1748c2ecf20Sopenharmony_ci	 * interrupts, which should show edge behaviour but don't.
1758c2ecf20Sopenharmony_ci	 * So interrupts are disabled until both insertion and
1768c2ecf20Sopenharmony_ci	 * ejection handler have been registered and the currently
1778c2ecf20Sopenharmony_ci	 * active one disabled.
1788c2ecf20Sopenharmony_ci	 */
1798c2ecf20Sopenharmony_ci	if ((sock->board_type == BOARD_TYPE_DB1200) ||
1808c2ecf20Sopenharmony_ci	    (sock->board_type == BOARD_TYPE_DB1300)) {
1818c2ecf20Sopenharmony_ci		ret = request_threaded_irq(sock->insert_irq, db1200_pcmcia_cdirq,
1828c2ecf20Sopenharmony_ci			db1200_pcmcia_cdirq_fn, 0, "pcmcia_insert", sock);
1838c2ecf20Sopenharmony_ci		if (ret)
1848c2ecf20Sopenharmony_ci			goto out1;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		ret = request_threaded_irq(sock->eject_irq, db1200_pcmcia_cdirq,
1878c2ecf20Sopenharmony_ci			db1200_pcmcia_cdirq_fn, 0, "pcmcia_eject", sock);
1888c2ecf20Sopenharmony_ci		if (ret) {
1898c2ecf20Sopenharmony_ci			free_irq(sock->insert_irq, sock);
1908c2ecf20Sopenharmony_ci			goto out1;
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		/* enable the currently silent one */
1948c2ecf20Sopenharmony_ci		if (db1x_card_inserted(sock))
1958c2ecf20Sopenharmony_ci			enable_irq(sock->eject_irq);
1968c2ecf20Sopenharmony_ci		else
1978c2ecf20Sopenharmony_ci			enable_irq(sock->insert_irq);
1988c2ecf20Sopenharmony_ci	} else {
1998c2ecf20Sopenharmony_ci		/* all other (older) Db1x00 boards use a GPIO to show
2008c2ecf20Sopenharmony_ci		 * card detection status:  use both-edge triggers.
2018c2ecf20Sopenharmony_ci		 */
2028c2ecf20Sopenharmony_ci		irq_set_irq_type(sock->insert_irq, IRQ_TYPE_EDGE_BOTH);
2038c2ecf20Sopenharmony_ci		ret = request_irq(sock->insert_irq, db1000_pcmcia_cdirq,
2048c2ecf20Sopenharmony_ci				  0, "pcmcia_carddetect", sock);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		if (ret)
2078c2ecf20Sopenharmony_ci			goto out1;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return 0;	/* all done */
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ciout1:
2138c2ecf20Sopenharmony_ci	if (sock->stschg_irq != -1)
2148c2ecf20Sopenharmony_ci		free_irq(sock->stschg_irq, sock);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return ret;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic void db1x_pcmcia_free_irqs(struct db1x_pcmcia_sock *sock)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	if (sock->stschg_irq != -1)
2228c2ecf20Sopenharmony_ci		free_irq(sock->stschg_irq, sock);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	free_irq(sock->insert_irq, sock);
2258c2ecf20Sopenharmony_ci	if (sock->eject_irq != -1)
2268c2ecf20Sopenharmony_ci		free_irq(sock->eject_irq, sock);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci/*
2308c2ecf20Sopenharmony_ci * configure a PCMCIA socket on the Db1x00 series of boards (and
2318c2ecf20Sopenharmony_ci * compatibles).
2328c2ecf20Sopenharmony_ci *
2338c2ecf20Sopenharmony_ci * 2 external registers are involved:
2348c2ecf20Sopenharmony_ci *   pcmcia_status (offset 0x04): bits [0:1/2:3]: read card voltage id
2358c2ecf20Sopenharmony_ci *   pcmcia_control(offset 0x10):
2368c2ecf20Sopenharmony_ci *	bits[0:1] set vcc for card
2378c2ecf20Sopenharmony_ci *	bits[2:3] set vpp for card
2388c2ecf20Sopenharmony_ci *	bit 4:	enable data buffers
2398c2ecf20Sopenharmony_ci *	bit 7:	reset# for card
2408c2ecf20Sopenharmony_ci *	add 8 for second socket.
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_cistatic int db1x_pcmcia_configure(struct pcmcia_socket *skt,
2438c2ecf20Sopenharmony_ci				 struct socket_state_t *state)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
2468c2ecf20Sopenharmony_ci	unsigned short cr_clr, cr_set;
2478c2ecf20Sopenharmony_ci	unsigned int changed;
2488c2ecf20Sopenharmony_ci	int v, p, ret;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/* card voltage setup */
2518c2ecf20Sopenharmony_ci	cr_clr = (0xf << (sock->nr * 8)); /* clear voltage settings */
2528c2ecf20Sopenharmony_ci	cr_set = 0;
2538c2ecf20Sopenharmony_ci	v = p = ret = 0;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	switch (state->Vcc) {
2568c2ecf20Sopenharmony_ci	case 50:
2578c2ecf20Sopenharmony_ci		++v;
2588c2ecf20Sopenharmony_ci		fallthrough;
2598c2ecf20Sopenharmony_ci	case 33:
2608c2ecf20Sopenharmony_ci		++v;
2618c2ecf20Sopenharmony_ci		fallthrough;
2628c2ecf20Sopenharmony_ci	case 0:
2638c2ecf20Sopenharmony_ci		break;
2648c2ecf20Sopenharmony_ci	default:
2658c2ecf20Sopenharmony_ci		printk(KERN_INFO "pcmcia%d unsupported Vcc %d\n",
2668c2ecf20Sopenharmony_ci			sock->nr, state->Vcc);
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	switch (state->Vpp) {
2708c2ecf20Sopenharmony_ci	case 12:
2718c2ecf20Sopenharmony_ci		++p;
2728c2ecf20Sopenharmony_ci		fallthrough;
2738c2ecf20Sopenharmony_ci	case 33:
2748c2ecf20Sopenharmony_ci	case 50:
2758c2ecf20Sopenharmony_ci		++p;
2768c2ecf20Sopenharmony_ci		fallthrough;
2778c2ecf20Sopenharmony_ci	case 0:
2788c2ecf20Sopenharmony_ci		break;
2798c2ecf20Sopenharmony_ci	default:
2808c2ecf20Sopenharmony_ci		printk(KERN_INFO "pcmcia%d unsupported Vpp %d\n",
2818c2ecf20Sopenharmony_ci			sock->nr, state->Vpp);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* sanity check: Vpp must be 0, 12, or Vcc */
2858c2ecf20Sopenharmony_ci	if (((state->Vcc == 33) && (state->Vpp == 50)) ||
2868c2ecf20Sopenharmony_ci	    ((state->Vcc == 50) && (state->Vpp == 33))) {
2878c2ecf20Sopenharmony_ci		printk(KERN_INFO "pcmcia%d bad Vcc/Vpp combo (%d %d)\n",
2888c2ecf20Sopenharmony_ci			sock->nr, state->Vcc, state->Vpp);
2898c2ecf20Sopenharmony_ci		v = p = 0;
2908c2ecf20Sopenharmony_ci		ret = -EINVAL;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* create new voltage code */
2948c2ecf20Sopenharmony_ci	if (sock->board_type != BOARD_TYPE_DB1300)
2958c2ecf20Sopenharmony_ci		cr_set |= ((v << 2) | p) << (sock->nr * 8);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	changed = state->flags ^ sock->old_flags;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (changed & SS_RESET) {
3008c2ecf20Sopenharmony_ci		if (state->flags & SS_RESET) {
3018c2ecf20Sopenharmony_ci			set_stschg(sock, 0);
3028c2ecf20Sopenharmony_ci			/* assert reset, disable io buffers */
3038c2ecf20Sopenharmony_ci			cr_clr |= (1 << (7 + (sock->nr * 8)));
3048c2ecf20Sopenharmony_ci			cr_clr |= (1 << (4 + (sock->nr * 8)));
3058c2ecf20Sopenharmony_ci		} else {
3068c2ecf20Sopenharmony_ci			/* de-assert reset, enable io buffers */
3078c2ecf20Sopenharmony_ci			cr_set |= 1 << (7 + (sock->nr * 8));
3088c2ecf20Sopenharmony_ci			cr_set |= 1 << (4 + (sock->nr * 8));
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* update PCMCIA configuration */
3138c2ecf20Sopenharmony_ci	bcsr_mod(BCSR_PCMCIA, cr_clr, cr_set);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	sock->old_flags = state->flags;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	/* reset was taken away: give card time to initialize properly */
3188c2ecf20Sopenharmony_ci	if ((changed & SS_RESET) && !(state->flags & SS_RESET)) {
3198c2ecf20Sopenharmony_ci		msleep(500);
3208c2ecf20Sopenharmony_ci		set_stschg(sock, 1);
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return ret;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci/* VCC bits at [3:2]/[11:10] */
3278c2ecf20Sopenharmony_ci#define GET_VCC(cr, socknr)		\
3288c2ecf20Sopenharmony_ci	((((cr) >> 2) >> ((socknr) * 8)) & 3)
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci/* VS bits at [0:1]/[3:2] */
3318c2ecf20Sopenharmony_ci#define GET_VS(sr, socknr)		\
3328c2ecf20Sopenharmony_ci	(((sr) >> (2 * (socknr))) & 3)
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/* reset bits at [7]/[15] */
3358c2ecf20Sopenharmony_ci#define GET_RESET(cr, socknr)		\
3368c2ecf20Sopenharmony_ci	((cr) & (1 << (7 + (8 * (socknr)))))
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int db1x_pcmcia_get_status(struct pcmcia_socket *skt,
3398c2ecf20Sopenharmony_ci				  unsigned int *value)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
3428c2ecf20Sopenharmony_ci	unsigned short cr, sr;
3438c2ecf20Sopenharmony_ci	unsigned int status;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	status = db1x_card_inserted(sock) ? SS_DETECT : 0;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	cr = bcsr_read(BCSR_PCMCIA);
3488c2ecf20Sopenharmony_ci	sr = bcsr_read(BCSR_STATUS);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	/* PB1100/PB1500: voltage key bits are at [5:4] */
3518c2ecf20Sopenharmony_ci	if (sock->board_type == BOARD_TYPE_PB1100)
3528c2ecf20Sopenharmony_ci		sr >>= 4;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	/* determine card type */
3558c2ecf20Sopenharmony_ci	switch (GET_VS(sr, sock->nr)) {
3568c2ecf20Sopenharmony_ci	case 0:
3578c2ecf20Sopenharmony_ci	case 2:
3588c2ecf20Sopenharmony_ci		status |= SS_3VCARD;	/* 3V card */
3598c2ecf20Sopenharmony_ci	case 3:
3608c2ecf20Sopenharmony_ci		break;			/* 5V card: set nothing */
3618c2ecf20Sopenharmony_ci	default:
3628c2ecf20Sopenharmony_ci		status |= SS_XVCARD;	/* treated as unsupported in core */
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* if Vcc is not zero, we have applied power to a card */
3668c2ecf20Sopenharmony_ci	status |= GET_VCC(cr, sock->nr) ? SS_POWERON : 0;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	/* DB1300: power always on, but don't tell when no card present */
3698c2ecf20Sopenharmony_ci	if ((sock->board_type == BOARD_TYPE_DB1300) && (status & SS_DETECT))
3708c2ecf20Sopenharmony_ci		status = SS_POWERON | SS_3VCARD | SS_DETECT;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* reset de-asserted? then we're ready */
3738c2ecf20Sopenharmony_ci	status |= (GET_RESET(cr, sock->nr)) ? SS_READY : SS_RESET;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	*value = status;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return 0;
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int db1x_pcmcia_sock_init(struct pcmcia_socket *skt)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	return 0;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic int db1x_pcmcia_sock_suspend(struct pcmcia_socket *skt)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic int au1x00_pcmcia_set_io_map(struct pcmcia_socket *skt,
3918c2ecf20Sopenharmony_ci				    struct pccard_io_map *map)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	map->start = (u32)sock->virt_io;
3968c2ecf20Sopenharmony_ci	map->stop = map->start + IO_MAP_SIZE;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic int au1x00_pcmcia_set_mem_map(struct pcmcia_socket *skt,
4028c2ecf20Sopenharmony_ci				     struct pccard_mem_map *map)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock = to_db1x_socket(skt);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	if (map->flags & MAP_ATTRIB)
4078c2ecf20Sopenharmony_ci		map->static_start = sock->phys_attr + map->card_start;
4088c2ecf20Sopenharmony_ci	else
4098c2ecf20Sopenharmony_ci		map->static_start = sock->phys_mem + map->card_start;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	return 0;
4128c2ecf20Sopenharmony_ci}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_cistatic struct pccard_operations db1x_pcmcia_operations = {
4158c2ecf20Sopenharmony_ci	.init			= db1x_pcmcia_sock_init,
4168c2ecf20Sopenharmony_ci	.suspend		= db1x_pcmcia_sock_suspend,
4178c2ecf20Sopenharmony_ci	.get_status		= db1x_pcmcia_get_status,
4188c2ecf20Sopenharmony_ci	.set_socket		= db1x_pcmcia_configure,
4198c2ecf20Sopenharmony_ci	.set_io_map		= au1x00_pcmcia_set_io_map,
4208c2ecf20Sopenharmony_ci	.set_mem_map		= au1x00_pcmcia_set_mem_map,
4218c2ecf20Sopenharmony_ci};
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int db1x_pcmcia_socket_probe(struct platform_device *pdev)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock;
4268c2ecf20Sopenharmony_ci	struct resource *r;
4278c2ecf20Sopenharmony_ci	int ret, bid;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	sock = kzalloc(sizeof(struct db1x_pcmcia_sock), GFP_KERNEL);
4308c2ecf20Sopenharmony_ci	if (!sock)
4318c2ecf20Sopenharmony_ci		return -ENOMEM;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	sock->nr = pdev->id;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	bid = BCSR_WHOAMI_BOARD(bcsr_read(BCSR_WHOAMI));
4368c2ecf20Sopenharmony_ci	switch (bid) {
4378c2ecf20Sopenharmony_ci	case BCSR_WHOAMI_PB1500:
4388c2ecf20Sopenharmony_ci	case BCSR_WHOAMI_PB1500R2:
4398c2ecf20Sopenharmony_ci	case BCSR_WHOAMI_PB1100:
4408c2ecf20Sopenharmony_ci		sock->board_type = BOARD_TYPE_PB1100;
4418c2ecf20Sopenharmony_ci		break;
4428c2ecf20Sopenharmony_ci	case BCSR_WHOAMI_DB1000 ... BCSR_WHOAMI_PB1550_SDR:
4438c2ecf20Sopenharmony_ci		sock->board_type = BOARD_TYPE_DEFAULT;
4448c2ecf20Sopenharmony_ci		break;
4458c2ecf20Sopenharmony_ci	case BCSR_WHOAMI_PB1200 ... BCSR_WHOAMI_DB1200:
4468c2ecf20Sopenharmony_ci		sock->board_type = BOARD_TYPE_DB1200;
4478c2ecf20Sopenharmony_ci		break;
4488c2ecf20Sopenharmony_ci	case BCSR_WHOAMI_DB1300:
4498c2ecf20Sopenharmony_ci		sock->board_type = BOARD_TYPE_DB1300;
4508c2ecf20Sopenharmony_ci		break;
4518c2ecf20Sopenharmony_ci	default:
4528c2ecf20Sopenharmony_ci		printk(KERN_INFO "db1xxx-ss: unknown board %d!\n", bid);
4538c2ecf20Sopenharmony_ci		ret = -ENODEV;
4548c2ecf20Sopenharmony_ci		goto out0;
4558c2ecf20Sopenharmony_ci	};
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	/*
4588c2ecf20Sopenharmony_ci	 * gather resources necessary and optional nice-to-haves to
4598c2ecf20Sopenharmony_ci	 * operate a socket:
4608c2ecf20Sopenharmony_ci	 * This includes IRQs for Carddetection/ejection, the card
4618c2ecf20Sopenharmony_ci	 *  itself and optional status change detection.
4628c2ecf20Sopenharmony_ci	 * Also, the memory areas covered by a socket.  For these
4638c2ecf20Sopenharmony_ci	 *  we require the real 36bit addresses (see the au1000.h
4648c2ecf20Sopenharmony_ci	 *  header for more information).
4658c2ecf20Sopenharmony_ci	 */
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* card: irq assigned to the card itself. */
4688c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "card");
4698c2ecf20Sopenharmony_ci	sock->card_irq = r ? r->start : 0;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* insert: irq which triggers on card insertion/ejection
4728c2ecf20Sopenharmony_ci	 * BIG FAT NOTE: on DB1000/1100/1500/1550 we pass a GPIO here!
4738c2ecf20Sopenharmony_ci	 */
4748c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "insert");
4758c2ecf20Sopenharmony_ci	sock->insert_irq = r ? r->start : -1;
4768c2ecf20Sopenharmony_ci	if (sock->board_type == BOARD_TYPE_DEFAULT) {
4778c2ecf20Sopenharmony_ci		sock->insert_gpio = r ? r->start : -1;
4788c2ecf20Sopenharmony_ci		sock->insert_irq = r ? gpio_to_irq(r->start) : -1;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	/* stschg: irq which trigger on card status change (optional) */
4828c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "stschg");
4838c2ecf20Sopenharmony_ci	sock->stschg_irq = r ? r->start : -1;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	/* eject: irq which triggers on ejection (DB1200/PB1200 only) */
4868c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "eject");
4878c2ecf20Sopenharmony_ci	sock->eject_irq = r ? r->start : -1;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	ret = -ENODEV;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* 36bit PCMCIA Attribute area address */
4928c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-attr");
4938c2ecf20Sopenharmony_ci	if (!r) {
4948c2ecf20Sopenharmony_ci		printk(KERN_ERR "pcmcia%d has no 'pseudo-attr' resource!\n",
4958c2ecf20Sopenharmony_ci			sock->nr);
4968c2ecf20Sopenharmony_ci		goto out0;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	sock->phys_attr = r->start;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/* 36bit PCMCIA Memory area address */
5018c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-mem");
5028c2ecf20Sopenharmony_ci	if (!r) {
5038c2ecf20Sopenharmony_ci		printk(KERN_ERR "pcmcia%d has no 'pseudo-mem' resource!\n",
5048c2ecf20Sopenharmony_ci			sock->nr);
5058c2ecf20Sopenharmony_ci		goto out0;
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci	sock->phys_mem = r->start;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	/* 36bit PCMCIA IO area address */
5108c2ecf20Sopenharmony_ci	r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcmcia-io");
5118c2ecf20Sopenharmony_ci	if (!r) {
5128c2ecf20Sopenharmony_ci		printk(KERN_ERR "pcmcia%d has no 'pseudo-io' resource!\n",
5138c2ecf20Sopenharmony_ci			sock->nr);
5148c2ecf20Sopenharmony_ci		goto out0;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci	sock->phys_io = r->start;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	/*
5198c2ecf20Sopenharmony_ci	 * PCMCIA client drivers use the inb/outb macros to access
5208c2ecf20Sopenharmony_ci	 * the IO registers.  Since mips_io_port_base is added
5218c2ecf20Sopenharmony_ci	 * to the access address of the mips implementation of
5228c2ecf20Sopenharmony_ci	 * inb/outb, we need to subtract it here because we want
5238c2ecf20Sopenharmony_ci	 * to access the I/O or MEM address directly, without
5248c2ecf20Sopenharmony_ci	 * going through this "mips_io_port_base" mechanism.
5258c2ecf20Sopenharmony_ci	 */
5268c2ecf20Sopenharmony_ci	sock->virt_io = (void *)(ioremap(sock->phys_io, IO_MAP_SIZE) -
5278c2ecf20Sopenharmony_ci				 mips_io_port_base);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (!sock->virt_io) {
5308c2ecf20Sopenharmony_ci		printk(KERN_ERR "pcmcia%d: cannot remap IO area\n",
5318c2ecf20Sopenharmony_ci			sock->nr);
5328c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5338c2ecf20Sopenharmony_ci		goto out0;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	sock->socket.ops	= &db1x_pcmcia_operations;
5378c2ecf20Sopenharmony_ci	sock->socket.owner	= THIS_MODULE;
5388c2ecf20Sopenharmony_ci	sock->socket.pci_irq	= sock->card_irq;
5398c2ecf20Sopenharmony_ci	sock->socket.features	= SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
5408c2ecf20Sopenharmony_ci	sock->socket.map_size	= MEM_MAP_SIZE;
5418c2ecf20Sopenharmony_ci	sock->socket.io_offset	= (unsigned long)sock->virt_io;
5428c2ecf20Sopenharmony_ci	sock->socket.dev.parent	= &pdev->dev;
5438c2ecf20Sopenharmony_ci	sock->socket.resource_ops = &pccard_static_ops;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, sock);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	ret = db1x_pcmcia_setup_irqs(sock);
5488c2ecf20Sopenharmony_ci	if (ret) {
5498c2ecf20Sopenharmony_ci		printk(KERN_ERR "pcmcia%d cannot setup interrupts\n",
5508c2ecf20Sopenharmony_ci			sock->nr);
5518c2ecf20Sopenharmony_ci		goto out1;
5528c2ecf20Sopenharmony_ci	}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	set_stschg(sock, 0);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	ret = pcmcia_register_socket(&sock->socket);
5578c2ecf20Sopenharmony_ci	if (ret) {
5588c2ecf20Sopenharmony_ci		printk(KERN_ERR "pcmcia%d failed to register\n", sock->nr);
5598c2ecf20Sopenharmony_ci		goto out2;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	printk(KERN_INFO "Alchemy Db/Pb1xxx pcmcia%d @ io/attr/mem %09llx"
5638c2ecf20Sopenharmony_ci		"(%p) %09llx %09llx  card/insert/stschg/eject irqs @ %d "
5648c2ecf20Sopenharmony_ci		"%d %d %d\n", sock->nr, sock->phys_io, sock->virt_io,
5658c2ecf20Sopenharmony_ci		sock->phys_attr, sock->phys_mem, sock->card_irq,
5668c2ecf20Sopenharmony_ci		sock->insert_irq, sock->stschg_irq, sock->eject_irq);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	return 0;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ciout2:
5718c2ecf20Sopenharmony_ci	db1x_pcmcia_free_irqs(sock);
5728c2ecf20Sopenharmony_ciout1:
5738c2ecf20Sopenharmony_ci	iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
5748c2ecf20Sopenharmony_ciout0:
5758c2ecf20Sopenharmony_ci	kfree(sock);
5768c2ecf20Sopenharmony_ci	return ret;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int db1x_pcmcia_socket_remove(struct platform_device *pdev)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct db1x_pcmcia_sock *sock = platform_get_drvdata(pdev);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	db1x_pcmcia_free_irqs(sock);
5848c2ecf20Sopenharmony_ci	pcmcia_unregister_socket(&sock->socket);
5858c2ecf20Sopenharmony_ci	iounmap((void *)(sock->virt_io + (u32)mips_io_port_base));
5868c2ecf20Sopenharmony_ci	kfree(sock);
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	return 0;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_cistatic struct platform_driver db1x_pcmcia_socket_driver = {
5928c2ecf20Sopenharmony_ci	.driver	= {
5938c2ecf20Sopenharmony_ci		.name	= "db1xxx_pcmcia",
5948c2ecf20Sopenharmony_ci	},
5958c2ecf20Sopenharmony_ci	.probe		= db1x_pcmcia_socket_probe,
5968c2ecf20Sopenharmony_ci	.remove		= db1x_pcmcia_socket_remove,
5978c2ecf20Sopenharmony_ci};
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cimodule_platform_driver(db1x_pcmcia_socket_driver);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6028c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PCMCIA Socket Services for Alchemy Db/Pb1x00 boards");
6038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Manuel Lauss");
604