| /* | |
| * Broadcom 43xx PCMCIA-SSB bridge module | |
| * | |
| * Copyright (c) 2007 Michael Buesch <m@bues.ch> | |
| * | |
| * Licensed under the GNU/GPL. See COPYING for details. | |
| */ | |
| #include <linux/ssb/ssb.h> | |
| #include <linux/slab.h> | |
| #include <linux/module.h> | |
| #include <pcmcia/cistpl.h> | |
| #include <pcmcia/ciscode.h> | |
| #include <pcmcia/ds.h> | |
| #include <pcmcia/cisreg.h> | |
| #include "ssb_private.h" | |
| static const struct pcmcia_device_id ssb_host_pcmcia_tbl[] = { | |
| PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448), | |
| PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x476), | |
| PCMCIA_DEVICE_NULL, | |
| }; | |
| MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl); | |
| static int ssb_host_pcmcia_probe(struct pcmcia_device *dev) | |
| { | |
| struct ssb_bus *ssb; | |
| int err = -ENOMEM; | |
| int res = 0; | |
| ssb = kzalloc(sizeof(*ssb), GFP_KERNEL); | |
| if (!ssb) | |
| goto out_error; | |
| err = -ENODEV; | |
| dev->config_flags |= CONF_ENABLE_IRQ; | |
| dev->resource[2]->flags |= WIN_ENABLE | WIN_DATA_WIDTH_16 | | |
| WIN_USE_WAIT; | |
| dev->resource[2]->start = 0; | |
| dev->resource[2]->end = SSB_CORE_SIZE; | |
| res = pcmcia_request_window(dev, dev->resource[2], 250); | |
| if (res != 0) | |
| goto err_kfree_ssb; | |
| res = pcmcia_map_mem_page(dev, dev->resource[2], 0); | |
| if (res != 0) | |
| goto err_disable; | |
| if (!dev->irq) | |
| goto err_disable; | |
| res = pcmcia_enable_device(dev); | |
| if (res != 0) | |
| goto err_disable; | |
| err = ssb_bus_pcmciabus_register(ssb, dev, dev->resource[2]->start); | |
| if (err) | |
| goto err_disable; | |
| dev->priv = ssb; | |
| return 0; | |
| err_disable: | |
| pcmcia_disable_device(dev); | |
| err_kfree_ssb: | |
| kfree(ssb); | |
| out_error: | |
| ssb_err("Initialization failed (%d, %d)\n", res, err); | |
| return err; | |
| } | |
| static void ssb_host_pcmcia_remove(struct pcmcia_device *dev) | |
| { | |
| struct ssb_bus *ssb = dev->priv; | |
| ssb_bus_unregister(ssb); | |
| pcmcia_disable_device(dev); | |
| kfree(ssb); | |
| dev->priv = NULL; | |
| } | |
| #ifdef CONFIG_PM | |
| static int ssb_host_pcmcia_suspend(struct pcmcia_device *dev) | |
| { | |
| struct ssb_bus *ssb = dev->priv; | |
| return ssb_bus_suspend(ssb); | |
| } | |
| static int ssb_host_pcmcia_resume(struct pcmcia_device *dev) | |
| { | |
| struct ssb_bus *ssb = dev->priv; | |
| return ssb_bus_resume(ssb); | |
| } | |
| #else /* CONFIG_PM */ | |
| # define ssb_host_pcmcia_suspend NULL | |
| # define ssb_host_pcmcia_resume NULL | |
| #endif /* CONFIG_PM */ | |
| static struct pcmcia_driver ssb_host_pcmcia_driver = { | |
| .owner = THIS_MODULE, | |
| .name = "ssb-pcmcia", | |
| .id_table = ssb_host_pcmcia_tbl, | |
| .probe = ssb_host_pcmcia_probe, | |
| .remove = ssb_host_pcmcia_remove, | |
| .suspend = ssb_host_pcmcia_suspend, | |
| .resume = ssb_host_pcmcia_resume, | |
| }; | |
| /* | |
| * These are not module init/exit functions! | |
| * The module_pcmcia_driver() helper cannot be used here. | |
| */ | |
| int ssb_host_pcmcia_init(void) | |
| { | |
| return pcmcia_register_driver(&ssb_host_pcmcia_driver); | |
| } | |
| void ssb_host_pcmcia_exit(void) | |
| { | |
| pcmcia_unregister_driver(&ssb_host_pcmcia_driver); | |
| } |