162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Broadcom B43 wireless driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * SDIO over Sonics Silicon Backplane bus glue for b43. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2009 Albert Herranz 862306a36Sopenharmony_ci * Copyright (C) 2009 Michael Buesch <m@bues.ch> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/mmc/card.h> 1362306a36Sopenharmony_ci#include <linux/mmc/sdio_func.h> 1462306a36Sopenharmony_ci#include <linux/mmc/sdio_ids.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/ssb/ssb.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "sdio.h" 1962306a36Sopenharmony_ci#include "b43.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define HNBU_CHIPID 0x01 /* vendor & device id */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define B43_SDIO_BLOCK_SIZE 64 /* rx fifo max size in bytes */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic const struct b43_sdio_quirk { 2862306a36Sopenharmony_ci u16 vendor; 2962306a36Sopenharmony_ci u16 device; 3062306a36Sopenharmony_ci unsigned int quirks; 3162306a36Sopenharmony_ci} b43_sdio_quirks[] = { 3262306a36Sopenharmony_ci { 0x14E4, 0x4318, SSB_QUIRK_SDIO_READ_AFTER_WRITE32, }, 3362306a36Sopenharmony_ci { }, 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic unsigned int b43_sdio_get_quirks(u16 vendor, u16 device) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci const struct b43_sdio_quirk *q; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci for (q = b43_sdio_quirks; q->quirks; q++) { 4262306a36Sopenharmony_ci if (vendor == q->vendor && device == q->device) 4362306a36Sopenharmony_ci return q->quirks; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return 0; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void b43_sdio_interrupt_dispatcher(struct sdio_func *func) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct b43_sdio *sdio = sdio_get_drvdata(func); 5262306a36Sopenharmony_ci struct b43_wldev *dev = sdio->irq_handler_opaque; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (unlikely(b43_status(dev) < B43_STAT_STARTED)) 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci sdio_release_host(func); 5862306a36Sopenharmony_ci sdio->irq_handler(dev); 5962306a36Sopenharmony_ci sdio_claim_host(func); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ciint b43_sdio_request_irq(struct b43_wldev *dev, 6362306a36Sopenharmony_ci void (*handler)(struct b43_wldev *dev)) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct ssb_bus *bus = dev->dev->sdev->bus; 6662306a36Sopenharmony_ci struct sdio_func *func = bus->host_sdio; 6762306a36Sopenharmony_ci struct b43_sdio *sdio = sdio_get_drvdata(func); 6862306a36Sopenharmony_ci int err; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci sdio->irq_handler_opaque = dev; 7162306a36Sopenharmony_ci sdio->irq_handler = handler; 7262306a36Sopenharmony_ci sdio_claim_host(func); 7362306a36Sopenharmony_ci err = sdio_claim_irq(func, b43_sdio_interrupt_dispatcher); 7462306a36Sopenharmony_ci sdio_release_host(func); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return err; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid b43_sdio_free_irq(struct b43_wldev *dev) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct ssb_bus *bus = dev->dev->sdev->bus; 8262306a36Sopenharmony_ci struct sdio_func *func = bus->host_sdio; 8362306a36Sopenharmony_ci struct b43_sdio *sdio = sdio_get_drvdata(func); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci sdio_claim_host(func); 8662306a36Sopenharmony_ci sdio_release_irq(func); 8762306a36Sopenharmony_ci sdio_release_host(func); 8862306a36Sopenharmony_ci sdio->irq_handler_opaque = NULL; 8962306a36Sopenharmony_ci sdio->irq_handler = NULL; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int b43_sdio_probe(struct sdio_func *func, 9362306a36Sopenharmony_ci const struct sdio_device_id *id) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct b43_sdio *sdio; 9662306a36Sopenharmony_ci struct sdio_func_tuple *tuple; 9762306a36Sopenharmony_ci u16 vendor = 0, device = 0; 9862306a36Sopenharmony_ci int error; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Look for the card chip identifier. */ 10162306a36Sopenharmony_ci tuple = func->tuples; 10262306a36Sopenharmony_ci while (tuple) { 10362306a36Sopenharmony_ci switch (tuple->code) { 10462306a36Sopenharmony_ci case 0x80: 10562306a36Sopenharmony_ci switch (tuple->data[0]) { 10662306a36Sopenharmony_ci case HNBU_CHIPID: 10762306a36Sopenharmony_ci if (tuple->size != 5) 10862306a36Sopenharmony_ci break; 10962306a36Sopenharmony_ci vendor = tuple->data[1] | (tuple->data[2]<<8); 11062306a36Sopenharmony_ci device = tuple->data[3] | (tuple->data[4]<<8); 11162306a36Sopenharmony_ci dev_info(&func->dev, "Chip ID %04x:%04x\n", 11262306a36Sopenharmony_ci vendor, device); 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci default: 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci default: 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci tuple = tuple->next; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci if (!vendor || !device) { 12462306a36Sopenharmony_ci error = -ENODEV; 12562306a36Sopenharmony_ci goto out; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci sdio_claim_host(func); 12962306a36Sopenharmony_ci error = sdio_set_block_size(func, B43_SDIO_BLOCK_SIZE); 13062306a36Sopenharmony_ci if (error) { 13162306a36Sopenharmony_ci dev_err(&func->dev, "failed to set block size to %u bytes," 13262306a36Sopenharmony_ci " error %d\n", B43_SDIO_BLOCK_SIZE, error); 13362306a36Sopenharmony_ci goto err_release_host; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci error = sdio_enable_func(func); 13662306a36Sopenharmony_ci if (error) { 13762306a36Sopenharmony_ci dev_err(&func->dev, "failed to enable func, error %d\n", error); 13862306a36Sopenharmony_ci goto err_release_host; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci sdio_release_host(func); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci sdio = kzalloc(sizeof(*sdio), GFP_KERNEL); 14362306a36Sopenharmony_ci if (!sdio) { 14462306a36Sopenharmony_ci error = -ENOMEM; 14562306a36Sopenharmony_ci dev_err(&func->dev, "failed to allocate ssb bus\n"); 14662306a36Sopenharmony_ci goto err_disable_func; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci error = ssb_bus_sdiobus_register(&sdio->ssb, func, 14962306a36Sopenharmony_ci b43_sdio_get_quirks(vendor, device)); 15062306a36Sopenharmony_ci if (error) { 15162306a36Sopenharmony_ci dev_err(&func->dev, "failed to register ssb sdio bus," 15262306a36Sopenharmony_ci " error %d\n", error); 15362306a36Sopenharmony_ci goto err_free_ssb; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci sdio_set_drvdata(func, sdio); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cierr_free_ssb: 16062306a36Sopenharmony_ci kfree(sdio); 16162306a36Sopenharmony_cierr_disable_func: 16262306a36Sopenharmony_ci sdio_claim_host(func); 16362306a36Sopenharmony_ci sdio_disable_func(func); 16462306a36Sopenharmony_cierr_release_host: 16562306a36Sopenharmony_ci sdio_release_host(func); 16662306a36Sopenharmony_ciout: 16762306a36Sopenharmony_ci return error; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void b43_sdio_remove(struct sdio_func *func) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct b43_sdio *sdio = sdio_get_drvdata(func); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ssb_bus_unregister(&sdio->ssb); 17562306a36Sopenharmony_ci sdio_claim_host(func); 17662306a36Sopenharmony_ci sdio_disable_func(func); 17762306a36Sopenharmony_ci sdio_release_host(func); 17862306a36Sopenharmony_ci kfree(sdio); 17962306a36Sopenharmony_ci sdio_set_drvdata(func, NULL); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct sdio_device_id b43_sdio_ids[] = { 18362306a36Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_NINTENDO_WII) }, 18462306a36Sopenharmony_ci { SDIO_DEVICE(SDIO_VENDOR_ID_CGUYS, SDIO_DEVICE_ID_CGUYS_EW_CG1102GC) }, 18562306a36Sopenharmony_ci { }, 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic struct sdio_driver b43_sdio_driver = { 18962306a36Sopenharmony_ci .name = "b43-sdio", 19062306a36Sopenharmony_ci .id_table = b43_sdio_ids, 19162306a36Sopenharmony_ci .probe = b43_sdio_probe, 19262306a36Sopenharmony_ci .remove = b43_sdio_remove, 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint b43_sdio_init(void) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci return sdio_register_driver(&b43_sdio_driver); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_civoid b43_sdio_exit(void) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci sdio_unregister_driver(&b43_sdio_driver); 20362306a36Sopenharmony_ci} 204