18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Sony MemoryStick support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Special thanks to Carlos Corbacho for providing various MemoryStick cards
88c2ecf20Sopenharmony_ci * that made this driver possible.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/memstick.h>
128c2ecf20Sopenharmony_ci#include <linux/idr.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/module.h>
178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define DRIVER_NAME "memstick"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic unsigned int cmd_retries = 3;
228c2ecf20Sopenharmony_cimodule_param(cmd_retries, uint, 0644);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic struct workqueue_struct *workqueue;
258c2ecf20Sopenharmony_cistatic DEFINE_IDR(memstick_host_idr);
268c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(memstick_host_lock);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic int memstick_dev_match(struct memstick_dev *card,
298c2ecf20Sopenharmony_ci			      struct memstick_device_id *id)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	if (id->match_flags & MEMSTICK_MATCH_ALL) {
328c2ecf20Sopenharmony_ci		if ((id->type == card->id.type)
338c2ecf20Sopenharmony_ci		    && (id->category == card->id.category)
348c2ecf20Sopenharmony_ci		    && (id->class == card->id.class))
358c2ecf20Sopenharmony_ci			return 1;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	return 0;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic int memstick_bus_match(struct device *dev, struct device_driver *drv)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct memstick_dev *card = container_of(dev, struct memstick_dev,
448c2ecf20Sopenharmony_ci						 dev);
458c2ecf20Sopenharmony_ci	struct memstick_driver *ms_drv = container_of(drv,
468c2ecf20Sopenharmony_ci						      struct memstick_driver,
478c2ecf20Sopenharmony_ci						      driver);
488c2ecf20Sopenharmony_ci	struct memstick_device_id *ids = ms_drv->id_table;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (ids) {
518c2ecf20Sopenharmony_ci		while (ids->match_flags) {
528c2ecf20Sopenharmony_ci			if (memstick_dev_match(card, ids))
538c2ecf20Sopenharmony_ci				return 1;
548c2ecf20Sopenharmony_ci			++ids;
558c2ecf20Sopenharmony_ci		}
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int memstick_uevent(struct device *dev, struct kobj_uevent_env *env)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct memstick_dev *card = container_of(dev, struct memstick_dev,
638c2ecf20Sopenharmony_ci						  dev);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "MEMSTICK_TYPE=%02X", card->id.type))
668c2ecf20Sopenharmony_ci		return -ENOMEM;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "MEMSTICK_CATEGORY=%02X", card->id.category))
698c2ecf20Sopenharmony_ci		return -ENOMEM;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	if (add_uevent_var(env, "MEMSTICK_CLASS=%02X", card->id.class))
728c2ecf20Sopenharmony_ci		return -ENOMEM;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int memstick_device_probe(struct device *dev)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct memstick_dev *card = container_of(dev, struct memstick_dev,
808c2ecf20Sopenharmony_ci						 dev);
818c2ecf20Sopenharmony_ci	struct memstick_driver *drv = container_of(dev->driver,
828c2ecf20Sopenharmony_ci						   struct memstick_driver,
838c2ecf20Sopenharmony_ci						   driver);
848c2ecf20Sopenharmony_ci	int rc = -ENODEV;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (dev->driver && drv->probe) {
878c2ecf20Sopenharmony_ci		rc = drv->probe(card);
888c2ecf20Sopenharmony_ci		if (!rc)
898c2ecf20Sopenharmony_ci			get_device(dev);
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci	return rc;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int memstick_device_remove(struct device *dev)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	struct memstick_dev *card = container_of(dev, struct memstick_dev,
978c2ecf20Sopenharmony_ci						  dev);
988c2ecf20Sopenharmony_ci	struct memstick_driver *drv = container_of(dev->driver,
998c2ecf20Sopenharmony_ci						   struct memstick_driver,
1008c2ecf20Sopenharmony_ci						   driver);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (dev->driver && drv->remove) {
1038c2ecf20Sopenharmony_ci		drv->remove(card);
1048c2ecf20Sopenharmony_ci		card->dev.driver = NULL;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	put_device(dev);
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int memstick_device_suspend(struct device *dev, pm_message_t state)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct memstick_dev *card = container_of(dev, struct memstick_dev,
1168c2ecf20Sopenharmony_ci						  dev);
1178c2ecf20Sopenharmony_ci	struct memstick_driver *drv = container_of(dev->driver,
1188c2ecf20Sopenharmony_ci						   struct memstick_driver,
1198c2ecf20Sopenharmony_ci						   driver);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (dev->driver && drv->suspend)
1228c2ecf20Sopenharmony_ci		return drv->suspend(card, state);
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int memstick_device_resume(struct device *dev)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct memstick_dev *card = container_of(dev, struct memstick_dev,
1298c2ecf20Sopenharmony_ci						  dev);
1308c2ecf20Sopenharmony_ci	struct memstick_driver *drv = container_of(dev->driver,
1318c2ecf20Sopenharmony_ci						   struct memstick_driver,
1328c2ecf20Sopenharmony_ci						   driver);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (dev->driver && drv->resume)
1358c2ecf20Sopenharmony_ci		return drv->resume(card);
1368c2ecf20Sopenharmony_ci	return 0;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#else
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci#define memstick_device_suspend NULL
1428c2ecf20Sopenharmony_ci#define memstick_device_resume NULL
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci#define MEMSTICK_ATTR(name, format)                                           \
1478c2ecf20Sopenharmony_cistatic ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
1488c2ecf20Sopenharmony_ci			    char *buf)                                        \
1498c2ecf20Sopenharmony_ci{                                                                             \
1508c2ecf20Sopenharmony_ci	struct memstick_dev *card = container_of(dev, struct memstick_dev,    \
1518c2ecf20Sopenharmony_ci						 dev);                        \
1528c2ecf20Sopenharmony_ci	return sprintf(buf, format, card->id.name);                           \
1538c2ecf20Sopenharmony_ci}                                                                             \
1548c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(name);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ciMEMSTICK_ATTR(type, "%02X");
1578c2ecf20Sopenharmony_ciMEMSTICK_ATTR(category, "%02X");
1588c2ecf20Sopenharmony_ciMEMSTICK_ATTR(class, "%02X");
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct attribute *memstick_dev_attrs[] = {
1618c2ecf20Sopenharmony_ci	&dev_attr_type.attr,
1628c2ecf20Sopenharmony_ci	&dev_attr_category.attr,
1638c2ecf20Sopenharmony_ci	&dev_attr_class.attr,
1648c2ecf20Sopenharmony_ci	NULL,
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(memstick_dev);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic struct bus_type memstick_bus_type = {
1698c2ecf20Sopenharmony_ci	.name           = "memstick",
1708c2ecf20Sopenharmony_ci	.dev_groups	= memstick_dev_groups,
1718c2ecf20Sopenharmony_ci	.match          = memstick_bus_match,
1728c2ecf20Sopenharmony_ci	.uevent         = memstick_uevent,
1738c2ecf20Sopenharmony_ci	.probe          = memstick_device_probe,
1748c2ecf20Sopenharmony_ci	.remove         = memstick_device_remove,
1758c2ecf20Sopenharmony_ci	.suspend        = memstick_device_suspend,
1768c2ecf20Sopenharmony_ci	.resume         = memstick_device_resume
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void memstick_free(struct device *dev)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct memstick_host *host = container_of(dev, struct memstick_host,
1828c2ecf20Sopenharmony_ci						  dev);
1838c2ecf20Sopenharmony_ci	kfree(host);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic struct class memstick_host_class = {
1878c2ecf20Sopenharmony_ci	.name        = "memstick_host",
1888c2ecf20Sopenharmony_ci	.dev_release = memstick_free
1898c2ecf20Sopenharmony_ci};
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic void memstick_free_card(struct device *dev)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct memstick_dev *card = container_of(dev, struct memstick_dev,
1948c2ecf20Sopenharmony_ci						 dev);
1958c2ecf20Sopenharmony_ci	kfree(card);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int memstick_dummy_check(struct memstick_dev *card)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	return 0;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci/**
2048c2ecf20Sopenharmony_ci * memstick_detect_change - schedule media detection on memstick host
2058c2ecf20Sopenharmony_ci * @host - host to use
2068c2ecf20Sopenharmony_ci */
2078c2ecf20Sopenharmony_civoid memstick_detect_change(struct memstick_host *host)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	queue_work(workqueue, &host->media_checker);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_detect_change);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/**
2148c2ecf20Sopenharmony_ci * memstick_next_req - called by host driver to obtain next request to process
2158c2ecf20Sopenharmony_ci * @host - host to use
2168c2ecf20Sopenharmony_ci * @mrq - pointer to stick the request to
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * Host calls this function from idle state (*mrq == NULL) or after finishing
2198c2ecf20Sopenharmony_ci * previous request (*mrq should point to it). If previous request was
2208c2ecf20Sopenharmony_ci * unsuccessful, it is retried for predetermined number of times. Return value
2218c2ecf20Sopenharmony_ci * of 0 means that new request was assigned to the host.
2228c2ecf20Sopenharmony_ci */
2238c2ecf20Sopenharmony_ciint memstick_next_req(struct memstick_host *host, struct memstick_request **mrq)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int rc = -ENXIO;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if ((*mrq) && (*mrq)->error && host->retries) {
2288c2ecf20Sopenharmony_ci		(*mrq)->error = rc;
2298c2ecf20Sopenharmony_ci		host->retries--;
2308c2ecf20Sopenharmony_ci		return 0;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (host->card && host->card->next_request)
2348c2ecf20Sopenharmony_ci		rc = host->card->next_request(host->card, mrq);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (!rc)
2378c2ecf20Sopenharmony_ci		host->retries = cmd_retries > 1 ? cmd_retries - 1 : 1;
2388c2ecf20Sopenharmony_ci	else
2398c2ecf20Sopenharmony_ci		*mrq = NULL;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return rc;
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_next_req);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/**
2468c2ecf20Sopenharmony_ci * memstick_new_req - notify the host that some requests are pending
2478c2ecf20Sopenharmony_ci * @host - host to use
2488c2ecf20Sopenharmony_ci */
2498c2ecf20Sopenharmony_civoid memstick_new_req(struct memstick_host *host)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	if (host->card) {
2528c2ecf20Sopenharmony_ci		host->retries = cmd_retries;
2538c2ecf20Sopenharmony_ci		reinit_completion(&host->card->mrq_complete);
2548c2ecf20Sopenharmony_ci		host->request(host);
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_new_req);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/**
2608c2ecf20Sopenharmony_ci * memstick_init_req_sg - set request fields needed for bulk data transfer
2618c2ecf20Sopenharmony_ci * @mrq - request to use
2628c2ecf20Sopenharmony_ci * @tpc - memstick Transport Protocol Command
2638c2ecf20Sopenharmony_ci * @sg - TPC argument
2648c2ecf20Sopenharmony_ci */
2658c2ecf20Sopenharmony_civoid memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
2668c2ecf20Sopenharmony_ci			  const struct scatterlist *sg)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	mrq->tpc = tpc;
2698c2ecf20Sopenharmony_ci	if (tpc & 8)
2708c2ecf20Sopenharmony_ci		mrq->data_dir = WRITE;
2718c2ecf20Sopenharmony_ci	else
2728c2ecf20Sopenharmony_ci		mrq->data_dir = READ;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	mrq->sg = *sg;
2758c2ecf20Sopenharmony_ci	mrq->long_data = 1;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
2788c2ecf20Sopenharmony_ci		mrq->need_card_int = 1;
2798c2ecf20Sopenharmony_ci	else
2808c2ecf20Sopenharmony_ci		mrq->need_card_int = 0;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_init_req_sg);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/**
2858c2ecf20Sopenharmony_ci * memstick_init_req - set request fields needed for short data transfer
2868c2ecf20Sopenharmony_ci * @mrq - request to use
2878c2ecf20Sopenharmony_ci * @tpc - memstick Transport Protocol Command
2888c2ecf20Sopenharmony_ci * @buf - TPC argument buffer
2898c2ecf20Sopenharmony_ci * @length - TPC argument size
2908c2ecf20Sopenharmony_ci *
2918c2ecf20Sopenharmony_ci * The intended use of this function (transfer of data items several bytes
2928c2ecf20Sopenharmony_ci * in size) allows us to just copy the value between request structure and
2938c2ecf20Sopenharmony_ci * user supplied buffer.
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_civoid memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
2968c2ecf20Sopenharmony_ci		       const void *buf, size_t length)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	mrq->tpc = tpc;
2998c2ecf20Sopenharmony_ci	if (tpc & 8)
3008c2ecf20Sopenharmony_ci		mrq->data_dir = WRITE;
3018c2ecf20Sopenharmony_ci	else
3028c2ecf20Sopenharmony_ci		mrq->data_dir = READ;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	mrq->data_len = length > sizeof(mrq->data) ? sizeof(mrq->data) : length;
3058c2ecf20Sopenharmony_ci	if (mrq->data_dir == WRITE)
3068c2ecf20Sopenharmony_ci		memcpy(mrq->data, buf, mrq->data_len);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	mrq->long_data = 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
3118c2ecf20Sopenharmony_ci		mrq->need_card_int = 1;
3128c2ecf20Sopenharmony_ci	else
3138c2ecf20Sopenharmony_ci		mrq->need_card_int = 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_init_req);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/*
3188c2ecf20Sopenharmony_ci * Functions prefixed with "h_" are protocol callbacks. They can be called from
3198c2ecf20Sopenharmony_ci * interrupt context. Return value of 0 means that request processing is still
3208c2ecf20Sopenharmony_ci * ongoing, while special error value of -EAGAIN means that current request is
3218c2ecf20Sopenharmony_ci * finished (and request processor should come back some time later).
3228c2ecf20Sopenharmony_ci */
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic int h_memstick_read_dev_id(struct memstick_dev *card,
3258c2ecf20Sopenharmony_ci				  struct memstick_request **mrq)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct ms_id_register id_reg;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (!(*mrq)) {
3308c2ecf20Sopenharmony_ci		memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, &id_reg,
3318c2ecf20Sopenharmony_ci				  sizeof(struct ms_id_register));
3328c2ecf20Sopenharmony_ci		*mrq = &card->current_mrq;
3338c2ecf20Sopenharmony_ci		return 0;
3348c2ecf20Sopenharmony_ci	} else {
3358c2ecf20Sopenharmony_ci		if (!(*mrq)->error) {
3368c2ecf20Sopenharmony_ci			memcpy(&id_reg, (*mrq)->data, sizeof(id_reg));
3378c2ecf20Sopenharmony_ci			card->id.match_flags = MEMSTICK_MATCH_ALL;
3388c2ecf20Sopenharmony_ci			card->id.type = id_reg.type;
3398c2ecf20Sopenharmony_ci			card->id.category = id_reg.category;
3408c2ecf20Sopenharmony_ci			card->id.class = id_reg.class;
3418c2ecf20Sopenharmony_ci			dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode);
3428c2ecf20Sopenharmony_ci		}
3438c2ecf20Sopenharmony_ci		complete(&card->mrq_complete);
3448c2ecf20Sopenharmony_ci		return -EAGAIN;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic int h_memstick_set_rw_addr(struct memstick_dev *card,
3498c2ecf20Sopenharmony_ci				  struct memstick_request **mrq)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	if (!(*mrq)) {
3528c2ecf20Sopenharmony_ci		memstick_init_req(&card->current_mrq, MS_TPC_SET_RW_REG_ADRS,
3538c2ecf20Sopenharmony_ci				  (char *)&card->reg_addr,
3548c2ecf20Sopenharmony_ci				  sizeof(card->reg_addr));
3558c2ecf20Sopenharmony_ci		*mrq = &card->current_mrq;
3568c2ecf20Sopenharmony_ci		return 0;
3578c2ecf20Sopenharmony_ci	} else {
3588c2ecf20Sopenharmony_ci		complete(&card->mrq_complete);
3598c2ecf20Sopenharmony_ci		return -EAGAIN;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci/**
3648c2ecf20Sopenharmony_ci * memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to
3658c2ecf20Sopenharmony_ci *                        complete
3668c2ecf20Sopenharmony_ci * @card - media device to use
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_ciint memstick_set_rw_addr(struct memstick_dev *card)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	card->next_request = h_memstick_set_rw_addr;
3718c2ecf20Sopenharmony_ci	memstick_new_req(card->host);
3728c2ecf20Sopenharmony_ci	wait_for_completion(&card->mrq_complete);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return card->current_mrq.error;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_set_rw_addr);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic struct memstick_dev *memstick_alloc_card(struct memstick_host *host)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev),
3818c2ecf20Sopenharmony_ci					    GFP_KERNEL);
3828c2ecf20Sopenharmony_ci	struct memstick_dev *old_card = host->card;
3838c2ecf20Sopenharmony_ci	struct ms_id_register id_reg;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (card) {
3868c2ecf20Sopenharmony_ci		card->host = host;
3878c2ecf20Sopenharmony_ci		dev_set_name(&card->dev, "%s", dev_name(&host->dev));
3888c2ecf20Sopenharmony_ci		card->dev.parent = &host->dev;
3898c2ecf20Sopenharmony_ci		card->dev.bus = &memstick_bus_type;
3908c2ecf20Sopenharmony_ci		card->dev.release = memstick_free_card;
3918c2ecf20Sopenharmony_ci		card->check = memstick_dummy_check;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		card->reg_addr.r_offset = offsetof(struct ms_register, id);
3948c2ecf20Sopenharmony_ci		card->reg_addr.r_length = sizeof(id_reg);
3958c2ecf20Sopenharmony_ci		card->reg_addr.w_offset = offsetof(struct ms_register, id);
3968c2ecf20Sopenharmony_ci		card->reg_addr.w_length = sizeof(id_reg);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci		init_completion(&card->mrq_complete);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci		host->card = card;
4018c2ecf20Sopenharmony_ci		if (memstick_set_rw_addr(card))
4028c2ecf20Sopenharmony_ci			goto err_out;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		card->next_request = h_memstick_read_dev_id;
4058c2ecf20Sopenharmony_ci		memstick_new_req(host);
4068c2ecf20Sopenharmony_ci		wait_for_completion(&card->mrq_complete);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		if (card->current_mrq.error)
4098c2ecf20Sopenharmony_ci			goto err_out;
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	host->card = old_card;
4128c2ecf20Sopenharmony_ci	return card;
4138c2ecf20Sopenharmony_cierr_out:
4148c2ecf20Sopenharmony_ci	host->card = old_card;
4158c2ecf20Sopenharmony_ci	kfree_const(card->dev.kobj.name);
4168c2ecf20Sopenharmony_ci	kfree(card);
4178c2ecf20Sopenharmony_ci	return NULL;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int memstick_power_on(struct memstick_host *host)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	int rc = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (!rc)
4258c2ecf20Sopenharmony_ci		rc = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return rc;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void memstick_check(struct work_struct *work)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct memstick_host *host = container_of(work, struct memstick_host,
4338c2ecf20Sopenharmony_ci						  media_checker);
4348c2ecf20Sopenharmony_ci	struct memstick_dev *card;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	dev_dbg(&host->dev, "memstick_check started\n");
4378c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(host->dev.parent);
4388c2ecf20Sopenharmony_ci	mutex_lock(&host->lock);
4398c2ecf20Sopenharmony_ci	if (!host->card) {
4408c2ecf20Sopenharmony_ci		if (memstick_power_on(host))
4418c2ecf20Sopenharmony_ci			goto out_power_off;
4428c2ecf20Sopenharmony_ci	} else if (host->card->stop)
4438c2ecf20Sopenharmony_ci		host->card->stop(host->card);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	if (host->removing)
4468c2ecf20Sopenharmony_ci		goto out_power_off;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	card = memstick_alloc_card(host);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (!card) {
4518c2ecf20Sopenharmony_ci		if (host->card) {
4528c2ecf20Sopenharmony_ci			device_unregister(&host->card->dev);
4538c2ecf20Sopenharmony_ci			host->card = NULL;
4548c2ecf20Sopenharmony_ci		}
4558c2ecf20Sopenharmony_ci	} else {
4568c2ecf20Sopenharmony_ci		dev_dbg(&host->dev, "new card %02x, %02x, %02x\n",
4578c2ecf20Sopenharmony_ci			card->id.type, card->id.category, card->id.class);
4588c2ecf20Sopenharmony_ci		if (host->card) {
4598c2ecf20Sopenharmony_ci			if (memstick_set_rw_addr(host->card)
4608c2ecf20Sopenharmony_ci			    || !memstick_dev_match(host->card, &card->id)
4618c2ecf20Sopenharmony_ci			    || !(host->card->check(host->card))) {
4628c2ecf20Sopenharmony_ci				device_unregister(&host->card->dev);
4638c2ecf20Sopenharmony_ci				host->card = NULL;
4648c2ecf20Sopenharmony_ci			} else if (host->card->start)
4658c2ecf20Sopenharmony_ci				host->card->start(host->card);
4668c2ecf20Sopenharmony_ci		}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		if (!host->card) {
4698c2ecf20Sopenharmony_ci			host->card = card;
4708c2ecf20Sopenharmony_ci			if (device_register(&card->dev)) {
4718c2ecf20Sopenharmony_ci				put_device(&card->dev);
4728c2ecf20Sopenharmony_ci				host->card = NULL;
4738c2ecf20Sopenharmony_ci			}
4748c2ecf20Sopenharmony_ci		} else {
4758c2ecf20Sopenharmony_ci			kfree_const(card->dev.kobj.name);
4768c2ecf20Sopenharmony_ci			kfree(card);
4778c2ecf20Sopenharmony_ci		}
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ciout_power_off:
4818c2ecf20Sopenharmony_ci	if (!host->card)
4828c2ecf20Sopenharmony_ci		host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	mutex_unlock(&host->lock);
4858c2ecf20Sopenharmony_ci	pm_runtime_put(host->dev.parent);
4868c2ecf20Sopenharmony_ci	dev_dbg(&host->dev, "memstick_check finished\n");
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci/**
4908c2ecf20Sopenharmony_ci * memstick_alloc_host - allocate a memstick_host structure
4918c2ecf20Sopenharmony_ci * @extra: size of the user private data to allocate
4928c2ecf20Sopenharmony_ci * @dev: parent device of the host
4938c2ecf20Sopenharmony_ci */
4948c2ecf20Sopenharmony_cistruct memstick_host *memstick_alloc_host(unsigned int extra,
4958c2ecf20Sopenharmony_ci					  struct device *dev)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	struct memstick_host *host;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	host = kzalloc(sizeof(struct memstick_host) + extra, GFP_KERNEL);
5008c2ecf20Sopenharmony_ci	if (host) {
5018c2ecf20Sopenharmony_ci		mutex_init(&host->lock);
5028c2ecf20Sopenharmony_ci		INIT_WORK(&host->media_checker, memstick_check);
5038c2ecf20Sopenharmony_ci		host->dev.class = &memstick_host_class;
5048c2ecf20Sopenharmony_ci		host->dev.parent = dev;
5058c2ecf20Sopenharmony_ci		device_initialize(&host->dev);
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci	return host;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_alloc_host);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci/**
5128c2ecf20Sopenharmony_ci * memstick_add_host - start request processing on memstick host
5138c2ecf20Sopenharmony_ci * @host - host to use
5148c2ecf20Sopenharmony_ci */
5158c2ecf20Sopenharmony_ciint memstick_add_host(struct memstick_host *host)
5168c2ecf20Sopenharmony_ci{
5178c2ecf20Sopenharmony_ci	int rc;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	idr_preload(GFP_KERNEL);
5208c2ecf20Sopenharmony_ci	spin_lock(&memstick_host_lock);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	rc = idr_alloc(&memstick_host_idr, host, 0, 0, GFP_NOWAIT);
5238c2ecf20Sopenharmony_ci	if (rc >= 0)
5248c2ecf20Sopenharmony_ci		host->id = rc;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	spin_unlock(&memstick_host_lock);
5278c2ecf20Sopenharmony_ci	idr_preload_end();
5288c2ecf20Sopenharmony_ci	if (rc < 0)
5298c2ecf20Sopenharmony_ci		return rc;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	dev_set_name(&host->dev, "memstick%u", host->id);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	rc = device_add(&host->dev);
5348c2ecf20Sopenharmony_ci	if (rc) {
5358c2ecf20Sopenharmony_ci		spin_lock(&memstick_host_lock);
5368c2ecf20Sopenharmony_ci		idr_remove(&memstick_host_idr, host->id);
5378c2ecf20Sopenharmony_ci		spin_unlock(&memstick_host_lock);
5388c2ecf20Sopenharmony_ci		return rc;
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
5428c2ecf20Sopenharmony_ci	memstick_detect_change(host);
5438c2ecf20Sopenharmony_ci	return 0;
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_add_host);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci/**
5488c2ecf20Sopenharmony_ci * memstick_remove_host - stop request processing on memstick host
5498c2ecf20Sopenharmony_ci * @host - host to use
5508c2ecf20Sopenharmony_ci */
5518c2ecf20Sopenharmony_civoid memstick_remove_host(struct memstick_host *host)
5528c2ecf20Sopenharmony_ci{
5538c2ecf20Sopenharmony_ci	host->removing = 1;
5548c2ecf20Sopenharmony_ci	flush_workqueue(workqueue);
5558c2ecf20Sopenharmony_ci	mutex_lock(&host->lock);
5568c2ecf20Sopenharmony_ci	if (host->card)
5578c2ecf20Sopenharmony_ci		device_unregister(&host->card->dev);
5588c2ecf20Sopenharmony_ci	host->card = NULL;
5598c2ecf20Sopenharmony_ci	host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
5608c2ecf20Sopenharmony_ci	mutex_unlock(&host->lock);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	spin_lock(&memstick_host_lock);
5638c2ecf20Sopenharmony_ci	idr_remove(&memstick_host_idr, host->id);
5648c2ecf20Sopenharmony_ci	spin_unlock(&memstick_host_lock);
5658c2ecf20Sopenharmony_ci	device_del(&host->dev);
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_remove_host);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/**
5708c2ecf20Sopenharmony_ci * memstick_free_host - free memstick host
5718c2ecf20Sopenharmony_ci * @host - host to use
5728c2ecf20Sopenharmony_ci */
5738c2ecf20Sopenharmony_civoid memstick_free_host(struct memstick_host *host)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	mutex_destroy(&host->lock);
5768c2ecf20Sopenharmony_ci	put_device(&host->dev);
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_free_host);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci/**
5818c2ecf20Sopenharmony_ci * memstick_suspend_host - notify bus driver of host suspension
5828c2ecf20Sopenharmony_ci * @host - host to use
5838c2ecf20Sopenharmony_ci */
5848c2ecf20Sopenharmony_civoid memstick_suspend_host(struct memstick_host *host)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	mutex_lock(&host->lock);
5878c2ecf20Sopenharmony_ci	host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
5888c2ecf20Sopenharmony_ci	mutex_unlock(&host->lock);
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_suspend_host);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci/**
5938c2ecf20Sopenharmony_ci * memstick_resume_host - notify bus driver of host resumption
5948c2ecf20Sopenharmony_ci * @host - host to use
5958c2ecf20Sopenharmony_ci */
5968c2ecf20Sopenharmony_civoid memstick_resume_host(struct memstick_host *host)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	int rc = 0;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	mutex_lock(&host->lock);
6018c2ecf20Sopenharmony_ci	if (host->card)
6028c2ecf20Sopenharmony_ci		rc = memstick_power_on(host);
6038c2ecf20Sopenharmony_ci	mutex_unlock(&host->lock);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (!rc)
6068c2ecf20Sopenharmony_ci		memstick_detect_change(host);
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_resume_host);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ciint memstick_register_driver(struct memstick_driver *drv)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	drv->driver.bus = &memstick_bus_type;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return driver_register(&drv->driver);
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_register_driver);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_civoid memstick_unregister_driver(struct memstick_driver *drv)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	driver_unregister(&drv->driver);
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(memstick_unregister_driver);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic int __init memstick_init(void)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	int rc;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	workqueue = create_freezable_workqueue("kmemstick");
6308c2ecf20Sopenharmony_ci	if (!workqueue)
6318c2ecf20Sopenharmony_ci		return -ENOMEM;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	rc = bus_register(&memstick_bus_type);
6348c2ecf20Sopenharmony_ci	if (rc)
6358c2ecf20Sopenharmony_ci		goto error_destroy_workqueue;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	rc = class_register(&memstick_host_class);
6388c2ecf20Sopenharmony_ci	if (rc)
6398c2ecf20Sopenharmony_ci		goto error_bus_unregister;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	return 0;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cierror_bus_unregister:
6448c2ecf20Sopenharmony_ci	bus_unregister(&memstick_bus_type);
6458c2ecf20Sopenharmony_cierror_destroy_workqueue:
6468c2ecf20Sopenharmony_ci	destroy_workqueue(workqueue);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return rc;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic void __exit memstick_exit(void)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	class_unregister(&memstick_host_class);
6548c2ecf20Sopenharmony_ci	bus_unregister(&memstick_bus_type);
6558c2ecf20Sopenharmony_ci	destroy_workqueue(workqueue);
6568c2ecf20Sopenharmony_ci	idr_destroy(&memstick_host_idr);
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cimodule_init(memstick_init);
6608c2ecf20Sopenharmony_cimodule_exit(memstick_exit);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alex Dubov");
6638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
6648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sony MemoryStick core driver");
665