diff options
Diffstat (limited to 'drivers/memstick')
| -rw-r--r-- | drivers/memstick/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/memstick/Makefile | 1 | ||||
| -rw-r--r-- | drivers/memstick/core/Kconfig | 33 | ||||
| -rw-r--r-- | drivers/memstick/core/Makefile | 3 | ||||
| -rw-r--r-- | drivers/memstick/core/memstick.c | 152 | ||||
| -rw-r--r-- | drivers/memstick/core/ms_block.c | 2343 | ||||
| -rw-r--r-- | drivers/memstick/core/ms_block.h | 286 | ||||
| -rw-r--r-- | drivers/memstick/core/mspro_block.c | 446 | ||||
| -rw-r--r-- | drivers/memstick/host/Kconfig | 15 | ||||
| -rw-r--r-- | drivers/memstick/host/Makefile | 3 | ||||
| -rw-r--r-- | drivers/memstick/host/jmb38x_ms.c | 107 | ||||
| -rw-r--r-- | drivers/memstick/host/r592.c | 78 | ||||
| -rw-r--r-- | drivers/memstick/host/r592.h | 5 | ||||
| -rw-r--r-- | drivers/memstick/host/rtsx_pci_ms.c | 648 | ||||
| -rw-r--r-- | drivers/memstick/host/rtsx_usb_ms.c | 872 | ||||
| -rw-r--r-- | drivers/memstick/host/tifm_ms.c | 40 |
16 files changed, 3900 insertions, 1133 deletions
diff --git a/drivers/memstick/Kconfig b/drivers/memstick/Kconfig index 1314605d791f..e6180135d48b 100644 --- a/drivers/memstick/Kconfig +++ b/drivers/memstick/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # MemoryStick subsystem configuration # diff --git a/drivers/memstick/Makefile b/drivers/memstick/Makefile index 98623590c7fe..61ea1d3abd37 100644 --- a/drivers/memstick/Makefile +++ b/drivers/memstick/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Makefile for the kernel MemoryStick device drivers. # diff --git a/drivers/memstick/core/Kconfig b/drivers/memstick/core/Kconfig index 95f1814b5368..50fa0711da9d 100644 --- a/drivers/memstick/core/Kconfig +++ b/drivers/memstick/core/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # MemoryStick core configuration # @@ -5,22 +6,36 @@ comment "MemoryStick drivers" config MEMSTICK_UNSAFE_RESUME - bool "Allow unsafe resume (DANGEROUS)" - help - If you say Y here, the MemoryStick layer will assume that all - cards stayed in their respective slots during the suspend. The - normal behaviour is to remove them at suspend and - redetecting them at resume. Breaking this assumption will - in most cases result in data corruption. + bool "Allow unsafe resume (DANGEROUS)" + help + If you say Y here, the MemoryStick layer will assume that all + cards stayed in their respective slots during the suspend. The + normal behaviour is to remove them at suspend and + redetecting them at resume. Breaking this assumption will + in most cases result in data corruption. - This option is usually just for embedded systems which use - a MemoryStick card for rootfs. Most people should say N here. + This option is usually just for embedded systems which use + a MemoryStick card for rootfs. Most people should say N here. config MSPRO_BLOCK tristate "MemoryStick Pro block device driver" depends on BLOCK + imply IOSCHED_BFQ help Say Y here to enable the MemoryStick Pro block device driver support. This provides a block device driver, which you can use to mount the filesystem. Almost everyone wishing MemoryStick support should say Y or M here. + +config MS_BLOCK + tristate "MemoryStick Standard device driver" + depends on BLOCK + imply IOSCHED_BFQ + help + Say Y here to enable the MemoryStick Standard device driver + support. This provides a block device driver, which you can use + to mount the filesystem. + This driver works with old (bulky) MemoryStick and MemoryStick Duo + but not PRO. Say Y if you have such card. + Driver is new and not yet well tested, thus it can damage your card + (even permanently) diff --git a/drivers/memstick/core/Makefile b/drivers/memstick/core/Makefile index ecd029937738..6b9b9ba4ab69 100644 --- a/drivers/memstick/core/Makefile +++ b/drivers/memstick/core/Makefile @@ -1,7 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Makefile for the kernel MemoryStick core. # obj-$(CONFIG_MEMSTICK) += memstick.o - +obj-$(CONFIG_MS_BLOCK) += ms_block.o obj-$(CONFIG_MSPRO_BLOCK) += mspro_block.o diff --git a/drivers/memstick/core/memstick.c b/drivers/memstick/core/memstick.c index ffcb10ac4341..acafc910bbac 100644 --- a/drivers/memstick/core/memstick.c +++ b/drivers/memstick/core/memstick.c @@ -1,15 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Sony MemoryStick support * * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> * - * 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. - * * Special thanks to Carlos Corbacho for providing various MemoryStick cards * that made this driver possible. - * */ #include <linux/memstick.h> @@ -18,6 +14,7 @@ #include <linux/delay.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #define DRIVER_NAME "memstick" @@ -29,7 +26,7 @@ static DEFINE_IDR(memstick_host_idr); static DEFINE_SPINLOCK(memstick_host_lock); static int memstick_dev_match(struct memstick_dev *card, - struct memstick_device_id *id) + const struct memstick_device_id *id) { if (id->match_flags & MEMSTICK_MATCH_ALL) { if ((id->type == card->id.type) @@ -41,14 +38,13 @@ static int memstick_dev_match(struct memstick_dev *card, return 0; } -static int memstick_bus_match(struct device *dev, struct device_driver *drv) +static int memstick_bus_match(struct device *dev, const struct device_driver *drv) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); - struct memstick_driver *ms_drv = container_of(drv, - struct memstick_driver, - driver); - struct memstick_device_id *ids = ms_drv->id_table; + const struct memstick_driver *ms_drv = container_of_const(drv, struct memstick_driver, + driver); + const struct memstick_device_id *ids = ms_drv->id_table; if (ids) { while (ids->match_flags) { @@ -60,10 +56,10 @@ static int memstick_bus_match(struct device *dev, struct device_driver *drv) return 0; } -static int memstick_uevent(struct device *dev, struct kobj_uevent_env *env) +static int memstick_uevent(const struct device *dev, struct kobj_uevent_env *env) { - struct memstick_dev *card = container_of(dev, struct memstick_dev, - dev); + const struct memstick_dev *card = container_of_const(dev, struct memstick_dev, + dev); if (add_uevent_var(env, "MEMSTICK_TYPE=%02X", card->id.type)) return -ENOMEM; @@ -94,7 +90,7 @@ static int memstick_device_probe(struct device *dev) return rc; } -static int memstick_device_remove(struct device *dev) +static void memstick_device_remove(struct device *dev) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); @@ -108,7 +104,6 @@ static int memstick_device_remove(struct device *dev) } put_device(dev); - return 0; } #ifdef CONFIG_PM @@ -153,24 +148,24 @@ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ struct memstick_dev *card = container_of(dev, struct memstick_dev, \ dev); \ return sprintf(buf, format, card->id.name); \ -} +} \ +static DEVICE_ATTR_RO(name); MEMSTICK_ATTR(type, "%02X"); MEMSTICK_ATTR(category, "%02X"); MEMSTICK_ATTR(class, "%02X"); -#define MEMSTICK_ATTR_RO(name) __ATTR(name, S_IRUGO, name##_show, NULL) - -static struct device_attribute memstick_dev_attrs[] = { - MEMSTICK_ATTR_RO(type), - MEMSTICK_ATTR_RO(category), - MEMSTICK_ATTR_RO(class), - __ATTR_NULL +static struct attribute *memstick_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_category.attr, + &dev_attr_class.attr, + NULL, }; +ATTRIBUTE_GROUPS(memstick_dev); -static struct bus_type memstick_bus_type = { +static const struct bus_type memstick_bus_type = { .name = "memstick", - .dev_attrs = memstick_dev_attrs, + .dev_groups = memstick_dev_groups, .match = memstick_bus_match, .uevent = memstick_uevent, .probe = memstick_device_probe, @@ -205,7 +200,7 @@ static int memstick_dummy_check(struct memstick_dev *card) /** * memstick_detect_change - schedule media detection on memstick host - * @host - host to use + * @host: host to use */ void memstick_detect_change(struct memstick_host *host) { @@ -215,13 +210,15 @@ EXPORT_SYMBOL(memstick_detect_change); /** * memstick_next_req - called by host driver to obtain next request to process - * @host - host to use - * @mrq - pointer to stick the request to + * @host: host to use + * @mrq: pointer to stick the request to * * Host calls this function from idle state (*mrq == NULL) or after finishing * previous request (*mrq should point to it). If previous request was - * unsuccessful, it is retried for predetermined number of times. Return value - * of 0 means that new request was assigned to the host. + * unsuccessful, it is retried for predetermined number of times. + * + * Returns: value of 0 means that new request was assigned to the host. + * Otherwise a negative error code is returned. */ int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq) { @@ -247,13 +244,13 @@ EXPORT_SYMBOL(memstick_next_req); /** * memstick_new_req - notify the host that some requests are pending - * @host - host to use + * @host: host to use */ void memstick_new_req(struct memstick_host *host) { if (host->card) { host->retries = cmd_retries; - INIT_COMPLETION(host->card->mrq_complete); + reinit_completion(&host->card->mrq_complete); host->request(host); } } @@ -261,9 +258,9 @@ EXPORT_SYMBOL(memstick_new_req); /** * memstick_init_req_sg - set request fields needed for bulk data transfer - * @mrq - request to use - * @tpc - memstick Transport Protocol Command - * @sg - TPC argument + * @mrq: request to use + * @tpc: memstick Transport Protocol Command + * @sg: TPC argument */ void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc, const struct scatterlist *sg) @@ -286,10 +283,10 @@ EXPORT_SYMBOL(memstick_init_req_sg); /** * memstick_init_req - set request fields needed for short data transfer - * @mrq - request to use - * @tpc - memstick Transport Protocol Command - * @buf - TPC argument buffer - * @length - TPC argument size + * @mrq: request to use + * @tpc: memstick Transport Protocol Command + * @buf: TPC argument buffer + * @length: TPC argument size * * The intended use of this function (transfer of data items several bytes * in size) allows us to just copy the value between request structure and @@ -327,25 +324,24 @@ EXPORT_SYMBOL(memstick_init_req); static int h_memstick_read_dev_id(struct memstick_dev *card, struct memstick_request **mrq) { - struct ms_id_register id_reg; + struct ms_id_register id_reg = {}; if (!(*mrq)) { - memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL, + memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, &id_reg, sizeof(struct ms_id_register)); *mrq = &card->current_mrq; return 0; - } else { - if (!(*mrq)->error) { - memcpy(&id_reg, (*mrq)->data, sizeof(id_reg)); - card->id.match_flags = MEMSTICK_MATCH_ALL; - card->id.type = id_reg.type; - card->id.category = id_reg.category; - card->id.class = id_reg.class; - dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode); - } - complete(&card->mrq_complete); - return -EAGAIN; } + if (!(*mrq)->error) { + memcpy(&id_reg, (*mrq)->data, sizeof(id_reg)); + card->id.match_flags = MEMSTICK_MATCH_ALL; + card->id.type = id_reg.type; + card->id.category = id_reg.category; + card->id.class = id_reg.class; + dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode); + } + complete(&card->mrq_complete); + return -EAGAIN; } static int h_memstick_set_rw_addr(struct memstick_dev *card, @@ -366,13 +362,17 @@ static int h_memstick_set_rw_addr(struct memstick_dev *card, /** * memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to * complete - * @card - media device to use + * @card: media device to use + * + * Returns: error setting for the current request */ int memstick_set_rw_addr(struct memstick_dev *card) { card->next_request = h_memstick_set_rw_addr; memstick_new_req(card->host); - wait_for_completion(&card->mrq_complete); + if (!wait_for_completion_timeout(&card->mrq_complete, + msecs_to_jiffies(500))) + card->current_mrq.error = -ETIMEDOUT; return card->current_mrq.error; } @@ -406,7 +406,9 @@ static struct memstick_dev *memstick_alloc_card(struct memstick_host *host) card->next_request = h_memstick_read_dev_id; memstick_new_req(host); - wait_for_completion(&card->mrq_complete); + if (!wait_for_completion_timeout(&card->mrq_complete, + msecs_to_jiffies(500))) + card->current_mrq.error = -ETIMEDOUT; if (card->current_mrq.error) goto err_out; @@ -415,6 +417,7 @@ static struct memstick_dev *memstick_alloc_card(struct memstick_host *host) return card; err_out: host->card = old_card; + kfree_const(card->dev.kobj.name); kfree(card); return NULL; } @@ -436,6 +439,7 @@ static void memstick_check(struct work_struct *work) struct memstick_dev *card; dev_dbg(&host->dev, "memstick_check started\n"); + pm_runtime_get_noresume(host->dev.parent); mutex_lock(&host->lock); if (!host->card) { if (memstick_power_on(host)) @@ -443,6 +447,9 @@ static void memstick_check(struct work_struct *work) } else if (host->card->stop) host->card->stop(host->card); + if (host->removing) + goto out_power_off; + card = memstick_alloc_card(host); if (!card) { @@ -467,11 +474,12 @@ static void memstick_check(struct work_struct *work) host->card = card; if (device_register(&card->dev)) { put_device(&card->dev); - kfree(host->card); host->card = NULL; } - } else + } else { + kfree_const(card->dev.kobj.name); kfree(card); + } } out_power_off: @@ -479,6 +487,7 @@ out_power_off: host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); mutex_unlock(&host->lock); + pm_runtime_put(host->dev.parent); dev_dbg(&host->dev, "memstick_check finished\n"); } @@ -486,6 +495,8 @@ out_power_off: * memstick_alloc_host - allocate a memstick_host structure * @extra: size of the user private data to allocate * @dev: parent device of the host + * + * Returns: %NULL on failure or the allocated &memstick_host pointer on success */ struct memstick_host *memstick_alloc_host(unsigned int extra, struct device *dev) @@ -506,7 +517,9 @@ EXPORT_SYMBOL(memstick_alloc_host); /** * memstick_add_host - start request processing on memstick host - * @host - host to use + * @host: host to use + * + * Returns: %0 on success or a negative error code on failure */ int memstick_add_host(struct memstick_host *host) { @@ -542,7 +555,7 @@ EXPORT_SYMBOL(memstick_add_host); /** * memstick_remove_host - stop request processing on memstick host - * @host - host to use + * @host: host to use */ void memstick_remove_host(struct memstick_host *host) { @@ -563,7 +576,7 @@ EXPORT_SYMBOL(memstick_remove_host); /** * memstick_free_host - free memstick host - * @host - host to use + * @host: host to use */ void memstick_free_host(struct memstick_host *host) { @@ -574,7 +587,7 @@ EXPORT_SYMBOL(memstick_free_host); /** * memstick_suspend_host - notify bus driver of host suspension - * @host - host to use + * @host: host to use */ void memstick_suspend_host(struct memstick_host *host) { @@ -586,7 +599,7 @@ EXPORT_SYMBOL(memstick_suspend_host); /** * memstick_resume_host - notify bus driver of host resumption - * @host - host to use + * @host: host to use */ void memstick_resume_host(struct memstick_host *host) { @@ -626,13 +639,18 @@ static int __init memstick_init(void) return -ENOMEM; rc = bus_register(&memstick_bus_type); - if (!rc) - rc = class_register(&memstick_host_class); + if (rc) + goto error_destroy_workqueue; - if (!rc) - return 0; + rc = class_register(&memstick_host_class); + if (rc) + goto error_bus_unregister; + + return 0; +error_bus_unregister: bus_unregister(&memstick_bus_type); +error_destroy_workqueue: destroy_workqueue(workqueue); return rc; diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c new file mode 100644 index 000000000000..1af157ce0a63 --- /dev/null +++ b/drivers/memstick/core/ms_block.c @@ -0,0 +1,2343 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ms_block.c - Sony MemoryStick (legacy) storage support + + * Copyright (C) 2013 Maxim Levitsky <maximlevitsky@gmail.com> + * + * Minor portions of the driver were copied from mspro_block.c which is + * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> + */ +#define DRIVER_NAME "ms_block" +#define pr_fmt(fmt) DRIVER_NAME ": " fmt + +#include <linux/module.h> +#include <linux/blk-mq.h> +#include <linux/memstick.h> +#include <linux/idr.h> +#include <linux/hdreg.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/bitmap.h> +#include <linux/scatterlist.h> +#include <linux/jiffies.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include "ms_block.h" + +static int debug; +static int cache_flush_timeout = 1000; +static bool verify_writes; + +/* + * Copies section of 'sg_from' starting from offset 'offset' and with length + * 'len' To another scatterlist of to_nents enties + */ +static size_t msb_sg_copy(struct scatterlist *sg_from, + struct scatterlist *sg_to, int to_nents, size_t offset, size_t len) +{ + size_t copied = 0; + + while (offset > 0) { + if (offset >= sg_from->length) { + if (sg_is_last(sg_from)) + return 0; + + offset -= sg_from->length; + sg_from = sg_next(sg_from); + continue; + } + + copied = min(len, sg_from->length - offset); + sg_set_page(sg_to, sg_page(sg_from), + copied, sg_from->offset + offset); + + len -= copied; + offset = 0; + + if (sg_is_last(sg_from) || !len) + goto out; + + sg_to = sg_next(sg_to); + to_nents--; + sg_from = sg_next(sg_from); + } + + while (len > sg_from->length && to_nents--) { + len -= sg_from->length; + copied += sg_from->length; + + sg_set_page(sg_to, sg_page(sg_from), + sg_from->length, sg_from->offset); + + if (sg_is_last(sg_from) || !len) + goto out; + + sg_from = sg_next(sg_from); + sg_to = sg_next(sg_to); + } + + if (len && to_nents) { + sg_set_page(sg_to, sg_page(sg_from), len, sg_from->offset); + copied += len; + } +out: + sg_mark_end(sg_to); + return copied; +} + +/* + * Compares section of 'sg' starting from offset 'offset' and with length 'len' + * to linear buffer of length 'len' at address 'buffer' + * Returns 0 if equal and -1 otherwice + */ +static int msb_sg_compare_to_buffer(struct scatterlist *sg, + size_t offset, u8 *buffer, size_t len) +{ + int retval = 0, cmplen; + struct sg_mapping_iter miter; + + sg_miter_start(&miter, sg, sg_nents(sg), + SG_MITER_ATOMIC | SG_MITER_FROM_SG); + + while (sg_miter_next(&miter) && len > 0) { + if (offset >= miter.length) { + offset -= miter.length; + continue; + } + + cmplen = min(miter.length - offset, len); + retval = memcmp(miter.addr + offset, buffer, cmplen) ? -1 : 0; + if (retval) + break; + + buffer += cmplen; + len -= cmplen; + offset = 0; + } + + if (!retval && len) + retval = -1; + + sg_miter_stop(&miter); + return retval; +} + + +/* Get zone at which block with logical address 'lba' lives + * Flash is broken into zones. + * Each zone consists of 512 eraseblocks, out of which in first + * zone 494 are used and 496 are for all following zones. + * Therefore zone #0 hosts blocks 0-493, zone #1 blocks 494-988, etc... + */ +static int msb_get_zone_from_lba(int lba) +{ + if (lba < 494) + return 0; + return ((lba - 494) / 496) + 1; +} + +/* Get zone of physical block. Trivial */ +static int msb_get_zone_from_pba(int pba) +{ + return pba / MS_BLOCKS_IN_ZONE; +} + +/* Debug test to validate free block counts */ +static int msb_validate_used_block_bitmap(struct msb_data *msb) +{ + int total_free_blocks = 0; + int i; + + if (!debug) + return 0; + + for (i = 0; i < msb->zone_count; i++) + total_free_blocks += msb->free_block_count[i]; + + if (msb->block_count - bitmap_weight(msb->used_blocks_bitmap, + msb->block_count) == total_free_blocks) + return 0; + + pr_err("BUG: free block counts don't match the bitmap"); + msb->read_only = true; + return -EINVAL; +} + +/* Mark physical block as used */ +static void msb_mark_block_used(struct msb_data *msb, int pba) +{ + int zone = msb_get_zone_from_pba(pba); + + if (test_bit(pba, msb->used_blocks_bitmap)) { + pr_err( + "BUG: attempt to mark already used pba %d as used", pba); + msb->read_only = true; + return; + } + + if (msb_validate_used_block_bitmap(msb)) + return; + + /* No races because all IO is single threaded */ + __set_bit(pba, msb->used_blocks_bitmap); + msb->free_block_count[zone]--; +} + +/* Mark physical block as free */ +static void msb_mark_block_unused(struct msb_data *msb, int pba) +{ + int zone = msb_get_zone_from_pba(pba); + + if (!test_bit(pba, msb->used_blocks_bitmap)) { + pr_err("BUG: attempt to mark already unused pba %d as unused" , pba); + msb->read_only = true; + return; + } + + if (msb_validate_used_block_bitmap(msb)) + return; + + /* No races because all IO is single threaded */ + __clear_bit(pba, msb->used_blocks_bitmap); + msb->free_block_count[zone]++; +} + +/* Invalidate current register window */ +static void msb_invalidate_reg_window(struct msb_data *msb) +{ + msb->reg_addr.w_offset = offsetof(struct ms_register, id); + msb->reg_addr.w_length = sizeof(struct ms_id_register); + msb->reg_addr.r_offset = offsetof(struct ms_register, id); + msb->reg_addr.r_length = sizeof(struct ms_id_register); + msb->addr_valid = false; +} + +/* Start a state machine */ +static int msb_run_state_machine(struct msb_data *msb, int (*state_func) + (struct memstick_dev *card, struct memstick_request **req)) +{ + struct memstick_dev *card = msb->card; + + WARN_ON(msb->state != -1); + msb->int_polling = false; + msb->state = 0; + msb->exit_error = 0; + + memset(&card->current_mrq, 0, sizeof(card->current_mrq)); + + card->next_request = state_func; + memstick_new_req(card->host); + wait_for_completion(&card->mrq_complete); + + WARN_ON(msb->state != -1); + return msb->exit_error; +} + +/* State machines call that to exit */ +static int msb_exit_state_machine(struct msb_data *msb, int error) +{ + WARN_ON(msb->state == -1); + + msb->state = -1; + msb->exit_error = error; + msb->card->next_request = h_msb_default_bad; + + /* Invalidate reg window on errors */ + if (error) + msb_invalidate_reg_window(msb); + + complete(&msb->card->mrq_complete); + return -ENXIO; +} + +/* read INT register */ +static int msb_read_int_reg(struct msb_data *msb, long timeout) +{ + struct memstick_request *mrq = &msb->card->current_mrq; + + WARN_ON(msb->state == -1); + + if (!msb->int_polling) { + msb->int_timeout = jiffies + + msecs_to_jiffies(timeout == -1 ? 500 : timeout); + msb->int_polling = true; + } else if (time_after(jiffies, msb->int_timeout)) { + mrq->data[0] = MEMSTICK_INT_CMDNAK; + return 0; + } + + if ((msb->caps & MEMSTICK_CAP_AUTO_GET_INT) && + mrq->need_card_int && !mrq->error) { + mrq->data[0] = mrq->int_reg; + mrq->need_card_int = false; + return 0; + } else { + memstick_init_req(mrq, MS_TPC_GET_INT, NULL, 1); + return 1; + } +} + +/* Read a register */ +static int msb_read_regs(struct msb_data *msb, int offset, int len) +{ + struct memstick_request *req = &msb->card->current_mrq; + + if (msb->reg_addr.r_offset != offset || + msb->reg_addr.r_length != len || !msb->addr_valid) { + + msb->reg_addr.r_offset = offset; + msb->reg_addr.r_length = len; + msb->addr_valid = true; + + memstick_init_req(req, MS_TPC_SET_RW_REG_ADRS, + &msb->reg_addr, sizeof(msb->reg_addr)); + return 0; + } + + memstick_init_req(req, MS_TPC_READ_REG, NULL, len); + return 1; +} + +/* Write a card register */ +static int msb_write_regs(struct msb_data *msb, int offset, int len, void *buf) +{ + struct memstick_request *req = &msb->card->current_mrq; + + if (msb->reg_addr.w_offset != offset || + msb->reg_addr.w_length != len || !msb->addr_valid) { + + msb->reg_addr.w_offset = offset; + msb->reg_addr.w_length = len; + msb->addr_valid = true; + + memstick_init_req(req, MS_TPC_SET_RW_REG_ADRS, + &msb->reg_addr, sizeof(msb->reg_addr)); + return 0; + } + + memstick_init_req(req, MS_TPC_WRITE_REG, buf, len); + return 1; +} + +/* Handler for absence of IO */ +static int h_msb_default_bad(struct memstick_dev *card, + struct memstick_request **mrq) +{ + return -ENXIO; +} + +/* + * This function is a handler for reads of one page from device. + * Writes output to msb->current_sg, takes sector address from msb->reg.param + * Can also be used to read extra data only. Set params accordintly. + */ +static int h_msb_read_page(struct memstick_dev *card, + struct memstick_request **out_mrq) +{ + struct msb_data *msb = memstick_get_drvdata(card); + struct memstick_request *mrq = *out_mrq = &card->current_mrq; + struct scatterlist sg[2]; + u8 command, intreg; + + if (mrq->error) { + dbg("read_page, unknown error"); + return msb_exit_state_machine(msb, mrq->error); + } +again: + switch (msb->state) { + case MSB_RP_SEND_BLOCK_ADDRESS: + /* msb_write_regs sometimes "fails" because it needs to update + * the reg window, and thus it returns request for that. + * Then we stay in this state and retry + */ + if (!msb_write_regs(msb, + offsetof(struct ms_register, param), + sizeof(struct ms_param_register), + (unsigned char *)&msb->regs.param)) + return 0; + + msb->state = MSB_RP_SEND_READ_COMMAND; + return 0; + + case MSB_RP_SEND_READ_COMMAND: + command = MS_CMD_BLOCK_READ; + memstick_init_req(mrq, MS_TPC_SET_CMD, &command, 1); + msb->state = MSB_RP_SEND_INT_REQ; + return 0; + + case MSB_RP_SEND_INT_REQ: + msb->state = MSB_RP_RECEIVE_INT_REQ_RESULT; + /* If dont actually need to send the int read request (only in + * serial mode), then just fall through + */ + if (msb_read_int_reg(msb, -1)) + return 0; + fallthrough; + + case MSB_RP_RECEIVE_INT_REQ_RESULT: + intreg = mrq->data[0]; + msb->regs.status.interrupt = intreg; + + if (intreg & MEMSTICK_INT_CMDNAK) + return msb_exit_state_machine(msb, -EIO); + + if (!(intreg & MEMSTICK_INT_CED)) { + msb->state = MSB_RP_SEND_INT_REQ; + goto again; + } + + msb->int_polling = false; + msb->state = (intreg & MEMSTICK_INT_ERR) ? + MSB_RP_SEND_READ_STATUS_REG : MSB_RP_SEND_OOB_READ; + goto again; + + case MSB_RP_SEND_READ_STATUS_REG: + /* read the status register to understand source of the INT_ERR */ + if (!msb_read_regs(msb, + offsetof(struct ms_register, status), + sizeof(struct ms_status_register))) + return 0; + + msb->state = MSB_RP_RECEIVE_STATUS_REG; + return 0; + + case MSB_RP_RECEIVE_STATUS_REG: + msb->regs.status = *(struct ms_status_register *)mrq->data; + msb->state = MSB_RP_SEND_OOB_READ; + fallthrough; + + case MSB_RP_SEND_OOB_READ: + if (!msb_read_regs(msb, + offsetof(struct ms_register, extra_data), + sizeof(struct ms_extra_data_register))) + return 0; + + msb->state = MSB_RP_RECEIVE_OOB_READ; + return 0; + + case MSB_RP_RECEIVE_OOB_READ: + msb->regs.extra_data = + *(struct ms_extra_data_register *) mrq->data; + msb->state = MSB_RP_SEND_READ_DATA; + fallthrough; + + case MSB_RP_SEND_READ_DATA: + /* Skip that state if we only read the oob */ + if (msb->regs.param.cp == MEMSTICK_CP_EXTRA) { + msb->state = MSB_RP_RECEIVE_READ_DATA; + goto again; + } + + sg_init_table(sg, ARRAY_SIZE(sg)); + msb_sg_copy(msb->current_sg, sg, ARRAY_SIZE(sg), + msb->current_sg_offset, + msb->page_size); + + memstick_init_req_sg(mrq, MS_TPC_READ_LONG_DATA, sg); + msb->state = MSB_RP_RECEIVE_READ_DATA; + return 0; + + case MSB_RP_RECEIVE_READ_DATA: + if (!(msb->regs.status.interrupt & MEMSTICK_INT_ERR)) { + msb->current_sg_offset += msb->page_size; + return msb_exit_state_machine(msb, 0); + } + + if (msb->regs.status.status1 & MEMSTICK_UNCORR_ERROR) { + dbg("read_page: uncorrectable error"); + return msb_exit_state_machine(msb, -EBADMSG); + } + + if (msb->regs.status.status1 & MEMSTICK_CORR_ERROR) { + dbg("read_page: correctable error"); + msb->current_sg_offset += msb->page_size; + return msb_exit_state_machine(msb, -EUCLEAN); + } else { + dbg("read_page: INT error, but no status error bits"); + return msb_exit_state_machine(msb, -EIO); + } + } + + BUG(); +} + +/* + * Handler of writes of exactly one block. + * Takes address from msb->regs.param. + * Writes same extra data to blocks, also taken + * from msb->regs.extra + * Returns -EBADMSG if write fails due to uncorrectable error, or -EIO if + * device refuses to take the command or something else + */ +static int h_msb_write_block(struct memstick_dev *card, + struct memstick_request **out_mrq) +{ + struct msb_data *msb = memstick_get_drvdata(card); + struct memstick_request *mrq = *out_mrq = &card->current_mrq; + struct scatterlist sg[2]; + u8 intreg, command; + + if (mrq->error) + return msb_exit_state_machine(msb, mrq->error); + +again: + switch (msb->state) { + + /* HACK: Jmicon handling of TPCs between 8 and + * sizeof(memstick_request.data) is broken due to hardware + * bug in PIO mode that is used for these TPCs + * Therefore split the write + */ + + case MSB_WB_SEND_WRITE_PARAMS: + if (!msb_write_regs(msb, + offsetof(struct ms_register, param), + sizeof(struct ms_param_register), + &msb->regs.param)) + return 0; + + msb->state = MSB_WB_SEND_WRITE_OOB; + return 0; + + case MSB_WB_SEND_WRITE_OOB: + if (!msb_write_regs(msb, + offsetof(struct ms_register, extra_data), + sizeof(struct ms_extra_data_register), + &msb->regs.extra_data)) + return 0; + msb->state = MSB_WB_SEND_WRITE_COMMAND; + return 0; + + + case MSB_WB_SEND_WRITE_COMMAND: + command = MS_CMD_BLOCK_WRITE; + memstick_init_req(mrq, MS_TPC_SET_CMD, &command, 1); + msb->state = MSB_WB_SEND_INT_REQ; + return 0; + + case MSB_WB_SEND_INT_REQ: + msb->state = MSB_WB_RECEIVE_INT_REQ; + if (msb_read_int_reg(msb, -1)) + return 0; + fallthrough; + + case MSB_WB_RECEIVE_INT_REQ: + intreg = mrq->data[0]; + msb->regs.status.interrupt = intreg; + + /* errors mean out of here, and fast... */ + if (intreg & (MEMSTICK_INT_CMDNAK)) + return msb_exit_state_machine(msb, -EIO); + + if (intreg & MEMSTICK_INT_ERR) + return msb_exit_state_machine(msb, -EBADMSG); + + + /* for last page we need to poll CED */ + if (msb->current_page == msb->pages_in_block) { + if (intreg & MEMSTICK_INT_CED) + return msb_exit_state_machine(msb, 0); + msb->state = MSB_WB_SEND_INT_REQ; + goto again; + + } + + /* for non-last page we need BREQ before writing next chunk */ + if (!(intreg & MEMSTICK_INT_BREQ)) { + msb->state = MSB_WB_SEND_INT_REQ; + goto again; + } + + msb->int_polling = false; + msb->state = MSB_WB_SEND_WRITE_DATA; + fallthrough; + + case MSB_WB_SEND_WRITE_DATA: + sg_init_table(sg, ARRAY_SIZE(sg)); + + if (msb_sg_copy(msb->current_sg, sg, ARRAY_SIZE(sg), + msb->current_sg_offset, + msb->page_size) < msb->page_size) + return msb_exit_state_machine(msb, -EIO); + + memstick_init_req_sg(mrq, MS_TPC_WRITE_LONG_DATA, sg); + mrq->need_card_int = 1; + msb->state = MSB_WB_RECEIVE_WRITE_CONFIRMATION; + return 0; + + case MSB_WB_RECEIVE_WRITE_CONFIRMATION: + msb->current_page++; + msb->current_sg_offset += msb->page_size; + msb->state = MSB_WB_SEND_INT_REQ; + goto again; + default: + BUG(); + } + + return 0; +} + +/* + * This function is used to send simple IO requests to device that consist + * of register write + command + */ +static int h_msb_send_command(struct memstick_dev *card, + struct memstick_request **out_mrq) +{ + struct msb_data *msb = memstick_get_drvdata(card); + struct memstick_request *mrq = *out_mrq = &card->current_mrq; + u8 intreg; + + if (mrq->error) { + dbg("send_command: unknown error"); + return msb_exit_state_machine(msb, mrq->error); + } +again: + switch (msb->state) { + + /* HACK: see h_msb_write_block */ + case MSB_SC_SEND_WRITE_PARAMS: /* write param register*/ + if (!msb_write_regs(msb, + offsetof(struct ms_register, param), + sizeof(struct ms_param_register), + &msb->regs.param)) + return 0; + msb->state = MSB_SC_SEND_WRITE_OOB; + return 0; + + case MSB_SC_SEND_WRITE_OOB: + if (!msb->command_need_oob) { + msb->state = MSB_SC_SEND_COMMAND; + goto again; + } + + if (!msb_write_regs(msb, + offsetof(struct ms_register, extra_data), + sizeof(struct ms_extra_data_register), + &msb->regs.extra_data)) + return 0; + + msb->state = MSB_SC_SEND_COMMAND; + return 0; + + case MSB_SC_SEND_COMMAND: + memstick_init_req(mrq, MS_TPC_SET_CMD, &msb->command_value, 1); + msb->state = MSB_SC_SEND_INT_REQ; + return 0; + + case MSB_SC_SEND_INT_REQ: + msb->state = MSB_SC_RECEIVE_INT_REQ; + if (msb_read_int_reg(msb, -1)) + return 0; + fallthrough; + + case MSB_SC_RECEIVE_INT_REQ: + intreg = mrq->data[0]; + + if (intreg & MEMSTICK_INT_CMDNAK) + return msb_exit_state_machine(msb, -EIO); + if (intreg & MEMSTICK_INT_ERR) + return msb_exit_state_machine(msb, -EBADMSG); + + if (!(intreg & MEMSTICK_INT_CED)) { + msb->state = MSB_SC_SEND_INT_REQ; + goto again; + } + + return msb_exit_state_machine(msb, 0); + } + + BUG(); +} + +/* Small handler for card reset */ +static int h_msb_reset(struct memstick_dev *card, + struct memstick_request **out_mrq) +{ + u8 command = MS_CMD_RESET; + struct msb_data *msb = memstick_get_drvdata(card); + struct memstick_request *mrq = *out_mrq = &card->current_mrq; + + if (mrq->error) + return msb_exit_state_machine(msb, mrq->error); + + switch (msb->state) { + case MSB_RS_SEND: + memstick_init_req(mrq, MS_TPC_SET_CMD, &command, 1); + mrq->need_card_int = 0; + msb->state = MSB_RS_CONFIRM; + return 0; + case MSB_RS_CONFIRM: + return msb_exit_state_machine(msb, 0); + } + BUG(); +} + +/* This handler is used to do serial->parallel switch */ +static int h_msb_parallel_switch(struct memstick_dev *card, + struct memstick_request **out_mrq) +{ + struct msb_data *msb = memstick_get_drvdata(card); + struct memstick_request *mrq = *out_mrq = &card->current_mrq; + struct memstick_host *host = card->host; + + if (mrq->error) { + dbg("parallel_switch: error"); + msb->regs.param.system &= ~MEMSTICK_SYS_PAM; + return msb_exit_state_machine(msb, mrq->error); + } + + switch (msb->state) { + case MSB_PS_SEND_SWITCH_COMMAND: + /* Set the parallel interface on memstick side */ + msb->regs.param.system |= MEMSTICK_SYS_PAM; + + if (!msb_write_regs(msb, + offsetof(struct ms_register, param), + 1, + (unsigned char *)&msb->regs.param)) + return 0; + + msb->state = MSB_PS_SWICH_HOST; + return 0; + + case MSB_PS_SWICH_HOST: + /* Set parallel interface on our side + send a dummy request + * to see if card responds + */ + host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_PAR4); + memstick_init_req(mrq, MS_TPC_GET_INT, NULL, 1); + msb->state = MSB_PS_CONFIRM; + return 0; + + case MSB_PS_CONFIRM: + return msb_exit_state_machine(msb, 0); + } + + BUG(); +} + +static int msb_switch_to_parallel(struct msb_data *msb); + +/* Reset the card, to guard against hw errors beeing treated as bad blocks */ +static int msb_reset(struct msb_data *msb, bool full) +{ + + bool was_parallel = msb->regs.param.system & MEMSTICK_SYS_PAM; + struct memstick_dev *card = msb->card; + struct memstick_host *host = card->host; + int error; + + /* Reset the card */ + msb->regs.param.system = MEMSTICK_SYS_BAMD; + + if (full) { + error = host->set_param(host, + MEMSTICK_POWER, MEMSTICK_POWER_OFF); + if (error) + goto out_error; + + msb_invalidate_reg_window(msb); + + error = host->set_param(host, + MEMSTICK_POWER, MEMSTICK_POWER_ON); + if (error) + goto out_error; + + error = host->set_param(host, + MEMSTICK_INTERFACE, MEMSTICK_SERIAL); + if (error) { +out_error: + dbg("Failed to reset the host controller"); + msb->read_only = true; + return -EFAULT; + } + } + + error = msb_run_state_machine(msb, h_msb_reset); + if (error) { + dbg("Failed to reset the card"); + msb->read_only = true; + return -ENODEV; + } + + /* Set parallel mode */ + if (was_parallel) + msb_switch_to_parallel(msb); + return 0; +} + +/* Attempts to switch interface to parallel mode */ +static int msb_switch_to_parallel(struct msb_data *msb) +{ + int error; + + error = msb_run_state_machine(msb, h_msb_parallel_switch); + if (error) { + pr_err("Switch to parallel failed"); + msb->regs.param.system &= ~MEMSTICK_SYS_PAM; + msb_reset(msb, true); + return -EFAULT; + } + + msb->caps |= MEMSTICK_CAP_AUTO_GET_INT; + return 0; +} + +/* Changes overwrite flag on a page */ +static int msb_set_overwrite_flag(struct msb_data *msb, + u16 pba, u8 page, u8 flag) +{ + if (msb->read_only) + return -EROFS; + + msb->regs.param.block_address = cpu_to_be16(pba); + msb->regs.param.page_address = page; + msb->regs.param.cp = MEMSTICK_CP_OVERWRITE; + msb->regs.extra_data.overwrite_flag = flag; + msb->command_value = MS_CMD_BLOCK_WRITE; + msb->command_need_oob = true; + + dbg_verbose("changing overwrite flag to %02x for sector %d, page %d", + flag, pba, page); + return msb_run_state_machine(msb, h_msb_send_command); +} + +static int msb_mark_bad(struct msb_data *msb, int pba) +{ + pr_notice("marking pba %d as bad", pba); + msb_reset(msb, true); + return msb_set_overwrite_flag( + msb, pba, 0, 0xFF & ~MEMSTICK_OVERWRITE_BKST); +} + +static int msb_mark_page_bad(struct msb_data *msb, int pba, int page) +{ + dbg("marking page %d of pba %d as bad", page, pba); + msb_reset(msb, true); + return msb_set_overwrite_flag(msb, + pba, page, ~MEMSTICK_OVERWRITE_PGST0); +} + +/* Erases one physical block */ +static int msb_erase_block(struct msb_data *msb, u16 pba) +{ + int error, try; + + if (msb->read_only) + return -EROFS; + + dbg_verbose("erasing pba %d", pba); + + for (try = 1; try < 3; try++) { + msb->regs.param.block_address = cpu_to_be16(pba); + msb->regs.param.page_address = 0; + msb->regs.param.cp = MEMSTICK_CP_BLOCK; + msb->command_value = MS_CMD_BLOCK_ERASE; + msb->command_need_oob = false; + + + error = msb_run_state_machine(msb, h_msb_send_command); + if (!error || msb_reset(msb, true)) + break; + } + + if (error) { + pr_err("erase failed, marking pba %d as bad", pba); + msb_mark_bad(msb, pba); + } + + dbg_verbose("erase success, marking pba %d as unused", pba); + msb_mark_block_unused(msb, pba); + __set_bit(pba, msb->erased_blocks_bitmap); + return error; +} + +/* Reads one page from device */ +static int msb_read_page(struct msb_data *msb, + u16 pba, u8 page, struct ms_extra_data_register *extra, + struct scatterlist *sg, int offset) +{ + int try, error; + + if (pba == MS_BLOCK_INVALID) { + unsigned long flags; + struct sg_mapping_iter miter; + size_t len = msb->page_size; + + dbg_verbose("read unmapped sector. returning 0xFF"); + + local_irq_save(flags); + sg_miter_start(&miter, sg, sg_nents(sg), + SG_MITER_ATOMIC | SG_MITER_TO_SG); + + while (sg_miter_next(&miter) && len > 0) { + + int chunklen; + + if (offset && offset >= miter.length) { + offset -= miter.length; + continue; + } + + chunklen = min(miter.length - offset, len); + memset(miter.addr + offset, 0xFF, chunklen); + len -= chunklen; + offset = 0; + } + + sg_miter_stop(&miter); + local_irq_restore(flags); + + if (offset) + return -EFAULT; + + if (extra) + memset(extra, 0xFF, sizeof(*extra)); + return 0; + } + + if (pba >= msb->block_count) { + pr_err("BUG: attempt to read beyond the end of the card at pba %d", pba); + return -EINVAL; + } + + for (try = 1; try < 3; try++) { + msb->regs.param.block_address = cpu_to_be16(pba); + msb->regs.param.page_address = page; + msb->regs.param.cp = MEMSTICK_CP_PAGE; + + msb->current_sg = sg; + msb->current_sg_offset = offset; + error = msb_run_state_machine(msb, h_msb_read_page); + + + if (error == -EUCLEAN) { + pr_notice("correctable error on pba %d, page %d", + pba, page); + error = 0; + } + + if (!error && extra) + *extra = msb->regs.extra_data; + + if (!error || msb_reset(msb, true)) + break; + + } + + /* Mark bad pages */ + if (error == -EBADMSG) { + pr_err("uncorrectable error on read of pba %d, page %d", + pba, page); + + if (msb->regs.extra_data.overwrite_flag & + MEMSTICK_OVERWRITE_PGST0) + msb_mark_page_bad(msb, pba, page); + return -EBADMSG; + } + + if (error) + pr_err("read of pba %d, page %d failed with error %d", + pba, page, error); + return error; +} + +/* Reads oob of page only */ +static int msb_read_oob(struct msb_data *msb, u16 pba, u16 page, + struct ms_extra_data_register *extra) +{ + int error; + + BUG_ON(!extra); + msb->regs.param.block_address = cpu_to_be16(pba); + msb->regs.param.page_address = page; + msb->regs.param.cp = MEMSTICK_CP_EXTRA; + + if (pba > msb->block_count) { + pr_err("BUG: attempt to read beyond the end of card at pba %d", pba); + return -EINVAL; + } + + error = msb_run_state_machine(msb, h_msb_read_page); + *extra = msb->regs.extra_data; + + if (error == -EUCLEAN) { + pr_notice("correctable error on pba %d, page %d", + pba, page); + return 0; + } + + return error; +} + +/* Reads a block and compares it with data contained in scatterlist orig_sg */ +static int msb_verify_block(struct msb_data *msb, u16 pba, + struct scatterlist *orig_sg, int offset) +{ + struct scatterlist sg; + int page = 0, error; + + sg_init_one(&sg, msb->block_buffer, msb->block_size); + + while (page < msb->pages_in_block) { + + error = msb_read_page(msb, pba, page, + NULL, &sg, page * msb->page_size); + if (error) + return error; + page++; + } + + if (msb_sg_compare_to_buffer(orig_sg, offset, + msb->block_buffer, msb->block_size)) + return -EIO; + return 0; +} + +/* Writes exactly one block + oob */ +static int msb_write_block(struct msb_data *msb, + u16 pba, u32 lba, struct scatterlist *sg, int offset) +{ + int error, current_try = 1; + + BUG_ON(sg->length < msb->page_size); + + if (msb->read_only) + return -EROFS; + + if (pba == MS_BLOCK_INVALID) { + pr_err( + "BUG: write: attempt to write MS_BLOCK_INVALID block"); + return -EINVAL; + } + + if (pba >= msb->block_count || lba >= msb->logical_block_count) { + pr_err( + "BUG: write: attempt to write beyond the end of device"); + return -EINVAL; + } + + if (msb_get_zone_from_lba(lba) != msb_get_zone_from_pba(pba)) { + pr_err("BUG: write: lba zone mismatch"); + return -EINVAL; + } + + if (pba == msb->boot_block_locations[0] || + pba == msb->boot_block_locations[1]) { + pr_err("BUG: write: attempt to write to boot blocks!"); + return -EINVAL; + } + + while (1) { + + if (msb->read_only) + return -EROFS; + + msb->regs.param.cp = MEMSTICK_CP_BLOCK; + msb->regs.param.page_address = 0; + msb->regs.param.block_address = cpu_to_be16(pba); + + msb->regs.extra_data.management_flag = 0xFF; + msb->regs.extra_data.overwrite_flag = 0xF8; + msb->regs.extra_data.logical_address = cpu_to_be16(lba); + + msb->current_sg = sg; + msb->current_sg_offset = offset; + msb->current_page = 0; + + error = msb_run_state_machine(msb, h_msb_write_block); + + /* Sector we just wrote to is assumed erased since its pba + * was erased. If it wasn't erased, write will succeed + * and will just clear the bits that were set in the block + * thus test that what we have written, + * matches what we expect. + * We do trust the blocks that we erased + */ + if (!error && (verify_writes || + !test_bit(pba, msb->erased_blocks_bitmap))) + error = msb_verify_block(msb, pba, sg, offset); + + if (!error) + break; + + if (current_try > 1 || msb_reset(msb, true)) + break; + + pr_err("write failed, trying to erase the pba %d", pba); + error = msb_erase_block(msb, pba); + if (error) + break; + + current_try++; + } + return error; +} + +/* Finds a free block for write replacement */ +static u16 msb_get_free_block(struct msb_data *msb, int zone) +{ + u16 pos; + int pba = zone * MS_BLOCKS_IN_ZONE; + int i; + + get_random_bytes(&pos, sizeof(pos)); + + if (!msb->free_block_count[zone]) { + pr_err("NO free blocks in the zone %d, to use for a write, (media is WORN out) switching to RO mode", zone); + msb->read_only = true; + return MS_BLOCK_INVALID; + } + + pos %= msb->free_block_count[zone]; + + dbg_verbose("have %d choices for a free block, selected randomly: %d", + msb->free_block_count[zone], pos); + + pba = find_next_zero_bit(msb->used_blocks_bitmap, + msb->block_count, pba); + for (i = 0; i < pos; ++i) + pba = find_next_zero_bit(msb->used_blocks_bitmap, + msb->block_count, pba + 1); + + dbg_verbose("result of the free blocks scan: pba %d", pba); + + if (pba == msb->block_count || (msb_get_zone_from_pba(pba)) != zone) { + pr_err("BUG: can't get a free block"); + msb->read_only = true; + return MS_BLOCK_INVALID; + } + + msb_mark_block_used(msb, pba); + return pba; +} + +static int msb_update_block(struct msb_data *msb, u16 lba, + struct scatterlist *sg, int offset) +{ + u16 pba, new_pba; + int error, try; + + pba = msb->lba_to_pba_table[lba]; + dbg_verbose("start of a block update at lba %d, pba %d", lba, pba); + + if (pba != MS_BLOCK_INVALID) { + dbg_verbose("setting the update flag on the block"); + msb_set_overwrite_flag(msb, pba, 0, + 0xFF & ~MEMSTICK_OVERWRITE_UDST); + } + + for (try = 0; try < 3; try++) { + new_pba = msb_get_free_block(msb, + msb_get_zone_from_lba(lba)); + + if (new_pba == MS_BLOCK_INVALID) { + error = -EIO; + goto out; + } + + dbg_verbose("block update: writing updated block to the pba %d", + new_pba); + error = msb_write_block(msb, new_pba, lba, sg, offset); + if (error == -EBADMSG) { + msb_mark_bad(msb, new_pba); + continue; + } + + if (error) + goto out; + + dbg_verbose("block update: erasing the old block"); + msb_erase_block(msb, pba); + msb->lba_to_pba_table[lba] = new_pba; + return 0; + } +out: + if (error) { + pr_err("block update error after %d tries, switching to r/o mode", try); + msb->read_only = true; + } + return error; +} + +/* Converts endiannes in the boot block for easy use */ +static void msb_fix_boot_page_endianness(struct ms_boot_page *p) +{ + p->header.block_id = be16_to_cpu(p->header.block_id); + p->header.format_reserved = be16_to_cpu(p->header.format_reserved); + p->entry.disabled_block.start_addr + = be32_to_cpu(p->entry.disabled_block.start_addr); + p->entry.disabled_block.data_size + = be32_to_cpu(p->entry.disabled_block.data_size); + p->entry.cis_idi.start_addr + = be32_to_cpu(p->entry.cis_idi.start_addr); + p->entry.cis_idi.data_size + = be32_to_cpu(p->entry.cis_idi.data_size); + p->attr.block_size = be16_to_cpu(p->attr.block_size); + p->attr.number_of_blocks = be16_to_cpu(p->attr.number_of_blocks); + p->attr.number_of_effective_blocks + = be16_to_cpu(p->attr.number_of_effective_blocks); + p->attr.page_size = be16_to_cpu(p->attr.page_size); + p->attr.memory_manufacturer_code + = be16_to_cpu(p->attr.memory_manufacturer_code); + p->attr.memory_device_code = be16_to_cpu(p->attr.memory_device_code); + p->attr.implemented_capacity + = be16_to_cpu(p->attr.implemented_capacity); + p->attr.controller_number = be16_to_cpu(p->attr.controller_number); + p->attr.controller_function = be16_to_cpu(p->attr.controller_function); +} + +static int msb_read_boot_blocks(struct msb_data *msb) +{ + int pba = 0; + struct scatterlist sg; + struct ms_extra_data_register extra; + struct ms_boot_page *page; + + msb->boot_block_locations[0] = MS_BLOCK_INVALID; + msb->boot_block_locations[1] = MS_BLOCK_INVALID; + msb->boot_block_count = 0; + + dbg_verbose("Start of a scan for the boot blocks"); + + if (!msb->boot_page) { + page = kmalloc_array(2, sizeof(struct ms_boot_page), + GFP_KERNEL); + if (!page) + return -ENOMEM; + + msb->boot_page = page; + } else + page = msb->boot_page; + + msb->block_count = MS_BLOCK_MAX_BOOT_ADDR; + + for (pba = 0; pba < MS_BLOCK_MAX_BOOT_ADDR; pba++) { + + sg_init_one(&sg, page, sizeof(*page)); + if (msb_read_page(msb, pba, 0, &extra, &sg, 0)) { + dbg("boot scan: can't read pba %d", pba); + continue; + } + + if (extra.management_flag & MEMSTICK_MANAGEMENT_SYSFLG) { + dbg("management flag doesn't indicate boot block %d", + pba); + continue; + } + + if (be16_to_cpu(page->header.block_id) != MS_BLOCK_BOOT_ID) { + dbg("the pba at %d doesn't contain boot block ID", pba); + continue; + } + + msb_fix_boot_page_endianness(page); + msb->boot_block_locations[msb->boot_block_count] = pba; + + page++; + msb->boot_block_count++; + + if (msb->boot_block_count == 2) + break; + } + + if (!msb->boot_block_count) { + pr_err("media doesn't contain master page, aborting"); + return -EIO; + } + + dbg_verbose("End of scan for boot blocks"); + return 0; +} + +static int msb_read_bad_block_table(struct msb_data *msb, int block_nr) +{ + struct ms_boot_page *boot_block; + struct scatterlist sg; + u16 *buffer = NULL; + int offset = 0; + int i, error = 0; + int data_size, data_offset, page, page_offset, size_to_read; + u16 pba; + + BUG_ON(block_nr > 1); + boot_block = &msb->boot_page[block_nr]; + pba = msb->boot_block_locations[block_nr]; + + if (msb->boot_block_locations[block_nr] == MS_BLOCK_INVALID) + return -EINVAL; + + data_size = boot_block->entry.disabled_block.data_size; + data_offset = sizeof(struct ms_boot_page) + + boot_block->entry.disabled_block.start_addr; + if (!data_size) + return 0; + + page = data_offset / msb->page_size; + page_offset = data_offset % msb->page_size; + size_to_read = + DIV_ROUND_UP(data_size + page_offset, msb->page_size) * + msb->page_size; + + dbg("reading bad block of boot block at pba %d, offset %d len %d", + pba, data_offset, data_size); + + buffer = kzalloc(size_to_read, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* Read the buffer */ + sg_init_one(&sg, buffer, size_to_read); + + while (offset < size_to_read) { + error = msb_read_page(msb, pba, page, NULL, &sg, offset); + if (error) + goto out; + + page++; + offset += msb->page_size; + + if (page == msb->pages_in_block) { + pr_err( + "bad block table extends beyond the boot block"); + break; + } + } + + /* Process the bad block table */ + for (i = page_offset; i < data_size / sizeof(u16); i++) { + + u16 bad_block = be16_to_cpu(buffer[i]); + + if (bad_block >= msb->block_count) { + dbg("bad block table contains invalid block %d", + bad_block); + continue; + } + + if (test_bit(bad_block, msb->used_blocks_bitmap)) { + dbg("duplicate bad block %d in the table", + bad_block); + continue; + } + + dbg("block %d is marked as factory bad", bad_block); + msb_mark_block_used(msb, bad_block); + } +out: + kfree(buffer); + return error; +} + +static int msb_ftl_initialize(struct msb_data *msb) +{ + int i; + + if (msb->ftl_initialized) + return 0; + + msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE; + msb->logical_block_count = msb->zone_count * 496 - 2; + + msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL); + msb->erased_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL); + msb->lba_to_pba_table = + kmalloc_array(msb->logical_block_count, sizeof(u16), + GFP_KERNEL); + + if (!msb->used_blocks_bitmap || !msb->lba_to_pba_table || + !msb->erased_blocks_bitmap) { + bitmap_free(msb->used_blocks_bitmap); + bitmap_free(msb->erased_blocks_bitmap); + kfree(msb->lba_to_pba_table); + return -ENOMEM; + } + + for (i = 0; i < msb->zone_count; i++) + msb->free_block_count[i] = MS_BLOCKS_IN_ZONE; + + memset(msb->lba_to_pba_table, MS_BLOCK_INVALID, + msb->logical_block_count * sizeof(u16)); + + dbg("initial FTL tables created. Zone count = %d, Logical block count = %d", + msb->zone_count, msb->logical_block_count); + + msb->ftl_initialized = true; + return 0; +} + +static int msb_ftl_scan(struct msb_data *msb) +{ + u16 pba, lba, other_block; + u8 overwrite_flag, management_flag, other_overwrite_flag; + int error; + struct ms_extra_data_register extra; + u8 *overwrite_flags = kzalloc(msb->block_count, GFP_KERNEL); + + if (!overwrite_flags) + return -ENOMEM; + + dbg("Start of media scanning"); + for (pba = 0; pba < msb->block_count; pba++) { + + if (pba == msb->boot_block_locations[0] || + pba == msb->boot_block_locations[1]) { + dbg_verbose("pba %05d -> [boot block]", pba); + msb_mark_block_used(msb, pba); + continue; + } + + if (test_bit(pba, msb->used_blocks_bitmap)) { + dbg_verbose("pba %05d -> [factory bad]", pba); + continue; + } + + memset(&extra, 0, sizeof(extra)); + error = msb_read_oob(msb, pba, 0, &extra); + + /* can't trust the page if we can't read the oob */ + if (error == -EBADMSG) { + pr_notice( + "oob of pba %d damaged, will try to erase it", pba); + msb_mark_block_used(msb, pba); + msb_erase_block(msb, pba); + continue; + } else if (error) { + pr_err("unknown error %d on read of oob of pba %d - aborting", + error, pba); + + kfree(overwrite_flags); + return error; + } + + lba = be16_to_cpu(extra.logical_address); + management_flag = extra.management_flag; + overwrite_flag = extra.overwrite_flag; + overwrite_flags[pba] = overwrite_flag; + + /* Skip bad blocks */ + if (!(overwrite_flag & MEMSTICK_OVERWRITE_BKST)) { + dbg("pba %05d -> [BAD]", pba); + msb_mark_block_used(msb, pba); + continue; + } + + /* Skip system/drm blocks */ + if ((management_flag & MEMSTICK_MANAGEMENT_FLAG_NORMAL) != + MEMSTICK_MANAGEMENT_FLAG_NORMAL) { + dbg("pba %05d -> [reserved management flag %02x]", + pba, management_flag); + msb_mark_block_used(msb, pba); + continue; + } + + /* Erase temporary tables */ + if (!(management_flag & MEMSTICK_MANAGEMENT_ATFLG)) { + dbg("pba %05d -> [temp table] - will erase", pba); + + msb_mark_block_used(msb, pba); + msb_erase_block(msb, pba); + continue; + } + + if (lba == MS_BLOCK_INVALID) { + dbg_verbose("pba %05d -> [free]", pba); + continue; + } + + msb_mark_block_used(msb, pba); + + /* Block has LBA not according to zoning*/ + if (msb_get_zone_from_lba(lba) != msb_get_zone_from_pba(pba)) { + pr_notice("pba %05d -> [bad lba %05d] - will erase", + pba, lba); + msb_erase_block(msb, pba); + continue; + } + + /* No collisions - great */ + if (msb->lba_to_pba_table[lba] == MS_BLOCK_INVALID) { + dbg_verbose("pba %05d -> [lba %05d]", pba, lba); + msb->lba_to_pba_table[lba] = pba; + continue; + } + + other_block = msb->lba_to_pba_table[lba]; + other_overwrite_flag = overwrite_flags[other_block]; + + pr_notice("Collision between pba %d and pba %d", + pba, other_block); + + if (!(overwrite_flag & MEMSTICK_OVERWRITE_UDST)) { + pr_notice("pba %d is marked as stable, use it", pba); + msb_erase_block(msb, other_block); + msb->lba_to_pba_table[lba] = pba; + continue; + } + + if (!(other_overwrite_flag & MEMSTICK_OVERWRITE_UDST)) { + pr_notice("pba %d is marked as stable, use it", + other_block); + msb_erase_block(msb, pba); + continue; + } + + pr_notice("collision between blocks %d and %d, without stable flag set on both, erasing pba %d", + pba, other_block, other_block); + + msb_erase_block(msb, other_block); + msb->lba_to_pba_table[lba] = pba; + } + + dbg("End of media scanning"); + kfree(overwrite_flags); + return 0; +} + +static void msb_cache_flush_timer(struct timer_list *t) +{ + struct msb_data *msb = timer_container_of(msb, t, cache_flush_timer); + + msb->need_flush_cache = true; + queue_work(msb->io_queue, &msb->io_work); +} + + +static void msb_cache_discard(struct msb_data *msb) +{ + if (msb->cache_block_lba == MS_BLOCK_INVALID) + return; + + timer_delete_sync(&msb->cache_flush_timer); + + dbg_verbose("Discarding the write cache"); + msb->cache_block_lba = MS_BLOCK_INVALID; + bitmap_zero(&msb->valid_cache_bitmap, msb->pages_in_block); +} + +static int msb_cache_init(struct msb_data *msb) +{ + timer_setup(&msb->cache_flush_timer, msb_cache_flush_timer, 0); + + if (!msb->cache) + msb->cache = kzalloc(msb->block_size, GFP_KERNEL); + if (!msb->cache) + return -ENOMEM; + + msb_cache_discard(msb); + return 0; +} + +static int msb_cache_flush(struct msb_data *msb) +{ + struct scatterlist sg; + struct ms_extra_data_register extra; + int page, offset, error; + u16 pba, lba; + + if (msb->read_only) + return -EROFS; + + if (msb->cache_block_lba == MS_BLOCK_INVALID) + return 0; + + lba = msb->cache_block_lba; + pba = msb->lba_to_pba_table[lba]; + + dbg_verbose("Flushing the write cache of pba %d (LBA %d)", + pba, msb->cache_block_lba); + + sg_init_one(&sg, msb->cache , msb->block_size); + + /* Read all missing pages in cache */ + for (page = 0; page < msb->pages_in_block; page++) { + + if (test_bit(page, &msb->valid_cache_bitmap)) + continue; + + offset = page * msb->page_size; + + dbg_verbose("reading non-present sector %d of cache block %d", + page, lba); + error = msb_read_page(msb, pba, page, &extra, &sg, offset); + + /* Bad pages are copied with 00 page status */ + if (error == -EBADMSG) { + pr_err("read error on sector %d, contents probably damaged", page); + continue; + } + + if (error) + return error; + + if ((extra.overwrite_flag & MEMSTICK_OV_PG_NORMAL) != + MEMSTICK_OV_PG_NORMAL) { + dbg("page %d is marked as bad", page); + continue; + } + + set_bit(page, &msb->valid_cache_bitmap); + } + + /* Write the cache now */ + error = msb_update_block(msb, msb->cache_block_lba, &sg, 0); + pba = msb->lba_to_pba_table[msb->cache_block_lba]; + + /* Mark invalid pages */ + if (!error) { + for (page = 0; page < msb->pages_in_block; page++) { + + if (test_bit(page, &msb->valid_cache_bitmap)) + continue; + + dbg("marking page %d as containing damaged data", + page); + msb_set_overwrite_flag(msb, + pba , page, 0xFF & ~MEMSTICK_OV_PG_NORMAL); + } + } + + msb_cache_discard(msb); + return error; +} + +static int msb_cache_write(struct msb_data *msb, int lba, + int page, bool add_to_cache_only, struct scatterlist *sg, int offset) +{ + int error; + struct scatterlist sg_tmp[10]; + + if (msb->read_only) + return -EROFS; + + if (msb->cache_block_lba == MS_BLOCK_INVALID || + lba != msb->cache_block_lba) + if (add_to_cache_only) + return 0; + + /* If we need to write different block */ + if (msb->cache_block_lba != MS_BLOCK_INVALID && + lba != msb->cache_block_lba) { + dbg_verbose("first flush the cache"); + error = msb_cache_flush(msb); + if (error) + return error; + } + + if (msb->cache_block_lba == MS_BLOCK_INVALID) { + msb->cache_block_lba = lba; + mod_timer(&msb->cache_flush_timer, + jiffies + msecs_to_jiffies(cache_flush_timeout)); + } + + dbg_verbose("Write of LBA %d page %d to cache ", lba, page); + + sg_init_table(sg_tmp, ARRAY_SIZE(sg_tmp)); + msb_sg_copy(sg, sg_tmp, ARRAY_SIZE(sg_tmp), offset, msb->page_size); + + sg_copy_to_buffer(sg_tmp, sg_nents(sg_tmp), + msb->cache + page * msb->page_size, msb->page_size); + + set_bit(page, &msb->valid_cache_bitmap); + return 0; +} + +static int msb_cache_read(struct msb_data *msb, int lba, + int page, struct scatterlist *sg, int offset) +{ + int pba = msb->lba_to_pba_table[lba]; + struct scatterlist sg_tmp[10]; + int error = 0; + + if (lba == msb->cache_block_lba && + test_bit(page, &msb->valid_cache_bitmap)) { + + dbg_verbose("Read of LBA %d (pba %d) sector %d from cache", + lba, pba, page); + + sg_init_table(sg_tmp, ARRAY_SIZE(sg_tmp)); + msb_sg_copy(sg, sg_tmp, ARRAY_SIZE(sg_tmp), + offset, msb->page_size); + sg_copy_from_buffer(sg_tmp, sg_nents(sg_tmp), + msb->cache + msb->page_size * page, + msb->page_size); + } else { + dbg_verbose("Read of LBA %d (pba %d) sector %d from device", + lba, pba, page); + + error = msb_read_page(msb, pba, page, NULL, sg, offset); + if (error) + return error; + + msb_cache_write(msb, lba, page, true, sg, offset); + } + return error; +} + +/* Emulated geometry table + * This table content isn't that importaint, + * One could put here different values, providing that they still + * cover whole disk. + * 64 MB entry is what windows reports for my 64M memstick + */ + +static const struct chs_entry chs_table[] = { +/* size sectors cylinders heads */ + { 4, 16, 247, 2 }, + { 8, 16, 495, 2 }, + { 16, 16, 495, 4 }, + { 32, 16, 991, 4 }, + { 64, 16, 991, 8 }, + {128, 16, 991, 16 }, + { 0 } +}; + +/* Load information about the card */ +static int msb_init_card(struct memstick_dev *card) +{ + struct msb_data *msb = memstick_get_drvdata(card); + struct memstick_host *host = card->host; + struct ms_boot_page *boot_block; + int error = 0, i, raw_size_in_megs; + + msb->caps = 0; + + if (card->id.class >= MEMSTICK_CLASS_ROM && + card->id.class <= MEMSTICK_CLASS_ROM) + msb->read_only = true; + + msb->state = -1; + error = msb_reset(msb, false); + if (error) + return error; + + /* Due to a bug in Jmicron driver written by Alex Dubov, + * its serial mode barely works, + * so we switch to parallel mode right away + */ + if (host->caps & MEMSTICK_CAP_PAR4) + msb_switch_to_parallel(msb); + + msb->page_size = sizeof(struct ms_boot_page); + + /* Read the boot page */ + error = msb_read_boot_blocks(msb); + if (error) + return -EIO; + + boot_block = &msb->boot_page[0]; + + /* Save interesting attributes from boot page */ + msb->block_count = boot_block->attr.number_of_blocks; + msb->page_size = boot_block->attr.page_size; + + msb->pages_in_block = boot_block->attr.block_size * 2; + msb->block_size = msb->page_size * msb->pages_in_block; + + if ((size_t)msb->page_size > PAGE_SIZE) { + /* this isn't supported by linux at all, anyway*/ + dbg("device page %d size isn't supported", msb->page_size); + return -EINVAL; + } + + msb->block_buffer = kzalloc(msb->block_size, GFP_KERNEL); + if (!msb->block_buffer) + return -ENOMEM; + + raw_size_in_megs = (msb->block_size * msb->block_count) >> 20; + + for (i = 0; chs_table[i].size; i++) { + + if (chs_table[i].size != raw_size_in_megs) + continue; + + msb->geometry.cylinders = chs_table[i].cyl; + msb->geometry.heads = chs_table[i].head; + msb->geometry.sectors = chs_table[i].sec; + break; + } + + if (boot_block->attr.transfer_supporting == 1) + msb->caps |= MEMSTICK_CAP_PAR4; + + if (boot_block->attr.device_type & 0x03) + msb->read_only = true; + + dbg("Total block count = %d", msb->block_count); + dbg("Each block consists of %d pages", msb->pages_in_block); + dbg("Page size = %d bytes", msb->page_size); + dbg("Parallel mode supported: %d", !!(msb->caps & MEMSTICK_CAP_PAR4)); + dbg("Read only: %d", msb->read_only); + +#if 0 + /* Now we can switch the interface */ + if (host->caps & msb->caps & MEMSTICK_CAP_PAR4) + msb_switch_to_parallel(msb); +#endif + + error = msb_cache_init(msb); + if (error) + return error; + + error = msb_ftl_initialize(msb); + if (error) + return error; + + + /* Read the bad block table */ + error = msb_read_bad_block_table(msb, 0); + + if (error && error != -ENOMEM) { + dbg("failed to read bad block table from primary boot block, trying from backup"); + error = msb_read_bad_block_table(msb, 1); + } + + if (error) + return error; + + /* *drum roll* Scan the media */ + error = msb_ftl_scan(msb); + if (error) { + pr_err("Scan of media failed"); + return error; + } + + return 0; + +} + +static int msb_do_write_request(struct msb_data *msb, int lba, + int page, struct scatterlist *sg, size_t len, int *sucessfuly_written) +{ + int error = 0; + off_t offset = 0; + *sucessfuly_written = 0; + + while (offset < len) { + if (page == 0 && len - offset >= msb->block_size) { + + if (msb->cache_block_lba == lba) + msb_cache_discard(msb); + + dbg_verbose("Writing whole lba %d", lba); + error = msb_update_block(msb, lba, sg, offset); + if (error) + return error; + + offset += msb->block_size; + *sucessfuly_written += msb->block_size; + lba++; + continue; + } + + error = msb_cache_write(msb, lba, page, false, sg, offset); + if (error) + return error; + + offset += msb->page_size; + *sucessfuly_written += msb->page_size; + + page++; + if (page == msb->pages_in_block) { + page = 0; + lba++; + } + } + return 0; +} + +static int msb_do_read_request(struct msb_data *msb, int lba, + int page, struct scatterlist *sg, int len, int *sucessfuly_read) +{ + int error = 0; + int offset = 0; + *sucessfuly_read = 0; + + while (offset < len) { + + error = msb_cache_read(msb, lba, page, sg, offset); + if (error) + return error; + + offset += msb->page_size; + *sucessfuly_read += msb->page_size; + + page++; + if (page == msb->pages_in_block) { + page = 0; + lba++; + } + } + return 0; +} + +static void msb_io_work(struct work_struct *work) +{ + struct msb_data *msb = container_of(work, struct msb_data, io_work); + int page, error, len; + sector_t lba; + struct scatterlist *sg = msb->prealloc_sg; + struct request *req; + + dbg_verbose("IO: work started"); + + while (1) { + spin_lock_irq(&msb->q_lock); + + if (msb->need_flush_cache) { + msb->need_flush_cache = false; + spin_unlock_irq(&msb->q_lock); + msb_cache_flush(msb); + continue; + } + + req = msb->req; + if (!req) { + dbg_verbose("IO: no more requests exiting"); + spin_unlock_irq(&msb->q_lock); + return; + } + + spin_unlock_irq(&msb->q_lock); + + /* process the request */ + dbg_verbose("IO: processing new request"); + blk_rq_map_sg(req, sg); + + lba = blk_rq_pos(req); + + sector_div(lba, msb->page_size / 512); + page = sector_div(lba, msb->pages_in_block); + + if (rq_data_dir(msb->req) == READ) + error = msb_do_read_request(msb, lba, page, sg, + blk_rq_bytes(req), &len); + else + error = msb_do_write_request(msb, lba, page, sg, + blk_rq_bytes(req), &len); + + if (len && !blk_update_request(req, BLK_STS_OK, len)) { + __blk_mq_end_request(req, BLK_STS_OK); + spin_lock_irq(&msb->q_lock); + msb->req = NULL; + spin_unlock_irq(&msb->q_lock); + } + + if (error && msb->req) { + blk_status_t ret = errno_to_blk_status(error); + + dbg_verbose("IO: ending one sector of the request with error"); + blk_mq_end_request(req, ret); + spin_lock_irq(&msb->q_lock); + msb->req = NULL; + spin_unlock_irq(&msb->q_lock); + } + + if (msb->req) + dbg_verbose("IO: request still pending"); + } +} + +static DEFINE_IDR(msb_disk_idr); /*set of used disk numbers */ +static DEFINE_MUTEX(msb_disk_lock); /* protects against races in open/release */ + +static void msb_data_clear(struct msb_data *msb) +{ + kfree(msb->boot_page); + bitmap_free(msb->used_blocks_bitmap); + bitmap_free(msb->erased_blocks_bitmap); + kfree(msb->lba_to_pba_table); + kfree(msb->cache); + msb->card = NULL; +} + +static int msb_bd_getgeo(struct gendisk *disk, + struct hd_geometry *geo) +{ + struct msb_data *msb = disk->private_data; + *geo = msb->geometry; + return 0; +} + +static void msb_bd_free_disk(struct gendisk *disk) +{ + struct msb_data *msb = disk->private_data; + + mutex_lock(&msb_disk_lock); + idr_remove(&msb_disk_idr, msb->disk_id); + mutex_unlock(&msb_disk_lock); + + kfree(msb); +} + +static blk_status_t msb_queue_rq(struct blk_mq_hw_ctx *hctx, + const struct blk_mq_queue_data *bd) +{ + struct memstick_dev *card = hctx->queue->queuedata; + struct msb_data *msb = memstick_get_drvdata(card); + struct request *req = bd->rq; + + dbg_verbose("Submit request"); + + spin_lock_irq(&msb->q_lock); + + if (msb->card_dead) { + dbg("Refusing requests on removed card"); + + WARN_ON(!msb->io_queue_stopped); + + spin_unlock_irq(&msb->q_lock); + blk_mq_start_request(req); + return BLK_STS_IOERR; + } + + if (msb->req) { + spin_unlock_irq(&msb->q_lock); + return BLK_STS_DEV_RESOURCE; + } + + blk_mq_start_request(req); + msb->req = req; + + if (!msb->io_queue_stopped) + queue_work(msb->io_queue, &msb->io_work); + + spin_unlock_irq(&msb->q_lock); + return BLK_STS_OK; +} + +static int msb_check_card(struct memstick_dev *card) +{ + struct msb_data *msb = memstick_get_drvdata(card); + + return (msb->card_dead == 0); +} + +static void msb_stop(struct memstick_dev *card) +{ + struct msb_data *msb = memstick_get_drvdata(card); + unsigned long flags; + + dbg("Stopping all msblock IO"); + + blk_mq_stop_hw_queues(msb->queue); + spin_lock_irqsave(&msb->q_lock, flags); + msb->io_queue_stopped = true; + spin_unlock_irqrestore(&msb->q_lock, flags); + + timer_delete_sync(&msb->cache_flush_timer); + flush_workqueue(msb->io_queue); + + spin_lock_irqsave(&msb->q_lock, flags); + if (msb->req) { + blk_mq_requeue_request(msb->req, false); + msb->req = NULL; + } + spin_unlock_irqrestore(&msb->q_lock, flags); +} + +static void msb_start(struct memstick_dev *card) +{ + struct msb_data *msb = memstick_get_drvdata(card); + unsigned long flags; + + dbg("Resuming IO from msblock"); + + msb_invalidate_reg_window(msb); + + spin_lock_irqsave(&msb->q_lock, flags); + if (!msb->io_queue_stopped || msb->card_dead) { + spin_unlock_irqrestore(&msb->q_lock, flags); + return; + } + spin_unlock_irqrestore(&msb->q_lock, flags); + + /* Kick cache flush anyway, its harmless */ + msb->need_flush_cache = true; + msb->io_queue_stopped = false; + + blk_mq_start_hw_queues(msb->queue); + + queue_work(msb->io_queue, &msb->io_work); + +} + +static const struct block_device_operations msb_bdops = { + .owner = THIS_MODULE, + .getgeo = msb_bd_getgeo, + .free_disk = msb_bd_free_disk, +}; + +static const struct blk_mq_ops msb_mq_ops = { + .queue_rq = msb_queue_rq, +}; + +/* Registers the block device */ +static int msb_init_disk(struct memstick_dev *card) +{ + struct msb_data *msb = memstick_get_drvdata(card); + struct queue_limits lim = { + .logical_block_size = msb->page_size, + .max_hw_sectors = MS_BLOCK_MAX_PAGES, + .max_segments = MS_BLOCK_MAX_SEGS, + .max_segment_size = MS_BLOCK_MAX_PAGES * msb->page_size, + }; + int rc; + unsigned long capacity; + + mutex_lock(&msb_disk_lock); + msb->disk_id = idr_alloc(&msb_disk_idr, card, 0, 256, GFP_KERNEL); + mutex_unlock(&msb_disk_lock); + + if (msb->disk_id < 0) + return msb->disk_id; + + rc = blk_mq_alloc_sq_tag_set(&msb->tag_set, &msb_mq_ops, 2, 0); + if (rc) + goto out_release_id; + + msb->disk = blk_mq_alloc_disk(&msb->tag_set, &lim, card); + if (IS_ERR(msb->disk)) { + rc = PTR_ERR(msb->disk); + goto out_free_tag_set; + } + msb->queue = msb->disk->queue; + + sprintf(msb->disk->disk_name, "msblk%d", msb->disk_id); + msb->disk->fops = &msb_bdops; + msb->disk->private_data = msb; + + capacity = msb->pages_in_block * msb->logical_block_count; + capacity *= (msb->page_size / 512); + set_capacity(msb->disk, capacity); + dbg("Set total disk size to %lu sectors", capacity); + + msb->io_queue = alloc_ordered_workqueue("ms_block", WQ_MEM_RECLAIM); + if (!msb->io_queue) { + rc = -ENOMEM; + goto out_cleanup_disk; + } + + INIT_WORK(&msb->io_work, msb_io_work); + sg_init_table(msb->prealloc_sg, MS_BLOCK_MAX_SEGS+1); + + if (msb->read_only) + set_disk_ro(msb->disk, 1); + + msb_start(card); + rc = device_add_disk(&card->dev, msb->disk, NULL); + if (rc) + goto out_destroy_workqueue; + dbg("Disk added"); + return 0; + +out_destroy_workqueue: + destroy_workqueue(msb->io_queue); +out_cleanup_disk: + put_disk(msb->disk); +out_free_tag_set: + blk_mq_free_tag_set(&msb->tag_set); +out_release_id: + mutex_lock(&msb_disk_lock); + idr_remove(&msb_disk_idr, msb->disk_id); + mutex_unlock(&msb_disk_lock); + return rc; +} + +static int msb_probe(struct memstick_dev *card) +{ + struct msb_data *msb; + int rc = 0; + + msb = kzalloc(sizeof(struct msb_data), GFP_KERNEL); + if (!msb) + return -ENOMEM; + memstick_set_drvdata(card, msb); + msb->card = card; + spin_lock_init(&msb->q_lock); + + rc = msb_init_card(card); + if (rc) + goto out_free; + + rc = msb_init_disk(card); + if (!rc) { + card->check = msb_check_card; + card->stop = msb_stop; + card->start = msb_start; + return 0; + } +out_free: + memstick_set_drvdata(card, NULL); + msb_data_clear(msb); + kfree(msb); + return rc; +} + +static void msb_remove(struct memstick_dev *card) +{ + struct msb_data *msb = memstick_get_drvdata(card); + unsigned long flags; + + if (!msb->io_queue_stopped) + msb_stop(card); + + dbg("Removing the disk device"); + + /* Take care of unhandled + new requests from now on */ + spin_lock_irqsave(&msb->q_lock, flags); + msb->card_dead = true; + spin_unlock_irqrestore(&msb->q_lock, flags); + blk_mq_start_hw_queues(msb->queue); + + /* Remove the disk */ + del_gendisk(msb->disk); + blk_mq_free_tag_set(&msb->tag_set); + msb->queue = NULL; + + mutex_lock(&msb_disk_lock); + msb_data_clear(msb); + mutex_unlock(&msb_disk_lock); + + put_disk(msb->disk); + memstick_set_drvdata(card, NULL); +} + +#ifdef CONFIG_PM + +static int msb_suspend(struct memstick_dev *card, pm_message_t state) +{ + msb_stop(card); + return 0; +} + +static int msb_resume(struct memstick_dev *card) +{ + struct msb_data *msb = memstick_get_drvdata(card); + struct msb_data *new_msb = NULL; + bool card_dead = true; + +#ifndef CONFIG_MEMSTICK_UNSAFE_RESUME + msb->card_dead = true; + return 0; +#endif + mutex_lock(&card->host->lock); + + new_msb = kzalloc(sizeof(struct msb_data), GFP_KERNEL); + if (!new_msb) + goto out; + + new_msb->card = card; + memstick_set_drvdata(card, new_msb); + spin_lock_init(&new_msb->q_lock); + sg_init_table(msb->prealloc_sg, MS_BLOCK_MAX_SEGS+1); + + if (msb_init_card(card)) + goto out; + + if (msb->block_size != new_msb->block_size) + goto out; + + if (memcmp(msb->boot_page, new_msb->boot_page, + sizeof(struct ms_boot_page))) + goto out; + + if (msb->logical_block_count != new_msb->logical_block_count || + memcmp(msb->lba_to_pba_table, new_msb->lba_to_pba_table, + msb->logical_block_count)) + goto out; + + if (msb->block_count != new_msb->block_count || + !bitmap_equal(msb->used_blocks_bitmap, new_msb->used_blocks_bitmap, + msb->block_count)) + goto out; + + card_dead = false; +out: + if (card_dead) + dbg("Card was removed/replaced during suspend"); + + msb->card_dead = card_dead; + memstick_set_drvdata(card, msb); + + if (new_msb) { + msb_data_clear(new_msb); + kfree(new_msb); + } + + msb_start(card); + mutex_unlock(&card->host->lock); + return 0; +} +#else + +#define msb_suspend NULL +#define msb_resume NULL + +#endif /* CONFIG_PM */ + +static const struct memstick_device_id msb_id_tbl[] = { + {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_LEGACY, MEMSTICK_CATEGORY_STORAGE, + MEMSTICK_CLASS_FLASH}, + + {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_LEGACY, MEMSTICK_CATEGORY_STORAGE, + MEMSTICK_CLASS_ROM}, + + {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_LEGACY, MEMSTICK_CATEGORY_STORAGE, + MEMSTICK_CLASS_RO}, + + {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_LEGACY, MEMSTICK_CATEGORY_STORAGE, + MEMSTICK_CLASS_WP}, + + {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_DUO, MEMSTICK_CATEGORY_STORAGE_DUO, + MEMSTICK_CLASS_DUO}, + {} +}; +MODULE_DEVICE_TABLE(memstick, msb_id_tbl); + + +static struct memstick_driver msb_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE + }, + .id_table = msb_id_tbl, + .probe = msb_probe, + .remove = msb_remove, + .suspend = msb_suspend, + .resume = msb_resume +}; + +static int __init msb_init(void) +{ + int rc = memstick_register_driver(&msb_driver); + + if (rc) + pr_err("failed to register memstick driver (error %d)\n", rc); + + return rc; +} + +static void __exit msb_exit(void) +{ + memstick_unregister_driver(&msb_driver); + idr_destroy(&msb_disk_idr); +} + +module_init(msb_init); +module_exit(msb_exit); + +module_param(cache_flush_timeout, int, S_IRUGO); +MODULE_PARM_DESC(cache_flush_timeout, + "Cache flush timeout in msec (1000 default)"); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +module_param(verify_writes, bool, S_IRUGO); +MODULE_PARM_DESC(verify_writes, "Read back and check all data that is written"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky"); +MODULE_DESCRIPTION("Sony MemoryStick block device driver"); diff --git a/drivers/memstick/core/ms_block.h b/drivers/memstick/core/ms_block.h new file mode 100644 index 000000000000..7058f9aefeb9 --- /dev/null +++ b/drivers/memstick/core/ms_block.h @@ -0,0 +1,286 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ms_block.h - Sony MemoryStick (legacy) storage support + + * Copyright (C) 2013 Maxim Levitsky <maximlevitsky@gmail.com> + * + * Minor portions of the driver are copied from mspro_block.c which is + * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> + * + * Also ms structures were copied from old broken driver by same author + * These probably come from MS spec + */ + +#ifndef MS_BLOCK_NEW_H +#define MS_BLOCK_NEW_H + +#define MS_BLOCK_MAX_SEGS 32 +#define MS_BLOCK_MAX_PAGES ((2 << 16) - 1) + +#define MS_BLOCK_MAX_BOOT_ADDR 0x000c +#define MS_BLOCK_BOOT_ID 0x0001 +#define MS_BLOCK_INVALID 0xffff +#define MS_MAX_ZONES 16 +#define MS_BLOCKS_IN_ZONE 512 + +#define MS_BLOCK_MAP_LINE_SZ 16 +#define MS_BLOCK_PART_SHIFT 3 + + +#define MEMSTICK_UNCORR_ERROR (MEMSTICK_STATUS1_UCFG | \ + MEMSTICK_STATUS1_UCEX | MEMSTICK_STATUS1_UCDT) + +#define MEMSTICK_CORR_ERROR (MEMSTICK_STATUS1_FGER | MEMSTICK_STATUS1_EXER | \ + MEMSTICK_STATUS1_DTER) + +#define MEMSTICK_INT_ERROR (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR) + +#define MEMSTICK_OVERWRITE_FLAG_NORMAL \ + (MEMSTICK_OVERWRITE_PGST1 | \ + MEMSTICK_OVERWRITE_PGST0 | \ + MEMSTICK_OVERWRITE_BKST) + +#define MEMSTICK_OV_PG_NORMAL \ + (MEMSTICK_OVERWRITE_PGST1 | MEMSTICK_OVERWRITE_PGST0) + +#define MEMSTICK_MANAGEMENT_FLAG_NORMAL \ + (MEMSTICK_MANAGEMENT_SYSFLG | \ + MEMSTICK_MANAGEMENT_SCMS1 | \ + MEMSTICK_MANAGEMENT_SCMS0) \ + +struct ms_boot_header { + unsigned short block_id; + unsigned short format_reserved; + unsigned char reserved0[184]; + unsigned char data_entry; + unsigned char reserved1[179]; +} __packed; + + +struct ms_system_item { + unsigned int start_addr; + unsigned int data_size; + unsigned char data_type_id; + unsigned char reserved[3]; +} __packed; + +struct ms_system_entry { + struct ms_system_item disabled_block; + struct ms_system_item cis_idi; + unsigned char reserved[24]; +} __packed; + +struct ms_boot_attr_info { + unsigned char memorystick_class; + unsigned char format_unique_value1; + unsigned short block_size; + unsigned short number_of_blocks; + unsigned short number_of_effective_blocks; + unsigned short page_size; + unsigned char extra_data_size; + unsigned char format_unique_value2; + unsigned char assembly_time[8]; + unsigned char format_unique_value3; + unsigned char serial_number[3]; + unsigned char assembly_manufacturer_code; + unsigned char assembly_model_code[3]; + unsigned short memory_manufacturer_code; + unsigned short memory_device_code; + unsigned short implemented_capacity; + unsigned char format_unique_value4[2]; + unsigned char vcc; + unsigned char vpp; + unsigned short controller_number; + unsigned short controller_function; + unsigned char reserved0[9]; + unsigned char transfer_supporting; + unsigned short format_unique_value5; + unsigned char format_type; + unsigned char memorystick_application; + unsigned char device_type; + unsigned char reserved1[22]; + unsigned char format_uniqure_value6[2]; + unsigned char reserved2[15]; +} __packed; + +struct ms_cis_idi { + unsigned short general_config; + unsigned short logical_cylinders; + unsigned short reserved0; + unsigned short logical_heads; + unsigned short track_size; + unsigned short page_size; + unsigned short pages_per_track; + unsigned short msw; + unsigned short lsw; + unsigned short reserved1; + unsigned char serial_number[20]; + unsigned short buffer_type; + unsigned short buffer_size_increments; + unsigned short long_command_ecc; + unsigned char firmware_version[28]; + unsigned char model_name[18]; + unsigned short reserved2[5]; + unsigned short pio_mode_number; + unsigned short dma_mode_number; + unsigned short field_validity; + unsigned short current_logical_cylinders; + unsigned short current_logical_heads; + unsigned short current_pages_per_track; + unsigned int current_page_capacity; + unsigned short mutiple_page_setting; + unsigned int addressable_pages; + unsigned short single_word_dma; + unsigned short multi_word_dma; + unsigned char reserved3[128]; +} __packed; + + +struct ms_boot_page { + struct ms_boot_header header; + struct ms_system_entry entry; + struct ms_boot_attr_info attr; +} __packed; + +struct msb_data { + struct memstick_dev *card; + struct gendisk *disk; + struct request_queue *queue; + spinlock_t q_lock; + struct blk_mq_tag_set tag_set; + struct hd_geometry geometry; + struct attribute_group attr_group; + struct request *req; + int caps; + int disk_id; + + /* IO */ + struct workqueue_struct *io_queue; + bool io_queue_stopped; + struct work_struct io_work; + bool card_dead; + + /* Media properties */ + struct ms_boot_page *boot_page; + u16 boot_block_locations[2]; + int boot_block_count; + + bool read_only; + unsigned short page_size; + int block_size; + int pages_in_block; + int zone_count; + int block_count; + int logical_block_count; + + /* FTL tables */ + unsigned long *used_blocks_bitmap; + unsigned long *erased_blocks_bitmap; + u16 *lba_to_pba_table; + int free_block_count[MS_MAX_ZONES]; + bool ftl_initialized; + + /* Cache */ + unsigned char *cache; + unsigned long valid_cache_bitmap; + int cache_block_lba; + bool need_flush_cache; + struct timer_list cache_flush_timer; + + /* Preallocated buffers */ + unsigned char *block_buffer; + struct scatterlist prealloc_sg[MS_BLOCK_MAX_SEGS+1]; + + + /* handler's local data */ + struct ms_register_addr reg_addr; + bool addr_valid; + + u8 command_value; + bool command_need_oob; + struct scatterlist *current_sg; + int current_sg_offset; + + struct ms_register regs; + int current_page; + + int state; + int exit_error; + bool int_polling; + unsigned long int_timeout; + +}; + +enum msb_readpage_states { + MSB_RP_SEND_BLOCK_ADDRESS = 0, + MSB_RP_SEND_READ_COMMAND, + + MSB_RP_SEND_INT_REQ, + MSB_RP_RECEIVE_INT_REQ_RESULT, + + MSB_RP_SEND_READ_STATUS_REG, + MSB_RP_RECEIVE_STATUS_REG, + + MSB_RP_SEND_OOB_READ, + MSB_RP_RECEIVE_OOB_READ, + + MSB_RP_SEND_READ_DATA, + MSB_RP_RECEIVE_READ_DATA, +}; + +enum msb_write_block_states { + MSB_WB_SEND_WRITE_PARAMS = 0, + MSB_WB_SEND_WRITE_OOB, + MSB_WB_SEND_WRITE_COMMAND, + + MSB_WB_SEND_INT_REQ, + MSB_WB_RECEIVE_INT_REQ, + + MSB_WB_SEND_WRITE_DATA, + MSB_WB_RECEIVE_WRITE_CONFIRMATION, +}; + +enum msb_send_command_states { + MSB_SC_SEND_WRITE_PARAMS, + MSB_SC_SEND_WRITE_OOB, + MSB_SC_SEND_COMMAND, + + MSB_SC_SEND_INT_REQ, + MSB_SC_RECEIVE_INT_REQ, + +}; + +enum msb_reset_states { + MSB_RS_SEND, + MSB_RS_CONFIRM, +}; + +enum msb_par_switch_states { + MSB_PS_SEND_SWITCH_COMMAND, + MSB_PS_SWICH_HOST, + MSB_PS_CONFIRM, +}; + +struct chs_entry { + unsigned long size; + unsigned char sec; + unsigned short cyl; + unsigned char head; +}; + +static int msb_reset(struct msb_data *msb, bool full); + +static int h_msb_default_bad(struct memstick_dev *card, + struct memstick_request **mrq); + +#define __dbg(level, format, ...) \ + do { \ + if (debug >= level) \ + pr_err(format "\n", ## __VA_ARGS__); \ + } while (0) + + +#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__) +#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__) + +#endif diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index f4176ca3a794..e507bb11c802 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -1,18 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Sony MemoryStick Pro storage support * * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> * - * 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. - * * Special thanks to Carlos Corbacho for providing various MemoryStick cards * that made this driver possible. - * */ -#include <linux/blkdev.h> +#include <linux/blk-mq.h> #include <linux/idr.h> #include <linux/hdreg.h> #include <linux/kthread.h> @@ -137,11 +133,11 @@ struct mspro_devinfo { struct mspro_block_data { struct memstick_dev *card; - unsigned int usage_count; unsigned int caps; struct gendisk *disk; struct request_queue *queue; struct request *block_req; + struct blk_mq_tag_set tag_set; spinlock_t q_lock; unsigned short page_size; @@ -152,7 +148,6 @@ struct mspro_block_data { unsigned char system; unsigned char read_only:1, eject:1, - has_request:1, data_dir:1, active:1; unsigned char transfer_cmd; @@ -182,59 +177,22 @@ static int mspro_block_complete_req(struct memstick_dev *card, int error); /*** Block device ***/ -static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode) -{ - struct gendisk *disk = bdev->bd_disk; - struct mspro_block_data *msb = disk->private_data; - int rc = -ENXIO; - - mutex_lock(&mspro_block_disk_lock); - - if (msb && msb->card) { - msb->usage_count++; - if ((mode & FMODE_WRITE) && msb->read_only) - rc = -EROFS; - else - rc = 0; - } - - mutex_unlock(&mspro_block_disk_lock); - - return rc; -} - - -static void mspro_block_disk_release(struct gendisk *disk) +static void mspro_block_bd_free_disk(struct gendisk *disk) { struct mspro_block_data *msb = disk->private_data; int disk_id = MINOR(disk_devt(disk)) >> MSPRO_BLOCK_PART_SHIFT; mutex_lock(&mspro_block_disk_lock); - - if (msb) { - if (msb->usage_count) - msb->usage_count--; - - if (!msb->usage_count) { - kfree(msb); - disk->private_data = NULL; - idr_remove(&mspro_block_disk_idr, disk_id); - put_disk(disk); - } - } - + idr_remove(&mspro_block_disk_idr, disk_id); mutex_unlock(&mspro_block_disk_lock); -} -static void mspro_block_bd_release(struct gendisk *disk, fmode_t mode) -{ - mspro_block_disk_release(disk); + kfree(msb); } -static int mspro_block_bd_getgeo(struct block_device *bdev, +static int mspro_block_bd_getgeo(struct gendisk *disk, struct hd_geometry *geo) { - struct mspro_block_data *msb = bdev->bd_disk->private_data; + struct mspro_block_data *msb = disk->private_data; geo->heads = msb->heads; geo->sectors = msb->sectors_per_track; @@ -244,10 +202,9 @@ static int mspro_block_bd_getgeo(struct block_device *bdev, } static const struct block_device_operations ms_block_bdops = { - .open = mspro_block_bd_open, - .release = mspro_block_bd_release, - .getgeo = mspro_block_bd_getgeo, - .owner = THIS_MODULE + .owner = THIS_MODULE, + .getgeo = mspro_block_bd_getgeo, + .free_disk = mspro_block_bd_free_disk, }; /*** Information ***/ @@ -280,7 +237,7 @@ static const char *mspro_block_attr_name(unsigned char tag) return "attr_devinfo"; default: return NULL; - }; + } } typedef ssize_t (*sysfs_show_t)(struct device *dev, @@ -303,8 +260,8 @@ static ssize_t mspro_block_attr_show_default(struct device *dev, buffer[rc++] = '\n'; } - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "%02x ", - ((unsigned char *)s_attr->data)[cnt]); + rc += sysfs_emit_at(buffer, rc, "%02x ", + ((unsigned char *)s_attr->data)[cnt]); } return rc; } @@ -333,61 +290,43 @@ static ssize_t mspro_block_attr_show_sysinfo(struct device *dev, date_tz_f *= 15; } - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "class: %x\n", - x_sys->class); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "block size: %x\n", - be16_to_cpu(x_sys->block_size)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "block count: %x\n", - be16_to_cpu(x_sys->block_count)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "user block count: %x\n", - be16_to_cpu(x_sys->user_block_count)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "page size: %x\n", - be16_to_cpu(x_sys->page_size)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly date: " - "GMT%+d:%d %04u-%02u-%02u %02u:%02u:%02u\n", - date_tz, date_tz_f, - be16_to_cpup((__be16 *)&x_sys->assembly_date[1]), - x_sys->assembly_date[3], x_sys->assembly_date[4], - x_sys->assembly_date[5], x_sys->assembly_date[6], - x_sys->assembly_date[7]); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "serial number: %x\n", - be32_to_cpu(x_sys->serial_number)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, - "assembly maker code: %x\n", - x_sys->assembly_maker_code); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly model code: " - "%02x%02x%02x\n", x_sys->assembly_model_code[0], - x_sys->assembly_model_code[1], - x_sys->assembly_model_code[2]); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "memory maker code: %x\n", - be16_to_cpu(x_sys->memory_maker_code)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "memory model code: %x\n", - be16_to_cpu(x_sys->memory_model_code)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "vcc: %x\n", - x_sys->vcc); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "vpp: %x\n", - x_sys->vpp); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "controller number: %x\n", - be16_to_cpu(x_sys->controller_number)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, - "controller function: %x\n", - be16_to_cpu(x_sys->controller_function)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n", - be16_to_cpu(x_sys->start_sector)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "unit size: %x\n", - be16_to_cpu(x_sys->unit_size)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "sub class: %x\n", - x_sys->ms_sub_class); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "interface type: %x\n", - x_sys->interface_type); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "controller code: %x\n", - be16_to_cpu(x_sys->controller_code)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "format type: %x\n", - x_sys->format_type); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "device type: %x\n", - x_sys->device_type); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "mspro id: %s\n", - x_sys->mspro_id); + rc += sysfs_emit_at(buffer, rc, "class: %x\n", x_sys->class); + rc += sysfs_emit_at(buffer, rc, "block size: %x\n", be16_to_cpu(x_sys->block_size)); + rc += sysfs_emit_at(buffer, rc, "block count: %x\n", be16_to_cpu(x_sys->block_count)); + rc += sysfs_emit_at(buffer, rc, "user block count: %x\n", + be16_to_cpu(x_sys->user_block_count)); + rc += sysfs_emit_at(buffer, rc, "page size: %x\n", be16_to_cpu(x_sys->page_size)); + rc += sysfs_emit_at(buffer, rc, "assembly date: GMT%+d:%d %04u-%02u-%02u %02u:%02u:%02u\n", + date_tz, date_tz_f, + be16_to_cpup((__be16 *)&x_sys->assembly_date[1]), + x_sys->assembly_date[3], x_sys->assembly_date[4], + x_sys->assembly_date[5], x_sys->assembly_date[6], + x_sys->assembly_date[7]); + rc += sysfs_emit_at(buffer, rc, "serial number: %x\n", be32_to_cpu(x_sys->serial_number)); + rc += sysfs_emit_at(buffer, rc, "assembly maker code: %x\n", x_sys->assembly_maker_code); + rc += sysfs_emit_at(buffer, rc, "assembly model code: %02x%02x%02x\n", + x_sys->assembly_model_code[0], + x_sys->assembly_model_code[1], + x_sys->assembly_model_code[2]); + rc += sysfs_emit_at(buffer, rc, "memory maker code: %x\n", + be16_to_cpu(x_sys->memory_maker_code)); + rc += sysfs_emit_at(buffer, rc, "memory model code: %x\n", + be16_to_cpu(x_sys->memory_model_code)); + rc += sysfs_emit_at(buffer, rc, "vcc: %x\n", x_sys->vcc); + rc += sysfs_emit_at(buffer, rc, "vpp: %x\n", x_sys->vpp); + rc += sysfs_emit_at(buffer, rc, "controller number: %x\n", + be16_to_cpu(x_sys->controller_number)); + rc += sysfs_emit_at(buffer, rc, "controller function: %x\n", + be16_to_cpu(x_sys->controller_function)); + rc += sysfs_emit_at(buffer, rc, "start sector: %x\n", be16_to_cpu(x_sys->start_sector)); + rc += sysfs_emit_at(buffer, rc, "unit size: %x\n", be16_to_cpu(x_sys->unit_size)); + rc += sysfs_emit_at(buffer, rc, "sub class: %x\n", x_sys->ms_sub_class); + rc += sysfs_emit_at(buffer, rc, "interface type: %x\n", x_sys->interface_type); + rc += sysfs_emit_at(buffer, rc, "controller code: %x\n", + be16_to_cpu(x_sys->controller_code)); + rc += sysfs_emit_at(buffer, rc, "format type: %x\n", x_sys->format_type); + rc += sysfs_emit_at(buffer, rc, "device type: %x\n", x_sys->device_type); + rc += sysfs_emit_at(buffer, rc, "mspro id: %s\n", x_sys->mspro_id); return rc; } @@ -399,7 +338,7 @@ static ssize_t mspro_block_attr_show_modelname(struct device *dev, struct mspro_sys_attr, dev_attr); - return scnprintf(buffer, PAGE_SIZE, "%s", (char *)s_attr->data); + return sysfs_emit(buffer, "%s", (char *)s_attr->data); } static ssize_t mspro_block_attr_show_mbr(struct device *dev, @@ -412,27 +351,17 @@ static ssize_t mspro_block_attr_show_mbr(struct device *dev, struct mspro_mbr *x_mbr = x_attr->data; ssize_t rc = 0; - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "boot partition: %x\n", - x_mbr->boot_partition); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start head: %x\n", - x_mbr->start_head); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n", - x_mbr->start_sector); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start cylinder: %x\n", - x_mbr->start_cylinder); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "partition type: %x\n", - x_mbr->partition_type); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end head: %x\n", - x_mbr->end_head); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end sector: %x\n", - x_mbr->end_sector); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end cylinder: %x\n", - x_mbr->end_cylinder); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sectors: %x\n", - x_mbr->start_sectors); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, - "sectors per partition: %x\n", - x_mbr->sectors_per_partition); + rc += sysfs_emit_at(buffer, rc, "boot partition: %x\n", x_mbr->boot_partition); + rc += sysfs_emit_at(buffer, rc, "start head: %x\n", x_mbr->start_head); + rc += sysfs_emit_at(buffer, rc, "start sector: %x\n", x_mbr->start_sector); + rc += sysfs_emit_at(buffer, rc, "start cylinder: %x\n", x_mbr->start_cylinder); + rc += sysfs_emit_at(buffer, rc, "partition type: %x\n", x_mbr->partition_type); + rc += sysfs_emit_at(buffer, rc, "end head: %x\n", x_mbr->end_head); + rc += sysfs_emit_at(buffer, rc, "end sector: %x\n", x_mbr->end_sector); + rc += sysfs_emit_at(buffer, rc, "end cylinder: %x\n", x_mbr->end_cylinder); + rc += sysfs_emit_at(buffer, rc, "start sectors: %x\n", x_mbr->start_sectors); + rc += sysfs_emit_at(buffer, rc, "sectors per partition: %x\n", + x_mbr->sectors_per_partition); return rc; } @@ -452,22 +381,19 @@ static ssize_t mspro_block_attr_show_specfile(struct device *dev, memcpy(ext, x_spfile->ext, 3); ext[3] = 0; - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "name: %s\n", name); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "ext: %s\n", ext); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "attribute: %x\n", - x_spfile->attr); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "time: %d:%d:%d\n", - x_spfile->time >> 11, - (x_spfile->time >> 5) & 0x3f, - (x_spfile->time & 0x1f) * 2); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "date: %d-%d-%d\n", - (x_spfile->date >> 9) + 1980, - (x_spfile->date >> 5) & 0xf, - x_spfile->date & 0x1f); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start cluster: %x\n", - x_spfile->cluster); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "size: %x\n", - x_spfile->size); + rc += sysfs_emit_at(buffer, rc, "name: %s\n", name); + rc += sysfs_emit_at(buffer, rc, "ext: %s\n", ext); + rc += sysfs_emit_at(buffer, rc, "attribute: %x\n", x_spfile->attr); + rc += sysfs_emit_at(buffer, rc, "time: %d:%d:%d\n", + x_spfile->time >> 11, + (x_spfile->time >> 5) & 0x3f, + (x_spfile->time & 0x1f) * 2); + rc += sysfs_emit_at(buffer, rc, "date: %d-%d-%d\n", + (x_spfile->date >> 9) + 1980, + (x_spfile->date >> 5) & 0xf, + x_spfile->date & 0x1f); + rc += sysfs_emit_at(buffer, rc, "start cluster: %x\n", x_spfile->cluster); + rc += sysfs_emit_at(buffer, rc, "size: %x\n", x_spfile->size); return rc; } @@ -481,16 +407,14 @@ static ssize_t mspro_block_attr_show_devinfo(struct device *dev, struct mspro_devinfo *x_devinfo = x_attr->data; ssize_t rc = 0; - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "cylinders: %x\n", - be16_to_cpu(x_devinfo->cylinders)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "heads: %x\n", - be16_to_cpu(x_devinfo->heads)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "bytes per track: %x\n", - be16_to_cpu(x_devinfo->bytes_per_track)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "bytes per sector: %x\n", - be16_to_cpu(x_devinfo->bytes_per_sector)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "sectors per track: %x\n", - be16_to_cpu(x_devinfo->sectors_per_track)); + rc += sysfs_emit_at(buffer, rc, "cylinders: %x\n", be16_to_cpu(x_devinfo->cylinders)); + rc += sysfs_emit_at(buffer, rc, "heads: %x\n", be16_to_cpu(x_devinfo->heads)); + rc += sysfs_emit_at(buffer, rc, "bytes per track: %x\n", + be16_to_cpu(x_devinfo->bytes_per_track)); + rc += sysfs_emit_at(buffer, rc, "bytes per sector: %x\n", + be16_to_cpu(x_devinfo->bytes_per_sector)); + rc += sysfs_emit_at(buffer, rc, "sectors per track: %x\n", + be16_to_cpu(x_devinfo->sectors_per_track)); return rc; } @@ -636,8 +560,7 @@ has_int_reg: t_offset += msb->current_page * msb->page_size; sg_set_page(&t_sg, - nth_page(sg_page(&(msb->req_sg[msb->current_seg])), - t_offset >> PAGE_SHIFT), + sg_page(&(msb->req_sg[msb->current_seg])) + (t_offset >> PAGE_SHIFT), msb->page_size, offset_in_page(t_offset)); memstick_init_req_sg(*mrq, msb->data_dir == READ @@ -694,23 +617,30 @@ static void h_mspro_block_setup_cmd(struct memstick_dev *card, u64 offset, /*** Data transfer ***/ -static int mspro_block_issue_req(struct memstick_dev *card, int chunk) +static int mspro_block_issue_req(struct memstick_dev *card) { struct mspro_block_data *msb = memstick_get_drvdata(card); u64 t_off; unsigned int count; -try_again: - while (chunk) { + while (true) { msb->current_page = 0; msb->current_seg = 0; - msb->seg_count = blk_rq_map_sg(msb->block_req->q, - msb->block_req, - msb->req_sg); + msb->seg_count = blk_rq_map_sg(msb->block_req, msb->req_sg); if (!msb->seg_count) { - chunk = __blk_end_request_cur(msb->block_req, -ENOMEM); - continue; + unsigned int bytes = blk_rq_cur_bytes(msb->block_req); + bool chunk; + + chunk = blk_update_request(msb->block_req, + BLK_STS_RESOURCE, + bytes); + if (chunk) + continue; + __blk_mq_end_request(msb->block_req, + BLK_STS_RESOURCE); + msb->block_req = NULL; + return -EAGAIN; } t_off = blk_rq_pos(msb->block_req); @@ -727,38 +657,28 @@ try_again: memstick_new_req(card->host); return 0; } - - dev_dbg(&card->dev, "blk_fetch\n"); - msb->block_req = blk_fetch_request(msb->queue); - if (!msb->block_req) { - dev_dbg(&card->dev, "issue end\n"); - return -EAGAIN; - } - - dev_dbg(&card->dev, "trying again\n"); - chunk = 1; - goto try_again; } static int mspro_block_complete_req(struct memstick_dev *card, int error) { struct mspro_block_data *msb = memstick_get_drvdata(card); - int chunk, cnt; + int cnt; + bool chunk; unsigned int t_len = 0; unsigned long flags; spin_lock_irqsave(&msb->q_lock, flags); - dev_dbg(&card->dev, "complete %d, %d\n", msb->has_request ? 1 : 0, + dev_dbg(&card->dev, "complete %d, %d\n", msb->block_req ? 1 : 0, error); - if (msb->has_request) { + if (msb->block_req) { /* Nothing to do - not really an error */ if (error == -EAGAIN) error = 0; if (error || (card->current_mrq.tpc == MSPRO_CMD_STOP)) { if (msb->data_dir == READ) { - for (cnt = 0; cnt < msb->current_seg; cnt++) + for (cnt = 0; cnt < msb->current_seg; cnt++) { t_len += msb->req_sg[cnt].length / msb->page_size; @@ -766,6 +686,7 @@ static int mspro_block_complete_req(struct memstick_dev *card, int error) t_len += msb->current_page - 1; t_len *= msb->page_size; + } } } else t_len = blk_rq_bytes(msb->block_req); @@ -775,14 +696,17 @@ static int mspro_block_complete_req(struct memstick_dev *card, int error) if (error && !t_len) t_len = blk_rq_cur_bytes(msb->block_req); - chunk = __blk_end_request(msb->block_req, error, t_len); - - error = mspro_block_issue_req(card, chunk); - - if (!error) - goto out; - else - msb->has_request = 0; + chunk = blk_update_request(msb->block_req, + errno_to_blk_status(error), t_len); + if (chunk) { + error = mspro_block_issue_req(card); + if (!error) + goto out; + } else { + __blk_mq_end_request(msb->block_req, + errno_to_blk_status(error)); + msb->block_req = NULL; + } } else { if (!error) error = -EAGAIN; @@ -803,8 +727,8 @@ static void mspro_block_stop(struct memstick_dev *card) while (1) { spin_lock_irqsave(&msb->q_lock, flags); - if (!msb->has_request) { - blk_stop_queue(msb->queue); + if (!msb->block_req) { + blk_mq_stop_hw_queues(msb->queue); rc = 1; } spin_unlock_irqrestore(&msb->q_lock, flags); @@ -819,45 +743,37 @@ static void mspro_block_stop(struct memstick_dev *card) static void mspro_block_start(struct memstick_dev *card) { struct mspro_block_data *msb = memstick_get_drvdata(card); - unsigned long flags; - spin_lock_irqsave(&msb->q_lock, flags); - blk_start_queue(msb->queue); - spin_unlock_irqrestore(&msb->q_lock, flags); + blk_mq_start_hw_queues(msb->queue); } -static int mspro_block_prepare_req(struct request_queue *q, struct request *req) +static blk_status_t mspro_queue_rq(struct blk_mq_hw_ctx *hctx, + const struct blk_mq_queue_data *bd) { - if (req->cmd_type != REQ_TYPE_FS && - req->cmd_type != REQ_TYPE_BLOCK_PC) { - blk_dump_rq_flags(req, "MSPro unsupported request"); - return BLKPREP_KILL; - } - - req->cmd_flags |= REQ_DONTPREP; - - return BLKPREP_OK; -} - -static void mspro_block_submit_req(struct request_queue *q) -{ - struct memstick_dev *card = q->queuedata; + struct memstick_dev *card = hctx->queue->queuedata; struct mspro_block_data *msb = memstick_get_drvdata(card); - struct request *req = NULL; - if (msb->has_request) - return; + spin_lock_irq(&msb->q_lock); - if (msb->eject) { - while ((req = blk_fetch_request(q)) != NULL) - __blk_end_request_all(req, -ENODEV); + if (msb->block_req) { + spin_unlock_irq(&msb->q_lock); + return BLK_STS_DEV_RESOURCE; + } - return; + if (msb->eject) { + spin_unlock_irq(&msb->q_lock); + blk_mq_start_request(bd->rq); + return BLK_STS_IOERR; } - msb->has_request = 1; - if (mspro_block_issue_req(card, 0)) - msb->has_request = 0; + msb->block_req = bd->rq; + blk_mq_start_request(bd->rq); + + if (mspro_block_issue_req(card)) + msb->block_req = NULL; + + spin_unlock_irq(&msb->q_lock); + return BLK_STS_OK; } /*** Initialization ***/ @@ -1023,8 +939,8 @@ static int mspro_block_read_attributes(struct memstick_dev *card) } else attr_count = attr->count; - msb->attr_group.attrs = kzalloc((attr_count + 1) - * sizeof(struct attribute), + msb->attr_group.attrs = kcalloc(attr_count + 1, + sizeof(*msb->attr_group.attrs), GFP_KERNEL); if (!msb->attr_group.attrs) { rc = -ENOMEM; @@ -1032,12 +948,11 @@ static int mspro_block_read_attributes(struct memstick_dev *card) } msb->attr_group.name = "media_attributes"; - buffer = kmalloc(attr_len, GFP_KERNEL); + buffer = kmemdup(attr, attr_len, GFP_KERNEL); if (!buffer) { rc = -ENOMEM; goto out_free_attr; } - memcpy(buffer, (char *)attr, attr_len); for (cnt = 0; cnt < attr_count; ++cnt) { s_attr = kzalloc(sizeof(struct mspro_sys_attr), GFP_KERNEL); @@ -1178,20 +1093,25 @@ static int mspro_block_init_card(struct memstick_dev *card) } +static const struct blk_mq_ops mspro_mq_ops = { + .queue_rq = mspro_queue_rq, +}; + static int mspro_block_init_disk(struct memstick_dev *card) { struct mspro_block_data *msb = memstick_get_drvdata(card); - struct memstick_host *host = card->host; + struct queue_limits lim = { + .logical_block_size = msb->page_size, + .max_hw_sectors = MSPRO_BLOCK_MAX_PAGES, + .max_segments = MSPRO_BLOCK_MAX_SEGS, + .max_segment_size = MSPRO_BLOCK_MAX_PAGES * msb->page_size, + }; struct mspro_devinfo *dev_info = NULL; struct mspro_sys_info *sys_info = NULL; struct mspro_sys_attr *s_attr = NULL; int rc, disk_id; - u64 limit = BLK_BOUNCE_HIGH; unsigned long capacity; - if (host->dev.dma_mask && *(host->dev.dma_mask)) - limit = *(host->dev.dma_mask); - for (rc = 0; msb->attr_group.attrs[rc]; ++rc) { s_attr = mspro_from_sysfs_attr(msb->attr_group.attrs[rc]); @@ -1216,51 +1136,44 @@ static int mspro_block_init_disk(struct memstick_dev *card) if (disk_id < 0) return disk_id; - msb->disk = alloc_disk(1 << MSPRO_BLOCK_PART_SHIFT); - if (!msb->disk) { - rc = -ENOMEM; + rc = blk_mq_alloc_sq_tag_set(&msb->tag_set, &mspro_mq_ops, 2, 0); + if (rc) goto out_release_id; - } - msb->queue = blk_init_queue(mspro_block_submit_req, &msb->q_lock); - if (!msb->queue) { - rc = -ENOMEM; - goto out_put_disk; + msb->disk = blk_mq_alloc_disk(&msb->tag_set, &lim, card); + if (IS_ERR(msb->disk)) { + rc = PTR_ERR(msb->disk); + goto out_free_tag_set; } - - msb->queue->queuedata = card; - blk_queue_prep_rq(msb->queue, mspro_block_prepare_req); - - blk_queue_bounce_limit(msb->queue, limit); - blk_queue_max_hw_sectors(msb->queue, MSPRO_BLOCK_MAX_PAGES); - blk_queue_max_segments(msb->queue, MSPRO_BLOCK_MAX_SEGS); - blk_queue_max_segment_size(msb->queue, - MSPRO_BLOCK_MAX_PAGES * msb->page_size); + msb->queue = msb->disk->queue; msb->disk->major = major; msb->disk->first_minor = disk_id << MSPRO_BLOCK_PART_SHIFT; + msb->disk->minors = 1 << MSPRO_BLOCK_PART_SHIFT; msb->disk->fops = &ms_block_bdops; - msb->usage_count = 1; msb->disk->private_data = msb; - msb->disk->queue = msb->queue; - msb->disk->driverfs_dev = &card->dev; sprintf(msb->disk->disk_name, "mspblk%d", disk_id); - blk_queue_logical_block_size(msb->queue, msb->page_size); - capacity = be16_to_cpu(sys_info->user_block_count); capacity *= be16_to_cpu(sys_info->block_size); capacity *= msb->page_size >> 9; set_capacity(msb->disk, capacity); dev_dbg(&card->dev, "capacity set %ld\n", capacity); - add_disk(msb->disk); + if (msb->read_only) + set_disk_ro(msb->disk, true); + + rc = device_add_disk(&card->dev, msb->disk, NULL); + if (rc) + goto out_cleanup_disk; msb->active = 1; return 0; -out_put_disk: +out_cleanup_disk: put_disk(msb->disk); +out_free_tag_set: + blk_mq_free_tag_set(&msb->tag_set); out_release_id: mutex_lock(&mspro_block_disk_lock); idr_remove(&mspro_block_disk_idr, disk_id); @@ -1337,13 +1250,13 @@ static void mspro_block_remove(struct memstick_dev *card) spin_lock_irqsave(&msb->q_lock, flags); msb->eject = 1; - blk_start_queue(msb->queue); spin_unlock_irqrestore(&msb->q_lock, flags); + blk_mq_start_hw_queues(msb->queue); del_gendisk(msb->disk); dev_dbg(&card->dev, "mspro block remove\n"); - blk_cleanup_queue(msb->queue); + blk_mq_free_tag_set(&msb->tag_set); msb->queue = NULL; sysfs_remove_group(&card->dev.kobj, &msb->attr_group); @@ -1352,7 +1265,7 @@ static void mspro_block_remove(struct memstick_dev *card) mspro_block_data_clear(msb); mutex_unlock(&mspro_block_disk_lock); - mspro_block_disk_release(msb->disk); + put_disk(msb->disk); memstick_set_drvdata(card, NULL); } @@ -1363,8 +1276,9 @@ static int mspro_block_suspend(struct memstick_dev *card, pm_message_t state) struct mspro_block_data *msb = memstick_get_drvdata(card); unsigned long flags; + blk_mq_stop_hw_queues(msb->queue); + spin_lock_irqsave(&msb->q_lock, flags); - blk_stop_queue(msb->queue); msb->active = 0; spin_unlock_irqrestore(&msb->q_lock, flags); @@ -1374,7 +1288,6 @@ static int mspro_block_suspend(struct memstick_dev *card, pm_message_t state) static int mspro_block_resume(struct memstick_dev *card) { struct mspro_block_data *msb = memstick_get_drvdata(card); - unsigned long flags; int rc = 0; #ifdef CONFIG_MEMSTICK_UNSAFE_RESUME @@ -1393,7 +1306,8 @@ static int mspro_block_resume(struct memstick_dev *card) new_msb->card = card; memstick_set_drvdata(card, new_msb); - if (mspro_block_init_card(card)) + rc = mspro_block_init_card(card); + if (rc) goto out_free; for (cnt = 0; new_msb->attr_group.attrs[cnt] @@ -1420,9 +1334,7 @@ out_unlock: #endif /* CONFIG_MEMSTICK_UNSAFE_RESUME */ - spin_lock_irqsave(&msb->q_lock, flags); - blk_start_queue(msb->queue); - spin_unlock_irqrestore(&msb->q_lock, flags); + blk_mq_start_hw_queues(msb->queue); return rc; } @@ -1433,7 +1345,7 @@ out_unlock: #endif /* CONFIG_PM */ -static struct memstick_device_id mspro_block_id_tbl[] = { +static const struct memstick_device_id mspro_block_id_tbl[] = { {MEMSTICK_MATCH_ALL, MEMSTICK_TYPE_PRO, MEMSTICK_CATEGORY_STORAGE_DUO, MEMSTICK_CLASS_DUO}, {} diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig index 1b37cf8cd204..fcd2c2cc3cb4 100644 --- a/drivers/memstick/host/Kconfig +++ b/drivers/memstick/host/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # MemoryStick host controller drivers # @@ -17,7 +18,7 @@ config MEMSTICK_TIFM_MS 'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support (TIFM_7XX1)'. - To compile this driver as a module, choose M here: the + To compile this driver as a module, choose M here: the module will be called tifm_ms. config MEMSTICK_JMICRON_38X @@ -28,7 +29,7 @@ config MEMSTICK_JMICRON_38X Say Y here if you want to be able to access MemoryStick cards with the JMicron(R) JMB38X MemoryStick card reader. - To compile this driver as a module, choose M here: the + To compile this driver as a module, choose M here: the module will be called jmb38x_ms. config MEMSTICK_R592 @@ -43,12 +44,12 @@ config MEMSTICK_R592 To compile this driver as a module, choose M here: the module will be called r592. -config MEMSTICK_REALTEK_PCI - tristate "Realtek PCI-E Memstick Card Interface Driver" - depends on MFD_RTSX_PCI +config MEMSTICK_REALTEK_USB + tristate "Realtek USB Memstick Card Interface Driver" + depends on MISC_RTSX_USB help Say Y here to include driver code to support Memstick card interface - of Realtek PCI-E card reader + of Realtek RTS5129/39 series USB card reader To compile this driver as a module, choose M here: the module will - be called rtsx_pci_ms. + be called rts5139_ms. diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile index af3459d7686e..0c90df33165d 100644 --- a/drivers/memstick/host/Makefile +++ b/drivers/memstick/host/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Makefile for MemoryStick host controller drivers # @@ -5,4 +6,4 @@ obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o obj-$(CONFIG_MEMSTICK_R592) += r592.o -obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o +obj-$(CONFIG_MEMSTICK_REALTEK_USB) += rtsx_usb_ms.o diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c index aeabaa5aedf7..79e66e30417c 100644 --- a/drivers/memstick/host/jmb38x_ms.c +++ b/drivers/memstick/host/jmb38x_ms.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * jmb38x_ms.c - JMicron jmb38x MemoryStick card reader * * Copyright (C) 2008 Alex Dubov <oakad@yahoo.com> - * - * 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 <linux/spinlock.h> @@ -59,6 +55,7 @@ struct jmb38x_ms_host { unsigned int block_pos; unsigned long timeout_jiffies; struct timer_list timer; + struct memstick_host *msh; struct memstick_request *req; unsigned char cmd_flags; unsigned char io_pos; @@ -69,7 +66,7 @@ struct jmb38x_ms_host { struct jmb38x_ms { struct pci_dev *pdev; int host_cnt; - struct memstick_host *hosts[]; + struct memstick_host *hosts[] __counted_by(host_cnt); }; #define BLOCK_COUNT_MASK 0xffff0000 @@ -258,9 +255,11 @@ static unsigned int jmb38x_ms_write_data(struct jmb38x_ms_host *host, case 3: host->io_word[0] |= buf[off + 2] << 16; host->io_pos++; + fallthrough; case 2: host->io_word[0] |= buf[off + 1] << 8; host->io_pos++; + fallthrough; case 1: host->io_word[0] |= buf[off]; host->io_pos++; @@ -315,11 +314,10 @@ static int jmb38x_ms_transfer_data(struct jmb38x_ms_host *host) } while (length) { - unsigned int uninitialized_var(p_off); + unsigned int p_off; if (host->req->long_data) { - pg = nth_page(sg_page(&host->req->sg), - off >> PAGE_SHIFT); + pg = sg_page(&host->req->sg) + (off >> PAGE_SHIFT); p_off = offset_in_page(off); p_cnt = PAGE_SIZE - p_off; p_cnt = min(p_cnt, length); @@ -367,7 +365,6 @@ static int jmb38x_ms_transfer_data(struct jmb38x_ms_host *host) static int jmb38x_ms_issue_cmd(struct memstick_host *msh) { struct jmb38x_ms_host *host = memstick_priv(msh); - unsigned char *data; unsigned int data_len, cmd, t_val; if (!(STATUS_HAS_MEDIA & readl(host->addr + STATUS))) { @@ -399,8 +396,6 @@ static int jmb38x_ms_issue_cmd(struct memstick_host *msh) cmd |= TPC_WAIT_INT; } - data = host->req->data; - if (!no_dma) host->cmd_flags |= DMA_DATA; @@ -419,10 +414,10 @@ static int jmb38x_ms_issue_cmd(struct memstick_host *msh) } if (host->cmd_flags & DMA_DATA) { - if (1 != pci_map_sg(host->chip->pdev, &host->req->sg, 1, + if (1 != dma_map_sg(&host->chip->pdev->dev, &host->req->sg, 1, host->req->data_dir == READ - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE)) { + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE)) { host->req->error = -ENOMEM; return host->req->error; } @@ -437,13 +432,13 @@ static int jmb38x_ms_issue_cmd(struct memstick_host *msh) writel(((1 << 16) & BLOCK_COUNT_MASK) | (data_len & BLOCK_SIZE_MASK), host->addr + BLOCK); - t_val = readl(host->addr + INT_STATUS_ENABLE); - t_val |= host->req->data_dir == READ - ? INT_STATUS_FIFO_RRDY - : INT_STATUS_FIFO_WRDY; + t_val = readl(host->addr + INT_STATUS_ENABLE); + t_val |= host->req->data_dir == READ + ? INT_STATUS_FIFO_RRDY + : INT_STATUS_FIFO_WRDY; - writel(t_val, host->addr + INT_STATUS_ENABLE); - writel(t_val, host->addr + INT_SIGNAL_ENABLE); + writel(t_val, host->addr + INT_STATUS_ENABLE); + writel(t_val, host->addr + INT_SIGNAL_ENABLE); } else { cmd &= ~(TPC_DATA_SEL | 0xf); host->cmd_flags |= REG_DATA; @@ -473,7 +468,7 @@ static void jmb38x_ms_complete_cmd(struct memstick_host *msh, int last) unsigned int t_val = 0; int rc; - del_timer(&host->timer); + timer_delete(&host->timer); dev_dbg(&msh->dev, "c control %08x\n", readl(host->addr + HOST_CONTROL)); @@ -487,9 +482,9 @@ static void jmb38x_ms_complete_cmd(struct memstick_host *msh, int last) writel(0, host->addr + DMA_CONTROL); if (host->cmd_flags & DMA_DATA) { - pci_unmap_sg(host->chip->pdev, &host->req->sg, 1, + dma_unmap_sg(&host->chip->pdev->dev, &host->req->sg, 1, host->req->data_dir == READ - ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } else { t_val = readl(host->addr + INT_STATUS_ENABLE); if (host->req->data_dir == READ) @@ -592,10 +587,10 @@ static irqreturn_t jmb38x_ms_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static void jmb38x_ms_abort(unsigned long data) +static void jmb38x_ms_abort(struct timer_list *t) { - struct memstick_host *msh = (struct memstick_host *)data; - struct jmb38x_ms_host *host = memstick_priv(msh); + struct jmb38x_ms_host *host = timer_container_of(host, t, timer); + struct memstick_host *msh = host->msh; unsigned long flags; dev_dbg(&host->chip->pdev->dev, "abort\n"); @@ -643,7 +638,6 @@ static int jmb38x_ms_reset(struct jmb38x_ms_host *host) writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN | readl(host->addr + HOST_CONTROL), host->addr + HOST_CONTROL); - mmiowb(); for (cnt = 0; cnt < 20; ++cnt) { if (!(HOST_CONTROL_RESET_REQ @@ -658,7 +652,6 @@ reset_next: writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN | readl(host->addr + HOST_CONTROL), host->addr + HOST_CONTROL); - mmiowb(); for (cnt = 0; cnt < 20; ++cnt) { if (!(HOST_CONTROL_RESET @@ -671,7 +664,6 @@ reset_next: return -EIO; reset_ok: - mmiowb(); writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE); writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE); return 0; @@ -755,7 +747,7 @@ static int jmb38x_ms_set_param(struct memstick_host *msh, clock_delay); host->ifmode = value; break; - }; + } return 0; } @@ -800,11 +792,10 @@ static int jmb38x_ms_pmos(struct pci_dev *pdev, int flag) return 0; } -#ifdef CONFIG_PM - -static int jmb38x_ms_suspend(struct pci_dev *dev, pm_message_t state) +static int __maybe_unused jmb38x_ms_suspend(struct device *dev) { - struct jmb38x_ms *jm = pci_get_drvdata(dev); + struct jmb38x_ms *jm = dev_get_drvdata(dev); + int cnt; for (cnt = 0; cnt < jm->host_cnt; ++cnt) { @@ -813,26 +804,17 @@ static int jmb38x_ms_suspend(struct pci_dev *dev, pm_message_t state) memstick_suspend_host(jm->hosts[cnt]); } - pci_save_state(dev); - pci_enable_wake(dev, pci_choose_state(dev, state), 0); - pci_disable_device(dev); - pci_set_power_state(dev, pci_choose_state(dev, state)); + device_wakeup_disable(dev); + return 0; } -static int jmb38x_ms_resume(struct pci_dev *dev) +static int __maybe_unused jmb38x_ms_resume(struct device *dev) { - struct jmb38x_ms *jm = pci_get_drvdata(dev); + struct jmb38x_ms *jm = dev_get_drvdata(dev); int rc; - pci_set_power_state(dev, PCI_D0); - pci_restore_state(dev); - rc = pci_enable_device(dev); - if (rc) - return rc; - pci_set_master(dev); - - jmb38x_ms_pmos(dev, 1); + jmb38x_ms_pmos(to_pci_dev(dev), 1); for (rc = 0; rc < jm->host_cnt; ++rc) { if (!jm->hosts[rc]) @@ -844,18 +826,11 @@ static int jmb38x_ms_resume(struct pci_dev *dev) return 0; } -#else - -#define jmb38x_ms_suspend NULL -#define jmb38x_ms_resume NULL - -#endif /* CONFIG_PM */ - static int jmb38x_ms_count_slots(struct pci_dev *pdev) { int cnt, rc = 0; - for (cnt = 0; cnt < PCI_ROM_RESOURCE; ++cnt) { + for (cnt = 0; cnt < PCI_STD_NUM_BARS; ++cnt) { if (!(IORESOURCE_MEM & pci_resource_flags(pdev, cnt))) break; @@ -878,6 +853,7 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) return NULL; host = memstick_priv(msh); + host->msh = msh; host->chip = jm; host->addr = ioremap(pci_resource_start(jm->pdev, cnt), pci_resource_len(jm->pdev, cnt)); @@ -897,7 +873,7 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) msh->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8; - setup_timer(&host->timer, jmb38x_ms_abort, (unsigned long)msh); + timer_setup(&host->timer, jmb38x_ms_abort, 0); if (!request_irq(host->irq, jmb38x_ms_isr, IRQF_SHARED, host->host_id, msh)) @@ -905,7 +881,7 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) iounmap(host->addr); err_out_free: - kfree(msh); + memstick_free_host(msh); return NULL; } @@ -925,7 +901,7 @@ static int jmb38x_ms_probe(struct pci_dev *pdev, int pci_dev_busy = 0; int rc, cnt; - rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (rc) return rc; @@ -947,11 +923,10 @@ static int jmb38x_ms_probe(struct pci_dev *pdev, if (!cnt) { rc = -ENODEV; pci_dev_busy = 1; - goto err_out; + goto err_out_int; } - jm = kzalloc(sizeof(struct jmb38x_ms) - + cnt * sizeof(struct memstick_host *), GFP_KERNEL); + jm = kzalloc(struct_size(jm, hosts, cnt), GFP_KERNEL); if (!jm) { rc = -ENOMEM; goto err_out_int; @@ -1007,7 +982,6 @@ static void jmb38x_ms_remove(struct pci_dev *dev) tasklet_kill(&host->notify); writel(0, host->addr + INT_SIGNAL_ENABLE); writel(0, host->addr + INT_STATUS_ENABLE); - mmiowb(); dev_dbg(&jm->pdev->dev, "interrupts off\n"); spin_lock_irqsave(&host->lock, flags); if (host->req) { @@ -1037,13 +1011,14 @@ static struct pci_device_id jmb38x_ms_id_tbl [] = { { } }; +static SIMPLE_DEV_PM_OPS(jmb38x_ms_pm_ops, jmb38x_ms_suspend, jmb38x_ms_resume); + static struct pci_driver jmb38x_ms_driver = { .name = DRIVER_NAME, .id_table = jmb38x_ms_id_tbl, .probe = jmb38x_ms_probe, .remove = jmb38x_ms_remove, - .suspend = jmb38x_ms_suspend, - .resume = jmb38x_ms_resume + .driver.pm = &jmb38x_ms_pm_ops, }; module_pci_driver(jmb38x_ms_driver); diff --git a/drivers/memstick/host/r592.c b/drivers/memstick/host/r592.c index 1b6e91345222..605b2265536f 100644 --- a/drivers/memstick/host/r592.c +++ b/drivers/memstick/host/r592.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2010 - Maxim Levitsky * driver for Ricoh memstick readers - * - * 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 <linux/kernel.h> @@ -47,12 +44,10 @@ static const char *tpc_names[] = { * memstick_debug_get_tpc_name - debug helper that returns string for * a TPC number */ -const char *memstick_debug_get_tpc_name(int tpc) +static __maybe_unused const char *memstick_debug_get_tpc_name(int tpc) { return tpc_names[tpc-1]; } -EXPORT_SYMBOL(memstick_debug_get_tpc_name); - /* Read a register*/ static inline u32 r592_read_reg(struct r592_device *dev, int address) @@ -188,6 +183,7 @@ static void r592_host_reset(struct r592_device *dev) r592_set_mode(dev, dev->parallel_mode); } +#ifdef CONFIG_PM_SLEEP /* Disable all hardware interrupts */ static void r592_clear_interrupts(struct r592_device *dev) { @@ -195,6 +191,7 @@ static void r592_clear_interrupts(struct r592_device *dev) r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_ACK_MASK); r592_clear_reg_mask(dev, R592_REG_MSC, IRQ_ALL_EN_MASK); } +#endif /* Tests if there is an CRC error */ static int r592_test_io_error(struct r592_device *dev) @@ -290,14 +287,13 @@ static int r592_transfer_fifo_dma(struct r592_device *dev) dbg_verbose("doing dma transfer"); dev->dma_error = 0; - INIT_COMPLETION(dev->dma_done); + reinit_completion(&dev->dma_done); /* TODO: hidden assumption about nenth beeing always 1 */ sg_count = dma_map_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ? - PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (sg_count != 1 || - (sg_dma_len(&dev->req->sg) < dev->req->sg.length)) { + if (sg_count != 1 || sg_dma_len(&dev->req->sg) < R592_LFIFO_SIZE) { message("problem in dma_map_sg"); return -EIO; } @@ -312,8 +308,7 @@ static int r592_transfer_fifo_dma(struct r592_device *dev) } dma_unmap_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ? - PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - + DMA_TO_DEVICE : DMA_FROM_DEVICE); return dev->dma_error; } @@ -361,13 +356,15 @@ static void r592_write_fifo_pio(struct r592_device *dev, /* Flushes the temporary FIFO used to make aligned DWORD writes */ static void r592_flush_fifo_write(struct r592_device *dev) { + int ret; u8 buffer[4] = { 0 }; - int len; if (kfifo_is_empty(&dev->pio_fifo)) return; - len = kfifo_out(&dev->pio_fifo, buffer, 4); + ret = kfifo_out(&dev->pio_fifo, buffer, 4); + /* intentionally ignore __must_check return code */ + (void)ret; r592_write_reg_raw_be(dev, R592_FIFO_PIO, *(u32 *)buffer); } @@ -615,9 +612,9 @@ static void r592_update_card_detect(struct r592_device *dev) } /* Timer routine that fires 1 second after last card detection event, */ -static void r592_detect_timer(long unsigned int data) +static void r592_detect_timer(struct timer_list *t) { - struct r592_device *dev = (struct r592_device *)data; + struct r592_device *dev = timer_container_of(dev, t, detect_timer); r592_update_card_detect(dev); memstick_detect_change(dev->host); } @@ -678,7 +675,7 @@ static irqreturn_t r592_irq(int irq, void *data) return ret; } -/* External inteface: set settings */ +/* External interface: set settings */ static int r592_set_param(struct memstick_host *host, enum memstick_param param, int value) { @@ -752,7 +749,7 @@ static int r592_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto error2; pci_set_master(pdev); - error = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + error = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); if (error) goto error3; @@ -761,16 +758,17 @@ static int r592_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto error3; dev->mmio = pci_ioremap_bar(pdev, 0); - if (!dev->mmio) + if (!dev->mmio) { + error = -ENOMEM; goto error4; + } dev->irq = pdev->irq; spin_lock_init(&dev->irq_lock); spin_lock_init(&dev->io_thread_lock); init_completion(&dev->dma_done); INIT_KFIFO(dev->pio_fifo); - setup_timer(&dev->detect_timer, - r592_detect_timer, (long unsigned int)dev); + timer_setup(&dev->detect_timer, r592_detect_timer, 0); /* Host initialization */ host->caps = MEMSTICK_CAP_PAR4; @@ -785,16 +783,18 @@ static int r592_probe(struct pci_dev *pdev, const struct pci_device_id *id) } /* This is just a precation, so don't fail */ - dev->dummy_dma_page = pci_alloc_consistent(pdev, PAGE_SIZE, - &dev->dummy_dma_page_physical_address); + dev->dummy_dma_page = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, + &dev->dummy_dma_page_physical_address, GFP_KERNEL); r592_stop_dma(dev , 0); - if (request_irq(dev->irq, &r592_irq, IRQF_SHARED, - DRV_NAME, dev)) + error = request_irq(dev->irq, &r592_irq, IRQF_SHARED, + DRV_NAME, dev); + if (error) goto error6; r592_update_card_detect(dev); - if (memstick_add_host(host)) + error = memstick_add_host(host); + if (error) goto error7; message("driver successfully loaded"); @@ -803,7 +803,7 @@ error7: free_irq(dev->irq, dev); error6: if (dev->dummy_dma_page) - pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page, + dma_free_coherent(&pdev->dev, PAGE_SIZE, dev->dummy_dma_page, dev->dummy_dma_page_physical_address); kthread_stop(dev->io_thread); @@ -827,7 +827,7 @@ static void r592_remove(struct pci_dev *pdev) /* Stop the processing thread. That ensures that we won't take any more requests */ kthread_stop(dev->io_thread); - + timer_delete_sync(&dev->detect_timer); r592_enable_device(dev, false); while (!error && dev->req) { @@ -836,33 +836,31 @@ static void r592_remove(struct pci_dev *pdev) } memstick_remove_host(dev->host); + if (dev->dummy_dma_page) + dma_free_coherent(&pdev->dev, PAGE_SIZE, dev->dummy_dma_page, + dev->dummy_dma_page_physical_address); + free_irq(dev->irq, dev); iounmap(dev->mmio); pci_release_regions(pdev); pci_disable_device(pdev); memstick_free_host(dev->host); - - if (dev->dummy_dma_page) - pci_free_consistent(pdev, PAGE_SIZE, dev->dummy_dma_page, - dev->dummy_dma_page_physical_address); } #ifdef CONFIG_PM_SLEEP static int r592_suspend(struct device *core_dev) { - struct pci_dev *pdev = to_pci_dev(core_dev); - struct r592_device *dev = pci_get_drvdata(pdev); + struct r592_device *dev = dev_get_drvdata(core_dev); r592_clear_interrupts(dev); memstick_suspend_host(dev->host); - del_timer_sync(&dev->detect_timer); + timer_delete_sync(&dev->detect_timer); return 0; } static int r592_resume(struct device *core_dev) { - struct pci_dev *pdev = to_pci_dev(core_dev); - struct r592_device *dev = pci_get_drvdata(pdev); + struct r592_device *dev = dev_get_drvdata(core_dev); r592_clear_interrupts(dev); r592_enable_device(dev, false); @@ -876,7 +874,7 @@ static SIMPLE_DEV_PM_OPS(r592_pm_ops, r592_suspend, r592_resume); MODULE_DEVICE_TABLE(pci, r592_pci_id_tbl); -static struct pci_driver r852_pci_driver = { +static struct pci_driver r592_pci_driver = { .name = DRV_NAME, .id_table = r592_pci_id_tbl, .probe = r592_probe, @@ -884,7 +882,7 @@ static struct pci_driver r852_pci_driver = { .driver.pm = &r592_pm_ops, }; -module_pci_driver(r852_pci_driver); +module_pci_driver(r592_pci_driver); module_param_named(enable_dma, r592_enable_dma, bool, S_IRUGO); MODULE_PARM_DESC(enable_dma, "Enable usage of the DMA (default)"); diff --git a/drivers/memstick/host/r592.h b/drivers/memstick/host/r592.h index c5726c1e8832..c161db70c8f5 100644 --- a/drivers/memstick/host/r592.h +++ b/drivers/memstick/host/r592.h @@ -1,10 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2010 - Maxim Levitsky * driver for Ricoh memstick readers - * - * 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. */ #ifndef R592_H diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c deleted file mode 100644 index 64a779c58a74..000000000000 --- a/drivers/memstick/host/rtsx_pci_ms.c +++ /dev/null @@ -1,648 +0,0 @@ -/* Realtek PCI-Express Memstick Card Interface driver - * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - * - * Author: - * Wei WANG <wei_wang@realsil.com.cn> - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China - */ - -#include <linux/module.h> -#include <linux/highmem.h> -#include <linux/delay.h> -#include <linux/platform_device.h> -#include <linux/memstick.h> -#include <linux/mfd/rtsx_pci.h> -#include <asm/unaligned.h> - -struct realtek_pci_ms { - struct platform_device *pdev; - struct rtsx_pcr *pcr; - struct memstick_host *msh; - struct memstick_request *req; - - struct mutex host_mutex; - struct work_struct handle_req; - - u8 ssc_depth; - unsigned int clock; - unsigned char ifmode; - bool eject; -}; - -static inline struct device *ms_dev(struct realtek_pci_ms *host) -{ - return &(host->pdev->dev); -} - -static inline void ms_clear_error(struct realtek_pci_ms *host) -{ - rtsx_pci_write_register(host->pcr, CARD_STOP, - MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR); -} - -#ifdef DEBUG - -static void ms_print_debug_regs(struct realtek_pci_ms *host) -{ - struct rtsx_pcr *pcr = host->pcr; - u16 i; - u8 *ptr; - - /* Print MS host internal registers */ - rtsx_pci_init_cmd(pcr); - for (i = 0xFD40; i <= 0xFD44; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); - for (i = 0xFD52; i <= 0xFD69; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); - rtsx_pci_send_cmd(pcr, 100); - - ptr = rtsx_pci_get_cmd_data(pcr); - for (i = 0xFD40; i <= 0xFD44; i++) - dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); - for (i = 0xFD52; i <= 0xFD69; i++) - dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); -} - -#else - -#define ms_print_debug_regs(host) - -#endif - -static int ms_power_on(struct realtek_pci_ms *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE, - CARD_SHARE_MASK, CARD_SHARE_48_MS); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, - MS_CLK_EN, MS_CLK_EN); - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_MS_CARD); - if (err < 0) - return err; - - err = rtsx_pci_card_power_on(pcr, RTSX_MS_CARD); - if (err < 0) - return err; - - /* Wait ms power stable */ - msleep(150); - - err = rtsx_pci_write_register(pcr, CARD_OE, - MS_OUTPUT_EN, MS_OUTPUT_EN); - if (err < 0) - return err; - - return 0; -} - -static int ms_power_off(struct realtek_pci_ms *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - err = rtsx_pci_card_power_off(pcr, RTSX_MS_CARD); - if (err < 0) - return err; - - return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD); -} - -static int ms_transfer_data(struct realtek_pci_ms *host, unsigned char data_dir, - u8 tpc, u8 cfg, struct scatterlist *sg) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - unsigned int length = sg->length; - u16 sec_cnt = (u16)(length / 512); - u8 val, trans_mode, dma_dir; - - dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n", - __func__, tpc, (data_dir == READ) ? "READ" : "WRITE", - length); - - if (data_dir == READ) { - dma_dir = DMA_DIR_FROM_CARD; - trans_mode = MS_TM_AUTO_READ; - } else { - dma_dir = DMA_DIR_TO_CARD; - trans_mode = MS_TM_AUTO_WRITE; - } - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_H, - 0xFF, (u8)(sec_cnt >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_L, - 0xFF, (u8)sec_cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, - DMA_DONE_INT, DMA_DONE_INT); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(length >> 24)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(length >> 16)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(length >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)length); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, - 0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, RING_BUFFER); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, - 0xFF, MS_TRANSFER_START | trans_mode); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - - rtsx_pci_send_cmd_no_wait(pcr); - - err = rtsx_pci_transfer_data(pcr, sg, 1, data_dir == READ, 10000); - if (err < 0) { - ms_clear_error(host); - return err; - } - - rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); - if (val & (MS_INT_CMDNK | MS_INT_ERR | MS_CRC16_ERR | MS_RDY_TIMEOUT)) - return -EIO; - - return 0; -} - -static int ms_write_bytes(struct realtek_pci_ms *host, u8 tpc, - u8 cfg, u8 cnt, u8 *data, u8 *int_reg) -{ - struct rtsx_pcr *pcr = host->pcr; - int err, i; - - dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc); - - if (!data) - return -EINVAL; - - rtsx_pci_init_cmd(pcr); - - for (i = 0; i < cnt; i++) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - PPBUF_BASE2 + i, 0xFF, data[i]); - if (cnt % 2) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - PPBUF_BASE2 + i, 0xFF, 0xFF); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, - 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - if (int_reg) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); - - err = rtsx_pci_send_cmd(pcr, 5000); - if (err < 0) { - u8 val; - - rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); - dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val); - - if (int_reg) - *int_reg = val & 0x0F; - - ms_print_debug_regs(host); - - ms_clear_error(host); - - if (!(tpc & 0x08)) { - if (val & MS_CRC16_ERR) - return -EIO; - } else { - if (!(val & 0x80)) { - if (val & (MS_INT_ERR | MS_INT_CMDNK)) - return -EIO; - } - } - - return -ETIMEDOUT; - } - - if (int_reg) { - u8 *ptr = rtsx_pci_get_cmd_data(pcr) + 1; - *int_reg = *ptr & 0x0F; - } - - return 0; -} - -static int ms_read_bytes(struct realtek_pci_ms *host, u8 tpc, - u8 cfg, u8 cnt, u8 *data, u8 *int_reg) -{ - struct rtsx_pcr *pcr = host->pcr; - int err, i; - u8 *ptr; - - dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc); - - if (!data) - return -EINVAL; - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, - 0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - for (i = 0; i < cnt - 1; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0); - if (cnt % 2) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0); - else - rtsx_pci_add_cmd(pcr, READ_REG_CMD, - PPBUF_BASE2 + cnt - 1, 0, 0); - if (int_reg) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); - - err = rtsx_pci_send_cmd(pcr, 5000); - if (err < 0) { - u8 val; - - rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); - dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val); - - if (int_reg) - *int_reg = val & 0x0F; - - ms_print_debug_regs(host); - - ms_clear_error(host); - - if (!(tpc & 0x08)) { - if (val & MS_CRC16_ERR) - return -EIO; - } else { - if (!(val & 0x80)) { - if (val & (MS_INT_ERR | MS_INT_CMDNK)) - return -EIO; - } - } - - return -ETIMEDOUT; - } - - ptr = rtsx_pci_get_cmd_data(pcr) + 1; - for (i = 0; i < cnt; i++) - data[i] = *ptr++; - - if (int_reg) - *int_reg = *ptr & 0x0F; - - return 0; -} - -static int rtsx_pci_ms_issue_cmd(struct realtek_pci_ms *host) -{ - struct memstick_request *req = host->req; - int err = 0; - u8 cfg = 0, int_reg; - - dev_dbg(ms_dev(host), "%s\n", __func__); - - if (req->need_card_int) { - if (host->ifmode != MEMSTICK_SERIAL) - cfg = WAIT_INT; - } - - if (req->long_data) { - err = ms_transfer_data(host, req->data_dir, - req->tpc, cfg, &(req->sg)); - } else { - if (req->data_dir == READ) { - err = ms_read_bytes(host, req->tpc, cfg, - req->data_len, req->data, &int_reg); - } else { - err = ms_write_bytes(host, req->tpc, cfg, - req->data_len, req->data, &int_reg); - } - } - if (err < 0) - return err; - - if (req->need_card_int && (host->ifmode == MEMSTICK_SERIAL)) { - err = ms_read_bytes(host, MS_TPC_GET_INT, - NO_WAIT_INT, 1, &int_reg, NULL); - if (err < 0) - return err; - } - - if (req->need_card_int) { - dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", int_reg); - - if (int_reg & MS_INT_CMDNK) - req->int_reg |= MEMSTICK_INT_CMDNAK; - if (int_reg & MS_INT_BREQ) - req->int_reg |= MEMSTICK_INT_BREQ; - if (int_reg & MS_INT_ERR) - req->int_reg |= MEMSTICK_INT_ERR; - if (int_reg & MS_INT_CED) - req->int_reg |= MEMSTICK_INT_CED; - } - - return 0; -} - -static void rtsx_pci_ms_handle_req(struct work_struct *work) -{ - struct realtek_pci_ms *host = container_of(work, - struct realtek_pci_ms, handle_req); - struct rtsx_pcr *pcr = host->pcr; - struct memstick_host *msh = host->msh; - int rc; - - mutex_lock(&pcr->pcr_mutex); - - rtsx_pci_start_run(pcr); - - rtsx_pci_switch_clock(host->pcr, host->clock, host->ssc_depth, - false, true, false); - rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, MS_MOD_SEL); - rtsx_pci_write_register(pcr, CARD_SHARE_MODE, - CARD_SHARE_MASK, CARD_SHARE_48_MS); - - if (!host->req) { - do { - rc = memstick_next_req(msh, &host->req); - dev_dbg(ms_dev(host), "next req %d\n", rc); - - if (!rc) - host->req->error = rtsx_pci_ms_issue_cmd(host); - } while (!rc); - } - - mutex_unlock(&pcr->pcr_mutex); -} - -static void rtsx_pci_ms_request(struct memstick_host *msh) -{ - struct realtek_pci_ms *host = memstick_priv(msh); - - dev_dbg(ms_dev(host), "--> %s\n", __func__); - - if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD)) - return; - - schedule_work(&host->handle_req); -} - -static int rtsx_pci_ms_set_param(struct memstick_host *msh, - enum memstick_param param, int value) -{ - struct realtek_pci_ms *host = memstick_priv(msh); - struct rtsx_pcr *pcr = host->pcr; - unsigned int clock = 0; - u8 ssc_depth = 0; - int err; - - dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n", - __func__, param, value); - - err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD); - if (err) - return err; - - switch (param) { - case MEMSTICK_POWER: - if (value == MEMSTICK_POWER_ON) - err = ms_power_on(host); - else if (value == MEMSTICK_POWER_OFF) - err = ms_power_off(host); - else - return -EINVAL; - break; - - case MEMSTICK_INTERFACE: - if (value == MEMSTICK_SERIAL) { - clock = 19000000; - ssc_depth = RTSX_SSC_DEPTH_500K; - - err = rtsx_pci_write_register(pcr, MS_CFG, - 0x18, MS_BUS_WIDTH_1); - if (err < 0) - return err; - } else if (value == MEMSTICK_PAR4) { - clock = 39000000; - ssc_depth = RTSX_SSC_DEPTH_1M; - - err = rtsx_pci_write_register(pcr, MS_CFG, - 0x58, MS_BUS_WIDTH_4 | PUSH_TIME_ODD); - if (err < 0) - return err; - } else { - return -EINVAL; - } - - err = rtsx_pci_switch_clock(pcr, clock, - ssc_depth, false, true, false); - if (err < 0) - return err; - - host->ssc_depth = ssc_depth; - host->clock = clock; - host->ifmode = value; - break; - } - - return 0; -} - -#ifdef CONFIG_PM - -static int rtsx_pci_ms_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct realtek_pci_ms *host = platform_get_drvdata(pdev); - struct memstick_host *msh = host->msh; - - dev_dbg(ms_dev(host), "--> %s\n", __func__); - - memstick_suspend_host(msh); - return 0; -} - -static int rtsx_pci_ms_resume(struct platform_device *pdev) -{ - struct realtek_pci_ms *host = platform_get_drvdata(pdev); - struct memstick_host *msh = host->msh; - - dev_dbg(ms_dev(host), "--> %s\n", __func__); - - memstick_resume_host(msh); - return 0; -} - -#else /* CONFIG_PM */ - -#define rtsx_pci_ms_suspend NULL -#define rtsx_pci_ms_resume NULL - -#endif /* CONFIG_PM */ - -static void rtsx_pci_ms_card_event(struct platform_device *pdev) -{ - struct realtek_pci_ms *host = platform_get_drvdata(pdev); - - memstick_detect_change(host->msh); -} - -static int rtsx_pci_ms_drv_probe(struct platform_device *pdev) -{ - struct memstick_host *msh; - struct realtek_pci_ms *host; - struct rtsx_pcr *pcr; - struct pcr_handle *handle = pdev->dev.platform_data; - int rc; - - if (!handle) - return -ENXIO; - - pcr = handle->pcr; - if (!pcr) - return -ENXIO; - - dev_dbg(&(pdev->dev), - ": Realtek PCI-E Memstick controller found\n"); - - msh = memstick_alloc_host(sizeof(*host), &pdev->dev); - if (!msh) - return -ENOMEM; - - host = memstick_priv(msh); - host->pcr = pcr; - host->msh = msh; - host->pdev = pdev; - platform_set_drvdata(pdev, host); - pcr->slots[RTSX_MS_CARD].p_dev = pdev; - pcr->slots[RTSX_MS_CARD].card_event = rtsx_pci_ms_card_event; - - mutex_init(&host->host_mutex); - - INIT_WORK(&host->handle_req, rtsx_pci_ms_handle_req); - msh->request = rtsx_pci_ms_request; - msh->set_param = rtsx_pci_ms_set_param; - msh->caps = MEMSTICK_CAP_PAR4; - - rc = memstick_add_host(msh); - if (rc) { - memstick_free_host(msh); - return rc; - } - - return 0; -} - -static int rtsx_pci_ms_drv_remove(struct platform_device *pdev) -{ - struct realtek_pci_ms *host = platform_get_drvdata(pdev); - struct rtsx_pcr *pcr; - struct memstick_host *msh; - int rc; - - if (!host) - return 0; - - pcr = host->pcr; - pcr->slots[RTSX_MS_CARD].p_dev = NULL; - pcr->slots[RTSX_MS_CARD].card_event = NULL; - msh = host->msh; - host->eject = true; - - mutex_lock(&host->host_mutex); - if (host->req) { - dev_dbg(&(pdev->dev), - "%s: Controller removed during transfer\n", - dev_name(&msh->dev)); - - rtsx_pci_complete_unfinished_transfer(pcr); - - host->req->error = -ENOMEDIUM; - do { - rc = memstick_next_req(msh, &host->req); - if (!rc) - host->req->error = -ENOMEDIUM; - } while (!rc); - } - mutex_unlock(&host->host_mutex); - - memstick_remove_host(msh); - memstick_free_host(msh); - - platform_set_drvdata(pdev, NULL); - - dev_dbg(&(pdev->dev), - ": Realtek PCI-E Memstick controller has been removed\n"); - - return 0; -} - -static struct platform_device_id rtsx_pci_ms_ids[] = { - { - .name = DRV_NAME_RTSX_PCI_MS, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, rtsx_pci_ms_ids); - -static struct platform_driver rtsx_pci_ms_driver = { - .probe = rtsx_pci_ms_drv_probe, - .remove = rtsx_pci_ms_drv_remove, - .id_table = rtsx_pci_ms_ids, - .suspend = rtsx_pci_ms_suspend, - .resume = rtsx_pci_ms_resume, - .driver = { - .owner = THIS_MODULE, - .name = DRV_NAME_RTSX_PCI_MS, - }, -}; -module_platform_driver(rtsx_pci_ms_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>"); -MODULE_DESCRIPTION("Realtek PCI-E Memstick Card Host Driver"); diff --git a/drivers/memstick/host/rtsx_usb_ms.c b/drivers/memstick/host/rtsx_usb_ms.c new file mode 100644 index 000000000000..beadc389f15f --- /dev/null +++ b/drivers/memstick/host/rtsx_usb_ms.c @@ -0,0 +1,872 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Realtek USB Memstick Card Interface driver + * + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. + * + * Author: + * Roger Tseng <rogerable@realtek.com> + */ + +#include <linux/module.h> +#include <linux/highmem.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/memstick.h> +#include <linux/kthread.h> +#include <linux/rtsx_usb.h> +#include <linux/pm_runtime.h> +#include <linux/mutex.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <linux/unaligned.h> + +struct rtsx_usb_ms { + struct platform_device *pdev; + struct rtsx_ucr *ucr; + struct memstick_host *msh; + struct memstick_request *req; + + struct mutex host_mutex; + struct work_struct handle_req; + struct delayed_work poll_card; + + u8 ssc_depth; + unsigned int clock; + int power_mode; + unsigned char ifmode; + bool eject; + bool system_suspending; +}; + +static inline struct device *ms_dev(struct rtsx_usb_ms *host) +{ + return &(host->pdev->dev); +} + +static inline void ms_clear_error(struct rtsx_usb_ms *host) +{ + struct rtsx_ucr *ucr = host->ucr; + rtsx_usb_ep0_write_register(ucr, CARD_STOP, + MS_STOP | MS_CLR_ERR, + MS_STOP | MS_CLR_ERR); + + rtsx_usb_clear_dma_err(ucr); + rtsx_usb_clear_fsm_err(ucr); +} + +#ifdef DEBUG + +static void ms_print_debug_regs(struct rtsx_usb_ms *host) +{ + struct rtsx_ucr *ucr = host->ucr; + u16 i; + u8 *ptr; + + /* Print MS host internal registers */ + rtsx_usb_init_cmd(ucr); + + /* MS_CFG to MS_INT_REG */ + for (i = 0xFD40; i <= 0xFD44; i++) + rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0); + + /* CARD_SHARE_MODE to CARD_GPIO */ + for (i = 0xFD51; i <= 0xFD56; i++) + rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0); + + /* CARD_PULL_CTLx */ + for (i = 0xFD60; i <= 0xFD65; i++) + rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 0); + + /* CARD_DATA_SOURCE, CARD_SELECT, CARD_CLK_EN, CARD_PWR_CTL */ + rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_DATA_SOURCE, 0, 0); + rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_SELECT, 0, 0); + rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_CLK_EN, 0, 0); + rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_PWR_CTL, 0, 0); + + rtsx_usb_send_cmd(ucr, MODE_CR, 100); + rtsx_usb_get_rsp(ucr, 21, 100); + + ptr = ucr->rsp_buf; + for (i = 0xFD40; i <= 0xFD44; i++) + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); + for (i = 0xFD51; i <= 0xFD56; i++) + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); + for (i = 0xFD60; i <= 0xFD65; i++) + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); + + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_DATA_SOURCE, *(ptr++)); + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_SELECT, *(ptr++)); + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_CLK_EN, *(ptr++)); + dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", CARD_PWR_CTL, *(ptr++)); +} + +#else + +static void ms_print_debug_regs(struct rtsx_usb_ms *host) +{ +} + +#endif + +static int ms_pull_ctl_disable_lqfp48(struct rtsx_ucr *ucr) +{ + rtsx_usb_init_cmd(ucr); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5); + + return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int ms_pull_ctl_disable_qfn24(struct rtsx_ucr *ucr) +{ + rtsx_usb_init_cmd(ucr); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x56); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59); + + return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int ms_pull_ctl_enable_lqfp48(struct rtsx_ucr *ucr) +{ + rtsx_usb_init_cmd(ucr); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5); + + return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int ms_pull_ctl_enable_qfn24(struct rtsx_ucr *ucr) +{ + rtsx_usb_init_cmd(ucr); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, 0xFF, 0x65); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59); + + return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int ms_power_on(struct rtsx_usb_ms *host) +{ + struct rtsx_ucr *ucr = host->ucr; + int err; + + dev_dbg(ms_dev(host), "%s\n", __func__); + + rtsx_usb_init_cmd(ucr); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE, + CARD_SHARE_MASK, CARD_SHARE_MS); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, + MS_CLK_EN, MS_CLK_EN); + err = rtsx_usb_send_cmd(ucr, MODE_C, 100); + if (err < 0) + return err; + + if (CHECK_PKG(ucr, LQFP48)) + err = ms_pull_ctl_enable_lqfp48(ucr); + else + err = ms_pull_ctl_enable_qfn24(ucr); + if (err < 0) + return err; + + err = rtsx_usb_write_register(ucr, CARD_PWR_CTL, + POWER_MASK, PARTIAL_POWER_ON); + if (err) + return err; + + usleep_range(800, 1000); + + rtsx_usb_init_cmd(ucr); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, + POWER_MASK, POWER_ON); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, + MS_OUTPUT_EN, MS_OUTPUT_EN); + + return rtsx_usb_send_cmd(ucr, MODE_C, 100); +} + +static int ms_power_off(struct rtsx_usb_ms *host) +{ + struct rtsx_ucr *ucr = host->ucr; + int err; + + dev_dbg(ms_dev(host), "%s\n", __func__); + + rtsx_usb_init_cmd(ucr); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, + POWER_MASK, POWER_OFF); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, + POWER_MASK | LDO3318_PWR_MASK, POWER_OFF | LDO_SUSPEND); + err = rtsx_usb_send_cmd(ucr, MODE_C, 100); + if (err < 0) + return err; + + if (CHECK_PKG(ucr, LQFP48)) + return ms_pull_ctl_disable_lqfp48(ucr); + + return ms_pull_ctl_disable_qfn24(ucr); +} + +static int ms_transfer_data(struct rtsx_usb_ms *host, unsigned char data_dir, + u8 tpc, u8 cfg, struct scatterlist *sg) +{ + struct rtsx_ucr *ucr = host->ucr; + int err; + unsigned int length = sg->length; + u16 sec_cnt = (u16)(length / 512); + u8 trans_mode, dma_dir, flag; + unsigned int pipe; + struct memstick_dev *card = host->msh->card; + + dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n", + __func__, tpc, (data_dir == READ) ? "READ" : "WRITE", + length); + + if (data_dir == READ) { + flag = MODE_CDIR; + dma_dir = DMA_DIR_FROM_CARD; + if (card->id.type != MEMSTICK_TYPE_PRO) + trans_mode = MS_TM_NORMAL_READ; + else + trans_mode = MS_TM_AUTO_READ; + pipe = usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN); + } else { + flag = MODE_CDOR; + dma_dir = DMA_DIR_TO_CARD; + if (card->id.type != MEMSTICK_TYPE_PRO) + trans_mode = MS_TM_NORMAL_WRITE; + else + trans_mode = MS_TM_AUTO_WRITE; + pipe = usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT); + } + + rtsx_usb_init_cmd(ucr); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); + if (card->id.type == MEMSTICK_TYPE_PRO) { + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_H, + 0xFF, (u8)(sec_cnt >> 8)); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_L, + 0xFF, (u8)sec_cnt); + } + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC3, + 0xFF, (u8)(length >> 24)); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC2, + 0xFF, (u8)(length >> 16)); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC1, + 0xFF, (u8)(length >> 8)); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC0, 0xFF, + (u8)length); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL, + 0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, RING_BUFFER); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, + 0xFF, MS_TRANSFER_START | trans_mode); + rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER, + MS_TRANSFER_END, MS_TRANSFER_END); + + err = rtsx_usb_send_cmd(ucr, flag | STAGE_MS_STATUS, 100); + if (err) + return err; + + err = rtsx_usb_transfer_data(ucr, pipe, sg, length, + 1, NULL, 10000); + if (err) + goto err_out; + + err = rtsx_usb_get_rsp(ucr, 3, 15000); + if (err) + goto err_out; + + if (ucr->rsp_buf[0] & MS_TRANSFER_ERR || + ucr->rsp_buf[1] & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) { + err = -EIO; + goto err_out; + } + return 0; +err_out: + ms_clear_error(host); + return err; +} + +static int ms_write_bytes(struct rtsx_usb_ms *host, u8 tpc, + u8 cfg, u8 cnt, u8 *data, u8 *int_reg) +{ + struct rtsx_ucr *ucr = host->ucr; + int err, i; + + dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc); + + rtsx_usb_init_cmd(ucr); + + for (i = 0; i < cnt; i++) + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, + PPBUF_BASE2 + i, 0xFF, data[i]); + + if (cnt % 2) + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, + PPBUF_BASE2 + i, 0xFF, 0xFF); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, PINGPONG_BUFFER); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, + 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES); + rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER, + MS_TRANSFER_END, MS_TRANSFER_END); + rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); + + err = rtsx_usb_send_cmd(ucr, MODE_CR, 100); + if (err) + return err; + + err = rtsx_usb_get_rsp(ucr, 2, 5000); + if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) { + u8 val; + + rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, &val); + dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val); + + if (int_reg) + *int_reg = val & 0x0F; + + ms_print_debug_regs(host); + + ms_clear_error(host); + + if (!(tpc & 0x08)) { + if (val & MS_CRC16_ERR) + return -EIO; + } else { + if (!(val & 0x80)) { + if (val & (MS_INT_ERR | MS_INT_CMDNK)) + return -EIO; + } + } + + return -ETIMEDOUT; + } + + if (int_reg) + *int_reg = ucr->rsp_buf[1] & 0x0F; + + return 0; +} + +static int ms_read_bytes(struct rtsx_usb_ms *host, u8 tpc, + u8 cfg, u8 cnt, u8 *data, u8 *int_reg) +{ + struct rtsx_ucr *ucr = host->ucr; + int err, i; + u8 *ptr; + + dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc); + + rtsx_usb_init_cmd(ucr); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, PINGPONG_BUFFER); + + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, + 0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES); + rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER, + MS_TRANSFER_END, MS_TRANSFER_END); + for (i = 0; i < cnt - 1; i++) + rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0); + if (cnt % 2) + rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0); + else + rtsx_usb_add_cmd(ucr, READ_REG_CMD, + PPBUF_BASE2 + cnt - 1, 0, 0); + + rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); + + err = rtsx_usb_send_cmd(ucr, MODE_CR, 100); + if (err) + return err; + + err = rtsx_usb_get_rsp(ucr, cnt + 2, 5000); + if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) { + u8 val; + + rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, &val); + dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val); + + if (int_reg && (host->ifmode != MEMSTICK_SERIAL)) + *int_reg = val & 0x0F; + + ms_print_debug_regs(host); + + ms_clear_error(host); + + if (!(tpc & 0x08)) { + if (val & MS_CRC16_ERR) + return -EIO; + } else { + if (!(val & 0x80)) { + if (val & (MS_INT_ERR | MS_INT_CMDNK)) + return -EIO; + } + } + + return -ETIMEDOUT; + } + + ptr = ucr->rsp_buf + 1; + for (i = 0; i < cnt; i++) + data[i] = *ptr++; + + + if (int_reg && (host->ifmode != MEMSTICK_SERIAL)) + *int_reg = *ptr & 0x0F; + + return 0; +} + +static int rtsx_usb_ms_issue_cmd(struct rtsx_usb_ms *host) +{ + struct memstick_request *req = host->req; + int err = 0; + u8 cfg = 0, int_reg; + + dev_dbg(ms_dev(host), "%s\n", __func__); + + if (req->need_card_int) { + if (host->ifmode != MEMSTICK_SERIAL) + cfg = WAIT_INT; + } + + if (req->long_data) { + err = ms_transfer_data(host, req->data_dir, + req->tpc, cfg, &(req->sg)); + } else { + if (req->data_dir == READ) + err = ms_read_bytes(host, req->tpc, cfg, + req->data_len, req->data, &int_reg); + else + err = ms_write_bytes(host, req->tpc, cfg, + req->data_len, req->data, &int_reg); + } + if (err < 0) + return err; + + if (req->need_card_int) { + if (host->ifmode == MEMSTICK_SERIAL) { + err = ms_read_bytes(host, MS_TPC_GET_INT, + NO_WAIT_INT, 1, &req->int_reg, NULL); + if (err < 0) + return err; + } else { + + if (int_reg & MS_INT_CMDNK) + req->int_reg |= MEMSTICK_INT_CMDNAK; + if (int_reg & MS_INT_BREQ) + req->int_reg |= MEMSTICK_INT_BREQ; + if (int_reg & MS_INT_ERR) + req->int_reg |= MEMSTICK_INT_ERR; + if (int_reg & MS_INT_CED) + req->int_reg |= MEMSTICK_INT_CED; + } + dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", req->int_reg); + } + + return 0; +} + +static void rtsx_usb_ms_handle_req(struct work_struct *work) +{ + struct rtsx_usb_ms *host = container_of(work, + struct rtsx_usb_ms, handle_req); + struct rtsx_ucr *ucr = host->ucr; + struct memstick_host *msh = host->msh; + int rc; + + if (!host->req) { + pm_runtime_get_sync(ms_dev(host)); + do { + rc = memstick_next_req(msh, &host->req); + dev_dbg(ms_dev(host), "next req %d\n", rc); + + if (!rc) { + mutex_lock(&ucr->dev_mutex); + + if (rtsx_usb_card_exclusive_check(ucr, + RTSX_USB_MS_CARD)) + host->req->error = -EIO; + else + host->req->error = + rtsx_usb_ms_issue_cmd(host); + + mutex_unlock(&ucr->dev_mutex); + + dev_dbg(ms_dev(host), "req result %d\n", + host->req->error); + } + } while (!rc); + pm_runtime_put_sync(ms_dev(host)); + } + +} + +static void rtsx_usb_ms_request(struct memstick_host *msh) +{ + struct rtsx_usb_ms *host = memstick_priv(msh); + + dev_dbg(ms_dev(host), "--> %s\n", __func__); + + if (!host->eject) + schedule_work(&host->handle_req); +} + +static int rtsx_usb_ms_set_param(struct memstick_host *msh, + enum memstick_param param, int value) +{ + struct rtsx_usb_ms *host = memstick_priv(msh); + struct rtsx_ucr *ucr = host->ucr; + unsigned int clock = 0; + u8 ssc_depth = 0; + int err; + + dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n", + __func__, param, value); + + pm_runtime_get_sync(ms_dev(host)); + mutex_lock(&ucr->dev_mutex); + + err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD); + if (err) + goto out; + + switch (param) { + case MEMSTICK_POWER: + if (value == host->power_mode) + break; + + if (value == MEMSTICK_POWER_ON) { + pm_runtime_get_noresume(ms_dev(host)); + err = ms_power_on(host); + if (err) + pm_runtime_put_noidle(ms_dev(host)); + } else if (value == MEMSTICK_POWER_OFF) { + err = ms_power_off(host); + if (!err) + pm_runtime_put_noidle(ms_dev(host)); + } else + err = -EINVAL; + if (!err) + host->power_mode = value; + break; + + case MEMSTICK_INTERFACE: + if (value == MEMSTICK_SERIAL) { + clock = 19000000; + ssc_depth = SSC_DEPTH_512K; + err = rtsx_usb_write_register(ucr, MS_CFG, 0x5A, + MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT); + if (err < 0) + break; + } else if (value == MEMSTICK_PAR4) { + clock = 39000000; + ssc_depth = SSC_DEPTH_1M; + + err = rtsx_usb_write_register(ucr, MS_CFG, 0x5A, + MS_BUS_WIDTH_4 | PUSH_TIME_ODD | + MS_NO_CHECK_INT); + if (err < 0) + break; + } else { + err = -EINVAL; + break; + } + + err = rtsx_usb_switch_clock(ucr, clock, + ssc_depth, false, true, false); + if (err < 0) { + dev_dbg(ms_dev(host), "switch clock failed\n"); + break; + } + + host->ssc_depth = ssc_depth; + host->clock = clock; + host->ifmode = value; + break; + default: + err = -EINVAL; + break; + } +out: + mutex_unlock(&ucr->dev_mutex); + pm_runtime_put_sync(ms_dev(host)); + + /* power-on delay */ + if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) { + usleep_range(10000, 12000); + + if (!host->eject) + schedule_delayed_work(&host->poll_card, 100); + } + + dev_dbg(ms_dev(host), "%s: return = %d\n", __func__, err); + return err; +} + +#ifdef CONFIG_PM_SLEEP +static int rtsx_usb_ms_suspend(struct device *dev) +{ + struct rtsx_usb_ms *host = dev_get_drvdata(dev); + struct memstick_host *msh = host->msh; + + /* Since we use rtsx_usb's resume callback to runtime resume its + * children to implement remote wakeup signaling, this causes + * rtsx_usb_ms' runtime resume callback runs after its suspend + * callback: + * rtsx_usb_ms_suspend() + * rtsx_usb_resume() + * -> rtsx_usb_ms_runtime_resume() + * -> memstick_detect_change() + * + * rtsx_usb_suspend() + * + * To avoid this, skip runtime resume/suspend if system suspend is + * underway. + */ + + host->system_suspending = true; + memstick_suspend_host(msh); + + return 0; +} + +static int rtsx_usb_ms_resume(struct device *dev) +{ + struct rtsx_usb_ms *host = dev_get_drvdata(dev); + struct memstick_host *msh = host->msh; + + memstick_resume_host(msh); + host->system_suspending = false; + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int rtsx_usb_ms_runtime_suspend(struct device *dev) +{ + struct rtsx_usb_ms *host = dev_get_drvdata(dev); + + if (host->system_suspending) + return 0; + + if (host->msh->card || host->power_mode != MEMSTICK_POWER_OFF) + return -EAGAIN; + + return 0; +} + +static int rtsx_usb_ms_runtime_resume(struct device *dev) +{ + struct rtsx_usb_ms *host = dev_get_drvdata(dev); + + + if (host->system_suspending) + return 0; + + memstick_detect_change(host->msh); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops rtsx_usb_ms_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rtsx_usb_ms_suspend, rtsx_usb_ms_resume) + SET_RUNTIME_PM_OPS(rtsx_usb_ms_runtime_suspend, rtsx_usb_ms_runtime_resume, NULL) +}; + + +static void rtsx_usb_ms_poll_card(struct work_struct *work) +{ + struct rtsx_usb_ms *host = container_of(work, struct rtsx_usb_ms, + poll_card.work); + struct rtsx_ucr *ucr = host->ucr; + int err; + u8 val; + + if (host->eject || host->power_mode != MEMSTICK_POWER_ON) + return; + + pm_runtime_get_sync(ms_dev(host)); + mutex_lock(&ucr->dev_mutex); + + /* Check pending MS card changes */ + err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val); + if (err) { + mutex_unlock(&ucr->dev_mutex); + goto poll_again; + } + + /* Clear the pending */ + rtsx_usb_write_register(ucr, CARD_INT_PEND, + XD_INT | MS_INT | SD_INT, + XD_INT | MS_INT | SD_INT); + + mutex_unlock(&ucr->dev_mutex); + + if (val & MS_INT) { + dev_dbg(ms_dev(host), "MS slot change detected\n"); + memstick_detect_change(host->msh); + } + +poll_again: + pm_runtime_put_sync(ms_dev(host)); + + if (!host->eject && host->power_mode == MEMSTICK_POWER_ON) + schedule_delayed_work(&host->poll_card, 100); +} + +static int rtsx_usb_ms_drv_probe(struct platform_device *pdev) +{ + struct memstick_host *msh; + struct rtsx_usb_ms *host; + struct rtsx_ucr *ucr; + int err; + + ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent)); + if (!ucr) + return -ENXIO; + + dev_dbg(&(pdev->dev), + "Realtek USB Memstick controller found\n"); + + msh = memstick_alloc_host(sizeof(*host), &pdev->dev); + if (!msh) + return -ENOMEM; + + host = memstick_priv(msh); + host->ucr = ucr; + host->msh = msh; + host->pdev = pdev; + host->power_mode = MEMSTICK_POWER_OFF; + platform_set_drvdata(pdev, host); + + mutex_init(&host->host_mutex); + INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req); + + INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card); + + msh->request = rtsx_usb_ms_request; + msh->set_param = rtsx_usb_ms_set_param; + msh->caps = MEMSTICK_CAP_PAR4; + + pm_runtime_get_noresume(ms_dev(host)); + pm_runtime_set_active(ms_dev(host)); + pm_runtime_enable(ms_dev(host)); + + err = memstick_add_host(msh); + if (err) + goto err_out; + + pm_runtime_put(ms_dev(host)); + + return 0; +err_out: + pm_runtime_disable(ms_dev(host)); + pm_runtime_put_noidle(ms_dev(host)); + memstick_free_host(msh); + return err; +} + +static void rtsx_usb_ms_drv_remove(struct platform_device *pdev) +{ + struct rtsx_usb_ms *host = platform_get_drvdata(pdev); + struct memstick_host *msh = host->msh; + int err; + + host->eject = true; + msh->removing = true; + cancel_work_sync(&host->handle_req); + cancel_delayed_work_sync(&host->poll_card); + + mutex_lock(&host->host_mutex); + if (host->req) { + dev_dbg(ms_dev(host), + "%s: Controller removed during transfer\n", + dev_name(&msh->dev)); + host->req->error = -ENOMEDIUM; + do { + err = memstick_next_req(msh, &host->req); + if (!err) + host->req->error = -ENOMEDIUM; + } while (!err); + } + mutex_unlock(&host->host_mutex); + + /* Balance possible unbalanced usage count + * e.g. unconditional module removal + */ + if (pm_runtime_active(ms_dev(host))) + pm_runtime_put(ms_dev(host)); + + pm_runtime_disable(ms_dev(host)); + memstick_remove_host(msh); + dev_dbg(ms_dev(host), + ": Realtek USB Memstick controller has been removed\n"); + memstick_free_host(msh); + platform_set_drvdata(pdev, NULL); +} + +static struct platform_device_id rtsx_usb_ms_ids[] = { + { + .name = "rtsx_usb_ms", + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, rtsx_usb_ms_ids); + +static struct platform_driver rtsx_usb_ms_driver = { + .probe = rtsx_usb_ms_drv_probe, + .remove = rtsx_usb_ms_drv_remove, + .id_table = rtsx_usb_ms_ids, + .driver = { + .name = "rtsx_usb_ms", + .pm = &rtsx_usb_ms_pm_ops, + }, +}; +module_platform_driver(rtsx_usb_ms_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>"); +MODULE_DESCRIPTION("Realtek USB Memstick Card Host Driver"); diff --git a/drivers/memstick/host/tifm_ms.c b/drivers/memstick/host/tifm_ms.c index 7bafa72f8f57..0b6a90661eee 100644 --- a/drivers/memstick/host/tifm_ms.c +++ b/drivers/memstick/host/tifm_ms.c @@ -1,15 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI FlashMedia driver * * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> * - * 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. - * * Special thanks to Carlos Corbacho for providing various MemoryStick cards * that made this driver possible. - * */ #include <linux/tifm.h> @@ -166,9 +162,11 @@ static unsigned int tifm_ms_write_data(struct tifm_ms *host, case 3: host->io_word |= buf[off + 2] << 16; host->io_pos++; + fallthrough; case 2: host->io_word |= buf[off + 1] << 8; host->io_pos++; + fallthrough; case 1: host->io_word |= buf[off]; host->io_pos++; @@ -200,11 +198,10 @@ static unsigned int tifm_ms_transfer_data(struct tifm_ms *host) host->block_pos); while (length) { - unsigned int uninitialized_var(p_off); + unsigned int p_off; if (host->req->long_data) { - pg = nth_page(sg_page(&host->req->sg), - off >> PAGE_SHIFT); + pg = sg_page(&host->req->sg) + (off >> PAGE_SHIFT); p_off = offset_in_page(off); p_cnt = PAGE_SIZE - p_off; p_cnt = min(p_cnt, length); @@ -254,7 +251,6 @@ static unsigned int tifm_ms_transfer_data(struct tifm_ms *host) static int tifm_ms_issue_cmd(struct tifm_ms *host) { struct tifm_dev *sock = host->dev; - unsigned char *data; unsigned int data_len, cmd, sys_param; host->cmd_flags = 0; @@ -263,8 +259,6 @@ static int tifm_ms_issue_cmd(struct tifm_ms *host) host->io_word = 0; host->cmd_flags = 0; - data = host->req->data; - host->use_dma = !no_dma; if (host->req->long_data) { @@ -284,8 +278,8 @@ static int tifm_ms_issue_cmd(struct tifm_ms *host) if (host->use_dma) { if (1 != tifm_map_sg(sock, &host->req->sg, 1, host->req->data_dir == READ - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE)) { + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE)) { host->req->error = -ENOMEM; return host->req->error; } @@ -342,7 +336,7 @@ static void tifm_ms_complete_cmd(struct tifm_ms *host) struct memstick_host *msh = tifm_get_drvdata(sock); int rc; - del_timer(&host->timer); + timer_delete(&host->timer); host->req->int_reg = readl(sock->addr + SOCK_MS_STATUS) & 0xff; host->req->int_reg = (host->req->int_reg & 1) @@ -355,8 +349,8 @@ static void tifm_ms_complete_cmd(struct tifm_ms *host) if (host->use_dma) { tifm_unmap_sg(sock, &host->req->sg, 1, host->req->data_dir == READ - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE); } writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), @@ -533,14 +527,14 @@ static int tifm_ms_set_param(struct memstick_host *msh, } else return -EINVAL; break; - }; + } return 0; } -static void tifm_ms_abort(unsigned long data) +static void tifm_ms_abort(struct timer_list *t) { - struct tifm_ms *host = (struct tifm_ms *)data; + struct tifm_ms *host = timer_container_of(host, t, timer); dev_dbg(&host->dev->dev, "status %x\n", readl(host->dev->addr + SOCK_MS_STATUS)); @@ -575,7 +569,7 @@ static int tifm_ms_probe(struct tifm_dev *sock) host->dev = sock; host->timeout_jiffies = msecs_to_jiffies(1000); - setup_timer(&host->timer, tifm_ms_abort, (unsigned long)host); + timer_setup(&host->timer, tifm_ms_abort, 0); tasklet_init(&host->notify, tifm_ms_req_tasklet, (unsigned long)msh); msh->request = tifm_ms_submit_req; @@ -605,15 +599,15 @@ static void tifm_ms_remove(struct tifm_dev *sock) spin_lock_irqsave(&sock->lock, flags); host->eject = 1; if (host->req) { - del_timer(&host->timer); + timer_delete(&host->timer); writel(TIFM_FIFO_INT_SETALL, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); if (host->use_dma) tifm_unmap_sg(sock, &host->req->sg, 1, host->req->data_dir == READ - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); host->req->error = -ETIME; do { |
