bridge_pcmcia_80211.c (2843B)
1/* 2 * Broadcom 43xx PCMCIA-SSB bridge module 3 * 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_PM 88static 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 NULL 103# define ssb_host_pcmcia_resume NULL 104#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}