Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | /* * Broadcom 43xx PCMCIA-SSB bridge module * * Copyright (c) 2007 Michael Buesch <m@bues.ch> * * Licensed under the GNU/GPL. See COPYING for details. */ #include "ssb_private.h" #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> 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: dev_err(&dev->dev, "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, }; static int pcmcia_init_failed; /* * These are not module init/exit functions! * The module_pcmcia_driver() helper cannot be used here. */ int ssb_host_pcmcia_init(void) { pcmcia_init_failed = pcmcia_register_driver(&ssb_host_pcmcia_driver); return pcmcia_init_failed; } void ssb_host_pcmcia_exit(void) { if (!pcmcia_init_failed) pcmcia_unregister_driver(&ssb_host_pcmcia_driver); } |