memstick: initial commit for Sony MemoryStick support
Sony MemoryStick cards are used in many products manufactured by Sony. They are available both as storage and as IO expansion cards. Currently, only MemoryStick Pro storage cards are supported via TI FlashMedia MemoryStick interface. [mboton@gmail.com: biuld fix] [akpm@linux-foundation.org: build fix] Signed-off-by: Alex Dubov <oakad@yahoo.com> Signed-off-by: Miguel Boton <mboton@gmail.co> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
941edd030b
commit
baf8532a14
|
@ -3627,6 +3627,13 @@ L: linux-acpi@vger.kernel.org
|
||||||
W: http://www.linux.it/~malattia/wiki/index.php/Sony_drivers
|
W: http://www.linux.it/~malattia/wiki/index.php/Sony_drivers
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
||||||
|
SONY MEMORYSTICK CARD SUPPORT
|
||||||
|
P: Alex Dubov
|
||||||
|
M: oakad@yahoo.com
|
||||||
|
L: linux-kernel@vger.kernel.org
|
||||||
|
W: http://tifmxx.berlios.de/
|
||||||
|
S: Maintained
|
||||||
|
|
||||||
SOUND
|
SOUND
|
||||||
P: Jaroslav Kysela
|
P: Jaroslav Kysela
|
||||||
M: perex@perex.cz
|
M: perex@perex.cz
|
||||||
|
|
|
@ -80,6 +80,8 @@ source "drivers/usb/Kconfig"
|
||||||
|
|
||||||
source "drivers/mmc/Kconfig"
|
source "drivers/mmc/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/memstick/Kconfig"
|
||||||
|
|
||||||
source "drivers/leds/Kconfig"
|
source "drivers/leds/Kconfig"
|
||||||
|
|
||||||
source "drivers/infiniband/Kconfig"
|
source "drivers/infiniband/Kconfig"
|
||||||
|
|
|
@ -78,6 +78,7 @@ obj-y += lguest/
|
||||||
obj-$(CONFIG_CPU_FREQ) += cpufreq/
|
obj-$(CONFIG_CPU_FREQ) += cpufreq/
|
||||||
obj-$(CONFIG_CPU_IDLE) += cpuidle/
|
obj-$(CONFIG_CPU_IDLE) += cpuidle/
|
||||||
obj-$(CONFIG_MMC) += mmc/
|
obj-$(CONFIG_MMC) += mmc/
|
||||||
|
obj-$(CONFIG_MEMSTICK) += memstick/
|
||||||
obj-$(CONFIG_NEW_LEDS) += leds/
|
obj-$(CONFIG_NEW_LEDS) += leds/
|
||||||
obj-$(CONFIG_INFINIBAND) += infiniband/
|
obj-$(CONFIG_INFINIBAND) += infiniband/
|
||||||
obj-$(CONFIG_SGI_SN) += sn/
|
obj-$(CONFIG_SGI_SN) += sn/
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#
|
||||||
|
# MemoryStick subsystem configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
menuconfig MEMSTICK
|
||||||
|
tristate "Sony MemoryStick card support (EXPERIMENTAL)"
|
||||||
|
help
|
||||||
|
Sony MemoryStick is a proprietary storage/extension card protocol.
|
||||||
|
|
||||||
|
If you want MemoryStick support, you should say Y here and also
|
||||||
|
to the specific driver for your MMC interface.
|
||||||
|
|
||||||
|
if MEMSTICK
|
||||||
|
|
||||||
|
config MEMSTICK_DEBUG
|
||||||
|
bool "MemoryStick debugging"
|
||||||
|
help
|
||||||
|
This is an option for use by developers; most people should
|
||||||
|
say N here. This enables MemoryStick core and driver debugging.
|
||||||
|
|
||||||
|
|
||||||
|
source "drivers/memstick/core/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/memstick/host/Kconfig"
|
||||||
|
|
||||||
|
endif # MEMSTICK
|
|
@ -0,0 +1,11 @@
|
||||||
|
#
|
||||||
|
# Makefile for the kernel MemoryStick device drivers.
|
||||||
|
#
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
|
||||||
|
EXTRA_CFLAGS += -DDEBUG
|
||||||
|
endif
|
||||||
|
|
||||||
|
obj-$(CONFIG_MEMSTICK) += core/
|
||||||
|
obj-$(CONFIG_MEMSTICK) += host/
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#
|
||||||
|
# MemoryStick core configuration
|
||||||
|
#
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
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.
|
|
@ -0,0 +1,11 @@
|
||||||
|
#
|
||||||
|
# Makefile for the kernel MemoryStick core.
|
||||||
|
#
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
|
||||||
|
EXTRA_CFLAGS += -DDEBUG
|
||||||
|
endif
|
||||||
|
|
||||||
|
obj-$(CONFIG_MEMSTICK) += memstick.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_MSPRO_BLOCK) += mspro_block.o
|
|
@ -0,0 +1,614 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
#include <linux/idr.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
|
#define DRIVER_NAME "memstick"
|
||||||
|
#define DRIVER_VERSION "0.2"
|
||||||
|
|
||||||
|
static unsigned int cmd_retries = 3;
|
||||||
|
module_param(cmd_retries, uint, 0644);
|
||||||
|
|
||||||
|
static struct workqueue_struct *workqueue;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (id->match_flags & MEMSTICK_MATCH_ALL) {
|
||||||
|
if ((id->type == card->id.type)
|
||||||
|
&& (id->category == card->id.category)
|
||||||
|
&& (id->class == card->id.class))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int memstick_bus_match(struct device *dev, 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;
|
||||||
|
|
||||||
|
if (ids) {
|
||||||
|
while (ids->match_flags) {
|
||||||
|
if (memstick_dev_match(card, ids))
|
||||||
|
return 1;
|
||||||
|
++ids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int memstick_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
|
{
|
||||||
|
struct memstick_dev *card = container_of(dev, struct memstick_dev,
|
||||||
|
dev);
|
||||||
|
|
||||||
|
if (add_uevent_var(env, "MEMSTICK_TYPE=%02X", card->id.type))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (add_uevent_var(env, "MEMSTICK_CATEGORY=%02X", card->id.category))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (add_uevent_var(env, "MEMSTICK_CLASS=%02X", card->id.class))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int memstick_device_probe(struct device *dev)
|
||||||
|
{
|
||||||
|
struct memstick_dev *card = container_of(dev, struct memstick_dev,
|
||||||
|
dev);
|
||||||
|
struct memstick_driver *drv = container_of(dev->driver,
|
||||||
|
struct memstick_driver,
|
||||||
|
driver);
|
||||||
|
int rc = -ENODEV;
|
||||||
|
|
||||||
|
if (dev->driver && drv->probe) {
|
||||||
|
rc = drv->probe(card);
|
||||||
|
if (!rc)
|
||||||
|
get_device(dev);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int memstick_device_remove(struct device *dev)
|
||||||
|
{
|
||||||
|
struct memstick_dev *card = container_of(dev, struct memstick_dev,
|
||||||
|
dev);
|
||||||
|
struct memstick_driver *drv = container_of(dev->driver,
|
||||||
|
struct memstick_driver,
|
||||||
|
driver);
|
||||||
|
|
||||||
|
if (dev->driver && drv->remove) {
|
||||||
|
drv->remove(card);
|
||||||
|
card->dev.driver = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
put_device(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
|
static int memstick_device_suspend(struct device *dev, pm_message_t state)
|
||||||
|
{
|
||||||
|
struct memstick_dev *card = container_of(dev, struct memstick_dev,
|
||||||
|
dev);
|
||||||
|
struct memstick_driver *drv = container_of(dev->driver,
|
||||||
|
struct memstick_driver,
|
||||||
|
driver);
|
||||||
|
|
||||||
|
if (dev->driver && drv->suspend)
|
||||||
|
return drv->suspend(card, state);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int memstick_device_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct memstick_dev *card = container_of(dev, struct memstick_dev,
|
||||||
|
dev);
|
||||||
|
struct memstick_driver *drv = container_of(dev->driver,
|
||||||
|
struct memstick_driver,
|
||||||
|
driver);
|
||||||
|
|
||||||
|
if (dev->driver && drv->resume)
|
||||||
|
return drv->resume(card);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define memstick_device_suspend NULL
|
||||||
|
#define memstick_device_resume NULL
|
||||||
|
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
#define MEMSTICK_ATTR(name, format) \
|
||||||
|
static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
|
||||||
|
char *buf) \
|
||||||
|
{ \
|
||||||
|
struct memstick_dev *card = container_of(dev, struct memstick_dev, \
|
||||||
|
dev); \
|
||||||
|
return sprintf(buf, format, card->id.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 bus_type memstick_bus_type = {
|
||||||
|
.name = "memstick",
|
||||||
|
.dev_attrs = memstick_dev_attrs,
|
||||||
|
.match = memstick_bus_match,
|
||||||
|
.uevent = memstick_uevent,
|
||||||
|
.probe = memstick_device_probe,
|
||||||
|
.remove = memstick_device_remove,
|
||||||
|
.suspend = memstick_device_suspend,
|
||||||
|
.resume = memstick_device_resume
|
||||||
|
};
|
||||||
|
|
||||||
|
static void memstick_free(struct class_device *cdev)
|
||||||
|
{
|
||||||
|
struct memstick_host *host = container_of(cdev, struct memstick_host,
|
||||||
|
cdev);
|
||||||
|
kfree(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct class memstick_host_class = {
|
||||||
|
.name = "memstick_host",
|
||||||
|
.release = memstick_free
|
||||||
|
};
|
||||||
|
|
||||||
|
static void memstick_free_card(struct device *dev)
|
||||||
|
{
|
||||||
|
struct memstick_dev *card = container_of(dev, struct memstick_dev,
|
||||||
|
dev);
|
||||||
|
kfree(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int memstick_dummy_check(struct memstick_dev *card)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memstick_detect_change - schedule media detection on memstick host
|
||||||
|
* @host - host to use
|
||||||
|
*/
|
||||||
|
void memstick_detect_change(struct memstick_host *host)
|
||||||
|
{
|
||||||
|
queue_work(workqueue, &host->media_checker);
|
||||||
|
}
|
||||||
|
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 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.
|
||||||
|
*/
|
||||||
|
int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq)
|
||||||
|
{
|
||||||
|
int rc = -ENXIO;
|
||||||
|
|
||||||
|
if ((*mrq) && (*mrq)->error && host->retries) {
|
||||||
|
(*mrq)->error = rc;
|
||||||
|
host->retries--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host->card && host->card->next_request)
|
||||||
|
rc = host->card->next_request(host->card, mrq);
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
host->retries = cmd_retries;
|
||||||
|
else
|
||||||
|
*mrq = NULL;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_next_req);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memstick_new_req - notify the host that some requests are pending
|
||||||
|
* @host - host to use
|
||||||
|
*/
|
||||||
|
void memstick_new_req(struct memstick_host *host)
|
||||||
|
{
|
||||||
|
host->retries = cmd_retries;
|
||||||
|
host->request(host);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
|
||||||
|
struct scatterlist *sg)
|
||||||
|
{
|
||||||
|
mrq->tpc = tpc;
|
||||||
|
if (tpc & 8)
|
||||||
|
mrq->data_dir = WRITE;
|
||||||
|
else
|
||||||
|
mrq->data_dir = READ;
|
||||||
|
|
||||||
|
mrq->sg = *sg;
|
||||||
|
mrq->io_type = MEMSTICK_IO_SG;
|
||||||
|
|
||||||
|
if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
|
||||||
|
mrq->need_card_int = 1;
|
||||||
|
else
|
||||||
|
mrq->need_card_int = 0;
|
||||||
|
|
||||||
|
mrq->get_int_reg = 0;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* user supplied buffer.
|
||||||
|
*/
|
||||||
|
void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
|
||||||
|
void *buf, size_t length)
|
||||||
|
{
|
||||||
|
mrq->tpc = tpc;
|
||||||
|
if (tpc & 8)
|
||||||
|
mrq->data_dir = WRITE;
|
||||||
|
else
|
||||||
|
mrq->data_dir = READ;
|
||||||
|
|
||||||
|
mrq->data_len = length > sizeof(mrq->data) ? sizeof(mrq->data) : length;
|
||||||
|
if (mrq->data_dir == WRITE)
|
||||||
|
memcpy(mrq->data, buf, mrq->data_len);
|
||||||
|
|
||||||
|
mrq->io_type = MEMSTICK_IO_VAL;
|
||||||
|
|
||||||
|
if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD)
|
||||||
|
mrq->need_card_int = 1;
|
||||||
|
else
|
||||||
|
mrq->need_card_int = 0;
|
||||||
|
|
||||||
|
mrq->get_int_reg = 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_init_req);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions prefixed with "h_" are protocol callbacks. They can be called from
|
||||||
|
* interrupt context. Return value of 0 means that request processing is still
|
||||||
|
* ongoing, while special error value of -EAGAIN means that current request is
|
||||||
|
* finished (and request processor should come back some time later).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int h_memstick_read_dev_id(struct memstick_dev *card,
|
||||||
|
struct memstick_request **mrq)
|
||||||
|
{
|
||||||
|
struct ms_id_register id_reg;
|
||||||
|
|
||||||
|
if (!(*mrq)) {
|
||||||
|
memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
complete(&card->mrq_complete);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int h_memstick_set_rw_addr(struct memstick_dev *card,
|
||||||
|
struct memstick_request **mrq)
|
||||||
|
{
|
||||||
|
if (!(*mrq)) {
|
||||||
|
memstick_init_req(&card->current_mrq, MS_TPC_SET_RW_REG_ADRS,
|
||||||
|
(char *)&card->reg_addr,
|
||||||
|
sizeof(card->reg_addr));
|
||||||
|
*mrq = &card->current_mrq;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
complete(&card->mrq_complete);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to
|
||||||
|
* complete
|
||||||
|
* @card - media device to use
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
return card->current_mrq.error;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_set_rw_addr);
|
||||||
|
|
||||||
|
static struct memstick_dev *memstick_alloc_card(struct memstick_host *host)
|
||||||
|
{
|
||||||
|
struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev),
|
||||||
|
GFP_KERNEL);
|
||||||
|
struct memstick_dev *old_card = host->card;
|
||||||
|
struct ms_id_register id_reg;
|
||||||
|
|
||||||
|
if (card) {
|
||||||
|
card->host = host;
|
||||||
|
snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
|
||||||
|
"%s", host->cdev.class_id);
|
||||||
|
card->dev.parent = host->cdev.dev;
|
||||||
|
card->dev.bus = &memstick_bus_type;
|
||||||
|
card->dev.release = memstick_free_card;
|
||||||
|
card->check = memstick_dummy_check;
|
||||||
|
|
||||||
|
card->reg_addr.r_offset = offsetof(struct ms_register, id);
|
||||||
|
card->reg_addr.r_length = sizeof(id_reg);
|
||||||
|
card->reg_addr.w_offset = offsetof(struct ms_register, id);
|
||||||
|
card->reg_addr.w_length = sizeof(id_reg);
|
||||||
|
|
||||||
|
init_completion(&card->mrq_complete);
|
||||||
|
|
||||||
|
host->card = card;
|
||||||
|
if (memstick_set_rw_addr(card))
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
card->next_request = h_memstick_read_dev_id;
|
||||||
|
memstick_new_req(host);
|
||||||
|
wait_for_completion(&card->mrq_complete);
|
||||||
|
|
||||||
|
if (card->current_mrq.error)
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
host->card = old_card;
|
||||||
|
return card;
|
||||||
|
err_out:
|
||||||
|
host->card = old_card;
|
||||||
|
kfree(card);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void memstick_power_on(struct memstick_host *host)
|
||||||
|
{
|
||||||
|
host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON);
|
||||||
|
host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL);
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void memstick_check(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct memstick_host *host = container_of(work, struct memstick_host,
|
||||||
|
media_checker);
|
||||||
|
struct memstick_dev *card;
|
||||||
|
|
||||||
|
dev_dbg(host->cdev.dev, "memstick_check started\n");
|
||||||
|
mutex_lock(&host->lock);
|
||||||
|
if (!host->card)
|
||||||
|
memstick_power_on(host);
|
||||||
|
|
||||||
|
card = memstick_alloc_card(host);
|
||||||
|
|
||||||
|
if (!card) {
|
||||||
|
if (host->card) {
|
||||||
|
device_unregister(&host->card->dev);
|
||||||
|
host->card = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dev_dbg(host->cdev.dev, "new card %02x, %02x, %02x\n",
|
||||||
|
card->id.type, card->id.category, card->id.class);
|
||||||
|
if (host->card) {
|
||||||
|
if (memstick_set_rw_addr(host->card)
|
||||||
|
|| !memstick_dev_match(host->card, &card->id)
|
||||||
|
|| !(host->card->check(host->card))) {
|
||||||
|
device_unregister(&host->card->dev);
|
||||||
|
host->card = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!host->card) {
|
||||||
|
host->card = card;
|
||||||
|
if (device_register(&card->dev)) {
|
||||||
|
kfree(host->card);
|
||||||
|
host->card = NULL;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
kfree(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!host->card)
|
||||||
|
host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
|
||||||
|
|
||||||
|
mutex_unlock(&host->lock);
|
||||||
|
dev_dbg(host->cdev.dev, "memstick_check finished\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memstick_alloc_host - allocate a memstick_host structure
|
||||||
|
* @extra: size of the user private data to allocate
|
||||||
|
* @dev: parent device of the host
|
||||||
|
*/
|
||||||
|
struct memstick_host *memstick_alloc_host(unsigned int extra,
|
||||||
|
struct device *dev)
|
||||||
|
{
|
||||||
|
struct memstick_host *host;
|
||||||
|
|
||||||
|
host = kzalloc(sizeof(struct memstick_host) + extra, GFP_KERNEL);
|
||||||
|
if (host) {
|
||||||
|
mutex_init(&host->lock);
|
||||||
|
INIT_WORK(&host->media_checker, memstick_check);
|
||||||
|
host->cdev.class = &memstick_host_class;
|
||||||
|
host->cdev.dev = dev;
|
||||||
|
class_device_initialize(&host->cdev);
|
||||||
|
}
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_alloc_host);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memstick_add_host - start request processing on memstick host
|
||||||
|
* @host - host to use
|
||||||
|
*/
|
||||||
|
int memstick_add_host(struct memstick_host *host)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (!idr_pre_get(&memstick_host_idr, GFP_KERNEL))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock(&memstick_host_lock);
|
||||||
|
rc = idr_get_new(&memstick_host_idr, host, &host->id);
|
||||||
|
spin_unlock(&memstick_host_lock);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
snprintf(host->cdev.class_id, BUS_ID_SIZE,
|
||||||
|
"memstick%u", host->id);
|
||||||
|
|
||||||
|
rc = class_device_add(&host->cdev);
|
||||||
|
if (rc) {
|
||||||
|
spin_lock(&memstick_host_lock);
|
||||||
|
idr_remove(&memstick_host_idr, host->id);
|
||||||
|
spin_unlock(&memstick_host_lock);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
|
||||||
|
memstick_detect_change(host);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_add_host);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memstick_remove_host - stop request processing on memstick host
|
||||||
|
* @host - host to use
|
||||||
|
*/
|
||||||
|
void memstick_remove_host(struct memstick_host *host)
|
||||||
|
{
|
||||||
|
flush_workqueue(workqueue);
|
||||||
|
mutex_lock(&host->lock);
|
||||||
|
if (host->card)
|
||||||
|
device_unregister(&host->card->dev);
|
||||||
|
host->card = NULL;
|
||||||
|
host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF);
|
||||||
|
mutex_unlock(&host->lock);
|
||||||
|
|
||||||
|
spin_lock(&memstick_host_lock);
|
||||||
|
idr_remove(&memstick_host_idr, host->id);
|
||||||
|
spin_unlock(&memstick_host_lock);
|
||||||
|
class_device_del(&host->cdev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_remove_host);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* memstick_free_host - free memstick host
|
||||||
|
* @host - host to use
|
||||||
|
*/
|
||||||
|
void memstick_free_host(struct memstick_host *host)
|
||||||
|
{
|
||||||
|
mutex_destroy(&host->lock);
|
||||||
|
class_device_put(&host->cdev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_free_host);
|
||||||
|
|
||||||
|
int memstick_register_driver(struct memstick_driver *drv)
|
||||||
|
{
|
||||||
|
drv->driver.bus = &memstick_bus_type;
|
||||||
|
|
||||||
|
return driver_register(&drv->driver);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_register_driver);
|
||||||
|
|
||||||
|
void memstick_unregister_driver(struct memstick_driver *drv)
|
||||||
|
{
|
||||||
|
driver_unregister(&drv->driver);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(memstick_unregister_driver);
|
||||||
|
|
||||||
|
|
||||||
|
static int __init memstick_init(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
workqueue = create_freezeable_workqueue("kmemstick");
|
||||||
|
if (!workqueue)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
rc = bus_register(&memstick_bus_type);
|
||||||
|
if (!rc)
|
||||||
|
rc = class_register(&memstick_host_class);
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bus_unregister(&memstick_bus_type);
|
||||||
|
destroy_workqueue(workqueue);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit memstick_exit(void)
|
||||||
|
{
|
||||||
|
class_unregister(&memstick_host_class);
|
||||||
|
bus_unregister(&memstick_bus_type);
|
||||||
|
destroy_workqueue(workqueue);
|
||||||
|
idr_destroy(&memstick_host_idr);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(memstick_init);
|
||||||
|
module_exit(memstick_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Alex Dubov");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DESCRIPTION("Sony MemoryStick core driver");
|
||||||
|
MODULE_VERSION(DRIVER_VERSION);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
||||||
|
#
|
||||||
|
# MemoryStick host controller drivers
|
||||||
|
#
|
||||||
|
|
||||||
|
comment "MemoryStick Host Controller Drivers"
|
||||||
|
|
||||||
|
config MEMSTICK_TIFM_MS
|
||||||
|
tristate "TI Flash Media MemoryStick Interface support (EXPERIMENTAL)"
|
||||||
|
depends on EXPERIMENTAL && PCI
|
||||||
|
select TIFM_CORE
|
||||||
|
help
|
||||||
|
Say Y here if you want to be able to access MemoryStick cards with
|
||||||
|
the Texas Instruments(R) Flash Media card reader, found in many
|
||||||
|
laptops.
|
||||||
|
This option 'selects' (turns on, enables) 'TIFM_CORE', but you
|
||||||
|
probably also need appropriate card reader host adapter, such as
|
||||||
|
'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support
|
||||||
|
(TIFM_7XX1)'.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the
|
||||||
|
module will be called tifm_ms.
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
#
|
||||||
|
# Makefile for MemoryStick host controller drivers
|
||||||
|
#
|
||||||
|
|
||||||
|
ifeq ($(CONFIG_MEMSTICK_DEBUG),y)
|
||||||
|
EXTRA_CFLAGS += -DDEBUG
|
||||||
|
endif
|
||||||
|
|
||||||
|
obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o
|
||||||
|
|
|
@ -0,0 +1,685 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
#include <linux/memstick.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/log2.h>
|
||||||
|
#include <asm/io.h>
|
||||||
|
|
||||||
|
#define DRIVER_NAME "tifm_ms"
|
||||||
|
#define DRIVER_VERSION "0.1"
|
||||||
|
|
||||||
|
static int no_dma;
|
||||||
|
module_param(no_dma, bool, 0644);
|
||||||
|
|
||||||
|
#define TIFM_MS_TIMEOUT 0x00100
|
||||||
|
#define TIFM_MS_BADCRC 0x00200
|
||||||
|
#define TIFM_MS_EOTPC 0x01000
|
||||||
|
#define TIFM_MS_INT 0x02000
|
||||||
|
|
||||||
|
/* The meaning of the bit majority in this constant is unknown. */
|
||||||
|
#define TIFM_MS_SERIAL 0x04010
|
||||||
|
|
||||||
|
#define TIFM_MS_SYS_LATCH 0x00100
|
||||||
|
#define TIFM_MS_SYS_NOT_RDY 0x00800
|
||||||
|
#define TIFM_MS_SYS_DATA 0x10000
|
||||||
|
|
||||||
|
/* Hardware flags */
|
||||||
|
enum {
|
||||||
|
CMD_READY = 0x0001,
|
||||||
|
FIFO_READY = 0x0002,
|
||||||
|
CARD_READY = 0x0004,
|
||||||
|
DATA_CARRY = 0x0008
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tifm_ms {
|
||||||
|
struct tifm_dev *dev;
|
||||||
|
unsigned short eject:1,
|
||||||
|
no_dma:1;
|
||||||
|
unsigned short cmd_flags;
|
||||||
|
unsigned int mode_mask;
|
||||||
|
unsigned int block_pos;
|
||||||
|
unsigned long timeout_jiffies;
|
||||||
|
|
||||||
|
struct timer_list timer;
|
||||||
|
struct memstick_request *req;
|
||||||
|
unsigned int io_word;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void tifm_ms_read_fifo(struct tifm_ms *host, unsigned int fifo_offset,
|
||||||
|
struct page *pg, unsigned int page_off,
|
||||||
|
unsigned int length)
|
||||||
|
{
|
||||||
|
struct tifm_dev *sock = host->dev;
|
||||||
|
unsigned int cnt = 0, off = 0;
|
||||||
|
unsigned char *buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + page_off;
|
||||||
|
|
||||||
|
if (host->cmd_flags & DATA_CARRY) {
|
||||||
|
while ((fifo_offset & 3) && length) {
|
||||||
|
buf[off++] = host->io_word & 0xff;
|
||||||
|
host->io_word >>= 8;
|
||||||
|
length--;
|
||||||
|
fifo_offset++;
|
||||||
|
}
|
||||||
|
if (!(fifo_offset & 3))
|
||||||
|
host->cmd_flags &= ~DATA_CARRY;
|
||||||
|
if (!length)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
host->io_word = readl(sock->addr + SOCK_FIFO_ACCESS
|
||||||
|
+ fifo_offset);
|
||||||
|
cnt = 4;
|
||||||
|
while (length && cnt) {
|
||||||
|
buf[off++] = (host->io_word >> 8) & 0xff;
|
||||||
|
cnt--;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
fifo_offset += 4 - cnt;
|
||||||
|
} while (length);
|
||||||
|
|
||||||
|
if (cnt)
|
||||||
|
host->cmd_flags |= DATA_CARRY;
|
||||||
|
|
||||||
|
kunmap_atomic(buf - page_off, KM_BIO_DST_IRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tifm_ms_write_fifo(struct tifm_ms *host, unsigned int fifo_offset,
|
||||||
|
struct page *pg, unsigned int page_off,
|
||||||
|
unsigned int length)
|
||||||
|
{
|
||||||
|
struct tifm_dev *sock = host->dev;
|
||||||
|
unsigned int cnt = 0, off = 0;
|
||||||
|
unsigned char *buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + page_off;
|
||||||
|
|
||||||
|
if (host->cmd_flags & DATA_CARRY) {
|
||||||
|
while (fifo_offset & 3) {
|
||||||
|
host->io_word |= buf[off++] << (8 * (fifo_offset & 3));
|
||||||
|
length--;
|
||||||
|
fifo_offset++;
|
||||||
|
}
|
||||||
|
if (!(fifo_offset & 3)) {
|
||||||
|
writel(host->io_word, sock->addr + SOCK_FIFO_ACCESS
|
||||||
|
+ fifo_offset - 4);
|
||||||
|
|
||||||
|
host->cmd_flags &= ~DATA_CARRY;
|
||||||
|
}
|
||||||
|
if (!length)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
cnt = 4;
|
||||||
|
host->io_word = 0;
|
||||||
|
while (length && cnt) {
|
||||||
|
host->io_word |= buf[off++] << (4 - cnt);
|
||||||
|
cnt--;
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
fifo_offset += 4 - cnt;
|
||||||
|
if (!cnt)
|
||||||
|
writel(host->io_word, sock->addr + SOCK_FIFO_ACCESS
|
||||||
|
+ fifo_offset - 4);
|
||||||
|
|
||||||
|
} while (length);
|
||||||
|
|
||||||
|
if (cnt)
|
||||||
|
host->cmd_flags |= DATA_CARRY;
|
||||||
|
|
||||||
|
kunmap_atomic(buf - page_off, KM_BIO_SRC_IRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tifm_ms_move_block(struct tifm_ms *host, unsigned int length)
|
||||||
|
{
|
||||||
|
unsigned int t_size;
|
||||||
|
unsigned int off = host->req->sg.offset + host->block_pos;
|
||||||
|
unsigned int p_off, p_cnt;
|
||||||
|
struct page *pg;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
dev_dbg(&host->dev->dev, "moving block\n");
|
||||||
|
local_irq_save(flags);
|
||||||
|
t_size = length;
|
||||||
|
while (t_size) {
|
||||||
|
pg = nth_page(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, t_size);
|
||||||
|
|
||||||
|
if (host->req->data_dir == WRITE)
|
||||||
|
tifm_ms_write_fifo(host, length - t_size,
|
||||||
|
pg, p_off, p_cnt);
|
||||||
|
else
|
||||||
|
tifm_ms_read_fifo(host, length - t_size,
|
||||||
|
pg, p_off, p_cnt);
|
||||||
|
|
||||||
|
t_size -= p_cnt;
|
||||||
|
}
|
||||||
|
local_irq_restore(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tifm_ms_transfer_data(struct tifm_ms *host, int skip)
|
||||||
|
{
|
||||||
|
struct tifm_dev *sock = host->dev;
|
||||||
|
unsigned int length = host->req->sg.length - host->block_pos;
|
||||||
|
|
||||||
|
if (!length)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (length > TIFM_FIFO_SIZE)
|
||||||
|
length = TIFM_FIFO_SIZE;
|
||||||
|
|
||||||
|
if (!skip) {
|
||||||
|
tifm_ms_move_block(host, length);
|
||||||
|
host->block_pos += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((host->req->data_dir == READ)
|
||||||
|
&& (host->block_pos == host->req->sg.length))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
writel(ilog2(length) - 2, sock->addr + SOCK_FIFO_PAGE_SIZE);
|
||||||
|
if (host->req->data_dir == WRITE)
|
||||||
|
writel((1 << 8) | TIFM_DMA_TX, sock->addr + SOCK_DMA_CONTROL);
|
||||||
|
else
|
||||||
|
writel((1 << 8), sock->addr + SOCK_DMA_CONTROL);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tifm_ms_issue_cmd(struct tifm_ms *host)
|
||||||
|
{
|
||||||
|
struct tifm_dev *sock = host->dev;
|
||||||
|
unsigned char *data;
|
||||||
|
unsigned int data_len = 0, cmd = 0, cmd_mask = 0, cnt, tval = 0;
|
||||||
|
|
||||||
|
host->cmd_flags = 0;
|
||||||
|
|
||||||
|
if (host->req->io_type == MEMSTICK_IO_SG) {
|
||||||
|
if (!host->no_dma) {
|
||||||
|
if (1 != tifm_map_sg(sock, &host->req->sg, 1,
|
||||||
|
host->req->data_dir == READ
|
||||||
|
? PCI_DMA_FROMDEVICE
|
||||||
|
: PCI_DMA_TODEVICE)) {
|
||||||
|
host->req->error = -ENOMEM;
|
||||||
|
return host->req->error;
|
||||||
|
}
|
||||||
|
data_len = sg_dma_len(&host->req->sg);
|
||||||
|
} else
|
||||||
|
data_len = host->req->sg.length;
|
||||||
|
|
||||||
|
writel(TIFM_FIFO_INT_SETALL,
|
||||||
|
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
|
||||||
|
writel(TIFM_FIFO_ENABLE,
|
||||||
|
sock->addr + SOCK_FIFO_CONTROL);
|
||||||
|
writel(TIFM_FIFO_INTMASK,
|
||||||
|
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
|
||||||
|
|
||||||
|
if (!host->no_dma) {
|
||||||
|
writel(ilog2(data_len) - 2,
|
||||||
|
sock->addr + SOCK_FIFO_PAGE_SIZE);
|
||||||
|
writel(sg_dma_address(&host->req->sg),
|
||||||
|
sock->addr + SOCK_DMA_ADDRESS);
|
||||||
|
if (host->req->data_dir == WRITE)
|
||||||
|
writel((1 << 8) | TIFM_DMA_TX | TIFM_DMA_EN,
|
||||||
|
sock->addr + SOCK_DMA_CONTROL);
|
||||||
|
else
|
||||||
|
writel((1 << 8) | TIFM_DMA_EN,
|
||||||
|
sock->addr + SOCK_DMA_CONTROL);
|
||||||
|
} else {
|
||||||
|
tifm_ms_transfer_data(host,
|
||||||
|
host->req->data_dir == READ);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_mask = readl(sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
cmd_mask |= TIFM_MS_SYS_DATA | TIFM_MS_SYS_NOT_RDY;
|
||||||
|
writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
} else if (host->req->io_type == MEMSTICK_IO_VAL) {
|
||||||
|
data = host->req->data;
|
||||||
|
data_len = host->req->data_len;
|
||||||
|
|
||||||
|
cmd_mask = host->mode_mask | 0x2607; /* unknown constant */
|
||||||
|
|
||||||
|
if (host->req->data_dir == WRITE) {
|
||||||
|
cmd_mask |= TIFM_MS_SYS_LATCH;
|
||||||
|
writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
for (cnt = 0; (data_len - cnt) >= 4; cnt += 4) {
|
||||||
|
writel(TIFM_MS_SYS_LATCH
|
||||||
|
| readl(sock->addr + SOCK_MS_SYSTEM),
|
||||||
|
sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
__raw_writel(*(unsigned int *)(data + cnt),
|
||||||
|
sock->addr + SOCK_MS_DATA);
|
||||||
|
dev_dbg(&sock->dev, "writing %x\n",
|
||||||
|
*(int *)(data + cnt));
|
||||||
|
}
|
||||||
|
switch (data_len - cnt) {
|
||||||
|
case 3:
|
||||||
|
tval |= data[cnt + 2] << 16;
|
||||||
|
case 2:
|
||||||
|
tval |= data[cnt + 1] << 8;
|
||||||
|
case 1:
|
||||||
|
tval |= data[cnt];
|
||||||
|
writel(TIFM_MS_SYS_LATCH
|
||||||
|
| readl(sock->addr + SOCK_MS_SYSTEM),
|
||||||
|
sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
writel(tval, sock->addr + SOCK_MS_DATA);
|
||||||
|
dev_dbg(&sock->dev, "writing %x\n", tval);
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(TIFM_MS_SYS_LATCH
|
||||||
|
| readl(sock->addr + SOCK_MS_SYSTEM),
|
||||||
|
sock + SOCK_MS_SYSTEM);
|
||||||
|
writel(0, sock->addr + SOCK_MS_DATA);
|
||||||
|
dev_dbg(&sock->dev, "writing %x\n", 0);
|
||||||
|
|
||||||
|
} else
|
||||||
|
writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
|
||||||
|
cmd_mask = readl(sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
cmd_mask &= ~TIFM_MS_SYS_DATA;
|
||||||
|
cmd_mask |= TIFM_MS_SYS_NOT_RDY;
|
||||||
|
dev_dbg(&sock->dev, "mask %x\n", cmd_mask);
|
||||||
|
writel(cmd_mask, sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
} else
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
mod_timer(&host->timer, jiffies + host->timeout_jiffies);
|
||||||
|
writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
|
||||||
|
sock->addr + SOCK_CONTROL);
|
||||||
|
host->req->error = 0;
|
||||||
|
|
||||||
|
cmd = (host->req->tpc & 0xf) << 12;
|
||||||
|
cmd |= data_len;
|
||||||
|
writel(cmd, sock->addr + SOCK_MS_COMMAND);
|
||||||
|
|
||||||
|
dev_dbg(&sock->dev, "executing TPC %x, %x\n", cmd, cmd_mask);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tifm_ms_complete_cmd(struct tifm_ms *host)
|
||||||
|
{
|
||||||
|
struct tifm_dev *sock = host->dev;
|
||||||
|
struct memstick_host *msh = tifm_get_drvdata(sock);
|
||||||
|
unsigned int tval = 0, data_len;
|
||||||
|
unsigned char *data;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
del_timer(&host->timer);
|
||||||
|
if (host->req->io_type == MEMSTICK_IO_SG) {
|
||||||
|
if (!host->no_dma)
|
||||||
|
tifm_unmap_sg(sock, &host->req->sg, 1,
|
||||||
|
host->req->data_dir == READ
|
||||||
|
? PCI_DMA_FROMDEVICE
|
||||||
|
: PCI_DMA_TODEVICE);
|
||||||
|
} else if (host->req->io_type == MEMSTICK_IO_VAL) {
|
||||||
|
writel(~TIFM_MS_SYS_DATA & readl(sock->addr + SOCK_MS_SYSTEM),
|
||||||
|
sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
|
||||||
|
data = host->req->data;
|
||||||
|
data_len = host->req->data_len;
|
||||||
|
|
||||||
|
if (host->req->data_dir == READ) {
|
||||||
|
for (rc = 0; (data_len - rc) >= 4; rc += 4)
|
||||||
|
*(int *)(data + rc)
|
||||||
|
= __raw_readl(sock->addr
|
||||||
|
+ SOCK_MS_DATA);
|
||||||
|
|
||||||
|
if (data_len - rc)
|
||||||
|
tval = readl(sock->addr + SOCK_MS_DATA);
|
||||||
|
switch (data_len - rc) {
|
||||||
|
case 3:
|
||||||
|
data[rc + 2] = (tval >> 16) & 0xff;
|
||||||
|
case 2:
|
||||||
|
data[rc + 1] = (tval >> 8) & 0xff;
|
||||||
|
case 1:
|
||||||
|
data[rc] = tval & 0xff;
|
||||||
|
}
|
||||||
|
readl(sock->addr + SOCK_MS_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL),
|
||||||
|
sock->addr + SOCK_CONTROL);
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = memstick_next_req(msh, &host->req);
|
||||||
|
} while (!rc && tifm_ms_issue_cmd(host));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tifm_ms_check_status(struct tifm_ms *host)
|
||||||
|
{
|
||||||
|
if (!host->req->error) {
|
||||||
|
if (!(host->cmd_flags & CMD_READY))
|
||||||
|
return 1;
|
||||||
|
if ((host->req->io_type == MEMSTICK_IO_SG)
|
||||||
|
&& !(host->cmd_flags & FIFO_READY))
|
||||||
|
return 1;
|
||||||
|
if (host->req->need_card_int
|
||||||
|
&& !(host->cmd_flags & CARD_READY))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called from interrupt handler */
|
||||||
|
static void tifm_ms_data_event(struct tifm_dev *sock)
|
||||||
|
{
|
||||||
|
struct tifm_ms *host;
|
||||||
|
unsigned int fifo_status = 0;
|
||||||
|
int rc = 1;
|
||||||
|
|
||||||
|
spin_lock(&sock->lock);
|
||||||
|
host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
|
||||||
|
fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
|
||||||
|
dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n",
|
||||||
|
fifo_status, host->cmd_flags);
|
||||||
|
|
||||||
|
if (host->req) {
|
||||||
|
if (fifo_status & TIFM_FIFO_READY) {
|
||||||
|
if (!host->no_dma || tifm_ms_transfer_data(host, 0)) {
|
||||||
|
host->cmd_flags |= FIFO_READY;
|
||||||
|
rc = tifm_ms_check_status(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
|
||||||
|
if (!rc)
|
||||||
|
tifm_ms_complete_cmd(host);
|
||||||
|
|
||||||
|
spin_unlock(&sock->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Called from interrupt handler */
|
||||||
|
static void tifm_ms_card_event(struct tifm_dev *sock)
|
||||||
|
{
|
||||||
|
struct tifm_ms *host;
|
||||||
|
unsigned int host_status = 0;
|
||||||
|
int rc = 1;
|
||||||
|
|
||||||
|
spin_lock(&sock->lock);
|
||||||
|
host = memstick_priv((struct memstick_host *)tifm_get_drvdata(sock));
|
||||||
|
host_status = readl(sock->addr + SOCK_MS_STATUS);
|
||||||
|
dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",
|
||||||
|
host_status, host->cmd_flags);
|
||||||
|
|
||||||
|
if (host->req) {
|
||||||
|
if (host_status & TIFM_MS_TIMEOUT)
|
||||||
|
host->req->error = -ETIME;
|
||||||
|
else if (host_status & TIFM_MS_BADCRC)
|
||||||
|
host->req->error = -EILSEQ;
|
||||||
|
|
||||||
|
if (host->req->error) {
|
||||||
|
writel(TIFM_FIFO_INT_SETALL,
|
||||||
|
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
|
||||||
|
writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host_status & TIFM_MS_EOTPC)
|
||||||
|
host->cmd_flags |= CMD_READY;
|
||||||
|
if (host_status & TIFM_MS_INT)
|
||||||
|
host->cmd_flags |= CARD_READY;
|
||||||
|
|
||||||
|
rc = tifm_ms_check_status(host);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(TIFM_MS_SYS_NOT_RDY | readl(sock->addr + SOCK_MS_SYSTEM),
|
||||||
|
sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
writel((~TIFM_MS_SYS_DATA) & readl(sock->addr + SOCK_MS_SYSTEM),
|
||||||
|
sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
tifm_ms_complete_cmd(host);
|
||||||
|
|
||||||
|
spin_unlock(&sock->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tifm_ms_request(struct memstick_host *msh)
|
||||||
|
{
|
||||||
|
struct tifm_ms *host = memstick_priv(msh);
|
||||||
|
struct tifm_dev *sock = host->dev;
|
||||||
|
unsigned long flags;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sock->lock, flags);
|
||||||
|
if (host->req) {
|
||||||
|
printk(KERN_ERR "%s : unfinished request detected\n",
|
||||||
|
sock->dev.bus_id);
|
||||||
|
spin_unlock_irqrestore(&sock->lock, flags);
|
||||||
|
tifm_eject(host->dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host->eject) {
|
||||||
|
do {
|
||||||
|
rc = memstick_next_req(msh, &host->req);
|
||||||
|
if (!rc)
|
||||||
|
host->req->error = -ETIME;
|
||||||
|
} while (!rc);
|
||||||
|
spin_unlock_irqrestore(&sock->lock, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = memstick_next_req(msh, &host->req);
|
||||||
|
} while (!rc && tifm_ms_issue_cmd(host));
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&sock->lock, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tifm_ms_set_param(struct memstick_host *msh,
|
||||||
|
enum memstick_param param,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
struct tifm_ms *host = memstick_priv(msh);
|
||||||
|
struct tifm_dev *sock = host->dev;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sock->lock, flags);
|
||||||
|
|
||||||
|
switch (param) {
|
||||||
|
case MEMSTICK_POWER:
|
||||||
|
/* this is set by card detection mechanism */
|
||||||
|
break;
|
||||||
|
case MEMSTICK_INTERFACE:
|
||||||
|
if (value == MEMSTICK_SERIAL) {
|
||||||
|
host->mode_mask = TIFM_MS_SERIAL;
|
||||||
|
writel((~TIFM_CTRL_FAST_CLK)
|
||||||
|
& readl(sock->addr + SOCK_CONTROL),
|
||||||
|
sock->addr + SOCK_CONTROL);
|
||||||
|
} else if (value == MEMSTICK_PARALLEL) {
|
||||||
|
host->mode_mask = 0;
|
||||||
|
writel(TIFM_CTRL_FAST_CLK
|
||||||
|
| readl(sock->addr + SOCK_CONTROL),
|
||||||
|
sock->addr + SOCK_CONTROL);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&sock->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tifm_ms_abort(unsigned long data)
|
||||||
|
{
|
||||||
|
struct tifm_ms *host = (struct tifm_ms *)data;
|
||||||
|
|
||||||
|
dev_dbg(&host->dev->dev, "status %x\n",
|
||||||
|
readl(host->dev->addr + SOCK_MS_STATUS));
|
||||||
|
printk(KERN_ERR
|
||||||
|
"%s : card failed to respond for a long period of time "
|
||||||
|
"(%x, %x)\n",
|
||||||
|
host->dev->dev.bus_id, host->req ? host->req->tpc : 0,
|
||||||
|
host->cmd_flags);
|
||||||
|
|
||||||
|
tifm_eject(host->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tifm_ms_initialize_host(struct tifm_ms *host)
|
||||||
|
{
|
||||||
|
struct tifm_dev *sock = host->dev;
|
||||||
|
struct memstick_host *msh = tifm_get_drvdata(sock);
|
||||||
|
|
||||||
|
host->mode_mask = TIFM_MS_SERIAL;
|
||||||
|
writel(0x8000, sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
writel(0x0200 | TIFM_MS_SYS_NOT_RDY, sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
|
||||||
|
if (tifm_has_ms_pif(sock))
|
||||||
|
msh->caps |= MEMSTICK_CAP_PARALLEL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tifm_ms_probe(struct tifm_dev *sock)
|
||||||
|
{
|
||||||
|
struct memstick_host *msh;
|
||||||
|
struct tifm_ms *host;
|
||||||
|
int rc = -EIO;
|
||||||
|
|
||||||
|
if (!(TIFM_SOCK_STATE_OCCUPIED
|
||||||
|
& readl(sock->addr + SOCK_PRESENT_STATE))) {
|
||||||
|
printk(KERN_WARNING "%s : card gone, unexpectedly\n",
|
||||||
|
sock->dev.bus_id);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
msh = memstick_alloc_host(sizeof(struct tifm_ms), &sock->dev);
|
||||||
|
if (!msh)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
host = memstick_priv(msh);
|
||||||
|
tifm_set_drvdata(sock, msh);
|
||||||
|
host->dev = sock;
|
||||||
|
host->timeout_jiffies = msecs_to_jiffies(1000);
|
||||||
|
host->no_dma = no_dma;
|
||||||
|
|
||||||
|
setup_timer(&host->timer, tifm_ms_abort, (unsigned long)host);
|
||||||
|
|
||||||
|
msh->request = tifm_ms_request;
|
||||||
|
msh->set_param = tifm_ms_set_param;
|
||||||
|
sock->card_event = tifm_ms_card_event;
|
||||||
|
sock->data_event = tifm_ms_data_event;
|
||||||
|
rc = tifm_ms_initialize_host(host);
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
rc = memstick_add_host(msh);
|
||||||
|
if (!rc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memstick_free_host(msh);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tifm_ms_remove(struct tifm_dev *sock)
|
||||||
|
{
|
||||||
|
struct memstick_host *msh = tifm_get_drvdata(sock);
|
||||||
|
struct tifm_ms *host = memstick_priv(msh);
|
||||||
|
int rc = 0;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sock->lock, flags);
|
||||||
|
host->eject = 1;
|
||||||
|
if (host->req) {
|
||||||
|
del_timer(&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->req->io_type == MEMSTICK_IO_SG) && !host->no_dma)
|
||||||
|
tifm_unmap_sg(sock, &host->req->sg, 1,
|
||||||
|
host->req->data_dir == READ
|
||||||
|
? PCI_DMA_TODEVICE
|
||||||
|
: PCI_DMA_FROMDEVICE);
|
||||||
|
host->req->error = -ETIME;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rc = memstick_next_req(msh, &host->req);
|
||||||
|
if (!rc)
|
||||||
|
host->req->error = -ETIME;
|
||||||
|
} while (!rc);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&sock->lock, flags);
|
||||||
|
|
||||||
|
memstick_remove_host(msh);
|
||||||
|
|
||||||
|
writel(0x0200 | TIFM_MS_SYS_NOT_RDY, sock->addr + SOCK_MS_SYSTEM);
|
||||||
|
writel(0xffffffff, sock->addr + SOCK_MS_STATUS);
|
||||||
|
|
||||||
|
memstick_free_host(msh);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
|
||||||
|
static int tifm_ms_suspend(struct tifm_dev *sock, pm_message_t state)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tifm_ms_resume(struct tifm_dev *sock)
|
||||||
|
{
|
||||||
|
struct memstick_host *msh = tifm_get_drvdata(sock);
|
||||||
|
struct tifm_ms *host = memstick_priv(msh);
|
||||||
|
|
||||||
|
tifm_ms_initialize_host(host);
|
||||||
|
memstick_detect_change(msh);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define tifm_ms_suspend NULL
|
||||||
|
#define tifm_ms_resume NULL
|
||||||
|
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
static struct tifm_device_id tifm_ms_id_tbl[] = {
|
||||||
|
{ TIFM_TYPE_MS }, { 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct tifm_driver tifm_ms_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = DRIVER_NAME,
|
||||||
|
.owner = THIS_MODULE
|
||||||
|
},
|
||||||
|
.id_table = tifm_ms_id_tbl,
|
||||||
|
.probe = tifm_ms_probe,
|
||||||
|
.remove = tifm_ms_remove,
|
||||||
|
.suspend = tifm_ms_suspend,
|
||||||
|
.resume = tifm_ms_resume
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init tifm_ms_init(void)
|
||||||
|
{
|
||||||
|
return tifm_register_driver(&tifm_ms_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit tifm_ms_exit(void)
|
||||||
|
{
|
||||||
|
tifm_unregister_driver(&tifm_ms_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Alex Dubov");
|
||||||
|
MODULE_DESCRIPTION("TI FlashMedia MemoryStick driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_DEVICE_TABLE(tifm, tifm_ms_id_tbl);
|
||||||
|
MODULE_VERSION(DRIVER_VERSION);
|
||||||
|
|
||||||
|
module_init(tifm_ms_init);
|
||||||
|
module_exit(tifm_ms_exit);
|
|
@ -302,6 +302,21 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
|
||||||
|
|
||||||
#endif /* CONFIG_PM */
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
|
static int tifm_7xx1_dummy_has_ms_pif(struct tifm_adapter *fm,
|
||||||
|
struct tifm_dev *sock)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tifm_7xx1_has_ms_pif(struct tifm_adapter *fm, struct tifm_dev *sock)
|
||||||
|
{
|
||||||
|
if (((fm->num_sockets == 4) && (sock->socket_id == 2))
|
||||||
|
|| ((fm->num_sockets == 2) && (sock->socket_id == 0)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int tifm_7xx1_probe(struct pci_dev *dev,
|
static int tifm_7xx1_probe(struct pci_dev *dev,
|
||||||
const struct pci_device_id *dev_id)
|
const struct pci_device_id *dev_id)
|
||||||
{
|
{
|
||||||
|
@ -336,6 +351,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
|
||||||
|
|
||||||
INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
|
INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media);
|
||||||
fm->eject = tifm_7xx1_eject;
|
fm->eject = tifm_7xx1_eject;
|
||||||
|
fm->has_ms_pif = tifm_7xx1_has_ms_pif;
|
||||||
pci_set_drvdata(dev, fm);
|
pci_set_drvdata(dev, fm);
|
||||||
|
|
||||||
fm->addr = ioremap(pci_resource_start(dev, 0),
|
fm->addr = ioremap(pci_resource_start(dev, 0),
|
||||||
|
@ -377,6 +393,7 @@ static void tifm_7xx1_remove(struct pci_dev *dev)
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
fm->eject = tifm_7xx1_dummy_eject;
|
fm->eject = tifm_7xx1_dummy_eject;
|
||||||
|
fm->has_ms_pif = tifm_7xx1_dummy_has_ms_pif;
|
||||||
writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
|
writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
|
||||||
mmiowb();
|
mmiowb();
|
||||||
free_irq(dev->irq, fm);
|
free_irq(dev->irq, fm);
|
||||||
|
|
|
@ -284,6 +284,13 @@ void tifm_eject(struct tifm_dev *sock)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tifm_eject);
|
EXPORT_SYMBOL(tifm_eject);
|
||||||
|
|
||||||
|
int tifm_has_ms_pif(struct tifm_dev *sock)
|
||||||
|
{
|
||||||
|
struct tifm_adapter *fm = dev_get_drvdata(sock->dev.parent);
|
||||||
|
return fm->has_ms_pif(fm, sock);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(tifm_has_ms_pif);
|
||||||
|
|
||||||
int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
|
int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
|
||||||
int direction)
|
int direction)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,299 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MEMSTICK_H
|
||||||
|
#define _MEMSTICK_H
|
||||||
|
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
|
||||||
|
/*** Hardware based structures ***/
|
||||||
|
|
||||||
|
struct ms_status_register {
|
||||||
|
unsigned char reserved;
|
||||||
|
unsigned char interrupt;
|
||||||
|
#define MEMSTICK_INT_CMDNAK 0x0001
|
||||||
|
#define MEMSTICK_INT_BREQ 0x0020
|
||||||
|
#define MEMSTICK_INT_ERR 0x0040
|
||||||
|
#define MEMSTICK_INT_CED 0x0080
|
||||||
|
|
||||||
|
unsigned char status0;
|
||||||
|
#define MEMSTICK_STATUS0_WP 0x0001
|
||||||
|
#define MEMSTICK_STATUS0_SL 0x0002
|
||||||
|
#define MEMSTICK_STATUS0_BF 0x0010
|
||||||
|
#define MEMSTICK_STATUS0_BE 0x0020
|
||||||
|
#define MEMSTICK_STATUS0_FB0 0x0040
|
||||||
|
#define MEMSTICK_STATUS0_MB 0x0080
|
||||||
|
|
||||||
|
unsigned char status1;
|
||||||
|
#define MEMSTICK_STATUS1_UCFG 0x0001
|
||||||
|
#define MEMSTICK_STATUS1_FGER 0x0002
|
||||||
|
#define MEMSTICK_STATUS1_UCEX 0x0004
|
||||||
|
#define MEMSTICK_STATUS1_EXER 0x0008
|
||||||
|
#define MEMSTICK_STATUS1_UCDT 0x0010
|
||||||
|
#define MEMSTICK_STATUS1_DTER 0x0020
|
||||||
|
#define MEMSTICK_STATUS1_FBI 0x0040
|
||||||
|
#define MEMSTICK_STATUS1_MB 0x0080
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct ms_id_register {
|
||||||
|
unsigned char type;
|
||||||
|
unsigned char reserved;
|
||||||
|
unsigned char category;
|
||||||
|
unsigned char class;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct ms_param_register {
|
||||||
|
unsigned char system;
|
||||||
|
unsigned char block_address_msb;
|
||||||
|
unsigned short block_address;
|
||||||
|
unsigned char cp;
|
||||||
|
#define MEMSTICK_CP_BLOCK 0x0000
|
||||||
|
#define MEMSTICK_CP_PAGE 0x0020
|
||||||
|
#define MEMSTICK_CP_EXTRA 0x0040
|
||||||
|
#define MEMSTICK_CP_OVERWRITE 0x0080
|
||||||
|
|
||||||
|
unsigned char page_address;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct ms_extra_data_register {
|
||||||
|
unsigned char overwrite_flag;
|
||||||
|
#define MEMSTICK_OVERWRITE_UPDATA 0x0010
|
||||||
|
#define MEMSTICK_OVERWRITE_PAGE 0x0060
|
||||||
|
#define MEMSTICK_OVERWRITE_BLOCK 0x0080
|
||||||
|
|
||||||
|
unsigned char management_flag;
|
||||||
|
#define MEMSTICK_MANAGEMENT_SYSTEM 0x0004
|
||||||
|
#define MEMSTICK_MANAGEMENT_TRANS_TABLE 0x0008
|
||||||
|
#define MEMSTICK_MANAGEMENT_COPY 0x0010
|
||||||
|
#define MEMSTICK_MANAGEMENT_ACCESS 0x0020
|
||||||
|
|
||||||
|
unsigned short logical_address;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct ms_register {
|
||||||
|
struct ms_status_register status;
|
||||||
|
struct ms_id_register id;
|
||||||
|
unsigned char reserved[8];
|
||||||
|
struct ms_param_register param;
|
||||||
|
struct ms_extra_data_register extra_data;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct mspro_param_register {
|
||||||
|
unsigned char system;
|
||||||
|
unsigned short data_count;
|
||||||
|
unsigned int data_address;
|
||||||
|
unsigned char cmd_param;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct mspro_register {
|
||||||
|
struct ms_status_register status;
|
||||||
|
struct ms_id_register id;
|
||||||
|
unsigned char reserved[8];
|
||||||
|
struct mspro_param_register param;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct ms_register_addr {
|
||||||
|
unsigned char r_offset;
|
||||||
|
unsigned char r_length;
|
||||||
|
unsigned char w_offset;
|
||||||
|
unsigned char w_length;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MS_TPC_READ_LONG_DATA = 0x02,
|
||||||
|
MS_TPC_READ_SHORT_DATA = 0x03,
|
||||||
|
MS_TPC_READ_REG = 0x04,
|
||||||
|
MS_TPC_READ_IO_DATA = 0x05, /* unverified */
|
||||||
|
MS_TPC_GET_INT = 0x07,
|
||||||
|
MS_TPC_SET_RW_REG_ADRS = 0x08,
|
||||||
|
MS_TPC_EX_SET_CMD = 0x09,
|
||||||
|
MS_TPC_WRITE_IO_DATA = 0x0a, /* unverified */
|
||||||
|
MS_TPC_WRITE_REG = 0x0b,
|
||||||
|
MS_TPC_WRITE_SHORT_DATA = 0x0c,
|
||||||
|
MS_TPC_WRITE_LONG_DATA = 0x0d,
|
||||||
|
MS_TPC_SET_CMD = 0x0e
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MS_CMD_BLOCK_END = 0x33,
|
||||||
|
MS_CMD_RESET = 0x3c,
|
||||||
|
MS_CMD_BLOCK_WRITE = 0x55,
|
||||||
|
MS_CMD_SLEEP = 0x5a,
|
||||||
|
MS_CMD_BLOCK_ERASE = 0x99,
|
||||||
|
MS_CMD_BLOCK_READ = 0xaa,
|
||||||
|
MS_CMD_CLEAR_BUF = 0xc3,
|
||||||
|
MS_CMD_FLASH_STOP = 0xcc,
|
||||||
|
MSPRO_CMD_FORMAT = 0x10,
|
||||||
|
MSPRO_CMD_SLEEP = 0x11,
|
||||||
|
MSPRO_CMD_READ_DATA = 0x20,
|
||||||
|
MSPRO_CMD_WRITE_DATA = 0x21,
|
||||||
|
MSPRO_CMD_READ_ATRB = 0x24,
|
||||||
|
MSPRO_CMD_STOP = 0x25,
|
||||||
|
MSPRO_CMD_ERASE = 0x26,
|
||||||
|
MSPRO_CMD_SET_IBA = 0x46,
|
||||||
|
MSPRO_CMD_SET_IBD = 0x47
|
||||||
|
/*
|
||||||
|
MSPRO_CMD_RESET
|
||||||
|
MSPRO_CMD_WAKEUP
|
||||||
|
MSPRO_CMD_IN_IO_DATA
|
||||||
|
MSPRO_CMD_OUT_IO_DATA
|
||||||
|
MSPRO_CMD_READ_IO_ATRB
|
||||||
|
MSPRO_CMD_IN_IO_FIFO
|
||||||
|
MSPRO_CMD_OUT_IO_FIFO
|
||||||
|
MSPRO_CMD_IN_IOM
|
||||||
|
MSPRO_CMD_OUT_IOM
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
/*** Driver structures and functions ***/
|
||||||
|
|
||||||
|
#define MEMSTICK_PART_SHIFT 3
|
||||||
|
|
||||||
|
enum memstick_param { MEMSTICK_POWER = 1, MEMSTICK_INTERFACE };
|
||||||
|
|
||||||
|
#define MEMSTICK_POWER_OFF 0
|
||||||
|
#define MEMSTICK_POWER_ON 1
|
||||||
|
|
||||||
|
#define MEMSTICK_SERIAL 0
|
||||||
|
#define MEMSTICK_PARALLEL 1
|
||||||
|
|
||||||
|
struct memstick_host;
|
||||||
|
struct memstick_driver;
|
||||||
|
|
||||||
|
#define MEMSTICK_MATCH_ALL 0x01
|
||||||
|
|
||||||
|
#define MEMSTICK_TYPE_LEGACY 0xff
|
||||||
|
#define MEMSTICK_TYPE_DUO 0x00
|
||||||
|
#define MEMSTICK_TYPE_PRO 0x01
|
||||||
|
|
||||||
|
#define MEMSTICK_CATEGORY_STORAGE 0xff
|
||||||
|
#define MEMSTICK_CATEGORY_STORAGE_DUO 0x00
|
||||||
|
|
||||||
|
#define MEMSTICK_CLASS_GENERIC 0xff
|
||||||
|
#define MEMSTICK_CLASS_GENERIC_DUO 0x00
|
||||||
|
|
||||||
|
|
||||||
|
struct memstick_device_id {
|
||||||
|
unsigned char match_flags;
|
||||||
|
unsigned char type;
|
||||||
|
unsigned char category;
|
||||||
|
unsigned char class;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct memstick_request {
|
||||||
|
unsigned char tpc;
|
||||||
|
unsigned char data_dir:1,
|
||||||
|
need_card_int:1,
|
||||||
|
get_int_reg:1,
|
||||||
|
io_type:2;
|
||||||
|
#define MEMSTICK_IO_NONE 0
|
||||||
|
#define MEMSTICK_IO_VAL 1
|
||||||
|
#define MEMSTICK_IO_SG 2
|
||||||
|
|
||||||
|
unsigned char int_reg;
|
||||||
|
int error;
|
||||||
|
union {
|
||||||
|
struct scatterlist sg;
|
||||||
|
struct {
|
||||||
|
unsigned char data_len;
|
||||||
|
unsigned char data[15];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct memstick_dev {
|
||||||
|
struct memstick_device_id id;
|
||||||
|
struct memstick_host *host;
|
||||||
|
struct ms_register_addr reg_addr;
|
||||||
|
struct completion mrq_complete;
|
||||||
|
struct memstick_request current_mrq;
|
||||||
|
|
||||||
|
/* Check that media driver is still willing to operate the device. */
|
||||||
|
int (*check)(struct memstick_dev *card);
|
||||||
|
/* Get next request from the media driver. */
|
||||||
|
int (*next_request)(struct memstick_dev *card,
|
||||||
|
struct memstick_request **mrq);
|
||||||
|
|
||||||
|
struct device dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct memstick_host {
|
||||||
|
struct mutex lock;
|
||||||
|
unsigned int id;
|
||||||
|
unsigned int caps;
|
||||||
|
#define MEMSTICK_CAP_PARALLEL 1
|
||||||
|
#define MEMSTICK_CAP_AUTO_GET_INT 2
|
||||||
|
|
||||||
|
struct work_struct media_checker;
|
||||||
|
struct class_device cdev;
|
||||||
|
|
||||||
|
struct memstick_dev *card;
|
||||||
|
unsigned int retries;
|
||||||
|
|
||||||
|
/* Notify the host that some requests are pending. */
|
||||||
|
void (*request)(struct memstick_host *host);
|
||||||
|
/* Set host IO parameters (power, clock, etc). */
|
||||||
|
void (*set_param)(struct memstick_host *host,
|
||||||
|
enum memstick_param param,
|
||||||
|
int value);
|
||||||
|
unsigned long private[0] ____cacheline_aligned;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct memstick_driver {
|
||||||
|
struct memstick_device_id *id_table;
|
||||||
|
int (*probe)(struct memstick_dev *card);
|
||||||
|
void (*remove)(struct memstick_dev *card);
|
||||||
|
int (*suspend)(struct memstick_dev *card,
|
||||||
|
pm_message_t state);
|
||||||
|
int (*resume)(struct memstick_dev *card);
|
||||||
|
|
||||||
|
struct device_driver driver;
|
||||||
|
};
|
||||||
|
|
||||||
|
int memstick_register_driver(struct memstick_driver *drv);
|
||||||
|
void memstick_unregister_driver(struct memstick_driver *drv);
|
||||||
|
|
||||||
|
struct memstick_host *memstick_alloc_host(unsigned int extra,
|
||||||
|
struct device *dev);
|
||||||
|
|
||||||
|
int memstick_add_host(struct memstick_host *host);
|
||||||
|
void memstick_remove_host(struct memstick_host *host);
|
||||||
|
void memstick_free_host(struct memstick_host *host);
|
||||||
|
void memstick_detect_change(struct memstick_host *host);
|
||||||
|
|
||||||
|
void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc,
|
||||||
|
struct scatterlist *sg);
|
||||||
|
void memstick_init_req(struct memstick_request *mrq, unsigned char tpc,
|
||||||
|
void *buf, size_t length);
|
||||||
|
int memstick_next_req(struct memstick_host *host,
|
||||||
|
struct memstick_request **mrq);
|
||||||
|
void memstick_new_req(struct memstick_host *host);
|
||||||
|
|
||||||
|
int memstick_set_rw_addr(struct memstick_dev *card);
|
||||||
|
|
||||||
|
static inline void *memstick_priv(struct memstick_host *host)
|
||||||
|
{
|
||||||
|
return (void *)host->private;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *memstick_get_drvdata(struct memstick_dev *card)
|
||||||
|
{
|
||||||
|
return dev_get_drvdata(&card->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void memstick_set_drvdata(struct memstick_dev *card, void *data)
|
||||||
|
{
|
||||||
|
dev_set_drvdata(&card->dev, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -72,6 +72,7 @@ enum {
|
||||||
#define TIFM_FIFO_READY 0x00000001
|
#define TIFM_FIFO_READY 0x00000001
|
||||||
#define TIFM_FIFO_INT_SETALL 0x0000ffff
|
#define TIFM_FIFO_INT_SETALL 0x0000ffff
|
||||||
#define TIFM_FIFO_INTMASK 0x00000005
|
#define TIFM_FIFO_INTMASK 0x00000005
|
||||||
|
#define TIFM_FIFO_SIZE 0x00000200
|
||||||
|
|
||||||
#define TIFM_DMA_RESET 0x00000002
|
#define TIFM_DMA_RESET 0x00000002
|
||||||
#define TIFM_DMA_TX 0x00008000
|
#define TIFM_DMA_TX 0x00008000
|
||||||
|
@ -124,6 +125,8 @@ struct tifm_adapter {
|
||||||
|
|
||||||
void (*eject)(struct tifm_adapter *fm,
|
void (*eject)(struct tifm_adapter *fm,
|
||||||
struct tifm_dev *sock);
|
struct tifm_dev *sock);
|
||||||
|
int (*has_ms_pif)(struct tifm_adapter *fm,
|
||||||
|
struct tifm_dev *sock);
|
||||||
|
|
||||||
struct tifm_dev *sockets[0];
|
struct tifm_dev *sockets[0];
|
||||||
};
|
};
|
||||||
|
@ -141,6 +144,7 @@ struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id,
|
||||||
int tifm_register_driver(struct tifm_driver *drv);
|
int tifm_register_driver(struct tifm_driver *drv);
|
||||||
void tifm_unregister_driver(struct tifm_driver *drv);
|
void tifm_unregister_driver(struct tifm_driver *drv);
|
||||||
void tifm_eject(struct tifm_dev *sock);
|
void tifm_eject(struct tifm_dev *sock);
|
||||||
|
int tifm_has_ms_pif(struct tifm_dev *sock);
|
||||||
int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
|
int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
|
||||||
int direction);
|
int direction);
|
||||||
void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
|
void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents,
|
||||||
|
|
Loading…
Reference in New Issue