134 lines · c
1/*2 * Broadcom 43xx PCMCIA-SSB bridge module3 *4 * Copyright (c) 2007 Michael Buesch <m@bues.ch>5 *6 * Licensed under the GNU/GPL. See COPYING for details.7 */8 9#include "ssb_private.h"10 11#include <linux/ssb/ssb.h>12#include <linux/slab.h>13#include <linux/module.h>14 15#include <pcmcia/cistpl.h>16#include <pcmcia/ciscode.h>17#include <pcmcia/ds.h>18#include <pcmcia/cisreg.h>19 20static const struct pcmcia_device_id ssb_host_pcmcia_tbl[] = {21 PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448),22 PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476),23 PCMCIA_DEVICE_NULL,24};25 26MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl);27 28static int ssb_host_pcmcia_probe(struct pcmcia_device *dev)29{30 struct ssb_bus *ssb;31 int err = -ENOMEM;32 int res = 0;33 34 ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);35 if (!ssb)36 goto out_error;37 38 err = -ENODEV;39 40 dev->config_flags |= CONF_ENABLE_IRQ;41 42 dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 |43 WIN_USE_WAIT;44 dev->resource[2]->start = 0;45 dev->resource[2]->end = SSB_CORE_SIZE;46 res = pcmcia_request_window(dev, dev->resource[2], 250);47 if (res != 0)48 goto err_kfree_ssb;49 50 res = pcmcia_map_mem_page(dev, dev->resource[2], 0);51 if (res != 0)52 goto err_disable;53 54 if (!dev->irq)55 goto err_disable;56 57 res = pcmcia_enable_device(dev);58 if (res != 0)59 goto err_disable;60 61 err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start);62 if (err)63 goto err_disable;64 dev->priv = ssb;65 66 return 0;67 68err_disable:69 pcmcia_disable_device(dev);70err_kfree_ssb:71 kfree(ssb);72out_error:73 dev_err(&dev->dev, "Initialization failed (%d, %d)\n", res, err);74 return err;75}76 77static void ssb_host_pcmcia_remove(struct pcmcia_device *dev)78{79 struct ssb_bus *ssb = dev->priv;80 81 ssb_bus_unregister(ssb);82 pcmcia_disable_device(dev);83 kfree(ssb);84 dev->priv = NULL;85}86 87#ifdef CONFIG_PM88static int ssb_host_pcmcia_suspend(struct pcmcia_device *dev)89{90 struct ssb_bus *ssb = dev->priv;91 92 return ssb_bus_suspend(ssb);93}94 95static int ssb_host_pcmcia_resume(struct pcmcia_device *dev)96{97 struct ssb_bus *ssb = dev->priv;98 99 return ssb_bus_resume(ssb);100}101#else /* CONFIG_PM */102# define ssb_host_pcmcia_suspend NULL103# define ssb_host_pcmcia_resume NULL104#endif /* CONFIG_PM */105 106static struct pcmcia_driver ssb_host_pcmcia_driver = {107 .owner = THIS_MODULE,108 .name = "ssb-pcmcia",109 .id_table = ssb_host_pcmcia_tbl,110 .probe = ssb_host_pcmcia_probe,111 .remove = ssb_host_pcmcia_remove,112 .suspend = ssb_host_pcmcia_suspend,113 .resume = ssb_host_pcmcia_resume,114};115 116static int pcmcia_init_failed;117 118/*119 * These are not module init/exit functions!120 * The module_pcmcia_driver() helper cannot be used here.121 */122int ssb_host_pcmcia_init(void)123{124 pcmcia_init_failed = pcmcia_register_driver(&ssb_host_pcmcia_driver);125 126 return pcmcia_init_failed;127}128 129void ssb_host_pcmcia_exit(void)130{131 if (!pcmcia_init_failed)132 pcmcia_unregister_driver(&ssb_host_pcmcia_driver);133}134