Dynamically create and register a platform driver, that will be used to to receive board-specific information like irq and reference clock numbers. The driver is created dynamically in order to avoid the 1-device limitation of a fixed platform driver name. Signed-off-by: Ohad Ben-Cohen <ohad@xxxxxxxxxx> --- drivers/net/wireless/wl12xx/wl1271.h | 12 ++++ drivers/net/wireless/wl12xx/wl1271_sdio.c | 103 +++++++++++++++++++++++++++-- 2 files changed, 108 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index faa5925..013eabb 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -31,6 +31,7 @@ #include <linux/list.h> #include <linux/bitops.h> #include <net/mac80211.h> +#include <linux/platform_device.h> #include "wl1271_conf.h" #include "wl1271_ini.h" @@ -319,8 +320,19 @@ struct wl1271_if_operations { void (*disable_irq)(struct wl1271 *wl); }; +/* exact size needed for "wl1271_plat.x" */ +#define WL12XX_PLAT_NAME_LEN 14 + +struct wl12xx_plat_instance { + struct platform_driver pdriver; + char name[WL12XX_PLAT_NAME_LEN]; + struct wl12xx_platform_data *pdata; + struct completion data_ready; +}; + struct wl1271 { struct platform_device *plat_dev; + struct wl12xx_plat_instance *pinstance; struct ieee80211_hw *hw; bool mac80211_registered; diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c index c41293a..5b43626 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c @@ -28,7 +28,11 @@ #include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_ids.h> #include <linux/mmc/card.h> +#include <linux/mmc/host.h> +#include <linux/wl12xx.h> #include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/completion.h> #include "wl1271.h" #include "wl12xx_80211.h" @@ -182,10 +186,84 @@ static struct wl1271_if_operations sdio_ops = { .disable_irq = wl1271_sdio_disable_interrupts }; +static int wl1271_plat_probe(struct platform_device *pdev) +{ + struct wl12xx_platform_data *pdata; + struct platform_driver *pdriver; + struct wl12xx_plat_instance *pinstance; + + pdata = pdev->dev.platform_data; + if (!pdata) { + wl1271_error("no platform data"); + return -ENODEV; + } + + pdriver = container_of(pdev->dev.driver, struct platform_driver, + driver); + pinstance = container_of(pdriver, struct wl12xx_plat_instance, pdriver); + + pinstance->pdata = pdata; + + complete(&pinstance->data_ready); + + return 0; +} + +static struct wl12xx_platform_data *wl1271_get_data(struct wl1271 *wl, int id) +{ + int ret; + struct wl12xx_plat_instance *pinstance; + + #define WL1271_PLAT_NAME "wl1271_plat.%d" + + pinstance = kzalloc(sizeof(*pinstance), GFP_KERNEL); + if (!pinstance) + return ERR_PTR(-ENOMEM); + + init_completion(&pinstance->data_ready); + + pinstance->pdriver.probe = wl1271_plat_probe; + + ret = snprintf(pinstance->name, ARRAY_SIZE(pinstance->name), + WL1271_PLAT_NAME, id); + if (ret >= WL12XX_PLAT_NAME_LEN) { + wl1271_error("truncated plat drv name\n"); + goto out_free; + } + + pinstance->pdriver.driver.name = pinstance->name; + pinstance->pdriver.driver.owner = THIS_MODULE; + + ret = platform_driver_register(&pinstance->pdriver); + if (ret < 0) { + wl1271_error("failed to register plat driver: %d", ret); + goto out_free; + } + + ret = wait_for_completion_interruptible_timeout(&pinstance->data_ready, + msecs_to_jiffies(15000)); + if (ret <= 0) { + wl1271_error("can't get platform device (%d)", ret); + ret = (ret == 0 ? -EAGAIN : ret); + goto unreg; + } + + wl->pinstance = pinstance; + + return pinstance->pdata; + +unreg: + platform_driver_unregister(&pinstance->pdriver); +out_free: + kfree(pinstance); + return ERR_PTR(ret); +} + static int __devinit wl1271_probe(struct sdio_func *func, const struct sdio_device_id *id) { struct ieee80211_hw *hw; + struct wl12xx_platform_data *wlan_data; struct wl1271 *wl; int ret; @@ -205,17 +283,24 @@ static int __devinit wl1271_probe(struct sdio_func *func, /* Grab access to FN0 for ELP reg. */ func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + wlan_data = wl1271_get_data(wl, func->card->host->index); + if (IS_ERR(wlan_data)) { + ret = PTR_ERR(wlan_data); + wl1271_error("missing wlan data (needed for irq/ref_clk)!"); + goto out_free; + } + wl->irq = gpio_to_irq(RX71_WL1271_IRQ_GPIO); if (wl->irq < 0) { ret = wl->irq; wl1271_error("could not get irq!"); - goto out_free; + goto put_data; } ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); - goto out_free; + goto put_data; } set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); @@ -236,13 +321,13 @@ static int __devinit wl1271_probe(struct sdio_func *func, return 0; - out_irq: +out_irq: free_irq(wl->irq, wl); - - - out_free: +put_data: + platform_driver_unregister(&wl->pinstance->pdriver); + kfree(wl->pinstance); +out_free: wl1271_free_hw(wl); - return ret; } @@ -253,6 +338,10 @@ static void __devexit wl1271_remove(struct sdio_func *func) free_irq(wl->irq, wl); wl1271_unregister_hw(wl); + + platform_driver_unregister(&wl->pinstance->pdriver); + kfree(wl->pinstance); + wl1271_free_hw(wl); } -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html