forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
2.8 KiB
133 lines
2.8 KiB
/* |
|
* Broadcom 43xx PCMCIA-SSB bridge module |
|
* |
|
* Copyright (c) 2007 Michael Buesch <[email protected]> |
|
* |
|
* 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); |
|
}
|
|
|