18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/ioport.h>
128c2ecf20Sopenharmony_ci#include <linux/timer.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/pci.h>
178c2ecf20Sopenharmony_ci#include <linux/gpio.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <bcm63xx_regs.h>
208c2ecf20Sopenharmony_ci#include <bcm63xx_io.h>
218c2ecf20Sopenharmony_ci#include "bcm63xx_pcmcia.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define PFX	"bcm63xx_pcmcia: "
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS
268c2ecf20Sopenharmony_ci/* if cardbus is used, platform device needs reference to actual pci
278c2ecf20Sopenharmony_ci * device */
288c2ecf20Sopenharmony_cistatic struct pci_dev *bcm63xx_cb_dev;
298c2ecf20Sopenharmony_ci#endif
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * read/write helper for pcmcia regs
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic inline u32 pcmcia_readl(struct bcm63xx_pcmcia_socket *skt, u32 off)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	return bcm_readl(skt->base + off);
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic inline void pcmcia_writel(struct bcm63xx_pcmcia_socket *skt,
408c2ecf20Sopenharmony_ci				 u32 val, u32 off)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	bcm_writel(val, skt->base + off);
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * This callback should (re-)initialise the socket, turn on status
478c2ecf20Sopenharmony_ci * interrupts and PCMCIA bus, and wait for power to stabilise so that
488c2ecf20Sopenharmony_ci * the card status signals report correctly.
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * Hardware cannot do that.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic int bcm63xx_pcmcia_sock_init(struct pcmcia_socket *sock)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/*
588c2ecf20Sopenharmony_ci * This callback should remove power on the socket, disable IRQs from
598c2ecf20Sopenharmony_ci * the card, turn off status interrupts, and disable the PCMCIA bus.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * Hardware cannot do that.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_cistatic int bcm63xx_pcmcia_suspend(struct pcmcia_socket *sock)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	return 0;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/*
698c2ecf20Sopenharmony_ci * Implements the set_socket() operation for the in-kernel PCMCIA
708c2ecf20Sopenharmony_ci * service (formerly SS_SetSocket in Card Services). We more or
718c2ecf20Sopenharmony_ci * less punt all of this work and let the kernel handle the details
728c2ecf20Sopenharmony_ci * of power configuration, reset, &c. We also record the value of
738c2ecf20Sopenharmony_ci * `state' in order to regurgitate it to the PCMCIA core later.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_cistatic int bcm63xx_pcmcia_set_socket(struct pcmcia_socket *sock,
768c2ecf20Sopenharmony_ci				     socket_state_t *state)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct bcm63xx_pcmcia_socket *skt;
798c2ecf20Sopenharmony_ci	unsigned long flags;
808c2ecf20Sopenharmony_ci	u32 val;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	skt = sock->driver_data;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	spin_lock_irqsave(&skt->lock, flags);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/* note: hardware cannot control socket power, so we will
878c2ecf20Sopenharmony_ci	 * always report SS_POWERON */
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* apply socket reset */
908c2ecf20Sopenharmony_ci	val = pcmcia_readl(skt, PCMCIA_C1_REG);
918c2ecf20Sopenharmony_ci	if (state->flags & SS_RESET)
928c2ecf20Sopenharmony_ci		val |= PCMCIA_C1_RESET_MASK;
938c2ecf20Sopenharmony_ci	else
948c2ecf20Sopenharmony_ci		val &= ~PCMCIA_C1_RESET_MASK;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* reverse reset logic for cardbus card */
978c2ecf20Sopenharmony_ci	if (skt->card_detected && (skt->card_type & CARD_CARDBUS))
988c2ecf20Sopenharmony_ci		val ^= PCMCIA_C1_RESET_MASK;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	pcmcia_writel(skt, val, PCMCIA_C1_REG);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* keep requested state for event reporting */
1038c2ecf20Sopenharmony_ci	skt->requested_state = *state;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&skt->lock, flags);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci/*
1118c2ecf20Sopenharmony_ci * identity cardtype from VS[12] input, CD[12] input while only VS2 is
1128c2ecf20Sopenharmony_ci * floating, and CD[12] input while only VS1 is floating
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_cienum {
1158c2ecf20Sopenharmony_ci	IN_VS1 = (1 << 0),
1168c2ecf20Sopenharmony_ci	IN_VS2 = (1 << 1),
1178c2ecf20Sopenharmony_ci	IN_CD1_VS2H = (1 << 2),
1188c2ecf20Sopenharmony_ci	IN_CD2_VS2H = (1 << 3),
1198c2ecf20Sopenharmony_ci	IN_CD1_VS1H = (1 << 4),
1208c2ecf20Sopenharmony_ci	IN_CD2_VS1H = (1 << 5),
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic const u8 vscd_to_cardtype[] = {
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* VS1 float, VS2 float */
1268c2ecf20Sopenharmony_ci	[IN_VS1 | IN_VS2] = (CARD_PCCARD | CARD_5V),
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* VS1 grounded, VS2 float */
1298c2ecf20Sopenharmony_ci	[IN_VS2] = (CARD_PCCARD | CARD_5V | CARD_3V),
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	/* VS1 grounded, VS2 grounded */
1328c2ecf20Sopenharmony_ci	[0] = (CARD_PCCARD | CARD_5V | CARD_3V | CARD_XV),
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* VS1 tied to CD1, VS2 float */
1358c2ecf20Sopenharmony_ci	[IN_VS1 | IN_VS2 | IN_CD1_VS1H] = (CARD_CARDBUS | CARD_3V),
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* VS1 grounded, VS2 tied to CD2 */
1388c2ecf20Sopenharmony_ci	[IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V | CARD_XV),
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* VS1 tied to CD2, VS2 grounded */
1418c2ecf20Sopenharmony_ci	[IN_VS1 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_3V | CARD_XV | CARD_YV),
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* VS1 float, VS2 grounded */
1448c2ecf20Sopenharmony_ci	[IN_VS1] = (CARD_PCCARD | CARD_XV),
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/* VS1 float, VS2 tied to CD2 */
1478c2ecf20Sopenharmony_ci	[IN_VS1 | IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V),
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* VS1 float, VS2 tied to CD1 */
1508c2ecf20Sopenharmony_ci	[IN_VS1 | IN_VS2 | IN_CD1_VS2H] = (CARD_CARDBUS | CARD_XV | CARD_YV),
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* VS1 tied to CD2, VS2 float */
1538c2ecf20Sopenharmony_ci	[IN_VS1 | IN_VS2 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_YV),
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* VS2 grounded, VS1 is tied to CD1, CD2 is grounded */
1568c2ecf20Sopenharmony_ci	[IN_VS1 | IN_CD1_VS1H] = 0, /* ignore cardbay */
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/*
1608c2ecf20Sopenharmony_ci * poll hardware to check card insertion status
1618c2ecf20Sopenharmony_ci */
1628c2ecf20Sopenharmony_cistatic unsigned int __get_socket_status(struct bcm63xx_pcmcia_socket *skt)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	unsigned int stat;
1658c2ecf20Sopenharmony_ci	u32 val;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	stat = 0;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/* check CD for card presence */
1708c2ecf20Sopenharmony_ci	val = pcmcia_readl(skt, PCMCIA_C1_REG);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (!(val & PCMCIA_C1_CD1_MASK) && !(val & PCMCIA_C1_CD2_MASK))
1738c2ecf20Sopenharmony_ci		stat |= SS_DETECT;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* if new insertion, detect cardtype */
1768c2ecf20Sopenharmony_ci	if ((stat & SS_DETECT) && !skt->card_detected) {
1778c2ecf20Sopenharmony_ci		unsigned int stat = 0;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci		/* float VS1, float VS2 */
1808c2ecf20Sopenharmony_ci		val |= PCMCIA_C1_VS1OE_MASK;
1818c2ecf20Sopenharmony_ci		val |= PCMCIA_C1_VS2OE_MASK;
1828c2ecf20Sopenharmony_ci		pcmcia_writel(skt, val, PCMCIA_C1_REG);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		/* wait for output to stabilize and read VS[12] */
1858c2ecf20Sopenharmony_ci		udelay(10);
1868c2ecf20Sopenharmony_ci		val = pcmcia_readl(skt, PCMCIA_C1_REG);
1878c2ecf20Sopenharmony_ci		stat |= (val & PCMCIA_C1_VS1_MASK) ? IN_VS1 : 0;
1888c2ecf20Sopenharmony_ci		stat |= (val & PCMCIA_C1_VS2_MASK) ? IN_VS2 : 0;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		/* drive VS1 low, float VS2 */
1918c2ecf20Sopenharmony_ci		val &= ~PCMCIA_C1_VS1OE_MASK;
1928c2ecf20Sopenharmony_ci		val |= PCMCIA_C1_VS2OE_MASK;
1938c2ecf20Sopenharmony_ci		pcmcia_writel(skt, val, PCMCIA_C1_REG);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		/* wait for output to stabilize and read CD[12] */
1968c2ecf20Sopenharmony_ci		udelay(10);
1978c2ecf20Sopenharmony_ci		val = pcmcia_readl(skt, PCMCIA_C1_REG);
1988c2ecf20Sopenharmony_ci		stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS2H : 0;
1998c2ecf20Sopenharmony_ci		stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS2H : 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		/* float VS1, drive VS2 low */
2028c2ecf20Sopenharmony_ci		val |= PCMCIA_C1_VS1OE_MASK;
2038c2ecf20Sopenharmony_ci		val &= ~PCMCIA_C1_VS2OE_MASK;
2048c2ecf20Sopenharmony_ci		pcmcia_writel(skt, val, PCMCIA_C1_REG);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci		/* wait for output to stabilize and read CD[12] */
2078c2ecf20Sopenharmony_ci		udelay(10);
2088c2ecf20Sopenharmony_ci		val = pcmcia_readl(skt, PCMCIA_C1_REG);
2098c2ecf20Sopenharmony_ci		stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS1H : 0;
2108c2ecf20Sopenharmony_ci		stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS1H : 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		/* guess cardtype from all this */
2138c2ecf20Sopenharmony_ci		skt->card_type = vscd_to_cardtype[stat];
2148c2ecf20Sopenharmony_ci		if (!skt->card_type)
2158c2ecf20Sopenharmony_ci			dev_err(&skt->socket.dev, "unsupported card type\n");
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		/* drive both VS pin to 0 again */
2188c2ecf20Sopenharmony_ci		val &= ~(PCMCIA_C1_VS1OE_MASK | PCMCIA_C1_VS2OE_MASK);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		/* enable correct logic */
2218c2ecf20Sopenharmony_ci		val &= ~(PCMCIA_C1_EN_PCMCIA_MASK | PCMCIA_C1_EN_CARDBUS_MASK);
2228c2ecf20Sopenharmony_ci		if (skt->card_type & CARD_PCCARD)
2238c2ecf20Sopenharmony_ci			val |= PCMCIA_C1_EN_PCMCIA_MASK;
2248c2ecf20Sopenharmony_ci		else
2258c2ecf20Sopenharmony_ci			val |= PCMCIA_C1_EN_CARDBUS_MASK;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		pcmcia_writel(skt, val, PCMCIA_C1_REG);
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci	skt->card_detected = (stat & SS_DETECT) ? 1 : 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* report card type/voltage */
2328c2ecf20Sopenharmony_ci	if (skt->card_type & CARD_CARDBUS)
2338c2ecf20Sopenharmony_ci		stat |= SS_CARDBUS;
2348c2ecf20Sopenharmony_ci	if (skt->card_type & CARD_3V)
2358c2ecf20Sopenharmony_ci		stat |= SS_3VCARD;
2368c2ecf20Sopenharmony_ci	if (skt->card_type & CARD_XV)
2378c2ecf20Sopenharmony_ci		stat |= SS_XVCARD;
2388c2ecf20Sopenharmony_ci	stat |= SS_POWERON;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (gpio_get_value(skt->pd->ready_gpio))
2418c2ecf20Sopenharmony_ci		stat |= SS_READY;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return stat;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci/*
2478c2ecf20Sopenharmony_ci * core request to get current socket status
2488c2ecf20Sopenharmony_ci */
2498c2ecf20Sopenharmony_cistatic int bcm63xx_pcmcia_get_status(struct pcmcia_socket *sock,
2508c2ecf20Sopenharmony_ci				     unsigned int *status)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct bcm63xx_pcmcia_socket *skt;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	skt = sock->driver_data;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	spin_lock_bh(&skt->lock);
2578c2ecf20Sopenharmony_ci	*status = __get_socket_status(skt);
2588c2ecf20Sopenharmony_ci	spin_unlock_bh(&skt->lock);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci/*
2648c2ecf20Sopenharmony_ci * socket polling timer callback
2658c2ecf20Sopenharmony_ci */
2668c2ecf20Sopenharmony_cistatic void bcm63xx_pcmcia_poll(struct timer_list *t)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct bcm63xx_pcmcia_socket *skt;
2698c2ecf20Sopenharmony_ci	unsigned int stat, events;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	skt = from_timer(skt, t, timer);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	spin_lock_bh(&skt->lock);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	stat = __get_socket_status(skt);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* keep only changed bits, and mask with required one from the
2788c2ecf20Sopenharmony_ci	 * core */
2798c2ecf20Sopenharmony_ci	events = (stat ^ skt->old_status) & skt->requested_state.csc_mask;
2808c2ecf20Sopenharmony_ci	skt->old_status = stat;
2818c2ecf20Sopenharmony_ci	spin_unlock_bh(&skt->lock);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (events)
2848c2ecf20Sopenharmony_ci		pcmcia_parse_events(&skt->socket, events);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	mod_timer(&skt->timer,
2878c2ecf20Sopenharmony_ci		  jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE));
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int bcm63xx_pcmcia_set_io_map(struct pcmcia_socket *sock,
2918c2ecf20Sopenharmony_ci				     struct pccard_io_map *map)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	/* this doesn't seem to be called by pcmcia layer if static
2948c2ecf20Sopenharmony_ci	 * mapping is used */
2958c2ecf20Sopenharmony_ci	return 0;
2968c2ecf20Sopenharmony_ci}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic int bcm63xx_pcmcia_set_mem_map(struct pcmcia_socket *sock,
2998c2ecf20Sopenharmony_ci				      struct pccard_mem_map *map)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct bcm63xx_pcmcia_socket *skt;
3028c2ecf20Sopenharmony_ci	struct resource *res;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	skt = sock->driver_data;
3058c2ecf20Sopenharmony_ci	if (map->flags & MAP_ATTRIB)
3068c2ecf20Sopenharmony_ci		res = skt->attr_res;
3078c2ecf20Sopenharmony_ci	else
3088c2ecf20Sopenharmony_ci		res = skt->common_res;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	map->static_start = res->start + map->card_start;
3118c2ecf20Sopenharmony_ci	return 0;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic struct pccard_operations bcm63xx_pcmcia_operations = {
3158c2ecf20Sopenharmony_ci	.init			= bcm63xx_pcmcia_sock_init,
3168c2ecf20Sopenharmony_ci	.suspend		= bcm63xx_pcmcia_suspend,
3178c2ecf20Sopenharmony_ci	.get_status		= bcm63xx_pcmcia_get_status,
3188c2ecf20Sopenharmony_ci	.set_socket		= bcm63xx_pcmcia_set_socket,
3198c2ecf20Sopenharmony_ci	.set_io_map		= bcm63xx_pcmcia_set_io_map,
3208c2ecf20Sopenharmony_ci	.set_mem_map		= bcm63xx_pcmcia_set_mem_map,
3218c2ecf20Sopenharmony_ci};
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/*
3248c2ecf20Sopenharmony_ci * register pcmcia socket to core
3258c2ecf20Sopenharmony_ci */
3268c2ecf20Sopenharmony_cistatic int bcm63xx_drv_pcmcia_probe(struct platform_device *pdev)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct bcm63xx_pcmcia_socket *skt;
3298c2ecf20Sopenharmony_ci	struct pcmcia_socket *sock;
3308c2ecf20Sopenharmony_ci	struct resource *res, *irq_res;
3318c2ecf20Sopenharmony_ci	unsigned int regmem_size = 0, iomem_size = 0;
3328c2ecf20Sopenharmony_ci	u32 val;
3338c2ecf20Sopenharmony_ci	int ret;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	skt = kzalloc(sizeof(*skt), GFP_KERNEL);
3368c2ecf20Sopenharmony_ci	if (!skt)
3378c2ecf20Sopenharmony_ci		return -ENOMEM;
3388c2ecf20Sopenharmony_ci	spin_lock_init(&skt->lock);
3398c2ecf20Sopenharmony_ci	sock = &skt->socket;
3408c2ecf20Sopenharmony_ci	sock->driver_data = skt;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* make sure we have all resources we need */
3438c2ecf20Sopenharmony_ci	skt->common_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
3448c2ecf20Sopenharmony_ci	skt->attr_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
3458c2ecf20Sopenharmony_ci	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
3468c2ecf20Sopenharmony_ci	skt->pd = pdev->dev.platform_data;
3478c2ecf20Sopenharmony_ci	if (!skt->common_res || !skt->attr_res || !irq_res || !skt->pd) {
3488c2ecf20Sopenharmony_ci		ret = -EINVAL;
3498c2ecf20Sopenharmony_ci		goto err;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/* remap pcmcia registers */
3538c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3548c2ecf20Sopenharmony_ci	regmem_size = resource_size(res);
3558c2ecf20Sopenharmony_ci	if (!request_mem_region(res->start, regmem_size, "bcm63xx_pcmcia")) {
3568c2ecf20Sopenharmony_ci		ret = -EINVAL;
3578c2ecf20Sopenharmony_ci		goto err;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci	skt->reg_res = res;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	skt->base = ioremap(res->start, regmem_size);
3628c2ecf20Sopenharmony_ci	if (!skt->base) {
3638c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3648c2ecf20Sopenharmony_ci		goto err;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* remap io registers */
3688c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
3698c2ecf20Sopenharmony_ci	iomem_size = resource_size(res);
3708c2ecf20Sopenharmony_ci	skt->io_base = ioremap(res->start, iomem_size);
3718c2ecf20Sopenharmony_ci	if (!skt->io_base) {
3728c2ecf20Sopenharmony_ci		ret = -ENOMEM;
3738c2ecf20Sopenharmony_ci		goto err;
3748c2ecf20Sopenharmony_ci	}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* resources are static */
3778c2ecf20Sopenharmony_ci	sock->resource_ops = &pccard_static_ops;
3788c2ecf20Sopenharmony_ci	sock->ops = &bcm63xx_pcmcia_operations;
3798c2ecf20Sopenharmony_ci	sock->owner = THIS_MODULE;
3808c2ecf20Sopenharmony_ci	sock->dev.parent = &pdev->dev;
3818c2ecf20Sopenharmony_ci	sock->features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;
3828c2ecf20Sopenharmony_ci	sock->io_offset = (unsigned long)skt->io_base;
3838c2ecf20Sopenharmony_ci	sock->pci_irq = irq_res->start;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS
3868c2ecf20Sopenharmony_ci	sock->cb_dev = bcm63xx_cb_dev;
3878c2ecf20Sopenharmony_ci	if (bcm63xx_cb_dev)
3888c2ecf20Sopenharmony_ci		sock->features |= SS_CAP_CARDBUS;
3898c2ecf20Sopenharmony_ci#endif
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* assume common & attribute memory have the same size */
3928c2ecf20Sopenharmony_ci	sock->map_size = resource_size(skt->common_res);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* initialize polling timer */
3958c2ecf20Sopenharmony_ci	timer_setup(&skt->timer, bcm63xx_pcmcia_poll, 0);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* initialize  pcmcia  control register,  drive  VS[12] to  0,
3988c2ecf20Sopenharmony_ci	 * leave CB IDSEL to the old  value since it is set by the PCI
3998c2ecf20Sopenharmony_ci	 * layer */
4008c2ecf20Sopenharmony_ci	val = pcmcia_readl(skt, PCMCIA_C1_REG);
4018c2ecf20Sopenharmony_ci	val &= PCMCIA_C1_CBIDSEL_MASK;
4028c2ecf20Sopenharmony_ci	val |= PCMCIA_C1_EN_PCMCIA_GPIO_MASK;
4038c2ecf20Sopenharmony_ci	pcmcia_writel(skt, val, PCMCIA_C1_REG);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/*
4068c2ecf20Sopenharmony_ci	 * Hardware has only one set of timings registers, not one for
4078c2ecf20Sopenharmony_ci	 * each memory access type, so we configure them for the
4088c2ecf20Sopenharmony_ci	 * slowest one: attribute memory.
4098c2ecf20Sopenharmony_ci	 */
4108c2ecf20Sopenharmony_ci	val = PCMCIA_C2_DATA16_MASK;
4118c2ecf20Sopenharmony_ci	val |= 10 << PCMCIA_C2_RWCOUNT_SHIFT;
4128c2ecf20Sopenharmony_ci	val |= 6 << PCMCIA_C2_INACTIVE_SHIFT;
4138c2ecf20Sopenharmony_ci	val |= 3 << PCMCIA_C2_SETUP_SHIFT;
4148c2ecf20Sopenharmony_ci	val |= 3 << PCMCIA_C2_HOLD_SHIFT;
4158c2ecf20Sopenharmony_ci	pcmcia_writel(skt, val, PCMCIA_C2_REG);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	ret = pcmcia_register_socket(sock);
4188c2ecf20Sopenharmony_ci	if (ret)
4198c2ecf20Sopenharmony_ci		goto err;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* start polling socket */
4228c2ecf20Sopenharmony_ci	mod_timer(&skt->timer,
4238c2ecf20Sopenharmony_ci		  jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE));
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, skt);
4268c2ecf20Sopenharmony_ci	return 0;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cierr:
4298c2ecf20Sopenharmony_ci	if (skt->io_base)
4308c2ecf20Sopenharmony_ci		iounmap(skt->io_base);
4318c2ecf20Sopenharmony_ci	if (skt->base)
4328c2ecf20Sopenharmony_ci		iounmap(skt->base);
4338c2ecf20Sopenharmony_ci	if (skt->reg_res)
4348c2ecf20Sopenharmony_ci		release_mem_region(skt->reg_res->start, regmem_size);
4358c2ecf20Sopenharmony_ci	kfree(skt);
4368c2ecf20Sopenharmony_ci	return ret;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int bcm63xx_drv_pcmcia_remove(struct platform_device *pdev)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct bcm63xx_pcmcia_socket *skt;
4428c2ecf20Sopenharmony_ci	struct resource *res;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	skt = platform_get_drvdata(pdev);
4458c2ecf20Sopenharmony_ci	del_timer_sync(&skt->timer);
4468c2ecf20Sopenharmony_ci	iounmap(skt->base);
4478c2ecf20Sopenharmony_ci	iounmap(skt->io_base);
4488c2ecf20Sopenharmony_ci	res = skt->reg_res;
4498c2ecf20Sopenharmony_ci	release_mem_region(res->start, resource_size(res));
4508c2ecf20Sopenharmony_ci	kfree(skt);
4518c2ecf20Sopenharmony_ci	return 0;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_cistruct platform_driver bcm63xx_pcmcia_driver = {
4558c2ecf20Sopenharmony_ci	.probe	= bcm63xx_drv_pcmcia_probe,
4568c2ecf20Sopenharmony_ci	.remove	= bcm63xx_drv_pcmcia_remove,
4578c2ecf20Sopenharmony_ci	.driver	= {
4588c2ecf20Sopenharmony_ci		.name	= "bcm63xx_pcmcia",
4598c2ecf20Sopenharmony_ci		.owner  = THIS_MODULE,
4608c2ecf20Sopenharmony_ci	},
4618c2ecf20Sopenharmony_ci};
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS
4648c2ecf20Sopenharmony_cistatic int bcm63xx_cb_probe(struct pci_dev *dev,
4658c2ecf20Sopenharmony_ci				      const struct pci_device_id *id)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	/* keep pci device */
4688c2ecf20Sopenharmony_ci	bcm63xx_cb_dev = dev;
4698c2ecf20Sopenharmony_ci	return platform_driver_register(&bcm63xx_pcmcia_driver);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic void bcm63xx_cb_exit(struct pci_dev *dev)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	platform_driver_unregister(&bcm63xx_pcmcia_driver);
4758c2ecf20Sopenharmony_ci	bcm63xx_cb_dev = NULL;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic const struct pci_device_id bcm63xx_cb_table[] = {
4798c2ecf20Sopenharmony_ci	{
4808c2ecf20Sopenharmony_ci		.vendor		= PCI_VENDOR_ID_BROADCOM,
4818c2ecf20Sopenharmony_ci		.device		= BCM6348_CPU_ID,
4828c2ecf20Sopenharmony_ci		.subvendor	= PCI_VENDOR_ID_BROADCOM,
4838c2ecf20Sopenharmony_ci		.subdevice	= PCI_ANY_ID,
4848c2ecf20Sopenharmony_ci		.class		= PCI_CLASS_BRIDGE_CARDBUS << 8,
4858c2ecf20Sopenharmony_ci		.class_mask	= ~0,
4868c2ecf20Sopenharmony_ci	},
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	{
4898c2ecf20Sopenharmony_ci		.vendor		= PCI_VENDOR_ID_BROADCOM,
4908c2ecf20Sopenharmony_ci		.device		= BCM6358_CPU_ID,
4918c2ecf20Sopenharmony_ci		.subvendor	= PCI_VENDOR_ID_BROADCOM,
4928c2ecf20Sopenharmony_ci		.subdevice	= PCI_ANY_ID,
4938c2ecf20Sopenharmony_ci		.class		= PCI_CLASS_BRIDGE_CARDBUS << 8,
4948c2ecf20Sopenharmony_ci		.class_mask	= ~0,
4958c2ecf20Sopenharmony_ci	},
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	{ },
4988c2ecf20Sopenharmony_ci};
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, bcm63xx_cb_table);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic struct pci_driver bcm63xx_cardbus_driver = {
5038c2ecf20Sopenharmony_ci	.name		= "bcm63xx_cardbus",
5048c2ecf20Sopenharmony_ci	.id_table	= bcm63xx_cb_table,
5058c2ecf20Sopenharmony_ci	.probe		= bcm63xx_cb_probe,
5068c2ecf20Sopenharmony_ci	.remove		= bcm63xx_cb_exit,
5078c2ecf20Sopenharmony_ci};
5088c2ecf20Sopenharmony_ci#endif
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci/*
5118c2ecf20Sopenharmony_ci * if cardbus support is enabled, register our platform device after
5128c2ecf20Sopenharmony_ci * our fake cardbus bridge has been registered
5138c2ecf20Sopenharmony_ci */
5148c2ecf20Sopenharmony_cistatic int __init bcm63xx_pcmcia_init(void)
5158c2ecf20Sopenharmony_ci{
5168c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS
5178c2ecf20Sopenharmony_ci	return pci_register_driver(&bcm63xx_cardbus_driver);
5188c2ecf20Sopenharmony_ci#else
5198c2ecf20Sopenharmony_ci	return platform_driver_register(&bcm63xx_pcmcia_driver);
5208c2ecf20Sopenharmony_ci#endif
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic void __exit bcm63xx_pcmcia_exit(void)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS
5268c2ecf20Sopenharmony_ci	return pci_unregister_driver(&bcm63xx_cardbus_driver);
5278c2ecf20Sopenharmony_ci#else
5288c2ecf20Sopenharmony_ci	platform_driver_unregister(&bcm63xx_pcmcia_driver);
5298c2ecf20Sopenharmony_ci#endif
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cimodule_init(bcm63xx_pcmcia_init);
5338c2ecf20Sopenharmony_cimodule_exit(bcm63xx_pcmcia_exit);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
5368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");
5378c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux PCMCIA Card Services: bcm63xx Socket Controller");
538