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

smssdio.c (8832B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  smssdio.c - Siano 1xxx SDIO interface driver
      4 *
      5 *  Copyright 2008 Pierre Ossman
      6 *
      7 * Based on code by Siano Mobile Silicon, Inc.,
      8 * Copyright (C) 2006-2008, Uri Shkolnik
      9 *
     10 * This hardware is a bit odd in that all transfers should be done
     11 * to/from the SMSSDIO_DATA register, yet the "increase address" bit
     12 * always needs to be set.
     13 *
     14 * Also, buffers from the card are always aligned to 128 byte
     15 * boundaries.
     16 */
     17
     18/*
     19 * General cleanup notes:
     20 *
     21 * - only typedefs should be name *_t
     22 *
     23 * - use ERR_PTR and friends for smscore_register_device()
     24 *
     25 * - smscore_getbuffer should zero fields
     26 *
     27 * Fix stop command
     28 */
     29
     30#include "smscoreapi.h"
     31
     32#include <linux/moduleparam.h>
     33#include <linux/slab.h>
     34#include <linux/firmware.h>
     35#include <linux/delay.h>
     36#include <linux/mmc/card.h>
     37#include <linux/mmc/sdio_func.h>
     38#include <linux/mmc/sdio_ids.h>
     39#include <linux/module.h>
     40
     41#include "sms-cards.h"
     42#include "smsendian.h"
     43
     44/* Registers */
     45
     46#define SMSSDIO_DATA		0x00
     47#define SMSSDIO_INT		0x04
     48#define SMSSDIO_BLOCK_SIZE	128
     49
     50static const struct sdio_device_id smssdio_ids[] = {
     51	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
     52	 .driver_data = SMS1XXX_BOARD_SIANO_STELLAR},
     53	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),
     54	 .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A},
     55	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0),
     56	 .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B},
     57	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0),
     58	 .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
     59	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
     60	 .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
     61	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_MING),
     62	.driver_data = SMS1XXX_BOARD_SIANO_MING},
     63	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_PELE),
     64	.driver_data = SMS1XXX_BOARD_SIANO_PELE},
     65	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_RIO),
     66	.driver_data = SMS1XXX_BOARD_SIANO_RIO},
     67	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_2160),
     68	.driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160},
     69	{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_1530),
     70	.driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530},
     71	{ /* end: all zeroes */ },
     72};
     73
     74MODULE_DEVICE_TABLE(sdio, smssdio_ids);
     75
     76struct smssdio_device {
     77	struct sdio_func *func;
     78
     79	struct smscore_device_t *coredev;
     80
     81	struct smscore_buffer_t *split_cb;
     82};
     83
     84/*******************************************************************/
     85/* Siano core callbacks                                            */
     86/*******************************************************************/
     87
     88static int smssdio_sendrequest(void *context, void *buffer, size_t size)
     89{
     90	int ret = 0;
     91	struct smssdio_device *smsdev;
     92
     93	smsdev = context;
     94
     95	sdio_claim_host(smsdev->func);
     96
     97	smsendian_handle_tx_message((struct sms_msg_data *) buffer);
     98	while (size >= smsdev->func->cur_blksize) {
     99		ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
    100					buffer, smsdev->func->cur_blksize);
    101		if (ret)
    102			goto out;
    103
    104		buffer += smsdev->func->cur_blksize;
    105		size -= smsdev->func->cur_blksize;
    106	}
    107
    108	if (size) {
    109		ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
    110					buffer, size);
    111	}
    112
    113out:
    114	sdio_release_host(smsdev->func);
    115
    116	return ret;
    117}
    118
    119/*******************************************************************/
    120/* SDIO callbacks                                                  */
    121/*******************************************************************/
    122
    123static void smssdio_interrupt(struct sdio_func *func)
    124{
    125	int ret;
    126
    127	struct smssdio_device *smsdev;
    128	struct smscore_buffer_t *cb;
    129	struct sms_msg_hdr *hdr;
    130	size_t size;
    131
    132	smsdev = sdio_get_drvdata(func);
    133
    134	/*
    135	 * The interrupt register has no defined meaning. It is just
    136	 * a way of turning of the level triggered interrupt.
    137	 */
    138	(void)sdio_readb(func, SMSSDIO_INT, &ret);
    139	if (ret) {
    140		pr_err("Unable to read interrupt register!\n");
    141		return;
    142	}
    143
    144	if (smsdev->split_cb == NULL) {
    145		cb = smscore_getbuffer(smsdev->coredev);
    146		if (!cb) {
    147			pr_err("Unable to allocate data buffer!\n");
    148			return;
    149		}
    150
    151		ret = sdio_memcpy_fromio(smsdev->func,
    152					 cb->p,
    153					 SMSSDIO_DATA,
    154					 SMSSDIO_BLOCK_SIZE);
    155		if (ret) {
    156			pr_err("Error %d reading initial block!\n", ret);
    157			return;
    158		}
    159
    160		hdr = cb->p;
    161
    162		if (hdr->msg_flags & MSG_HDR_FLAG_SPLIT_MSG) {
    163			smsdev->split_cb = cb;
    164			return;
    165		}
    166
    167		if (hdr->msg_length > smsdev->func->cur_blksize)
    168			size = hdr->msg_length - smsdev->func->cur_blksize;
    169		else
    170			size = 0;
    171	} else {
    172		cb = smsdev->split_cb;
    173		hdr = cb->p;
    174
    175		size = hdr->msg_length - sizeof(struct sms_msg_hdr);
    176
    177		smsdev->split_cb = NULL;
    178	}
    179
    180	if (size) {
    181		void *buffer;
    182
    183		buffer = cb->p + (hdr->msg_length - size);
    184		size = ALIGN(size, SMSSDIO_BLOCK_SIZE);
    185
    186		BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE);
    187
    188		/*
    189		 * First attempt to transfer all of it in one go...
    190		 */
    191		ret = sdio_memcpy_fromio(smsdev->func,
    192					 buffer,
    193					 SMSSDIO_DATA,
    194					 size);
    195		if (ret && ret != -EINVAL) {
    196			smscore_putbuffer(smsdev->coredev, cb);
    197			pr_err("Error %d reading data from card!\n", ret);
    198			return;
    199		}
    200
    201		/*
    202		 * ..then fall back to one block at a time if that is
    203		 * not possible...
    204		 *
    205		 * (we have to do this manually because of the
    206		 * problem with the "increase address" bit)
    207		 */
    208		if (ret == -EINVAL) {
    209			while (size) {
    210				ret = sdio_memcpy_fromio(smsdev->func,
    211						  buffer, SMSSDIO_DATA,
    212						  smsdev->func->cur_blksize);
    213				if (ret) {
    214					smscore_putbuffer(smsdev->coredev, cb);
    215					pr_err("Error %d reading data from card!\n",
    216					       ret);
    217					return;
    218				}
    219
    220				buffer += smsdev->func->cur_blksize;
    221				if (size > smsdev->func->cur_blksize)
    222					size -= smsdev->func->cur_blksize;
    223				else
    224					size = 0;
    225			}
    226		}
    227	}
    228
    229	cb->size = hdr->msg_length;
    230	cb->offset = 0;
    231
    232	smsendian_handle_rx_message((struct sms_msg_data *) cb->p);
    233	smscore_onresponse(smsdev->coredev, cb);
    234}
    235
    236static int smssdio_probe(struct sdio_func *func,
    237			 const struct sdio_device_id *id)
    238{
    239	int ret;
    240
    241	int board_id;
    242	struct smssdio_device *smsdev;
    243	struct smsdevice_params_t params;
    244
    245	board_id = id->driver_data;
    246
    247	smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);
    248	if (!smsdev)
    249		return -ENOMEM;
    250
    251	smsdev->func = func;
    252
    253	memset(&params, 0, sizeof(struct smsdevice_params_t));
    254
    255	params.device = &func->dev;
    256	params.buffer_size = 0x5000;	/* ?? */
    257	params.num_buffers = 22;	/* ?? */
    258	params.context = smsdev;
    259
    260	snprintf(params.devpath, sizeof(params.devpath),
    261		 "sdio\\%s", sdio_func_id(func));
    262
    263	params.sendrequest_handler = smssdio_sendrequest;
    264
    265	params.device_type = sms_get_board(board_id)->type;
    266
    267	if (params.device_type != SMS_STELLAR)
    268		params.flags |= SMS_DEVICE_FAMILY2;
    269	else {
    270		/*
    271		 * FIXME: Stellar needs special handling...
    272		 */
    273		ret = -ENODEV;
    274		goto free;
    275	}
    276
    277	ret = smscore_register_device(&params, &smsdev->coredev, GFP_DMA, NULL);
    278	if (ret < 0)
    279		goto free;
    280
    281	smscore_set_board_id(smsdev->coredev, board_id);
    282
    283	sdio_claim_host(func);
    284
    285	ret = sdio_enable_func(func);
    286	if (ret)
    287		goto release;
    288
    289	ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE);
    290	if (ret)
    291		goto disable;
    292
    293	ret = sdio_claim_irq(func, smssdio_interrupt);
    294	if (ret)
    295		goto disable;
    296
    297	sdio_set_drvdata(func, smsdev);
    298
    299	sdio_release_host(func);
    300
    301	ret = smscore_start_device(smsdev->coredev);
    302	if (ret < 0)
    303		goto reclaim;
    304
    305	return 0;
    306
    307reclaim:
    308	sdio_claim_host(func);
    309	sdio_release_irq(func);
    310disable:
    311	sdio_disable_func(func);
    312release:
    313	sdio_release_host(func);
    314	smscore_unregister_device(smsdev->coredev);
    315free:
    316	kfree(smsdev);
    317
    318	return ret;
    319}
    320
    321static void smssdio_remove(struct sdio_func *func)
    322{
    323	struct smssdio_device *smsdev;
    324
    325	smsdev = sdio_get_drvdata(func);
    326
    327	/* FIXME: racy! */
    328	if (smsdev->split_cb)
    329		smscore_putbuffer(smsdev->coredev, smsdev->split_cb);
    330
    331	smscore_unregister_device(smsdev->coredev);
    332
    333	sdio_claim_host(func);
    334	sdio_release_irq(func);
    335	sdio_disable_func(func);
    336	sdio_release_host(func);
    337
    338	kfree(smsdev);
    339}
    340
    341static struct sdio_driver smssdio_driver = {
    342	.name = "smssdio",
    343	.id_table = smssdio_ids,
    344	.probe = smssdio_probe,
    345	.remove = smssdio_remove,
    346};
    347
    348/*******************************************************************/
    349/* Module functions                                                */
    350/*******************************************************************/
    351
    352static int __init smssdio_module_init(void)
    353{
    354	int ret = 0;
    355
    356	printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
    357	printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
    358
    359	ret = sdio_register_driver(&smssdio_driver);
    360
    361	return ret;
    362}
    363
    364static void __exit smssdio_module_exit(void)
    365{
    366	sdio_unregister_driver(&smssdio_driver);
    367}
    368
    369module_init(smssdio_module_init);
    370module_exit(smssdio_module_exit);
    371
    372MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
    373MODULE_AUTHOR("Pierre Ossman");
    374MODULE_LICENSE("GPL");