linux/drivers/net/wireless/cw1200/cw1200_sdio.c
<<
>>
Prefs
   1/*
   2 * Mac80211 SDIO driver for ST-Ericsson CW1200 device
   3 *
   4 * Copyright (c) 2010, ST-Ericsson
   5 * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/gpio.h>
  14#include <linux/delay.h>
  15#include <linux/mmc/host.h>
  16#include <linux/mmc/sdio_func.h>
  17#include <linux/mmc/card.h>
  18#include <linux/mmc/sdio.h>
  19#include <net/mac80211.h>
  20
  21#include "cw1200.h"
  22#include "hwbus.h"
  23#include <linux/platform_data/net-cw1200.h>
  24#include "hwio.h"
  25
  26MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>");
  27MODULE_DESCRIPTION("mac80211 ST-Ericsson CW1200 SDIO driver");
  28MODULE_LICENSE("GPL");
  29
  30#define SDIO_BLOCK_SIZE (512)
  31
  32/* Default platform data for Sagrad modules */
  33static struct cw1200_platform_data_sdio sagrad_109x_evk_platform_data = {
  34        .ref_clk = 38400,
  35        .have_5ghz = false,
  36        .sdd_file = "sdd_sagrad_1091_1098.bin",
  37};
  38
  39/* Allow platform data to be overridden */
  40static struct cw1200_platform_data_sdio *global_plat_data = &sagrad_109x_evk_platform_data;
  41
  42void __init cw1200_sdio_set_platform_data(struct cw1200_platform_data_sdio *pdata)
  43{
  44        global_plat_data = pdata;
  45}
  46
  47struct hwbus_priv {
  48        struct sdio_func        *func;
  49        struct cw1200_common    *core;
  50        const struct cw1200_platform_data_sdio *pdata;
  51};
  52
  53#ifndef SDIO_VENDOR_ID_STE
  54#define SDIO_VENDOR_ID_STE              0x0020
  55#endif
  56
  57#ifndef SDIO_DEVICE_ID_STE_CW1200
  58#define SDIO_DEVICE_ID_STE_CW1200       0x2280
  59#endif
  60
  61static const struct sdio_device_id cw1200_sdio_ids[] = {
  62        { SDIO_DEVICE(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200) },
  63        { /* end: all zeroes */                 },
  64};
  65
  66/* hwbus_ops implemetation */
  67
  68static int cw1200_sdio_memcpy_fromio(struct hwbus_priv *self,
  69                                     unsigned int addr,
  70                                     void *dst, int count)
  71{
  72        return sdio_memcpy_fromio(self->func, dst, addr, count);
  73}
  74
  75static int cw1200_sdio_memcpy_toio(struct hwbus_priv *self,
  76                                   unsigned int addr,
  77                                   const void *src, int count)
  78{
  79        return sdio_memcpy_toio(self->func, addr, (void *)src, count);
  80}
  81
  82static void cw1200_sdio_lock(struct hwbus_priv *self)
  83{
  84        sdio_claim_host(self->func);
  85}
  86
  87static void cw1200_sdio_unlock(struct hwbus_priv *self)
  88{
  89        sdio_release_host(self->func);
  90}
  91
  92static void cw1200_sdio_irq_handler(struct sdio_func *func)
  93{
  94        struct hwbus_priv *self = sdio_get_drvdata(func);
  95
  96        /* note:  sdio_host already claimed here. */
  97        if (self->core)
  98                cw1200_irq_handler(self->core);
  99}
 100
 101static irqreturn_t cw1200_gpio_hardirq(int irq, void *dev_id)
 102{
 103        return IRQ_WAKE_THREAD;
 104}
 105
 106static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id)
 107{
 108        struct hwbus_priv *self = dev_id;
 109
 110        if (self->core) {
 111                cw1200_sdio_lock(self);
 112                cw1200_irq_handler(self->core);
 113                cw1200_sdio_unlock(self);
 114                return IRQ_HANDLED;
 115        } else {
 116                return IRQ_NONE;
 117        }
 118}
 119
 120static int cw1200_request_irq(struct hwbus_priv *self)
 121{
 122        int ret;
 123        u8 cccr;
 124
 125        cccr = sdio_f0_readb(self->func, SDIO_CCCR_IENx, &ret);
 126        if (WARN_ON(ret))
 127                goto err;
 128
 129        /* Master interrupt enable ... */
 130        cccr |= BIT(0);
 131
 132        /* ... for our function */
 133        cccr |= BIT(self->func->num);
 134
 135        sdio_f0_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
 136        if (WARN_ON(ret))
 137                goto err;
 138
 139        ret = enable_irq_wake(self->pdata->irq);
 140        if (WARN_ON(ret))
 141                goto err;
 142
 143        /* Request the IRQ */
 144        ret =  request_threaded_irq(self->pdata->irq, cw1200_gpio_hardirq,
 145                                    cw1200_gpio_irq,
 146                                    IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
 147                                    "cw1200_wlan_irq", self);
 148        if (WARN_ON(ret))
 149                goto err;
 150
 151        return 0;
 152
 153err:
 154        return ret;
 155}
 156
 157static int cw1200_sdio_irq_subscribe(struct hwbus_priv *self)
 158{
 159        int ret = 0;
 160
 161        pr_debug("SW IRQ subscribe\n");
 162        sdio_claim_host(self->func);
 163        if (self->pdata->irq)
 164                ret = cw1200_request_irq(self);
 165        else
 166                ret = sdio_claim_irq(self->func, cw1200_sdio_irq_handler);
 167
 168        sdio_release_host(self->func);
 169        return ret;
 170}
 171
 172static int cw1200_sdio_irq_unsubscribe(struct hwbus_priv *self)
 173{
 174        int ret = 0;
 175
 176        pr_debug("SW IRQ unsubscribe\n");
 177
 178        if (self->pdata->irq) {
 179                disable_irq_wake(self->pdata->irq);
 180                free_irq(self->pdata->irq, self);
 181        } else {
 182                sdio_claim_host(self->func);
 183                ret = sdio_release_irq(self->func);
 184                sdio_release_host(self->func);
 185        }
 186        return ret;
 187}
 188
 189static int cw1200_sdio_off(const struct cw1200_platform_data_sdio *pdata)
 190{
 191        if (pdata->reset) {
 192                gpio_set_value(pdata->reset, 0);
 193                msleep(30); /* Min is 2 * CLK32K cycles */
 194                gpio_free(pdata->reset);
 195        }
 196
 197        if (pdata->power_ctrl)
 198                pdata->power_ctrl(pdata, false);
 199        if (pdata->clk_ctrl)
 200                pdata->clk_ctrl(pdata, false);
 201
 202        return 0;
 203}
 204
 205static int cw1200_sdio_on(const struct cw1200_platform_data_sdio *pdata)
 206{
 207        /* Ensure I/Os are pulled low */
 208        if (pdata->reset) {
 209                gpio_request(pdata->reset, "cw1200_wlan_reset");
 210                gpio_direction_output(pdata->reset, 0);
 211        }
 212        if (pdata->powerup) {
 213                gpio_request(pdata->powerup, "cw1200_wlan_powerup");
 214                gpio_direction_output(pdata->powerup, 0);
 215        }
 216        if (pdata->reset || pdata->powerup)
 217                msleep(10); /* Settle time? */
 218
 219        /* Enable 3v3 and 1v8 to hardware */
 220        if (pdata->power_ctrl) {
 221                if (pdata->power_ctrl(pdata, true)) {
 222                        pr_err("power_ctrl() failed!\n");
 223                        return -1;
 224                }
 225        }
 226
 227        /* Enable CLK32K */
 228        if (pdata->clk_ctrl) {
 229                if (pdata->clk_ctrl(pdata, true)) {
 230                        pr_err("clk_ctrl() failed!\n");
 231                        return -1;
 232                }
 233                msleep(10); /* Delay until clock is stable for 2 cycles */
 234        }
 235
 236        /* Enable POWERUP signal */
 237        if (pdata->powerup) {
 238                gpio_set_value(pdata->powerup, 1);
 239                msleep(250); /* or more..? */
 240        }
 241        /* Enable RSTn signal */
 242        if (pdata->reset) {
 243                gpio_set_value(pdata->reset, 1);
 244                msleep(50); /* Or more..? */
 245        }
 246        return 0;
 247}
 248
 249static size_t cw1200_sdio_align_size(struct hwbus_priv *self, size_t size)
 250{
 251        if (self->pdata->no_nptb)
 252                size = round_up(size, SDIO_BLOCK_SIZE);
 253        else
 254                size = sdio_align_size(self->func, size);
 255
 256        return size;
 257}
 258
 259static int cw1200_sdio_pm(struct hwbus_priv *self, bool suspend)
 260{
 261        int ret = 0;
 262
 263        if (self->pdata->irq)
 264                ret = irq_set_irq_wake(self->pdata->irq, suspend);
 265        return ret;
 266}
 267
 268static struct hwbus_ops cw1200_sdio_hwbus_ops = {
 269        .hwbus_memcpy_fromio    = cw1200_sdio_memcpy_fromio,
 270        .hwbus_memcpy_toio      = cw1200_sdio_memcpy_toio,
 271        .lock                   = cw1200_sdio_lock,
 272        .unlock                 = cw1200_sdio_unlock,
 273        .align_size             = cw1200_sdio_align_size,
 274        .power_mgmt             = cw1200_sdio_pm,
 275};
 276
 277/* Probe Function to be called by SDIO stack when device is discovered */
 278static int cw1200_sdio_probe(struct sdio_func *func,
 279                             const struct sdio_device_id *id)
 280{
 281        struct hwbus_priv *self;
 282        int status;
 283
 284        pr_info("cw1200_wlan_sdio: Probe called\n");
 285
 286        /* We are only able to handle the wlan function */
 287        if (func->num != 0x01)
 288                return -ENODEV;
 289
 290        self = kzalloc(sizeof(*self), GFP_KERNEL);
 291        if (!self) {
 292                pr_err("Can't allocate SDIO hwbus_priv.\n");
 293                return -ENOMEM;
 294        }
 295
 296        func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
 297
 298        self->pdata = global_plat_data; /* FIXME */
 299        self->func = func;
 300        sdio_set_drvdata(func, self);
 301        sdio_claim_host(func);
 302        sdio_enable_func(func);
 303        sdio_release_host(func);
 304
 305        status = cw1200_sdio_irq_subscribe(self);
 306
 307        status = cw1200_core_probe(&cw1200_sdio_hwbus_ops,
 308                                   self, &func->dev, &self->core,
 309                                   self->pdata->ref_clk,
 310                                   self->pdata->macaddr,
 311                                   self->pdata->sdd_file,
 312                                   self->pdata->have_5ghz);
 313        if (status) {
 314                cw1200_sdio_irq_unsubscribe(self);
 315                sdio_claim_host(func);
 316                sdio_disable_func(func);
 317                sdio_release_host(func);
 318                sdio_set_drvdata(func, NULL);
 319                kfree(self);
 320        }
 321
 322        return status;
 323}
 324
 325/* Disconnect Function to be called by SDIO stack when
 326 * device is disconnected
 327 */
 328static void cw1200_sdio_disconnect(struct sdio_func *func)
 329{
 330        struct hwbus_priv *self = sdio_get_drvdata(func);
 331
 332        if (self) {
 333                cw1200_sdio_irq_unsubscribe(self);
 334                if (self->core) {
 335                        cw1200_core_release(self->core);
 336                        self->core = NULL;
 337                }
 338                sdio_claim_host(func);
 339                sdio_disable_func(func);
 340                sdio_release_host(func);
 341                sdio_set_drvdata(func, NULL);
 342                kfree(self);
 343        }
 344}
 345
 346#ifdef CONFIG_PM
 347static int cw1200_sdio_suspend(struct device *dev)
 348{
 349        int ret;
 350        struct sdio_func *func = dev_to_sdio_func(dev);
 351        struct hwbus_priv *self = sdio_get_drvdata(func);
 352
 353        if (!cw1200_can_suspend(self->core))
 354                return -EAGAIN;
 355
 356        /* Notify SDIO that CW1200 will remain powered during suspend */
 357        ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
 358        if (ret)
 359                pr_err("Error setting SDIO pm flags: %i\n", ret);
 360
 361        return ret;
 362}
 363
 364static int cw1200_sdio_resume(struct device *dev)
 365{
 366        return 0;
 367}
 368
 369static const struct dev_pm_ops cw1200_pm_ops = {
 370        .suspend = cw1200_sdio_suspend,
 371        .resume = cw1200_sdio_resume,
 372};
 373#endif
 374
 375static struct sdio_driver sdio_driver = {
 376        .name           = "cw1200_wlan_sdio",
 377        .id_table       = cw1200_sdio_ids,
 378        .probe          = cw1200_sdio_probe,
 379        .remove         = cw1200_sdio_disconnect,
 380#ifdef CONFIG_PM
 381        .drv = {
 382                .pm = &cw1200_pm_ops,
 383        }
 384#endif
 385};
 386
 387/* Init Module function -> Called by insmod */
 388static int __init cw1200_sdio_init(void)
 389{
 390        const struct cw1200_platform_data_sdio *pdata;
 391        int ret;
 392
 393        /* FIXME -- this won't support multiple devices */
 394        pdata = global_plat_data;
 395
 396        if (cw1200_sdio_on(pdata)) {
 397                ret = -1;
 398                goto err;
 399        }
 400
 401        ret = sdio_register_driver(&sdio_driver);
 402        if (ret)
 403                goto err;
 404
 405        return 0;
 406
 407err:
 408        cw1200_sdio_off(pdata);
 409        return ret;
 410}
 411
 412/* Called at Driver Unloading */
 413static void __exit cw1200_sdio_exit(void)
 414{
 415        const struct cw1200_platform_data_sdio *pdata;
 416
 417        /* FIXME -- this won't support multiple devices */
 418        pdata = global_plat_data;
 419        sdio_unregister_driver(&sdio_driver);
 420        cw1200_sdio_off(pdata);
 421}
 422
 423
 424module_init(cw1200_sdio_init);
 425module_exit(cw1200_sdio_exit);
 426
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.