cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cw1200_sdio.c (9344B)


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