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 20 static 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 26 MODULE_DEVICE_TABLE(pcmcia, ssb_host_pcmcia_tbl); 27 28 static 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 68 err_disable: 69 pcmcia_disable_device(dev); 70 err_kfree_ssb: 71 kfree(ssb); 72 out_error: 73 dev_err(&dev->dev, "Initialization failed (%d, %d)\n", res, err); 74 return err; 75 } 76 77 static 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 88 static 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 95 static 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 106 static 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 116 static 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 */ 122 int 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 129 void ssb_host_pcmcia_exit(void) 130 { 131 if (!pcmcia_init_failed) 132 pcmcia_unregister_driver(&ssb_host_pcmcia_driver); 133 } 134