18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* vmu-flash.c 38c2ecf20Sopenharmony_ci * Driver for SEGA Dreamcast Visual Memory Unit 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) Adrian McMenamin 2002 - 2009 68c2ecf20Sopenharmony_ci * Copyright (c) Paul Mundt 2001 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/sched.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/maple.h> 138c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct vmu_cache { 178c2ecf20Sopenharmony_ci unsigned char *buffer; /* Cache */ 188c2ecf20Sopenharmony_ci unsigned int block; /* Which block was cached */ 198c2ecf20Sopenharmony_ci unsigned long jiffies_atc; /* When was it cached? */ 208c2ecf20Sopenharmony_ci int valid; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct mdev_part { 248c2ecf20Sopenharmony_ci struct maple_device *mdev; 258c2ecf20Sopenharmony_ci int partition; 268c2ecf20Sopenharmony_ci}; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct vmupart { 298c2ecf20Sopenharmony_ci u16 user_blocks; 308c2ecf20Sopenharmony_ci u16 root_block; 318c2ecf20Sopenharmony_ci u16 numblocks; 328c2ecf20Sopenharmony_ci char *name; 338c2ecf20Sopenharmony_ci struct vmu_cache *pcache; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct memcard { 378c2ecf20Sopenharmony_ci u16 tempA; 388c2ecf20Sopenharmony_ci u16 tempB; 398c2ecf20Sopenharmony_ci u32 partitions; 408c2ecf20Sopenharmony_ci u32 blocklen; 418c2ecf20Sopenharmony_ci u32 writecnt; 428c2ecf20Sopenharmony_ci u32 readcnt; 438c2ecf20Sopenharmony_ci u32 removable; 448c2ecf20Sopenharmony_ci int partition; 458c2ecf20Sopenharmony_ci int read; 468c2ecf20Sopenharmony_ci unsigned char *blockread; 478c2ecf20Sopenharmony_ci struct vmupart *parts; 488c2ecf20Sopenharmony_ci struct mtd_info *mtd; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct vmu_block { 528c2ecf20Sopenharmony_ci unsigned int num; /* block number */ 538c2ecf20Sopenharmony_ci unsigned int ofs; /* block offset */ 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic struct vmu_block *ofs_to_block(unsigned long src_ofs, 578c2ecf20Sopenharmony_ci struct mtd_info *mtd, int partition) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct vmu_block *vblock; 608c2ecf20Sopenharmony_ci struct maple_device *mdev; 618c2ecf20Sopenharmony_ci struct memcard *card; 628c2ecf20Sopenharmony_ci struct mdev_part *mpart; 638c2ecf20Sopenharmony_ci int num; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci mpart = mtd->priv; 668c2ecf20Sopenharmony_ci mdev = mpart->mdev; 678c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (src_ofs >= card->parts[partition].numblocks * card->blocklen) 708c2ecf20Sopenharmony_ci goto failed; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci num = src_ofs / card->blocklen; 738c2ecf20Sopenharmony_ci if (num > card->parts[partition].numblocks) 748c2ecf20Sopenharmony_ci goto failed; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci vblock = kmalloc(sizeof(struct vmu_block), GFP_KERNEL); 778c2ecf20Sopenharmony_ci if (!vblock) 788c2ecf20Sopenharmony_ci goto failed; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci vblock->num = num; 818c2ecf20Sopenharmony_ci vblock->ofs = src_ofs % card->blocklen; 828c2ecf20Sopenharmony_ci return vblock; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cifailed: 858c2ecf20Sopenharmony_ci return NULL; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Maple bus callback function for reads */ 898c2ecf20Sopenharmony_cistatic void vmu_blockread(struct mapleq *mq) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct maple_device *mdev; 928c2ecf20Sopenharmony_ci struct memcard *card; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci mdev = mq->dev; 958c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 968c2ecf20Sopenharmony_ci /* copy the read in data */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (unlikely(!card->blockread)) 998c2ecf20Sopenharmony_ci return; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci memcpy(card->blockread, mq->recvbuf->buf + 12, 1028c2ecf20Sopenharmony_ci card->blocklen/card->readcnt); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* Interface with maple bus to read blocks 1078c2ecf20Sopenharmony_ci * caching the results so that other parts 1088c2ecf20Sopenharmony_ci * of the driver can access block reads */ 1098c2ecf20Sopenharmony_cistatic int maple_vmu_read_block(unsigned int num, unsigned char *buf, 1108c2ecf20Sopenharmony_ci struct mtd_info *mtd) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct memcard *card; 1138c2ecf20Sopenharmony_ci struct mdev_part *mpart; 1148c2ecf20Sopenharmony_ci struct maple_device *mdev; 1158c2ecf20Sopenharmony_ci int partition, error = 0, x, wait; 1168c2ecf20Sopenharmony_ci unsigned char *blockread = NULL; 1178c2ecf20Sopenharmony_ci struct vmu_cache *pcache; 1188c2ecf20Sopenharmony_ci __be32 sendbuf; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci mpart = mtd->priv; 1218c2ecf20Sopenharmony_ci mdev = mpart->mdev; 1228c2ecf20Sopenharmony_ci partition = mpart->partition; 1238c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 1248c2ecf20Sopenharmony_ci pcache = card->parts[partition].pcache; 1258c2ecf20Sopenharmony_ci pcache->valid = 0; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* prepare the cache for this block */ 1288c2ecf20Sopenharmony_ci if (!pcache->buffer) { 1298c2ecf20Sopenharmony_ci pcache->buffer = kmalloc(card->blocklen, GFP_KERNEL); 1308c2ecf20Sopenharmony_ci if (!pcache->buffer) { 1318c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "VMU at (%d, %d) - read fails due" 1328c2ecf20Sopenharmony_ci " to lack of memory\n", mdev->port, 1338c2ecf20Sopenharmony_ci mdev->unit); 1348c2ecf20Sopenharmony_ci error = -ENOMEM; 1358c2ecf20Sopenharmony_ci goto outB; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* 1408c2ecf20Sopenharmony_ci * Reads may be phased - again the hardware spec 1418c2ecf20Sopenharmony_ci * supports this - though may not be any devices in 1428c2ecf20Sopenharmony_ci * the wild that implement it, but we will here 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci for (x = 0; x < card->readcnt; x++) { 1458c2ecf20Sopenharmony_ci sendbuf = cpu_to_be32(partition << 24 | x << 16 | num); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (atomic_read(&mdev->busy) == 1) { 1488c2ecf20Sopenharmony_ci wait_event_interruptible_timeout(mdev->maple_wait, 1498c2ecf20Sopenharmony_ci atomic_read(&mdev->busy) == 0, HZ); 1508c2ecf20Sopenharmony_ci if (atomic_read(&mdev->busy) == 1) { 1518c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, "VMU at (%d, %d)" 1528c2ecf20Sopenharmony_ci " is busy\n", mdev->port, mdev->unit); 1538c2ecf20Sopenharmony_ci error = -EAGAIN; 1548c2ecf20Sopenharmony_ci goto outB; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 1); 1598c2ecf20Sopenharmony_ci blockread = kmalloc(card->blocklen/card->readcnt, GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!blockread) { 1618c2ecf20Sopenharmony_ci error = -ENOMEM; 1628c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 0); 1638c2ecf20Sopenharmony_ci goto outB; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci card->blockread = blockread; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci maple_getcond_callback(mdev, vmu_blockread, 0, 1688c2ecf20Sopenharmony_ci MAPLE_FUNC_MEMCARD); 1698c2ecf20Sopenharmony_ci error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD, 1708c2ecf20Sopenharmony_ci MAPLE_COMMAND_BREAD, 2, &sendbuf); 1718c2ecf20Sopenharmony_ci /* Very long timeouts seem to be needed when box is stressed */ 1728c2ecf20Sopenharmony_ci wait = wait_event_interruptible_timeout(mdev->maple_wait, 1738c2ecf20Sopenharmony_ci (atomic_read(&mdev->busy) == 0 || 1748c2ecf20Sopenharmony_ci atomic_read(&mdev->busy) == 2), HZ * 3); 1758c2ecf20Sopenharmony_ci /* 1768c2ecf20Sopenharmony_ci * MTD layer does not handle hotplugging well 1778c2ecf20Sopenharmony_ci * so have to return errors when VMU is unplugged 1788c2ecf20Sopenharmony_ci * in the middle of a read (busy == 2) 1798c2ecf20Sopenharmony_ci */ 1808c2ecf20Sopenharmony_ci if (error || atomic_read(&mdev->busy) == 2) { 1818c2ecf20Sopenharmony_ci if (atomic_read(&mdev->busy) == 2) 1828c2ecf20Sopenharmony_ci error = -ENXIO; 1838c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 0); 1848c2ecf20Sopenharmony_ci card->blockread = NULL; 1858c2ecf20Sopenharmony_ci goto outA; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci if (wait == 0 || wait == -ERESTARTSYS) { 1888c2ecf20Sopenharmony_ci card->blockread = NULL; 1898c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 0); 1908c2ecf20Sopenharmony_ci error = -EIO; 1918c2ecf20Sopenharmony_ci list_del_init(&(mdev->mq->list)); 1928c2ecf20Sopenharmony_ci kfree(mdev->mq->sendbuf); 1938c2ecf20Sopenharmony_ci mdev->mq->sendbuf = NULL; 1948c2ecf20Sopenharmony_ci if (wait == -ERESTARTSYS) { 1958c2ecf20Sopenharmony_ci dev_warn(&mdev->dev, "VMU read on (%d, %d)" 1968c2ecf20Sopenharmony_ci " interrupted on block 0x%X\n", 1978c2ecf20Sopenharmony_ci mdev->port, mdev->unit, num); 1988c2ecf20Sopenharmony_ci } else 1998c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, "VMU read on (%d, %d)" 2008c2ecf20Sopenharmony_ci " timed out on block 0x%X\n", 2018c2ecf20Sopenharmony_ci mdev->port, mdev->unit, num); 2028c2ecf20Sopenharmony_ci goto outA; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci memcpy(buf + (card->blocklen/card->readcnt) * x, blockread, 2068c2ecf20Sopenharmony_ci card->blocklen/card->readcnt); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci memcpy(pcache->buffer + (card->blocklen/card->readcnt) * x, 2098c2ecf20Sopenharmony_ci card->blockread, card->blocklen/card->readcnt); 2108c2ecf20Sopenharmony_ci card->blockread = NULL; 2118c2ecf20Sopenharmony_ci pcache->block = num; 2128c2ecf20Sopenharmony_ci pcache->jiffies_atc = jiffies; 2138c2ecf20Sopenharmony_ci pcache->valid = 1; 2148c2ecf20Sopenharmony_ci kfree(blockread); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return error; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cioutA: 2208c2ecf20Sopenharmony_ci kfree(blockread); 2218c2ecf20Sopenharmony_cioutB: 2228c2ecf20Sopenharmony_ci return error; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* communicate with maple bus for phased writing */ 2268c2ecf20Sopenharmony_cistatic int maple_vmu_write_block(unsigned int num, const unsigned char *buf, 2278c2ecf20Sopenharmony_ci struct mtd_info *mtd) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct memcard *card; 2308c2ecf20Sopenharmony_ci struct mdev_part *mpart; 2318c2ecf20Sopenharmony_ci struct maple_device *mdev; 2328c2ecf20Sopenharmony_ci int partition, error, locking, x, phaselen, wait; 2338c2ecf20Sopenharmony_ci __be32 *sendbuf; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci mpart = mtd->priv; 2368c2ecf20Sopenharmony_ci mdev = mpart->mdev; 2378c2ecf20Sopenharmony_ci partition = mpart->partition; 2388c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci phaselen = card->blocklen/card->writecnt; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci sendbuf = kmalloc(phaselen + 4, GFP_KERNEL); 2438c2ecf20Sopenharmony_ci if (!sendbuf) { 2448c2ecf20Sopenharmony_ci error = -ENOMEM; 2458c2ecf20Sopenharmony_ci goto fail_nosendbuf; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci for (x = 0; x < card->writecnt; x++) { 2488c2ecf20Sopenharmony_ci sendbuf[0] = cpu_to_be32(partition << 24 | x << 16 | num); 2498c2ecf20Sopenharmony_ci memcpy(&sendbuf[1], buf + phaselen * x, phaselen); 2508c2ecf20Sopenharmony_ci /* wait until the device is not busy doing something else 2518c2ecf20Sopenharmony_ci * or 1 second - which ever is longer */ 2528c2ecf20Sopenharmony_ci if (atomic_read(&mdev->busy) == 1) { 2538c2ecf20Sopenharmony_ci wait_event_interruptible_timeout(mdev->maple_wait, 2548c2ecf20Sopenharmony_ci atomic_read(&mdev->busy) == 0, HZ); 2558c2ecf20Sopenharmony_ci if (atomic_read(&mdev->busy) == 1) { 2568c2ecf20Sopenharmony_ci error = -EBUSY; 2578c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, "VMU write at (%d, %d)" 2588c2ecf20Sopenharmony_ci "failed - device is busy\n", 2598c2ecf20Sopenharmony_ci mdev->port, mdev->unit); 2608c2ecf20Sopenharmony_ci goto fail_nolock; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 1); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD, 2668c2ecf20Sopenharmony_ci MAPLE_COMMAND_BWRITE, phaselen / 4 + 2, sendbuf); 2678c2ecf20Sopenharmony_ci wait = wait_event_interruptible_timeout(mdev->maple_wait, 2688c2ecf20Sopenharmony_ci atomic_read(&mdev->busy) == 0, HZ/10); 2698c2ecf20Sopenharmony_ci if (locking) { 2708c2ecf20Sopenharmony_ci error = -EIO; 2718c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 0); 2728c2ecf20Sopenharmony_ci goto fail_nolock; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci if (atomic_read(&mdev->busy) == 2) { 2758c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 0); 2768c2ecf20Sopenharmony_ci } else if (wait == 0 || wait == -ERESTARTSYS) { 2778c2ecf20Sopenharmony_ci error = -EIO; 2788c2ecf20Sopenharmony_ci dev_warn(&mdev->dev, "Write at (%d, %d) of block" 2798c2ecf20Sopenharmony_ci " 0x%X at phase %d failed: could not" 2808c2ecf20Sopenharmony_ci " communicate with VMU", mdev->port, 2818c2ecf20Sopenharmony_ci mdev->unit, num, x); 2828c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 0); 2838c2ecf20Sopenharmony_ci kfree(mdev->mq->sendbuf); 2848c2ecf20Sopenharmony_ci mdev->mq->sendbuf = NULL; 2858c2ecf20Sopenharmony_ci list_del_init(&(mdev->mq->list)); 2868c2ecf20Sopenharmony_ci goto fail_nolock; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci kfree(sendbuf); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return card->blocklen; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cifail_nolock: 2948c2ecf20Sopenharmony_ci kfree(sendbuf); 2958c2ecf20Sopenharmony_cifail_nosendbuf: 2968c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "VMU (%d, %d): write failed\n", mdev->port, 2978c2ecf20Sopenharmony_ci mdev->unit); 2988c2ecf20Sopenharmony_ci return error; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* mtd function to simulate reading byte by byte */ 3028c2ecf20Sopenharmony_cistatic unsigned char vmu_flash_read_char(unsigned long ofs, int *retval, 3038c2ecf20Sopenharmony_ci struct mtd_info *mtd) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct vmu_block *vblock; 3068c2ecf20Sopenharmony_ci struct memcard *card; 3078c2ecf20Sopenharmony_ci struct mdev_part *mpart; 3088c2ecf20Sopenharmony_ci struct maple_device *mdev; 3098c2ecf20Sopenharmony_ci unsigned char *buf, ret; 3108c2ecf20Sopenharmony_ci int partition, error; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci mpart = mtd->priv; 3138c2ecf20Sopenharmony_ci mdev = mpart->mdev; 3148c2ecf20Sopenharmony_ci partition = mpart->partition; 3158c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 3168c2ecf20Sopenharmony_ci *retval = 0; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci buf = kmalloc(card->blocklen, GFP_KERNEL); 3198c2ecf20Sopenharmony_ci if (!buf) { 3208c2ecf20Sopenharmony_ci *retval = 1; 3218c2ecf20Sopenharmony_ci ret = -ENOMEM; 3228c2ecf20Sopenharmony_ci goto finish; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci vblock = ofs_to_block(ofs, mtd, partition); 3268c2ecf20Sopenharmony_ci if (!vblock) { 3278c2ecf20Sopenharmony_ci *retval = 3; 3288c2ecf20Sopenharmony_ci ret = -ENOMEM; 3298c2ecf20Sopenharmony_ci goto out_buf; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci error = maple_vmu_read_block(vblock->num, buf, mtd); 3338c2ecf20Sopenharmony_ci if (error) { 3348c2ecf20Sopenharmony_ci ret = error; 3358c2ecf20Sopenharmony_ci *retval = 2; 3368c2ecf20Sopenharmony_ci goto out_vblock; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = buf[vblock->ofs]; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ciout_vblock: 3428c2ecf20Sopenharmony_ci kfree(vblock); 3438c2ecf20Sopenharmony_ciout_buf: 3448c2ecf20Sopenharmony_ci kfree(buf); 3458c2ecf20Sopenharmony_cifinish: 3468c2ecf20Sopenharmony_ci return ret; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci/* mtd higher order function to read flash */ 3508c2ecf20Sopenharmony_cistatic int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len, 3518c2ecf20Sopenharmony_ci size_t *retlen, u_char *buf) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct maple_device *mdev; 3548c2ecf20Sopenharmony_ci struct memcard *card; 3558c2ecf20Sopenharmony_ci struct mdev_part *mpart; 3568c2ecf20Sopenharmony_ci struct vmu_cache *pcache; 3578c2ecf20Sopenharmony_ci struct vmu_block *vblock; 3588c2ecf20Sopenharmony_ci int index = 0, retval, partition, leftover, numblocks; 3598c2ecf20Sopenharmony_ci unsigned char cx; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci mpart = mtd->priv; 3628c2ecf20Sopenharmony_ci mdev = mpart->mdev; 3638c2ecf20Sopenharmony_ci partition = mpart->partition; 3648c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci numblocks = card->parts[partition].numblocks; 3678c2ecf20Sopenharmony_ci if (from + len > numblocks * card->blocklen) 3688c2ecf20Sopenharmony_ci len = numblocks * card->blocklen - from; 3698c2ecf20Sopenharmony_ci if (len == 0) 3708c2ecf20Sopenharmony_ci return -EIO; 3718c2ecf20Sopenharmony_ci /* Have we cached this bit already? */ 3728c2ecf20Sopenharmony_ci pcache = card->parts[partition].pcache; 3738c2ecf20Sopenharmony_ci do { 3748c2ecf20Sopenharmony_ci vblock = ofs_to_block(from + index, mtd, partition); 3758c2ecf20Sopenharmony_ci if (!vblock) 3768c2ecf20Sopenharmony_ci return -ENOMEM; 3778c2ecf20Sopenharmony_ci /* Have we cached this and is the cache valid and timely? */ 3788c2ecf20Sopenharmony_ci if (pcache->valid && 3798c2ecf20Sopenharmony_ci time_before(jiffies, pcache->jiffies_atc + HZ) && 3808c2ecf20Sopenharmony_ci (pcache->block == vblock->num)) { 3818c2ecf20Sopenharmony_ci /* we have cached it, so do necessary copying */ 3828c2ecf20Sopenharmony_ci leftover = card->blocklen - vblock->ofs; 3838c2ecf20Sopenharmony_ci if (vblock->ofs + len - index < card->blocklen) { 3848c2ecf20Sopenharmony_ci /* only a bit of this block to copy */ 3858c2ecf20Sopenharmony_ci memcpy(buf + index, 3868c2ecf20Sopenharmony_ci pcache->buffer + vblock->ofs, 3878c2ecf20Sopenharmony_ci len - index); 3888c2ecf20Sopenharmony_ci index = len; 3898c2ecf20Sopenharmony_ci } else { 3908c2ecf20Sopenharmony_ci /* otherwise copy remainder of whole block */ 3918c2ecf20Sopenharmony_ci memcpy(buf + index, pcache->buffer + 3928c2ecf20Sopenharmony_ci vblock->ofs, leftover); 3938c2ecf20Sopenharmony_ci index += leftover; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci } else { 3968c2ecf20Sopenharmony_ci /* 3978c2ecf20Sopenharmony_ci * Not cached so read one byte - 3988c2ecf20Sopenharmony_ci * but cache the rest of the block 3998c2ecf20Sopenharmony_ci */ 4008c2ecf20Sopenharmony_ci cx = vmu_flash_read_char(from + index, &retval, mtd); 4018c2ecf20Sopenharmony_ci if (retval) { 4028c2ecf20Sopenharmony_ci *retlen = index; 4038c2ecf20Sopenharmony_ci kfree(vblock); 4048c2ecf20Sopenharmony_ci return cx; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci memset(buf + index, cx, 1); 4078c2ecf20Sopenharmony_ci index++; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci kfree(vblock); 4108c2ecf20Sopenharmony_ci } while (len > index); 4118c2ecf20Sopenharmony_ci *retlen = index; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len, 4178c2ecf20Sopenharmony_ci size_t *retlen, const u_char *buf) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct maple_device *mdev; 4208c2ecf20Sopenharmony_ci struct memcard *card; 4218c2ecf20Sopenharmony_ci struct mdev_part *mpart; 4228c2ecf20Sopenharmony_ci int index = 0, partition, error = 0, numblocks; 4238c2ecf20Sopenharmony_ci struct vmu_cache *pcache; 4248c2ecf20Sopenharmony_ci struct vmu_block *vblock; 4258c2ecf20Sopenharmony_ci unsigned char *buffer; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci mpart = mtd->priv; 4288c2ecf20Sopenharmony_ci mdev = mpart->mdev; 4298c2ecf20Sopenharmony_ci partition = mpart->partition; 4308c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci numblocks = card->parts[partition].numblocks; 4338c2ecf20Sopenharmony_ci if (to + len > numblocks * card->blocklen) 4348c2ecf20Sopenharmony_ci len = numblocks * card->blocklen - to; 4358c2ecf20Sopenharmony_ci if (len == 0) { 4368c2ecf20Sopenharmony_ci error = -EIO; 4378c2ecf20Sopenharmony_ci goto failed; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci vblock = ofs_to_block(to, mtd, partition); 4418c2ecf20Sopenharmony_ci if (!vblock) { 4428c2ecf20Sopenharmony_ci error = -ENOMEM; 4438c2ecf20Sopenharmony_ci goto failed; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci buffer = kmalloc(card->blocklen, GFP_KERNEL); 4478c2ecf20Sopenharmony_ci if (!buffer) { 4488c2ecf20Sopenharmony_ci error = -ENOMEM; 4498c2ecf20Sopenharmony_ci goto fail_buffer; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci do { 4538c2ecf20Sopenharmony_ci /* Read in the block we are to write to */ 4548c2ecf20Sopenharmony_ci error = maple_vmu_read_block(vblock->num, buffer, mtd); 4558c2ecf20Sopenharmony_ci if (error) 4568c2ecf20Sopenharmony_ci goto fail_io; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci do { 4598c2ecf20Sopenharmony_ci buffer[vblock->ofs] = buf[index]; 4608c2ecf20Sopenharmony_ci vblock->ofs++; 4618c2ecf20Sopenharmony_ci index++; 4628c2ecf20Sopenharmony_ci if (index >= len) 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci } while (vblock->ofs < card->blocklen); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* write out new buffer */ 4678c2ecf20Sopenharmony_ci error = maple_vmu_write_block(vblock->num, buffer, mtd); 4688c2ecf20Sopenharmony_ci /* invalidate the cache */ 4698c2ecf20Sopenharmony_ci pcache = card->parts[partition].pcache; 4708c2ecf20Sopenharmony_ci pcache->valid = 0; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (error != card->blocklen) 4738c2ecf20Sopenharmony_ci goto fail_io; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci vblock->num++; 4768c2ecf20Sopenharmony_ci vblock->ofs = 0; 4778c2ecf20Sopenharmony_ci } while (len > index); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci kfree(buffer); 4808c2ecf20Sopenharmony_ci *retlen = index; 4818c2ecf20Sopenharmony_ci kfree(vblock); 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cifail_io: 4858c2ecf20Sopenharmony_ci kfree(buffer); 4868c2ecf20Sopenharmony_cifail_buffer: 4878c2ecf20Sopenharmony_ci kfree(vblock); 4888c2ecf20Sopenharmony_cifailed: 4898c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "VMU write failing with error %d\n", error); 4908c2ecf20Sopenharmony_ci return error; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic void vmu_flash_sync(struct mtd_info *mtd) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci /* Do nothing here */ 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/* Maple bus callback function to recursively query hardware details */ 4998c2ecf20Sopenharmony_cistatic void vmu_queryblocks(struct mapleq *mq) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct maple_device *mdev; 5028c2ecf20Sopenharmony_ci unsigned short *res; 5038c2ecf20Sopenharmony_ci struct memcard *card; 5048c2ecf20Sopenharmony_ci __be32 partnum; 5058c2ecf20Sopenharmony_ci struct vmu_cache *pcache; 5068c2ecf20Sopenharmony_ci struct mdev_part *mpart; 5078c2ecf20Sopenharmony_ci struct mtd_info *mtd_cur; 5088c2ecf20Sopenharmony_ci struct vmupart *part_cur; 5098c2ecf20Sopenharmony_ci int error; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci mdev = mq->dev; 5128c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 5138c2ecf20Sopenharmony_ci res = (unsigned short *) (mq->recvbuf->buf); 5148c2ecf20Sopenharmony_ci card->tempA = res[12]; 5158c2ecf20Sopenharmony_ci card->tempB = res[6]; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci dev_info(&mdev->dev, "VMU device at partition %d has %d user " 5188c2ecf20Sopenharmony_ci "blocks with a root block at %d\n", card->partition, 5198c2ecf20Sopenharmony_ci card->tempA, card->tempB); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci part_cur = &card->parts[card->partition]; 5228c2ecf20Sopenharmony_ci part_cur->user_blocks = card->tempA; 5238c2ecf20Sopenharmony_ci part_cur->root_block = card->tempB; 5248c2ecf20Sopenharmony_ci part_cur->numblocks = card->tempB + 1; 5258c2ecf20Sopenharmony_ci part_cur->name = kmalloc(12, GFP_KERNEL); 5268c2ecf20Sopenharmony_ci if (!part_cur->name) 5278c2ecf20Sopenharmony_ci goto fail_name; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci sprintf(part_cur->name, "vmu%d.%d.%d", 5308c2ecf20Sopenharmony_ci mdev->port, mdev->unit, card->partition); 5318c2ecf20Sopenharmony_ci mtd_cur = &card->mtd[card->partition]; 5328c2ecf20Sopenharmony_ci mtd_cur->name = part_cur->name; 5338c2ecf20Sopenharmony_ci mtd_cur->type = 8; 5348c2ecf20Sopenharmony_ci mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE; 5358c2ecf20Sopenharmony_ci mtd_cur->size = part_cur->numblocks * card->blocklen; 5368c2ecf20Sopenharmony_ci mtd_cur->erasesize = card->blocklen; 5378c2ecf20Sopenharmony_ci mtd_cur->_write = vmu_flash_write; 5388c2ecf20Sopenharmony_ci mtd_cur->_read = vmu_flash_read; 5398c2ecf20Sopenharmony_ci mtd_cur->_sync = vmu_flash_sync; 5408c2ecf20Sopenharmony_ci mtd_cur->writesize = card->blocklen; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL); 5438c2ecf20Sopenharmony_ci if (!mpart) 5448c2ecf20Sopenharmony_ci goto fail_mpart; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci mpart->mdev = mdev; 5478c2ecf20Sopenharmony_ci mpart->partition = card->partition; 5488c2ecf20Sopenharmony_ci mtd_cur->priv = mpart; 5498c2ecf20Sopenharmony_ci mtd_cur->owner = THIS_MODULE; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL); 5528c2ecf20Sopenharmony_ci if (!pcache) 5538c2ecf20Sopenharmony_ci goto fail_cache_create; 5548c2ecf20Sopenharmony_ci part_cur->pcache = pcache; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci error = mtd_device_register(mtd_cur, NULL, 0); 5578c2ecf20Sopenharmony_ci if (error) 5588c2ecf20Sopenharmony_ci goto fail_mtd_register; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci maple_getcond_callback(mdev, NULL, 0, 5618c2ecf20Sopenharmony_ci MAPLE_FUNC_MEMCARD); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci /* 5648c2ecf20Sopenharmony_ci * Set up a recursive call to the (probably theoretical) 5658c2ecf20Sopenharmony_ci * second or more partition 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ci if (++card->partition < card->partitions) { 5688c2ecf20Sopenharmony_ci partnum = cpu_to_be32(card->partition << 24); 5698c2ecf20Sopenharmony_ci maple_getcond_callback(mdev, vmu_queryblocks, 0, 5708c2ecf20Sopenharmony_ci MAPLE_FUNC_MEMCARD); 5718c2ecf20Sopenharmony_ci maple_add_packet(mdev, MAPLE_FUNC_MEMCARD, 5728c2ecf20Sopenharmony_ci MAPLE_COMMAND_GETMINFO, 2, &partnum); 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci return; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cifail_mtd_register: 5778c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "Could not register maple device at (%d, %d)" 5788c2ecf20Sopenharmony_ci "error is 0x%X\n", mdev->port, mdev->unit, error); 5798c2ecf20Sopenharmony_ci for (error = 0; error <= card->partition; error++) { 5808c2ecf20Sopenharmony_ci kfree(((card->parts)[error]).pcache); 5818c2ecf20Sopenharmony_ci ((card->parts)[error]).pcache = NULL; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_cifail_cache_create: 5848c2ecf20Sopenharmony_cifail_mpart: 5858c2ecf20Sopenharmony_ci for (error = 0; error <= card->partition; error++) { 5868c2ecf20Sopenharmony_ci kfree(((card->mtd)[error]).priv); 5878c2ecf20Sopenharmony_ci ((card->mtd)[error]).priv = NULL; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci maple_getcond_callback(mdev, NULL, 0, 5908c2ecf20Sopenharmony_ci MAPLE_FUNC_MEMCARD); 5918c2ecf20Sopenharmony_ci kfree(part_cur->name); 5928c2ecf20Sopenharmony_cifail_name: 5938c2ecf20Sopenharmony_ci return; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci/* Handles very basic info about the flash, queries for details */ 5978c2ecf20Sopenharmony_cistatic int vmu_connect(struct maple_device *mdev) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci unsigned long test_flash_data, basic_flash_data; 6008c2ecf20Sopenharmony_ci int c, error; 6018c2ecf20Sopenharmony_ci struct memcard *card; 6028c2ecf20Sopenharmony_ci u32 partnum = 0; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci test_flash_data = be32_to_cpu(mdev->devinfo.function); 6058c2ecf20Sopenharmony_ci /* Need to count how many bits are set - to find out which 6068c2ecf20Sopenharmony_ci * function_data element has details of the memory card 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci c = hweight_long(test_flash_data); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci card = kmalloc(sizeof(struct memcard), GFP_KERNEL); 6138c2ecf20Sopenharmony_ci if (!card) { 6148c2ecf20Sopenharmony_ci error = -ENOMEM; 6158c2ecf20Sopenharmony_ci goto fail_nomem; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci card->partitions = (basic_flash_data >> 24 & 0xFF) + 1; 6198c2ecf20Sopenharmony_ci card->blocklen = ((basic_flash_data >> 16 & 0xFF) + 1) << 5; 6208c2ecf20Sopenharmony_ci card->writecnt = basic_flash_data >> 12 & 0xF; 6218c2ecf20Sopenharmony_ci card->readcnt = basic_flash_data >> 8 & 0xF; 6228c2ecf20Sopenharmony_ci card->removable = basic_flash_data >> 7 & 1; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci card->partition = 0; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* 6278c2ecf20Sopenharmony_ci * Not sure there are actually any multi-partition devices in the 6288c2ecf20Sopenharmony_ci * real world, but the hardware supports them, so, so will we 6298c2ecf20Sopenharmony_ci */ 6308c2ecf20Sopenharmony_ci card->parts = kmalloc_array(card->partitions, sizeof(struct vmupart), 6318c2ecf20Sopenharmony_ci GFP_KERNEL); 6328c2ecf20Sopenharmony_ci if (!card->parts) { 6338c2ecf20Sopenharmony_ci error = -ENOMEM; 6348c2ecf20Sopenharmony_ci goto fail_partitions; 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci card->mtd = kmalloc_array(card->partitions, sizeof(struct mtd_info), 6388c2ecf20Sopenharmony_ci GFP_KERNEL); 6398c2ecf20Sopenharmony_ci if (!card->mtd) { 6408c2ecf20Sopenharmony_ci error = -ENOMEM; 6418c2ecf20Sopenharmony_ci goto fail_mtd_info; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci maple_set_drvdata(mdev, card); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* 6478c2ecf20Sopenharmony_ci * We want to trap meminfo not get cond 6488c2ecf20Sopenharmony_ci * so set interval to zero, but rely on maple bus 6498c2ecf20Sopenharmony_ci * driver to pass back the results of the meminfo 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci maple_getcond_callback(mdev, vmu_queryblocks, 0, 6528c2ecf20Sopenharmony_ci MAPLE_FUNC_MEMCARD); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* Make sure we are clear to go */ 6558c2ecf20Sopenharmony_ci if (atomic_read(&mdev->busy) == 1) { 6568c2ecf20Sopenharmony_ci wait_event_interruptible_timeout(mdev->maple_wait, 6578c2ecf20Sopenharmony_ci atomic_read(&mdev->busy) == 0, HZ); 6588c2ecf20Sopenharmony_ci if (atomic_read(&mdev->busy) == 1) { 6598c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, "VMU at (%d, %d) is busy\n", 6608c2ecf20Sopenharmony_ci mdev->port, mdev->unit); 6618c2ecf20Sopenharmony_ci error = -EAGAIN; 6628c2ecf20Sopenharmony_ci goto fail_device_busy; 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci atomic_set(&mdev->busy, 1); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* 6698c2ecf20Sopenharmony_ci * Set up the minfo call: vmu_queryblocks will handle 6708c2ecf20Sopenharmony_ci * the information passed back 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD, 6738c2ecf20Sopenharmony_ci MAPLE_COMMAND_GETMINFO, 2, &partnum); 6748c2ecf20Sopenharmony_ci if (error) { 6758c2ecf20Sopenharmony_ci dev_err(&mdev->dev, "Could not lock VMU at (%d, %d)" 6768c2ecf20Sopenharmony_ci " error is 0x%X\n", mdev->port, mdev->unit, error); 6778c2ecf20Sopenharmony_ci goto fail_mtd_info; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci return 0; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cifail_device_busy: 6828c2ecf20Sopenharmony_ci kfree(card->mtd); 6838c2ecf20Sopenharmony_cifail_mtd_info: 6848c2ecf20Sopenharmony_ci kfree(card->parts); 6858c2ecf20Sopenharmony_cifail_partitions: 6868c2ecf20Sopenharmony_ci kfree(card); 6878c2ecf20Sopenharmony_cifail_nomem: 6888c2ecf20Sopenharmony_ci return error; 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic void vmu_disconnect(struct maple_device *mdev) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct memcard *card; 6948c2ecf20Sopenharmony_ci struct mdev_part *mpart; 6958c2ecf20Sopenharmony_ci int x; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci mdev->callback = NULL; 6988c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 6998c2ecf20Sopenharmony_ci for (x = 0; x < card->partitions; x++) { 7008c2ecf20Sopenharmony_ci mpart = ((card->mtd)[x]).priv; 7018c2ecf20Sopenharmony_ci mpart->mdev = NULL; 7028c2ecf20Sopenharmony_ci mtd_device_unregister(&((card->mtd)[x])); 7038c2ecf20Sopenharmony_ci kfree(((card->parts)[x]).name); 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci kfree(card->parts); 7068c2ecf20Sopenharmony_ci kfree(card->mtd); 7078c2ecf20Sopenharmony_ci kfree(card); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci/* Callback to handle eccentricities of both mtd subsystem 7118c2ecf20Sopenharmony_ci * and general flakyness of Dreamcast VMUs 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_cistatic int vmu_can_unload(struct maple_device *mdev) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct memcard *card; 7168c2ecf20Sopenharmony_ci int x; 7178c2ecf20Sopenharmony_ci struct mtd_info *mtd; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci card = maple_get_drvdata(mdev); 7208c2ecf20Sopenharmony_ci for (x = 0; x < card->partitions; x++) { 7218c2ecf20Sopenharmony_ci mtd = &((card->mtd)[x]); 7228c2ecf20Sopenharmony_ci if (mtd->usecount > 0) 7238c2ecf20Sopenharmony_ci return 0; 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci return 1; 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci#define ERRSTR "VMU at (%d, %d) file error -" 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic void vmu_file_error(struct maple_device *mdev, void *recvbuf) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci enum maple_file_errors error = ((int *)recvbuf)[1]; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci switch (error) { 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci case MAPLE_FILEERR_INVALID_PARTITION: 7378c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, ERRSTR " invalid partition number\n", 7388c2ecf20Sopenharmony_ci mdev->port, mdev->unit); 7398c2ecf20Sopenharmony_ci break; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci case MAPLE_FILEERR_PHASE_ERROR: 7428c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, ERRSTR " phase error\n", 7438c2ecf20Sopenharmony_ci mdev->port, mdev->unit); 7448c2ecf20Sopenharmony_ci break; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci case MAPLE_FILEERR_INVALID_BLOCK: 7478c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, ERRSTR " invalid block number\n", 7488c2ecf20Sopenharmony_ci mdev->port, mdev->unit); 7498c2ecf20Sopenharmony_ci break; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci case MAPLE_FILEERR_WRITE_ERROR: 7528c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, ERRSTR " write error\n", 7538c2ecf20Sopenharmony_ci mdev->port, mdev->unit); 7548c2ecf20Sopenharmony_ci break; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci case MAPLE_FILEERR_INVALID_WRITE_LENGTH: 7578c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, ERRSTR " invalid write length\n", 7588c2ecf20Sopenharmony_ci mdev->port, mdev->unit); 7598c2ecf20Sopenharmony_ci break; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci case MAPLE_FILEERR_BAD_CRC: 7628c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, ERRSTR " bad CRC\n", 7638c2ecf20Sopenharmony_ci mdev->port, mdev->unit); 7648c2ecf20Sopenharmony_ci break; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci default: 7678c2ecf20Sopenharmony_ci dev_notice(&mdev->dev, ERRSTR " 0x%X\n", 7688c2ecf20Sopenharmony_ci mdev->port, mdev->unit, error); 7698c2ecf20Sopenharmony_ci } 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic int probe_maple_vmu(struct device *dev) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct maple_device *mdev = to_maple_dev(dev); 7768c2ecf20Sopenharmony_ci struct maple_driver *mdrv = to_maple_driver(dev->driver); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci mdev->can_unload = vmu_can_unload; 7798c2ecf20Sopenharmony_ci mdev->fileerr_handler = vmu_file_error; 7808c2ecf20Sopenharmony_ci mdev->driver = mdrv; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci return vmu_connect(mdev); 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int remove_maple_vmu(struct device *dev) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct maple_device *mdev = to_maple_dev(dev); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci vmu_disconnect(mdev); 7908c2ecf20Sopenharmony_ci return 0; 7918c2ecf20Sopenharmony_ci} 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_cistatic struct maple_driver vmu_flash_driver = { 7948c2ecf20Sopenharmony_ci .function = MAPLE_FUNC_MEMCARD, 7958c2ecf20Sopenharmony_ci .drv = { 7968c2ecf20Sopenharmony_ci .name = "Dreamcast_visual_memory", 7978c2ecf20Sopenharmony_ci .probe = probe_maple_vmu, 7988c2ecf20Sopenharmony_ci .remove = remove_maple_vmu, 7998c2ecf20Sopenharmony_ci }, 8008c2ecf20Sopenharmony_ci}; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic int __init vmu_flash_map_init(void) 8038c2ecf20Sopenharmony_ci{ 8048c2ecf20Sopenharmony_ci return maple_driver_register(&vmu_flash_driver); 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic void __exit vmu_flash_map_exit(void) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci maple_driver_unregister(&vmu_flash_driver); 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cimodule_init(vmu_flash_map_init); 8138c2ecf20Sopenharmony_cimodule_exit(vmu_flash_map_exit); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8168c2ecf20Sopenharmony_ciMODULE_AUTHOR("Adrian McMenamin"); 8178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Flash mapping for Sega Dreamcast visual memory"); 818