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