root/drivers/ssb/bridge_pcmcia_80211.c
/*
 * 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_obj(*ssb);
        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);
}