|  | /* | 
|  |  | 
|  | Broadcom B43 wireless driver | 
|  |  | 
|  | Copyright (c) 2007 Michael Buesch <mb@bu3sch.de> | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 2 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; see the file COPYING.  If not, write to | 
|  | the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, | 
|  | Boston, MA 02110-1301, USA. | 
|  |  | 
|  | */ | 
|  |  | 
|  | #include "pcmcia.h" | 
|  |  | 
|  | #include <linux/ssb/ssb.h> | 
|  |  | 
|  | #include <pcmcia/cs_types.h> | 
|  | #include <pcmcia/cs.h> | 
|  | #include <pcmcia/cistpl.h> | 
|  | #include <pcmcia/ciscode.h> | 
|  | #include <pcmcia/ds.h> | 
|  | #include <pcmcia/cisreg.h> | 
|  |  | 
|  |  | 
|  | static /*const */ struct pcmcia_device_id b43_pcmcia_tbl[] = { | 
|  | PCMCIA_DEVICE_MANF_CARD(0x2D0, 0x448), | 
|  | PCMCIA_DEVICE_NULL, | 
|  | }; | 
|  |  | 
|  | MODULE_DEVICE_TABLE(pcmcia, b43_pcmcia_tbl); | 
|  |  | 
|  | #ifdef CONFIG_PM | 
|  | static int b43_pcmcia_suspend(struct pcmcia_device *dev) | 
|  | { | 
|  | struct ssb_bus *ssb = dev->priv; | 
|  |  | 
|  | return ssb_bus_suspend(ssb); | 
|  | } | 
|  |  | 
|  | static int b43_pcmcia_resume(struct pcmcia_device *dev) | 
|  | { | 
|  | struct ssb_bus *ssb = dev->priv; | 
|  |  | 
|  | return ssb_bus_resume(ssb); | 
|  | } | 
|  | #else /* CONFIG_PM */ | 
|  | # define b43_pcmcia_suspend		NULL | 
|  | # define b43_pcmcia_resume		NULL | 
|  | #endif /* CONFIG_PM */ | 
|  |  | 
|  | static int __devinit b43_pcmcia_probe(struct pcmcia_device *dev) | 
|  | { | 
|  | struct ssb_bus *ssb; | 
|  | win_req_t win; | 
|  | memreq_t mem; | 
|  | tuple_t tuple; | 
|  | cisparse_t parse; | 
|  | int err = -ENOMEM; | 
|  | int res = 0; | 
|  | unsigned char buf[64]; | 
|  |  | 
|  | ssb = kzalloc(sizeof(*ssb), GFP_KERNEL); | 
|  | if (!ssb) | 
|  | goto out_error; | 
|  |  | 
|  | err = -ENODEV; | 
|  | tuple.DesiredTuple = CISTPL_CONFIG; | 
|  | tuple.Attributes = 0; | 
|  | tuple.TupleData = buf; | 
|  | tuple.TupleDataMax = sizeof(buf); | 
|  | tuple.TupleOffset = 0; | 
|  |  | 
|  | res = pcmcia_get_first_tuple(dev, &tuple); | 
|  | if (res != CS_SUCCESS) | 
|  | goto err_kfree_ssb; | 
|  | res = pcmcia_get_tuple_data(dev, &tuple); | 
|  | if (res != CS_SUCCESS) | 
|  | goto err_kfree_ssb; | 
|  | res = pcmcia_parse_tuple(dev, &tuple, &parse); | 
|  | if (res != CS_SUCCESS) | 
|  | goto err_kfree_ssb; | 
|  |  | 
|  | dev->conf.ConfigBase = parse.config.base; | 
|  | dev->conf.Present = parse.config.rmask[0]; | 
|  | dev->conf.Attributes = CONF_ENABLE_IRQ; | 
|  | dev->conf.IntType = INT_MEMORY_AND_IO; | 
|  |  | 
|  | dev->io.BasePort2 = 0; | 
|  | dev->io.NumPorts2 = 0; | 
|  | dev->io.Attributes2 = 0; | 
|  |  | 
|  | win.Attributes = WIN_ADDR_SPACE_MEM | WIN_MEMORY_TYPE_CM | | 
|  | WIN_ENABLE | WIN_DATA_WIDTH_16 | | 
|  | WIN_USE_WAIT; | 
|  | win.Base = 0; | 
|  | win.Size = SSB_CORE_SIZE; | 
|  | win.AccessSpeed = 250; | 
|  | res = pcmcia_request_window(&dev, &win, &dev->win); | 
|  | if (res != CS_SUCCESS) | 
|  | goto err_kfree_ssb; | 
|  |  | 
|  | mem.CardOffset = 0; | 
|  | mem.Page = 0; | 
|  | res = pcmcia_map_mem_page(dev->win, &mem); | 
|  | if (res != CS_SUCCESS) | 
|  | goto err_disable; | 
|  |  | 
|  | dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; | 
|  | dev->irq.IRQInfo1 = IRQ_LEVEL_ID; | 
|  | dev->irq.Handler = NULL; /* The handler is registered later. */ | 
|  | dev->irq.Instance = NULL; | 
|  | res = pcmcia_request_irq(dev, &dev->irq); | 
|  | if (res != CS_SUCCESS) | 
|  | goto err_disable; | 
|  |  | 
|  | res = pcmcia_request_configuration(dev, &dev->conf); | 
|  | if (res != CS_SUCCESS) | 
|  | goto err_disable; | 
|  |  | 
|  | err = ssb_bus_pcmciabus_register(ssb, dev, win.Base); | 
|  | if (err) | 
|  | goto err_disable; | 
|  | dev->priv = ssb; | 
|  |  | 
|  | return 0; | 
|  |  | 
|  | err_disable: | 
|  | pcmcia_disable_device(dev); | 
|  | err_kfree_ssb: | 
|  | kfree(ssb); | 
|  | out_error: | 
|  | printk(KERN_ERR "b43-pcmcia: Initialization failed (%d, %d)\n", | 
|  | res, err); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static void __devexit b43_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; | 
|  | } | 
|  |  | 
|  | static struct pcmcia_driver b43_pcmcia_driver = { | 
|  | .owner		= THIS_MODULE, | 
|  | .drv		= { | 
|  | .name = "b43-pcmcia", | 
|  | }, | 
|  | .id_table	= b43_pcmcia_tbl, | 
|  | .probe		= b43_pcmcia_probe, | 
|  | .remove		= __devexit_p(b43_pcmcia_remove), | 
|  | .suspend	= b43_pcmcia_suspend, | 
|  | .resume		= b43_pcmcia_resume, | 
|  | }; | 
|  |  | 
|  | int b43_pcmcia_init(void) | 
|  | { | 
|  | return pcmcia_register_driver(&b43_pcmcia_driver); | 
|  | } | 
|  |  | 
|  | void b43_pcmcia_exit(void) | 
|  | { | 
|  | pcmcia_unregister_driver(&b43_pcmcia_driver); | 
|  | } |