/* * SDIO driver for XRadio drivers * * Copyright (c) 2013 * Xradio Technology Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "platform.h" #include "xradio.h" #include "sbus.h" /* sdio vendor id and device id*/ #define SDIO_VENDOR_ID_XRADIO 0x0020 #define SDIO_DEVICE_ID_XRADIO 0x2281 #define SDIO_VENDOR_ID_XR829 0x0A9E #define SDIO_DEVICE_ID_XR829 0x2282 static const struct sdio_device_id xradio_sdio_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_XRADIO, SDIO_DEVICE_ID_XRADIO)}, {SDIO_DEVICE(SDIO_VENDOR_ID_XR829, SDIO_DEVICE_ID_XR829)}, /*{ SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) },*/ { /* end: all zeroes */ }, }; /* sbus_ops implemetation */ static int sdio_data_read(struct sbus_priv *self, unsigned int addr, void *dst, int count) { return sdio_memcpy_fromio(self->func, dst, addr, count); } static int sdio_data_write(struct sbus_priv *self, unsigned int addr, const void *src, int count) { return sdio_memcpy_toio(self->func, addr, (void *)src, count); } static void sdio_lock(struct sbus_priv *self) { sdio_claim_host(self->func); } static void sdio_unlock(struct sbus_priv *self) { sdio_release_host(self->func); } static size_t sdio_align_len(struct sbus_priv *self, size_t size) { return sdio_align_size(self->func, size); } static int sdio_set_blk_size(struct sbus_priv *self, size_t size) { sbus_printk(XRADIO_DBG_NIY, "set blocksize=%zu\n", size); return sdio_set_block_size(self->func, size); } static size_t sdio_cur_blk_size(struct sbus_priv *self) { return (size_t)self->func->cur_blksize; } #ifndef CONFIG_XRADIO_USE_GPIO_IRQ static void sdio_irq_handler(struct sdio_func *func) { struct sbus_priv *self = sdio_get_drvdata(func); unsigned long flags; sbus_printk(XRADIO_DBG_TRC, "%s\n", __func__); SYS_BUG(!self); spin_lock_irqsave(&self->lock, flags); if (self->irq_handler) self->irq_handler(self->irq_priv); spin_unlock_irqrestore(&self->lock, flags); } #endif static int sdio_irq_subscribe(struct sbus_priv *self, sbus_irq_handler handler, void *priv) { int ret = 0; unsigned long flags; if (!handler) return -EINVAL; sbus_printk(XRADIO_DBG_TRC, "%s\n", __func__); spin_lock_irqsave(&self->lock, flags); self->irq_priv = priv; self->irq_handler = handler; spin_unlock_irqrestore(&self->lock, flags); sdio_claim_host(self->func); #ifndef CONFIG_XRADIO_USE_GPIO_IRQ ret = sdio_claim_irq(self->func, sdio_irq_handler); if (ret) { sbus_printk(XRADIO_DBG_ERROR, "%s:sdio_claim_irq failed(%d).\n", __func__, ret); } else { sbus_printk(XRADIO_DBG_NIY, "%s:sdio_claim_irq success.\n", __func__); } #else ret = xradio_request_gpio_irq(&(self->func->dev), self); if (!ret) { /* Hack to access Fuction-0 */ u8 cccr; int func_num = self->func->num; sbus_printk(XRADIO_DBG_NIY, "%s:xradio_request_gpio_irq success.\n", __func__); self->func->num = 0; cccr = sdio_readb(self->func, SDIO_CCCR_IENx, &ret); cccr |= BIT(0); /* Master interrupt enable ... */ cccr |= BIT(func_num); /* ... for our function */ sdio_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); if (ret) { xradio_free_gpio_irq(&(self->func->dev), self); if (MCI_CHECK_READY(self->func->card->host, 1000) != 0) sbus_printk(XRADIO_DBG_ERROR, "%s:MCI_CHECK_READY timeout\n", __func__); } /* Restore the WLAN function number */ self->func->num = func_num; } else { sbus_printk(XRADIO_DBG_ERROR, "%s:xradio_request_gpio_irq failed(%d).\n", __func__, ret); } #endif sdio_release_host(self->func); return ret; } static int sdio_irq_unsubscribe(struct sbus_priv *self) { int ret = 0; unsigned long flags; sbus_printk(XRADIO_DBG_TRC, "%s\n", __func__); if (!self->irq_handler) { sbus_printk(XRADIO_DBG_ERROR, "%s:irq_handler is NULL!\n", __func__); return 0; } #ifndef CONFIG_XRADIO_USE_GPIO_IRQ sdio_claim_host(self->func); ret = sdio_release_irq(self->func); sdio_release_host(self->func); #else xradio_free_gpio_irq(&(self->func->dev), self); #endif /*CONFIG_XRADIO_USE_GPIO_IRQ*/ spin_lock_irqsave(&self->lock, flags); self->irq_priv = NULL; self->irq_handler = NULL; spin_unlock_irqrestore(&self->lock, flags); return ret; } static int sdio_pm(struct sbus_priv *self, bool suspend) { int ret = 0; if (suspend) { /* Notify SDIO that XRADIO will remain powered during suspend */ ret = sdio_set_host_pm_flags(self->func, MMC_PM_KEEP_POWER); if (ret) sbus_printk(XRADIO_DBG_ERROR, "Error setting SDIO pm flags: %i\n", ret); } return ret; } static int sdio_reset(struct sbus_priv *self) { return 0; } static struct sbus_ops sdio_sbus_ops = { .sbus_data_read = sdio_data_read, .sbus_data_write = sdio_data_write, .lock = sdio_lock, .unlock = sdio_unlock, .align_size = sdio_align_len, .set_block_size = sdio_set_blk_size, .get_block_size = sdio_cur_blk_size, .irq_subscribe = sdio_irq_subscribe, .irq_unsubscribe = sdio_irq_unsubscribe, .power_mgmt = sdio_pm, .reset = sdio_reset, }; static struct sbus_priv sdio_self; #if (defined(CONFIG_XRADIO_DEBUGFS)) u32 dbg_sdio_clk; static int sdio_set_clk(struct sdio_func *func, u32 clk) { if (func) { /* set min to 1M */ if (func->card->host->ops->set_ios && clk >= 1000000) { sdio_claim_host(func); func->card->host->ios.clock = (clk < 50000000) ? clk : 50000000; func->card->host->ops->set_ios(func->card->host, &func->card->host->ios); sdio_release_host(func); sbus_printk(XRADIO_DBG_ALWY, "%s:change mmc clk=%d\n", __func__, func->card->host->ios.clock); } else { sbus_printk(XRADIO_DBG_ALWY, "%s:fail change mmc clk=%d\n", __func__, clk); } } return 0; sbus_printk(XRADIO_DBG_TRC, "%s\n", __func__); } #endif /* Probe Function to be called by SDIO stack when device is discovered */ static int sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { sbus_printk(XRADIO_DBG_ALWY, "XRadio Device:sdio clk=%d\n", func->card->host->ios.clock); sbus_printk(XRADIO_DBG_NIY, "sdio func->class=%x\n", func->class); sbus_printk(XRADIO_DBG_NIY, "sdio_vendor: 0x%04x\n", func->vendor); sbus_printk(XRADIO_DBG_NIY, "sdio_device: 0x%04x\n", func->device); sbus_printk(XRADIO_DBG_NIY, "Function#: 0x%04x\n", func->num); sbus_printk(XRADIO_DBG_NIY, "max_blksize:%u\n", func->max_blksize); sbus_printk(XRADIO_DBG_NIY, "cur_blksize:%u\n", func->cur_blksize); #if (defined(CONFIG_XRADIO_DEBUGFS)) if (dbg_sdio_clk) sdio_set_clk(func, dbg_sdio_clk); #endif #if 0 /*for odly and sdly debug.*/ #ifdef __io_address #undef __io_address #endif #define __io_address(_n) (0xf0000000+(_n)) { u32 sdio_param = 0; u32 s_dly = 7; u32 smc_no = 0x1; sdio_param = readl(__io_address(0x01c20088 + 0x4 * smc_no)); sbus_printk(XRADIO_DBG_ALWY, "%s[read ]: 0x01c20088=0x%08x\n", __func__, sdio_param); sdio_param &= ~(0xf << 8); sdio_param |= 3 << 8; sdio_param &= ~(0xf << 20); sdio_param |= s_dly << 20; writel(sdio_param, __io_address(0x01c20088 + 0x4 * smc_no)); sbus_printk(XRADIO_DBG_ALWY, "%s[write]: 0x01c20088=0x%08x\n", __func__, sdio_param); sdio_param = readl(__io_address(0x01c20088 + 0x4 * smc_no)); sbus_printk(XRADIO_DBG_ALWY, "%s[read2]: 0x01c20088=0x%08x\n", __func__, sdio_param); } #endif sdio_self.func = func; sdio_self.func->card->quirks |= MMC_QUIRK_BROKEN_BYTE_MODE_512; sdio_set_drvdata(func, &sdio_self); sdio_claim_host(func); sdio_enable_func(func); sdio_release_host(func); sdio_self.load_state = SDIO_LOAD; wake_up(&sdio_self.init_wq); return 0; } /* Disconnect Function to be called by SDIO stack when * device is disconnected */ static void sdio_remove(struct sdio_func *func) { struct sbus_priv *self = sdio_get_drvdata(func); sbus_printk(XRADIO_DBG_NIY, "%s\n", __func__); sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); sdio_set_drvdata(func, NULL); if (self) { self->func = NULL; self->load_state = SDIO_UNLOAD; wake_up(&sdio_self.init_wq); } } #if 0 static int sdio_suspend(struct device *dev) { int ret = 0; sbus_printk(XRADIO_DBG_NIY, "%s\n", __func__); #ifdef CONFIG_XRADIO_ETF ret = xradio_etf_suspend(); if (ret) { sbus_printk(XRADIO_DBG_ERROR, "xradio_etf_suspend failed(%d)\n", ret); } #endif return ret; } #endif /* * wifi device need to use sdio while working, so sdio should not suspend before wifi device. * However, sdio may suspend before wifi device, which is controlled by system.so we need to * keep sdio working.The better way is register sdio device as the father device of wifi device, * so it will suspend wifi device first */ static int sdio_suspend(struct device *dev) { int ret = 0; sbus_printk(XRADIO_DBG_NIY, "%s\n", __func__); #ifdef CONFIG_XRADIO_ETF ret = xradio_etf_suspend(); if (!ret) { struct sdio_func *func = dev_to_sdio_func(dev); ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) { xradio_etf_resume(); sbus_printk(XRADIO_DBG_ERROR, "set MMC_PM_KEEP_POWER error\n"); } } else { sbus_printk(XRADIO_DBG_ERROR, "xradio_etf_suspend failed\n"); } #else struct sdio_func *func = dev_to_sdio_func(dev); ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) { sbus_printk(XRADIO_DBG_ERROR, "set MMC_PM_KEEP_POWER error\n"); } #endif return ret; } static int sdio_resume(struct device *dev) { sbus_printk(XRADIO_DBG_NIY, "%s\n", __func__); #ifdef CONFIG_XRADIO_ETF xradio_etf_resume(); #endif return 0; } static const struct dev_pm_ops sdio_pm_ops = { .suspend = sdio_suspend, .resume = sdio_resume, }; static struct sdio_driver sdio_driver = { .name = "xradio_wlan", .id_table = xradio_sdio_ids, .probe = sdio_probe, .remove = sdio_remove, .drv = { .pm = &sdio_pm_ops, } }; /* Init Module function -> Called by insmod */ struct device *sbus_sdio_init(struct sbus_ops **sdio_ops, struct sbus_priv **sdio_priv) { int ret = 0; struct device *sdio_dev = NULL; sbus_printk(XRADIO_DBG_TRC, "%s\n", __func__); /*initialize sbus_priv.*/ if (sdio_self.load_state == SDIO_UNLOAD) { spin_lock_init(&sdio_self.lock); init_waitqueue_head(&sdio_self.init_wq); /*module power up.*/ xradio_wlan_power(1); /*detect sdio card.*/ xradio_sdio_detect(1); /*setup sdio driver.*/ ret = sdio_register_driver(&sdio_driver); if (ret) { sbus_printk(XRADIO_DBG_ERROR, "sdio_register_driver failed!\n"); return NULL; } if (wait_event_timeout(sdio_self.init_wq, sdio_self.load_state == SDIO_LOAD, 2*HZ) <= 0) { sdio_unregister_driver(&sdio_driver); sdio_self.load_state = SDIO_UNLOAD; xradio_wlan_power(0); /*power down.*/ xradio_sdio_detect(0); sbus_printk(XRADIO_DBG_ERROR, "sdio probe timeout!\n"); return NULL; } } /*register sbus.*/ sdio_dev = &(sdio_self.func->dev); *sdio_ops = &sdio_sbus_ops; *sdio_priv = &sdio_self; return sdio_dev; } /* SDIO Driver Unloading */ void sbus_sdio_deinit(void) { int retrycnt = 0; sbus_printk(XRADIO_DBG_TRC, "%s\n", __func__); if (sdio_self.load_state != SDIO_UNLOAD) { xradio_wlan_power(0); /*power down.*/ retry: xradio_sdio_detect(0); if (wait_event_interruptible_timeout(sdio_self.init_wq, sdio_self.load_state == SDIO_UNLOAD, 2*HZ) <= 0) { sbus_printk(XRADIO_DBG_ERROR, "%s remove timeout!\n", __func__); if (++retrycnt <= 2) goto retry; } sdio_unregister_driver(&sdio_driver); memset(&sdio_self, 0, sizeof(sdio_self)); msleep(5); } else { sbus_printk(XRADIO_DBG_ERROR, "%s sdio did not init!\n", __func__); } }