352 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * smssdio.c - Siano 1xxx SDIO interface driver4 *5 * Copyright 2008 Pierre Ossman6 *7 * Based on code by Siano Mobile Silicon, Inc.,8 * Copyright (C) 2006-2008, Uri Shkolnik9 *10 * This hardware is a bit odd in that all transfers should be done11 * to/from the SMSSDIO_DATA register, yet the "increase address" bit12 * always needs to be set.13 *14 * Also, buffers from the card are always aligned to 128 byte15 * boundaries.16 */17 18/*19 * General cleanup notes:20 *21 * - only typedefs should be name *_t22 *23 * - use ERR_PTR and friends for smscore_register_device()24 *25 * - smscore_getbuffer should zero fields26 *27 * Fix stop command28 */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 0x0047#define SMSSDIO_INT 0x0448#define SMSSDIO_BLOCK_SIZE 12849 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 just136 * 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 else170 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 is203 * not possible...204 *205 * (we have to do this manually because of the206 * 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 else224 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(¶ms, 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(¶ms, &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};347module_sdio_driver(smssdio_driver);348 349MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");350MODULE_AUTHOR("Pierre Ossman");351MODULE_LICENSE("GPL");352