| /* |
| |
| 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); |
| } |