brintos

brintos / linux-shallow public Read only

0
0
Text · 13.3 KiB · 5bda3e6 Raw
537 lines · c
1/*2 * This file is subject to the terms and conditions of the GNU General Public3 * License.  See the file "COPYING" in the main directory of this archive4 * for more details.5 *6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>7 */8 9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/ioport.h>12#include <linux/timer.h>13#include <linux/platform_device.h>14#include <linux/slab.h>15#include <linux/delay.h>16#include <linux/pci.h>17#include <linux/gpio.h>18 19#include <bcm63xx_regs.h>20#include <bcm63xx_io.h>21#include "bcm63xx_pcmcia.h"22 23#define PFX	"bcm63xx_pcmcia: "24 25#ifdef CONFIG_CARDBUS26/* if cardbus is used, platform device needs reference to actual pci27 * device */28static struct pci_dev *bcm63xx_cb_dev;29#endif30 31/*32 * read/write helper for pcmcia regs33 */34static inline u32 pcmcia_readl(struct bcm63xx_pcmcia_socket *skt, u32 off)35{36	return bcm_readl(skt->base + off);37}38 39static inline void pcmcia_writel(struct bcm63xx_pcmcia_socket *skt,40				 u32 val, u32 off)41{42	bcm_writel(val, skt->base + off);43}44 45/*46 * This callback should (re-)initialise the socket, turn on status47 * interrupts and PCMCIA bus, and wait for power to stabilise so that48 * the card status signals report correctly.49 *50 * Hardware cannot do that.51 */52static int bcm63xx_pcmcia_sock_init(struct pcmcia_socket *sock)53{54	return 0;55}56 57/*58 * This callback should remove power on the socket, disable IRQs from59 * the card, turn off status interrupts, and disable the PCMCIA bus.60 *61 * Hardware cannot do that.62 */63static int bcm63xx_pcmcia_suspend(struct pcmcia_socket *sock)64{65	return 0;66}67 68/*69 * Implements the set_socket() operation for the in-kernel PCMCIA70 * service (formerly SS_SetSocket in Card Services). We more or71 * less punt all of this work and let the kernel handle the details72 * of power configuration, reset, &c. We also record the value of73 * `state' in order to regurgitate it to the PCMCIA core later.74 */75static int bcm63xx_pcmcia_set_socket(struct pcmcia_socket *sock,76				     socket_state_t *state)77{78	struct bcm63xx_pcmcia_socket *skt;79	unsigned long flags;80	u32 val;81 82	skt = sock->driver_data;83 84	spin_lock_irqsave(&skt->lock, flags);85 86	/* note: hardware cannot control socket power, so we will87	 * always report SS_POWERON */88 89	/* apply socket reset */90	val = pcmcia_readl(skt, PCMCIA_C1_REG);91	if (state->flags & SS_RESET)92		val |= PCMCIA_C1_RESET_MASK;93	else94		val &= ~PCMCIA_C1_RESET_MASK;95 96	/* reverse reset logic for cardbus card */97	if (skt->card_detected && (skt->card_type & CARD_CARDBUS))98		val ^= PCMCIA_C1_RESET_MASK;99 100	pcmcia_writel(skt, val, PCMCIA_C1_REG);101 102	/* keep requested state for event reporting */103	skt->requested_state = *state;104 105	spin_unlock_irqrestore(&skt->lock, flags);106 107	return 0;108}109 110/*111 * identity cardtype from VS[12] input, CD[12] input while only VS2 is112 * floating, and CD[12] input while only VS1 is floating113 */114enum {115	IN_VS1 = (1 << 0),116	IN_VS2 = (1 << 1),117	IN_CD1_VS2H = (1 << 2),118	IN_CD2_VS2H = (1 << 3),119	IN_CD1_VS1H = (1 << 4),120	IN_CD2_VS1H = (1 << 5),121};122 123static const u8 vscd_to_cardtype[] = {124 125	/* VS1 float, VS2 float */126	[IN_VS1 | IN_VS2] = (CARD_PCCARD | CARD_5V),127 128	/* VS1 grounded, VS2 float */129	[IN_VS2] = (CARD_PCCARD | CARD_5V | CARD_3V),130 131	/* VS1 grounded, VS2 grounded */132	[0] = (CARD_PCCARD | CARD_5V | CARD_3V | CARD_XV),133 134	/* VS1 tied to CD1, VS2 float */135	[IN_VS1 | IN_VS2 | IN_CD1_VS1H] = (CARD_CARDBUS | CARD_3V),136 137	/* VS1 grounded, VS2 tied to CD2 */138	[IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V | CARD_XV),139 140	/* VS1 tied to CD2, VS2 grounded */141	[IN_VS1 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_3V | CARD_XV | CARD_YV),142 143	/* VS1 float, VS2 grounded */144	[IN_VS1] = (CARD_PCCARD | CARD_XV),145 146	/* VS1 float, VS2 tied to CD2 */147	[IN_VS1 | IN_VS2 | IN_CD2_VS2H] = (CARD_CARDBUS | CARD_3V),148 149	/* VS1 float, VS2 tied to CD1 */150	[IN_VS1 | IN_VS2 | IN_CD1_VS2H] = (CARD_CARDBUS | CARD_XV | CARD_YV),151 152	/* VS1 tied to CD2, VS2 float */153	[IN_VS1 | IN_VS2 | IN_CD2_VS1H] = (CARD_CARDBUS | CARD_YV),154 155	/* VS2 grounded, VS1 is tied to CD1, CD2 is grounded */156	[IN_VS1 | IN_CD1_VS1H] = 0, /* ignore cardbay */157};158 159/*160 * poll hardware to check card insertion status161 */162static unsigned int __get_socket_status(struct bcm63xx_pcmcia_socket *skt)163{164	unsigned int stat;165	u32 val;166 167	stat = 0;168 169	/* check CD for card presence */170	val = pcmcia_readl(skt, PCMCIA_C1_REG);171 172	if (!(val & PCMCIA_C1_CD1_MASK) && !(val & PCMCIA_C1_CD2_MASK))173		stat |= SS_DETECT;174 175	/* if new insertion, detect cardtype */176	if ((stat & SS_DETECT) && !skt->card_detected) {177		unsigned int stat = 0;178 179		/* float VS1, float VS2 */180		val |= PCMCIA_C1_VS1OE_MASK;181		val |= PCMCIA_C1_VS2OE_MASK;182		pcmcia_writel(skt, val, PCMCIA_C1_REG);183 184		/* wait for output to stabilize and read VS[12] */185		udelay(10);186		val = pcmcia_readl(skt, PCMCIA_C1_REG);187		stat |= (val & PCMCIA_C1_VS1_MASK) ? IN_VS1 : 0;188		stat |= (val & PCMCIA_C1_VS2_MASK) ? IN_VS2 : 0;189 190		/* drive VS1 low, float VS2 */191		val &= ~PCMCIA_C1_VS1OE_MASK;192		val |= PCMCIA_C1_VS2OE_MASK;193		pcmcia_writel(skt, val, PCMCIA_C1_REG);194 195		/* wait for output to stabilize and read CD[12] */196		udelay(10);197		val = pcmcia_readl(skt, PCMCIA_C1_REG);198		stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS2H : 0;199		stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS2H : 0;200 201		/* float VS1, drive VS2 low */202		val |= PCMCIA_C1_VS1OE_MASK;203		val &= ~PCMCIA_C1_VS2OE_MASK;204		pcmcia_writel(skt, val, PCMCIA_C1_REG);205 206		/* wait for output to stabilize and read CD[12] */207		udelay(10);208		val = pcmcia_readl(skt, PCMCIA_C1_REG);209		stat |= (val & PCMCIA_C1_CD1_MASK) ? IN_CD1_VS1H : 0;210		stat |= (val & PCMCIA_C1_CD2_MASK) ? IN_CD2_VS1H : 0;211 212		/* guess cardtype from all this */213		skt->card_type = vscd_to_cardtype[stat];214		if (!skt->card_type)215			dev_err(&skt->socket.dev, "unsupported card type\n");216 217		/* drive both VS pin to 0 again */218		val &= ~(PCMCIA_C1_VS1OE_MASK | PCMCIA_C1_VS2OE_MASK);219 220		/* enable correct logic */221		val &= ~(PCMCIA_C1_EN_PCMCIA_MASK | PCMCIA_C1_EN_CARDBUS_MASK);222		if (skt->card_type & CARD_PCCARD)223			val |= PCMCIA_C1_EN_PCMCIA_MASK;224		else225			val |= PCMCIA_C1_EN_CARDBUS_MASK;226 227		pcmcia_writel(skt, val, PCMCIA_C1_REG);228	}229	skt->card_detected = (stat & SS_DETECT) ? 1 : 0;230 231	/* report card type/voltage */232	if (skt->card_type & CARD_CARDBUS)233		stat |= SS_CARDBUS;234	if (skt->card_type & CARD_3V)235		stat |= SS_3VCARD;236	if (skt->card_type & CARD_XV)237		stat |= SS_XVCARD;238	stat |= SS_POWERON;239 240	if (gpio_get_value(skt->pd->ready_gpio))241		stat |= SS_READY;242 243	return stat;244}245 246/*247 * core request to get current socket status248 */249static int bcm63xx_pcmcia_get_status(struct pcmcia_socket *sock,250				     unsigned int *status)251{252	struct bcm63xx_pcmcia_socket *skt;253 254	skt = sock->driver_data;255 256	spin_lock_bh(&skt->lock);257	*status = __get_socket_status(skt);258	spin_unlock_bh(&skt->lock);259 260	return 0;261}262 263/*264 * socket polling timer callback265 */266static void bcm63xx_pcmcia_poll(struct timer_list *t)267{268	struct bcm63xx_pcmcia_socket *skt;269	unsigned int stat, events;270 271	skt = from_timer(skt, t, timer);272 273	spin_lock_bh(&skt->lock);274 275	stat = __get_socket_status(skt);276 277	/* keep only changed bits, and mask with required one from the278	 * core */279	events = (stat ^ skt->old_status) & skt->requested_state.csc_mask;280	skt->old_status = stat;281	spin_unlock_bh(&skt->lock);282 283	if (events)284		pcmcia_parse_events(&skt->socket, events);285 286	mod_timer(&skt->timer,287		  jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE));288}289 290static int bcm63xx_pcmcia_set_io_map(struct pcmcia_socket *sock,291				     struct pccard_io_map *map)292{293	/* this doesn't seem to be called by pcmcia layer if static294	 * mapping is used */295	return 0;296}297 298static int bcm63xx_pcmcia_set_mem_map(struct pcmcia_socket *sock,299				      struct pccard_mem_map *map)300{301	struct bcm63xx_pcmcia_socket *skt;302	struct resource *res;303 304	skt = sock->driver_data;305	if (map->flags & MAP_ATTRIB)306		res = skt->attr_res;307	else308		res = skt->common_res;309 310	map->static_start = res->start + map->card_start;311	return 0;312}313 314static struct pccard_operations bcm63xx_pcmcia_operations = {315	.init			= bcm63xx_pcmcia_sock_init,316	.suspend		= bcm63xx_pcmcia_suspend,317	.get_status		= bcm63xx_pcmcia_get_status,318	.set_socket		= bcm63xx_pcmcia_set_socket,319	.set_io_map		= bcm63xx_pcmcia_set_io_map,320	.set_mem_map		= bcm63xx_pcmcia_set_mem_map,321};322 323/*324 * register pcmcia socket to core325 */326static int bcm63xx_drv_pcmcia_probe(struct platform_device *pdev)327{328	struct bcm63xx_pcmcia_socket *skt;329	struct pcmcia_socket *sock;330	struct resource *res;331	unsigned int regmem_size = 0, iomem_size = 0;332	u32 val;333	int ret;334	int irq;335 336	skt = kzalloc(sizeof(*skt), GFP_KERNEL);337	if (!skt)338		return -ENOMEM;339	spin_lock_init(&skt->lock);340	sock = &skt->socket;341	sock->driver_data = skt;342 343	/* make sure we have all resources we need */344	skt->common_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);345	skt->attr_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);346	irq = platform_get_irq(pdev, 0);347	skt->pd = pdev->dev.platform_data;348	if (!skt->common_res || !skt->attr_res || (irq < 0) || !skt->pd) {349		ret = -EINVAL;350		goto err;351	}352 353	/* remap pcmcia registers */354	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);355	regmem_size = resource_size(res);356	if (!request_mem_region(res->start, regmem_size, "bcm63xx_pcmcia")) {357		ret = -EINVAL;358		goto err;359	}360	skt->reg_res = res;361 362	skt->base = ioremap(res->start, regmem_size);363	if (!skt->base) {364		ret = -ENOMEM;365		goto err;366	}367 368	/* remap io registers */369	res = platform_get_resource(pdev, IORESOURCE_MEM, 3);370	iomem_size = resource_size(res);371	skt->io_base = ioremap(res->start, iomem_size);372	if (!skt->io_base) {373		ret = -ENOMEM;374		goto err;375	}376 377	/* resources are static */378	sock->resource_ops = &pccard_static_ops;379	sock->ops = &bcm63xx_pcmcia_operations;380	sock->owner = THIS_MODULE;381	sock->dev.parent = &pdev->dev;382	sock->features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD;383	sock->io_offset = (unsigned long)skt->io_base;384	sock->pci_irq = irq;385 386#ifdef CONFIG_CARDBUS387	sock->cb_dev = bcm63xx_cb_dev;388	if (bcm63xx_cb_dev)389		sock->features |= SS_CAP_CARDBUS;390#endif391 392	/* assume common & attribute memory have the same size */393	sock->map_size = resource_size(skt->common_res);394 395	/* initialize polling timer */396	timer_setup(&skt->timer, bcm63xx_pcmcia_poll, 0);397 398	/* initialize  pcmcia  control register,  drive  VS[12] to  0,399	 * leave CB IDSEL to the old  value since it is set by the PCI400	 * layer */401	val = pcmcia_readl(skt, PCMCIA_C1_REG);402	val &= PCMCIA_C1_CBIDSEL_MASK;403	val |= PCMCIA_C1_EN_PCMCIA_GPIO_MASK;404	pcmcia_writel(skt, val, PCMCIA_C1_REG);405 406	/*407	 * Hardware has only one set of timings registers, not one for408	 * each memory access type, so we configure them for the409	 * slowest one: attribute memory.410	 */411	val = PCMCIA_C2_DATA16_MASK;412	val |= 10 << PCMCIA_C2_RWCOUNT_SHIFT;413	val |= 6 << PCMCIA_C2_INACTIVE_SHIFT;414	val |= 3 << PCMCIA_C2_SETUP_SHIFT;415	val |= 3 << PCMCIA_C2_HOLD_SHIFT;416	pcmcia_writel(skt, val, PCMCIA_C2_REG);417 418	ret = pcmcia_register_socket(sock);419	if (ret)420		goto err;421 422	/* start polling socket */423	mod_timer(&skt->timer,424		  jiffies + msecs_to_jiffies(BCM63XX_PCMCIA_POLL_RATE));425 426	platform_set_drvdata(pdev, skt);427	return 0;428 429err:430	if (skt->io_base)431		iounmap(skt->io_base);432	if (skt->base)433		iounmap(skt->base);434	if (skt->reg_res)435		release_mem_region(skt->reg_res->start, regmem_size);436	kfree(skt);437	return ret;438}439 440static void bcm63xx_drv_pcmcia_remove(struct platform_device *pdev)441{442	struct bcm63xx_pcmcia_socket *skt;443	struct resource *res;444 445	skt = platform_get_drvdata(pdev);446	timer_shutdown_sync(&skt->timer);447	iounmap(skt->base);448	iounmap(skt->io_base);449	res = skt->reg_res;450	release_mem_region(res->start, resource_size(res));451	kfree(skt);452}453 454struct platform_driver bcm63xx_pcmcia_driver = {455	.probe	= bcm63xx_drv_pcmcia_probe,456	.remove_new = bcm63xx_drv_pcmcia_remove,457	.driver	= {458		.name	= "bcm63xx_pcmcia",459	},460};461 462#ifdef CONFIG_CARDBUS463static int bcm63xx_cb_probe(struct pci_dev *dev,464				      const struct pci_device_id *id)465{466	/* keep pci device */467	bcm63xx_cb_dev = dev;468	return platform_driver_register(&bcm63xx_pcmcia_driver);469}470 471static void bcm63xx_cb_exit(struct pci_dev *dev)472{473	platform_driver_unregister(&bcm63xx_pcmcia_driver);474	bcm63xx_cb_dev = NULL;475}476 477static const struct pci_device_id bcm63xx_cb_table[] = {478	{479		.vendor		= PCI_VENDOR_ID_BROADCOM,480		.device		= BCM6348_CPU_ID,481		.subvendor	= PCI_VENDOR_ID_BROADCOM,482		.subdevice	= PCI_ANY_ID,483		.class		= PCI_CLASS_BRIDGE_CARDBUS << 8,484		.class_mask	= ~0,485	},486 487	{488		.vendor		= PCI_VENDOR_ID_BROADCOM,489		.device		= BCM6358_CPU_ID,490		.subvendor	= PCI_VENDOR_ID_BROADCOM,491		.subdevice	= PCI_ANY_ID,492		.class		= PCI_CLASS_BRIDGE_CARDBUS << 8,493		.class_mask	= ~0,494	},495 496	{ },497};498 499MODULE_DEVICE_TABLE(pci, bcm63xx_cb_table);500 501static struct pci_driver bcm63xx_cardbus_driver = {502	.name		= "bcm63xx_cardbus",503	.id_table	= bcm63xx_cb_table,504	.probe		= bcm63xx_cb_probe,505	.remove		= bcm63xx_cb_exit,506};507#endif508 509/*510 * if cardbus support is enabled, register our platform device after511 * our fake cardbus bridge has been registered512 */513static int __init bcm63xx_pcmcia_init(void)514{515#ifdef CONFIG_CARDBUS516	return pci_register_driver(&bcm63xx_cardbus_driver);517#else518	return platform_driver_register(&bcm63xx_pcmcia_driver);519#endif520}521 522static void __exit bcm63xx_pcmcia_exit(void)523{524#ifdef CONFIG_CARDBUS525	return pci_unregister_driver(&bcm63xx_cardbus_driver);526#else527	platform_driver_unregister(&bcm63xx_pcmcia_driver);528#endif529}530 531module_init(bcm63xx_pcmcia_init);532module_exit(bcm63xx_pcmcia_exit);533 534MODULE_LICENSE("GPL");535MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>");536MODULE_DESCRIPTION("Linux PCMCIA Card Services: bcm63xx Socket Controller");537