From 98ccf14909ba02a41c5925b0b2c92aeeef23d3b9 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 12 May 2007 00:26:16 +0200 Subject: mmc: bounce requests for simple hosts Some hosts cannot do scatter/gather in hardware. Since not doing sg is such a big performance hit, we (optionally) bounce the requests to a simple linear buffer that we hand over to the driver. Signed-off-by: Pierre Ossman --- drivers/mmc/card/Kconfig | 18 +++++ drivers/mmc/card/block.c | 7 +- drivers/mmc/card/queue.c | 191 +++++++++++++++++++++++++++++++++++++++++++---- drivers/mmc/card/queue.h | 7 ++ 4 files changed, 208 insertions(+), 15 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index 9320a8c7323..a49cb9737cd 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -14,3 +14,21 @@ config MMC_BLOCK mount the filesystem. Almost everyone wishing MMC support should say Y or M here. +config MMC_BLOCK_BOUNCE + bool "Use bounce buffer for simple hosts" + depends on MMC_BLOCK + default y + help + SD/MMC is a high latency protocol where it is crucial to + send large requests in order to get high performance. Many + controllers, however, are restricted to continuous memory + (i.e. they can't do scatter-gather), something the kernel + rarely can provide. + + Say Y here to help these restricted hosts by bouncing + requests back and forth from a large buffer. You will get + a big performance gain at the cost of up to 64 KiB of + physical memory. + + If unsure, say Y here. + diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 540ff4bea54..cbd4b6e3e17 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -262,7 +262,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } brq.data.sg = mq->sg; - brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); + brq.data.sg_len = mmc_queue_map_sg(mq); + + mmc_queue_bounce_pre(mq); if (brq.data.blocks != (req->nr_sectors >> (md->block_bits - 9))) { @@ -279,6 +281,9 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } mmc_wait_for_req(card->host, &brq.mrq); + + mmc_queue_bounce_post(mq); + if (brq.cmd.error) { printk(KERN_ERR "%s: error %d sending read/write command\n", req->rq_disk->disk_name, brq.cmd.error); diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index dd97bc79840..4fb2089dc69 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -17,6 +17,8 @@ #include #include "queue.h" +#define MMC_QUEUE_BOUNCESZ 65536 + #define MMC_QUEUE_SUSPENDED (1 << 0) /* @@ -118,6 +120,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock struct mmc_host *host = card->host; u64 limit = BLK_BOUNCE_HIGH; int ret; + unsigned int bouncesz; if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) limit = *mmc_dev(host)->dma_mask; @@ -127,21 +130,61 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock if (!mq->queue) return -ENOMEM; - blk_queue_prep_rq(mq->queue, mmc_prep_request); - blk_queue_bounce_limit(mq->queue, limit); - blk_queue_max_sectors(mq->queue, host->max_req_size / 512); - blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); - blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); - blk_queue_max_segment_size(mq->queue, host->max_seg_size); - mq->queue->queuedata = mq; mq->req = NULL; - mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs, - GFP_KERNEL); - if (!mq->sg) { - ret = -ENOMEM; - goto cleanup_queue; + blk_queue_prep_rq(mq->queue, mmc_prep_request); + +#ifdef CONFIG_MMC_BLOCK_BOUNCE + if (host->max_hw_segs == 1) { + bouncesz = MMC_QUEUE_BOUNCESZ; + + if (bouncesz > host->max_req_size) + bouncesz = host->max_req_size; + if (bouncesz > host->max_seg_size) + bouncesz = host->max_seg_size; + + mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); + if (!mq->bounce_buf) { + printk(KERN_WARNING "%s: unable to allocate " + "bounce buffer\n", mmc_card_name(card)); + } else { + blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH); + blk_queue_max_sectors(mq->queue, bouncesz / 512); + blk_queue_max_phys_segments(mq->queue, bouncesz / 512); + blk_queue_max_hw_segments(mq->queue, bouncesz / 512); + blk_queue_max_segment_size(mq->queue, bouncesz); + + mq->sg = kmalloc(sizeof(struct scatterlist), + GFP_KERNEL); + if (!mq->sg) { + ret = -ENOMEM; + goto free_bounce_buf; + } + + mq->bounce_sg = kmalloc(sizeof(struct scatterlist) * + bouncesz / 512, GFP_KERNEL); + if (!mq->bounce_sg) { + ret = -ENOMEM; + goto free_sg; + } + } + } +#endif + + if (!mq->bounce_buf) { + blk_queue_bounce_limit(mq->queue, limit); + blk_queue_max_sectors(mq->queue, host->max_req_size / 512); + blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); + blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); + blk_queue_max_segment_size(mq->queue, host->max_seg_size); + + mq->sg = kmalloc(sizeof(struct scatterlist) * + host->max_phys_segs, GFP_KERNEL); + if (!mq->sg) { + ret = -ENOMEM; + goto cleanup_queue; + } } init_MUTEX(&mq->thread_sem); @@ -149,14 +192,21 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd"); if (IS_ERR(mq->thread)) { ret = PTR_ERR(mq->thread); - goto free_sg; + goto free_bounce_sg; } return 0; - + free_bounce_sg: + if (mq->bounce_sg) + kfree(mq->bounce_sg); + mq->bounce_sg = NULL; free_sg: kfree(mq->sg); mq->sg = NULL; + free_bounce_buf: + if (mq->bounce_buf) + kfree(mq->bounce_buf); + mq->bounce_buf = NULL; cleanup_queue: blk_cleanup_queue(mq->queue); return ret; @@ -178,9 +228,17 @@ void mmc_cleanup_queue(struct mmc_queue *mq) /* Then terminate our worker thread */ kthread_stop(mq->thread); + if (mq->bounce_sg) + kfree(mq->bounce_sg); + mq->bounce_sg = NULL; + kfree(mq->sg); mq->sg = NULL; + if (mq->bounce_buf) + kfree(mq->bounce_buf); + mq->bounce_buf = NULL; + blk_cleanup_queue(mq->queue); mq->card = NULL; @@ -231,3 +289,108 @@ void mmc_queue_resume(struct mmc_queue *mq) } } +static void copy_sg(struct scatterlist *dst, unsigned int dst_len, + struct scatterlist *src, unsigned int src_len) +{ + unsigned int chunk; + char *dst_buf, *src_buf; + unsigned int dst_size, src_size; + + dst_buf = NULL; + src_buf = NULL; + dst_size = 0; + src_size = 0; + + while (src_len) { + BUG_ON(dst_len == 0); + + if (dst_size == 0) { + dst_buf = page_address(dst->page) + dst->offset; + dst_size = dst->length; + } + + if (src_size == 0) { + src_buf = page_address(src->page) + src->offset; + src_size = src->length; + } + + chunk = min(dst_size, src_size); + + memcpy(dst_buf, src_buf, chunk); + + dst_buf += chunk; + src_buf += chunk; + dst_size -= chunk; + src_size -= chunk; + + if (dst_size == 0) { + dst++; + dst_len--; + } + + if (src_size == 0) { + src++; + src_len--; + } + } +} + +unsigned int mmc_queue_map_sg(struct mmc_queue *mq) +{ + unsigned int sg_len; + + if (!mq->bounce_buf) + return blk_rq_map_sg(mq->queue, mq->req, mq->sg); + + BUG_ON(!mq->bounce_sg); + + sg_len = blk_rq_map_sg(mq->queue, mq->req, mq->bounce_sg); + + mq->bounce_sg_len = sg_len; + + /* + * Shortcut in the event we only get a single entry. + */ + if (sg_len == 1) { + memcpy(mq->sg, mq->bounce_sg, sizeof(struct scatterlist)); + return 1; + } + + mq->sg[0].page = virt_to_page(mq->bounce_buf); + mq->sg[0].offset = offset_in_page(mq->bounce_buf); + mq->sg[0].length = 0; + + while (sg_len) { + mq->sg[0].length += mq->bounce_sg[sg_len - 1].length; + sg_len--; + } + + return 1; +} + +void mmc_queue_bounce_pre(struct mmc_queue *mq) +{ + if (!mq->bounce_buf) + return; + + if (mq->bounce_sg_len == 1) + return; + if (rq_data_dir(mq->req) != WRITE) + return; + + copy_sg(mq->sg, 1, mq->bounce_sg, mq->bounce_sg_len); +} + +void mmc_queue_bounce_post(struct mmc_queue *mq) +{ + if (!mq->bounce_buf) + return; + + if (mq->bounce_sg_len == 1) + return; + if (rq_data_dir(mq->req) != READ) + return; + + copy_sg(mq->bounce_sg, mq->bounce_sg_len, mq->sg, 1); +} + diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index 1590b3f3f1f..64e66e0d499 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -14,6 +14,9 @@ struct mmc_queue { void *data; struct request_queue *queue; struct scatterlist *sg; + char *bounce_buf; + struct scatterlist *bounce_sg; + unsigned int bounce_sg_len; }; extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); @@ -21,4 +24,8 @@ extern void mmc_cleanup_queue(struct mmc_queue *); extern void mmc_queue_suspend(struct mmc_queue *); extern void mmc_queue_resume(struct mmc_queue *); +extern unsigned int mmc_queue_map_sg(struct mmc_queue *); +extern void mmc_queue_bounce_pre(struct mmc_queue *); +extern void mmc_queue_bounce_post(struct mmc_queue *); + #endif -- cgit v1.2.3 From 7de064ebc67d9baf6c933d3a7046feb9b4eced05 Mon Sep 17 00:00:00 2001 From: Milko Krachounov Date: Sat, 19 May 2007 01:18:03 +0200 Subject: sdhci: add ene controller id ENE has a very weird design where an SDHCI device (0805) is presented on the PCI bus, but that device is non-functional, and the real device is hidden as a more generic device. Signed-off-by: Milko Krachounov Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a359efdd77e..5e9a6ce8355 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -70,6 +70,14 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE, }, + { + .vendor = PCI_VENDOR_ID_ENE, + .device = PCI_DEVICE_ID_ENE_CB712_SD_2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE, + }, + { /* Generic SD host controller */ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) }, -- cgit v1.2.3 From 4101c16a910b15afd190c6bc7d45864461cf5c25 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 19 May 2007 13:39:01 +0200 Subject: mmc: refactor bus operations Move bus operations to its own file for the sake of clarity. Also delegate sysfs attributes to bus handlers in preparation for other more exotic types. Signed-off-by: Pierre Ossman --- drivers/mmc/core/Makefile | 3 +- drivers/mmc/core/bus.c | 253 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/bus.h | 22 ++++ drivers/mmc/core/core.c | 16 --- drivers/mmc/core/core.h | 4 +- drivers/mmc/core/mmc.c | 59 +++++++++-- drivers/mmc/core/sd.c | 63 ++++++++++-- drivers/mmc/core/sysfs.c | 228 ++++------------------------------------- drivers/mmc/core/sysfs.h | 14 ++- 9 files changed, 418 insertions(+), 244 deletions(-) create mode 100644 drivers/mmc/core/bus.c create mode 100644 drivers/mmc/core/bus.h (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 1075b02ae75..54261e3724c 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -7,5 +7,6 @@ ifeq ($(CONFIG_MMC_DEBUG),y) endif obj-$(CONFIG_MMC) += mmc_core.o -mmc_core-y := core.o sysfs.o mmc.o mmc_ops.o sd.o sd_ops.o +mmc_core-y := core.o sysfs.o bus.o \ + mmc.o mmc_ops.o sd.o sd_ops.o diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c new file mode 100644 index 00000000000..348b566bf4f --- /dev/null +++ b/drivers/mmc/core/bus.c @@ -0,0 +1,253 @@ +/* + * linux/drivers/mmc/core/bus.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright (C) 2007 Pierre Ossman + * + * 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. + * + * MMC card bus driver model + */ + +#include +#include + +#include +#include + +#include "sysfs.h" +#include "core.h" +#include "bus.h" + +#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) +#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) + +static ssize_t mmc_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + + switch (card->type) { + case MMC_TYPE_MMC: + return sprintf(buf, "MMC\n"); + case MMC_TYPE_SD: + return sprintf(buf, "SD\n"); + default: + return -EFAULT; + } +} + +static struct device_attribute mmc_dev_attrs[] = { + MMC_ATTR_RO(type), + __ATTR_NULL, +}; + +/* + * This currently matches any MMC driver to any MMC card - drivers + * themselves make the decision whether to drive this card in their + * probe method. + */ +static int mmc_bus_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +static int +mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, + int buf_size) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + int retval = 0, i = 0, length = 0; + +#define add_env(fmt,val) do { \ + retval = add_uevent_var(envp, num_envp, &i, \ + buf, buf_size, &length, \ + fmt, val); \ + if (retval) \ + return retval; \ +} while (0); + + switch (card->type) { + case MMC_TYPE_MMC: + add_env("MMC_TYPE=%s", "MMC"); + break; + case MMC_TYPE_SD: + add_env("MMC_TYPE=%s", "SD"); + break; + } + + add_env("MMC_NAME=%s", mmc_card_name(card)); + +#undef add_env + + envp[i] = NULL; + + return 0; +} + +static int mmc_bus_probe(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + + return drv->probe(card); +} + +static int mmc_bus_remove(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + + drv->remove(card); + + return 0; +} + +static int mmc_bus_suspend(struct device *dev, pm_message_t state) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + int ret = 0; + + if (dev->driver && drv->suspend) + ret = drv->suspend(card, state); + return ret; +} + +static int mmc_bus_resume(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + int ret = 0; + + if (dev->driver && drv->resume) + ret = drv->resume(card); + return ret; +} + +static struct bus_type mmc_bus_type = { + .name = "mmc", + .dev_attrs = mmc_dev_attrs, + .match = mmc_bus_match, + .uevent = mmc_bus_uevent, + .probe = mmc_bus_probe, + .remove = mmc_bus_remove, + .suspend = mmc_bus_suspend, + .resume = mmc_bus_resume, +}; + +int mmc_register_bus(void) +{ + return bus_register(&mmc_bus_type); +} + +void mmc_unregister_bus(void) +{ + bus_unregister(&mmc_bus_type); +} + +/** + * mmc_register_driver - register a media driver + * @drv: MMC media driver + */ +int mmc_register_driver(struct mmc_driver *drv) +{ + drv->drv.bus = &mmc_bus_type; + return driver_register(&drv->drv); +} + +EXPORT_SYMBOL(mmc_register_driver); + +/** + * mmc_unregister_driver - unregister a media driver + * @drv: MMC media driver + */ +void mmc_unregister_driver(struct mmc_driver *drv) +{ + drv->drv.bus = &mmc_bus_type; + driver_unregister(&drv->drv); +} + +EXPORT_SYMBOL(mmc_unregister_driver); + +static void mmc_release_card(struct device *dev) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + + kfree(card); +} + +/* + * Allocate and initialise a new MMC card structure. + */ +struct mmc_card *mmc_alloc_card(struct mmc_host *host) +{ + struct mmc_card *card; + + card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + memset(card, 0, sizeof(struct mmc_card)); + + card->host = host; + + device_initialize(&card->dev); + + card->dev.parent = mmc_classdev(host); + card->dev.bus = &mmc_bus_type; + card->dev.release = mmc_release_card; + + return card; +} + +/* + * Register a new MMC card with the driver model. + */ +int mmc_add_card(struct mmc_card *card) +{ + int ret; + + snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), + "%s:%04x", mmc_hostname(card->host), card->rca); + + card->dev.uevent_suppress = 1; + + ret = device_add(&card->dev); + if (ret) + return ret; + + if (card->host->bus_ops->sysfs_add) { + ret = card->host->bus_ops->sysfs_add(card->host, card); + if (ret) { + device_del(&card->dev); + return ret; + } + } + + card->dev.uevent_suppress = 0; + + kobject_uevent(&card->dev.kobj, KOBJ_ADD); + + mmc_card_set_present(card); + + return 0; +} + +/* + * Unregister a new MMC card with the driver model, and + * (eventually) free it. + */ +void mmc_remove_card(struct mmc_card *card) +{ + if (mmc_card_present(card)) { + if (card->host->bus_ops->sysfs_remove) + card->host->bus_ops->sysfs_remove(card->host, card); + device_del(&card->dev); + } + + put_device(&card->dev); +} + diff --git a/drivers/mmc/core/bus.h b/drivers/mmc/core/bus.h new file mode 100644 index 00000000000..4f35431116a --- /dev/null +++ b/drivers/mmc/core/bus.h @@ -0,0 +1,22 @@ +/* + * linux/drivers/mmc/core/bus.h + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright 2007 Pierre Ossman + * + * 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 _MMC_CORE_BUS_H +#define _MMC_CORE_BUS_H + +struct mmc_card *mmc_alloc_card(struct mmc_host *host); +int mmc_add_card(struct mmc_card *card); +void mmc_remove_card(struct mmc_card *card); + +int mmc_register_bus(void); +void mmc_unregister_bus(void); + +#endif + diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7385acfa1dd..d876adf4bd4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -368,22 +368,6 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing) mmc_set_ios(host); } -/* - * Allocate a new MMC card - */ -struct mmc_card *mmc_alloc_card(struct mmc_host *host) -{ - struct mmc_card *card; - - card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); - if (!card) - return ERR_PTR(-ENOMEM); - - mmc_init_card(card, host); - - return card; -} - /* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 177264d090a..1be86c792a5 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -18,6 +18,8 @@ struct mmc_bus_ops { void (*remove)(struct mmc_host *); void (*detect)(struct mmc_host *); + int (*sysfs_add)(struct mmc_host *, struct mmc_card *card); + void (*sysfs_remove)(struct mmc_host *, struct mmc_card *card); void (*suspend)(struct mmc_host *); void (*resume)(struct mmc_host *); }; @@ -54,8 +56,6 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); void mmc_set_timing(struct mmc_host *host, unsigned int timing); -struct mmc_card *mmc_alloc_card(struct mmc_host *host); - static inline void mmc_delay(unsigned int ms) { if (ms < 1000 / HZ) { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 42cc2867ed7..e4371555160 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -18,6 +18,7 @@ #include "core.h" #include "sysfs.h" +#include "bus.h" #include "mmc_ops.h" static const unsigned int tran_exp[] = { @@ -413,8 +414,7 @@ static void mmc_detect(struct mmc_host *host) mmc_release_host(host); if (err != MMC_ERR_NONE) { - mmc_remove_card(host->card); - host->card = NULL; + mmc_remove(host); mmc_claim_host(host); mmc_detach_bus(host); @@ -422,6 +422,53 @@ static void mmc_detect(struct mmc_host *host) } } +MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], + card->raw_cid[2], card->raw_cid[3]); +MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], + card->raw_csd[2], card->raw_csd[3]); +MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year); +MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev); +MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev); +MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid); +MMC_ATTR_FN(name, "%s\n", card->cid.prod_name); +MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid); +MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial); + +static struct device_attribute mmc_dev_attrs[] = { + MMC_ATTR_RO(cid), + MMC_ATTR_RO(csd), + MMC_ATTR_RO(date), + MMC_ATTR_RO(fwrev), + MMC_ATTR_RO(hwrev), + MMC_ATTR_RO(manfid), + MMC_ATTR_RO(name), + MMC_ATTR_RO(oemid), + MMC_ATTR_RO(serial), + __ATTR_NULL, +}; + +/* + * Adds sysfs entries as relevant. + */ +static int mmc_sysfs_add(struct mmc_host *host, struct mmc_card *card) +{ + int ret; + + ret = mmc_add_attrs(card, mmc_dev_attrs); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Removes the sysfs entries added by mmc_sysfs_add(). + */ +static void mmc_sysfs_remove(struct mmc_host *host, struct mmc_card *card) +{ + mmc_remove_attrs(card, mmc_dev_attrs); +} + #ifdef CONFIG_MMC_UNSAFE_RESUME /* @@ -455,9 +502,7 @@ static void mmc_resume(struct mmc_host *host) err = mmc_sd_init_card(host, host->ocr, host->card); if (err != MMC_ERR_NONE) { - mmc_remove_card(host->card); - host->card = NULL; - + mmc_remove(host); mmc_detach_bus(host); } @@ -474,6 +519,8 @@ static void mmc_resume(struct mmc_host *host) static const struct mmc_bus_ops mmc_ops = { .remove = mmc_remove, .detect = mmc_detect, + .sysfs_add = mmc_sysfs_add, + .sysfs_remove = mmc_sysfs_remove, .suspend = mmc_suspend, .resume = mmc_resume, }; @@ -518,7 +565,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_release_host(host); - err = mmc_register_card(host->card); + err = mmc_add_card(host->card); if (err) goto reclaim_host; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 918477c490b..1240684083f 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -19,11 +19,10 @@ #include "core.h" #include "sysfs.h" +#include "bus.h" #include "mmc_ops.h" #include "sd_ops.h" -#include "core.h" - static const unsigned int tran_exp[] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 @@ -487,8 +486,7 @@ static void mmc_sd_detect(struct mmc_host *host) mmc_release_host(host); if (err != MMC_ERR_NONE) { - mmc_remove_card(host->card); - host->card = NULL; + mmc_sd_remove(host); mmc_claim_host(host); mmc_detach_bus(host); @@ -496,6 +494,55 @@ static void mmc_sd_detect(struct mmc_host *host) } } +MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], + card->raw_cid[2], card->raw_cid[3]); +MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], + card->raw_csd[2], card->raw_csd[3]); +MMC_ATTR_FN(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); +MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year); +MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev); +MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev); +MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid); +MMC_ATTR_FN(name, "%s\n", card->cid.prod_name); +MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid); +MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial); + +static struct device_attribute mmc_sd_dev_attrs[] = { + MMC_ATTR_RO(cid), + MMC_ATTR_RO(csd), + MMC_ATTR_RO(scr), + MMC_ATTR_RO(date), + MMC_ATTR_RO(fwrev), + MMC_ATTR_RO(hwrev), + MMC_ATTR_RO(manfid), + MMC_ATTR_RO(name), + MMC_ATTR_RO(oemid), + MMC_ATTR_RO(serial), + __ATTR_NULL, +}; + +/* + * Adds sysfs entries as relevant. + */ +static int mmc_sd_sysfs_add(struct mmc_host *host, struct mmc_card *card) +{ + int ret; + + ret = mmc_add_attrs(card, mmc_sd_dev_attrs); + if (ret < 0) + return ret; + + return 0; +} + +/* + * Removes the sysfs entries added by mmc_sysfs_add(). + */ +static void mmc_sd_sysfs_remove(struct mmc_host *host, struct mmc_card *card) +{ + mmc_remove_attrs(card, mmc_sd_dev_attrs); +} + #ifdef CONFIG_MMC_UNSAFE_RESUME /* @@ -529,9 +576,7 @@ static void mmc_sd_resume(struct mmc_host *host) err = mmc_sd_init_card(host, host->ocr, host->card); if (err != MMC_ERR_NONE) { - mmc_remove_card(host->card); - host->card = NULL; - + mmc_sd_remove(host); mmc_detach_bus(host); } @@ -548,6 +593,8 @@ static void mmc_sd_resume(struct mmc_host *host) static const struct mmc_bus_ops mmc_sd_ops = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, + .sysfs_add = mmc_sd_sysfs_add, + .sysfs_remove = mmc_sd_sysfs_remove, .suspend = mmc_sd_suspend, .resume = mmc_sd_resume, }; @@ -599,7 +646,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) mmc_release_host(host); - err = mmc_register_card(host->card); + err = mmc_add_card(host->card); if (err) goto reclaim_host; diff --git a/drivers/mmc/core/sysfs.c b/drivers/mmc/core/sysfs.c index 843b1fbba55..a43a96c2281 100644 --- a/drivers/mmc/core/sysfs.c +++ b/drivers/mmc/core/sysfs.c @@ -2,6 +2,7 @@ * linux/drivers/mmc/core/sysfs.c * * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright 2007 Pierre Ossman * * 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 @@ -18,226 +19,37 @@ #include #include +#include "bus.h" #include "sysfs.h" -#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) #define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) -#define MMC_ATTR(name, fmt, args...) \ -static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct mmc_card *card = dev_to_mmc_card(dev); \ - return sprintf(buf, fmt, args); \ -} - -MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], - card->raw_cid[2], card->raw_cid[3]); -MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], - card->raw_csd[2], card->raw_csd[3]); -MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); -MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); -MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); -MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); -MMC_ATTR(manfid, "0x%06x\n", card->cid.manfid); -MMC_ATTR(name, "%s\n", card->cid.prod_name); -MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid); -MMC_ATTR(serial, "0x%08x\n", card->cid.serial); - -#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL) - -static struct device_attribute mmc_dev_attrs[] = { - MMC_ATTR_RO(cid), - MMC_ATTR_RO(csd), - MMC_ATTR_RO(date), - MMC_ATTR_RO(fwrev), - MMC_ATTR_RO(hwrev), - MMC_ATTR_RO(manfid), - MMC_ATTR_RO(name), - MMC_ATTR_RO(oemid), - MMC_ATTR_RO(serial), - __ATTR_NULL -}; - -static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr); - - -static void mmc_release_card(struct device *dev) +int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs) { - struct mmc_card *card = dev_to_mmc_card(dev); - - kfree(card); -} - -/* - * This currently matches any MMC driver to any MMC card - drivers - * themselves make the decision whether to drive this card in their - * probe method. - */ -static int mmc_bus_match(struct device *dev, struct device_driver *drv) -{ - return 1; -} - -static int -mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, - int buf_size) -{ - struct mmc_card *card = dev_to_mmc_card(dev); - char ccc[13]; - int retval = 0, i = 0, length = 0; - -#define add_env(fmt,val) do { \ - retval = add_uevent_var(envp, num_envp, &i, \ - buf, buf_size, &length, \ - fmt, val); \ - if (retval) \ - return retval; \ -} while (0); - - for (i = 0; i < 12; i++) - ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; - ccc[12] = '\0'; + int error = 0; + int i; - add_env("MMC_CCC=%s", ccc); - add_env("MMC_MANFID=%06x", card->cid.manfid); - add_env("MMC_NAME=%s", mmc_card_name(card)); - add_env("MMC_OEMID=%04x", card->cid.oemid); -#undef add_env - envp[i] = NULL; - - return 0; -} - -static int mmc_bus_suspend(struct device *dev, pm_message_t state) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - int ret = 0; - - if (dev->driver && drv->suspend) - ret = drv->suspend(card, state); - return ret; -} - -static int mmc_bus_resume(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - int ret = 0; - - if (dev->driver && drv->resume) - ret = drv->resume(card); - return ret; -} - -static int mmc_bus_probe(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - - return drv->probe(card); -} - -static int mmc_bus_remove(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - - drv->remove(card); - - return 0; -} - -static struct bus_type mmc_bus_type = { - .name = "mmc", - .dev_attrs = mmc_dev_attrs, - .match = mmc_bus_match, - .uevent = mmc_bus_uevent, - .probe = mmc_bus_probe, - .remove = mmc_bus_remove, - .suspend = mmc_bus_suspend, - .resume = mmc_bus_resume, -}; - -/** - * mmc_register_driver - register a media driver - * @drv: MMC media driver - */ -int mmc_register_driver(struct mmc_driver *drv) -{ - drv->drv.bus = &mmc_bus_type; - return driver_register(&drv->drv); -} - -EXPORT_SYMBOL(mmc_register_driver); - -/** - * mmc_unregister_driver - unregister a media driver - * @drv: MMC media driver - */ -void mmc_unregister_driver(struct mmc_driver *drv) -{ - drv->drv.bus = &mmc_bus_type; - driver_unregister(&drv->drv); -} - -EXPORT_SYMBOL(mmc_unregister_driver); - - -/* - * Internal function. Initialise a MMC card structure. - */ -void mmc_init_card(struct mmc_card *card, struct mmc_host *host) -{ - memset(card, 0, sizeof(struct mmc_card)); - card->host = host; - device_initialize(&card->dev); - card->dev.parent = mmc_classdev(host); - card->dev.bus = &mmc_bus_type; - card->dev.release = mmc_release_card; -} - -/* - * Internal function. Register a new MMC card with the driver model. - */ -int mmc_register_card(struct mmc_card *card) -{ - int ret; - - snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), - "%s:%04x", mmc_hostname(card->host), card->rca); - - ret = device_add(&card->dev); - if (ret == 0) { - if (mmc_card_sd(card)) { - ret = device_create_file(&card->dev, &mmc_dev_attr_scr); - if (ret) - device_del(&card->dev); + for (i = 0; attr_name(attrs[i]); i++) { + error = device_create_file(&card->dev, &attrs[i]); + if (error) { + while (--i >= 0) + device_remove_file(&card->dev, &attrs[i]); + break; } } - if (ret == 0) - mmc_card_set_present(card); - return ret; + + return error; } -/* - * Internal function. Unregister a new MMC card with the - * driver model, and (eventually) free it. - */ -void mmc_remove_card(struct mmc_card *card) +void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs) { - if (mmc_card_present(card)) { - if (mmc_card_sd(card)) - device_remove_file(&card->dev, &mmc_dev_attr_scr); - - device_del(&card->dev); - } + int i; - put_device(&card->dev); + for (i = 0; attr_name(attrs[i]); i++) + device_remove_file(&card->dev, &attrs[i]); } - static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); @@ -340,11 +152,11 @@ static int __init mmc_init(void) if (!workqueue) return -ENOMEM; - ret = bus_register(&mmc_bus_type); + ret = mmc_register_bus(); if (ret == 0) { ret = class_register(&mmc_host_class); if (ret) - bus_unregister(&mmc_bus_type); + mmc_unregister_bus(); } return ret; } @@ -352,7 +164,7 @@ static int __init mmc_init(void) static void __exit mmc_exit(void) { class_unregister(&mmc_host_class); - bus_unregister(&mmc_bus_type); + mmc_unregister_bus(); destroy_workqueue(workqueue); } diff --git a/drivers/mmc/core/sysfs.h b/drivers/mmc/core/sysfs.h index 80e29b35828..2a326a5b83c 100644 --- a/drivers/mmc/core/sysfs.h +++ b/drivers/mmc/core/sysfs.h @@ -11,9 +11,17 @@ #ifndef _MMC_CORE_SYSFS_H #define _MMC_CORE_SYSFS_H -void mmc_init_card(struct mmc_card *card, struct mmc_host *host); -int mmc_register_card(struct mmc_card *card); -void mmc_remove_card(struct mmc_card *card); +#define MMC_ATTR_FN(name, fmt, args...) \ +static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct mmc_card *card = container_of(dev, struct mmc_card, dev);\ + return sprintf(buf, fmt, args); \ +} + +#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL) + +int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs); +void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs); struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); int mmc_add_host_sysfs(struct mmc_host *host); -- cgit v1.2.3 From b93931a61a119575f84c33af2438b9384fde9eb7 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 19 May 2007 14:06:24 +0200 Subject: mmc: refactor host class handling Move basic host class device handling to its own file for clarity. Signed-off-by: Pierre Ossman --- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/core.c | 82 ++---------------------- drivers/mmc/core/core.h | 4 ++ drivers/mmc/core/host.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/host.h | 18 ++++++ drivers/mmc/core/sysfs.c | 84 +------------------------ drivers/mmc/core/sysfs.h | 5 -- 7 files changed, 187 insertions(+), 164 deletions(-) create mode 100644 drivers/mmc/core/host.c create mode 100644 drivers/mmc/core/host.h (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 54261e3724c..3fdd08c7f14 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -7,6 +7,6 @@ ifeq ($(CONFIG_MMC_DEBUG),y) endif obj-$(CONFIG_MMC) += mmc_core.o -mmc_core-y := core.o sysfs.o bus.o \ +mmc_core-y := core.o sysfs.o bus.o host.o \ mmc.o mmc_ops.o sd.o sd_ops.o diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d876adf4bd4..66e463d100c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -496,7 +496,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) EXPORT_SYMBOL(mmc_detect_change); -static void mmc_rescan(struct work_struct *work) +void mmc_rescan(struct work_struct *work) { struct mmc_host *host = container_of(work, struct mmc_host, detect.work); @@ -545,69 +545,13 @@ static void mmc_rescan(struct work_struct *work) } } - -/** - * mmc_alloc_host - initialise the per-host structure. - * @extra: sizeof private data structure - * @dev: pointer to host device model structure - * - * Initialise the per-host structure. - */ -struct mmc_host *mmc_alloc_host(int extra, struct device *dev) -{ - struct mmc_host *host; - - host = mmc_alloc_host_sysfs(extra, dev); - if (host) { - spin_lock_init(&host->lock); - init_waitqueue_head(&host->wq); - INIT_DELAYED_WORK(&host->detect, mmc_rescan); - - /* - * By default, hosts do not support SGIO or large requests. - * They have to set these according to their abilities. - */ - host->max_hw_segs = 1; - host->max_phys_segs = 1; - host->max_seg_size = PAGE_CACHE_SIZE; - - host->max_req_size = PAGE_CACHE_SIZE; - host->max_blk_size = 512; - host->max_blk_count = PAGE_CACHE_SIZE / 512; - } - - return host; -} - -EXPORT_SYMBOL(mmc_alloc_host); - -/** - * mmc_add_host - initialise host hardware - * @host: mmc host - */ -int mmc_add_host(struct mmc_host *host) +void mmc_start_host(struct mmc_host *host) { - int ret; - - ret = mmc_add_host_sysfs(host); - if (ret == 0) { - mmc_power_off(host); - mmc_detect_change(host, 0); - } - - return ret; + mmc_power_off(host); + mmc_detect_change(host, 0); } -EXPORT_SYMBOL(mmc_add_host); - -/** - * mmc_remove_host - remove host hardware - * @host: mmc host - * - * Unregister and remove all cards associated with this host, - * and power down the MMC bus. - */ -void mmc_remove_host(struct mmc_host *host) +void mmc_stop_host(struct mmc_host *host) { #ifdef CONFIG_MMC_DEBUG unsigned long flags; @@ -632,24 +576,8 @@ void mmc_remove_host(struct mmc_host *host) BUG_ON(host->card); mmc_power_off(host); - mmc_remove_host_sysfs(host); } -EXPORT_SYMBOL(mmc_remove_host); - -/** - * mmc_free_host - free the host structure - * @host: mmc host - * - * Free the host once all references to it have been dropped. - */ -void mmc_free_host(struct mmc_host *host) -{ - mmc_free_host_sysfs(host); -} - -EXPORT_SYMBOL(mmc_free_host); - #ifdef CONFIG_PM /** diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 1be86c792a5..ae006b30dd8 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -66,5 +66,9 @@ static inline void mmc_delay(unsigned int ms) } } +void mmc_rescan(struct work_struct *work); +void mmc_start_host(struct mmc_host *host); +void mmc_stop_host(struct mmc_host *host); + #endif diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c new file mode 100644 index 00000000000..1433d95c40b --- /dev/null +++ b/drivers/mmc/core/host.c @@ -0,0 +1,156 @@ +/* + * linux/drivers/mmc/core/host.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright (C) 2007 Pierre Ossman + * + * 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. + * + * MMC host class device management + */ + +#include +#include +#include +#include + +#include + +#include "core.h" +#include "host.h" + +#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) + +static void mmc_host_classdev_release(struct device *dev) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + kfree(host); +} + +static struct class mmc_host_class = { + .name = "mmc_host", + .dev_release = mmc_host_classdev_release, +}; + +int mmc_register_host_class(void) +{ + return class_register(&mmc_host_class); +} + +void mmc_unregister_host_class(void) +{ + class_unregister(&mmc_host_class); +} + +static DEFINE_IDR(mmc_host_idr); +static DEFINE_SPINLOCK(mmc_host_lock); + +/** + * mmc_alloc_host - initialise the per-host structure. + * @extra: sizeof private data structure + * @dev: pointer to host device model structure + * + * Initialise the per-host structure. + */ +struct mmc_host *mmc_alloc_host(int extra, struct device *dev) +{ + struct mmc_host *host; + + host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); + if (!host) + return NULL; + + memset(host, 0, sizeof(struct mmc_host) + extra); + + host->parent = dev; + host->class_dev.parent = dev; + host->class_dev.class = &mmc_host_class; + device_initialize(&host->class_dev); + + spin_lock_init(&host->lock); + init_waitqueue_head(&host->wq); + INIT_DELAYED_WORK(&host->detect, mmc_rescan); + + /* + * By default, hosts do not support SGIO or large requests. + * They have to set these according to their abilities. + */ + host->max_hw_segs = 1; + host->max_phys_segs = 1; + host->max_seg_size = PAGE_CACHE_SIZE; + + host->max_req_size = PAGE_CACHE_SIZE; + host->max_blk_size = 512; + host->max_blk_count = PAGE_CACHE_SIZE / 512; + + return host; +} + +EXPORT_SYMBOL(mmc_alloc_host); + +/** + * mmc_add_host - initialise host hardware + * @host: mmc host + */ +int mmc_add_host(struct mmc_host *host) +{ + int err; + + if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) + return -ENOMEM; + + spin_lock(&mmc_host_lock); + err = idr_get_new(&mmc_host_idr, host, &host->index); + spin_unlock(&mmc_host_lock); + if (err) + return err; + + snprintf(host->class_dev.bus_id, BUS_ID_SIZE, + "mmc%d", host->index); + + err = device_add(&host->class_dev); + if (err) + return err; + + mmc_start_host(host); + + return 0; +} + +EXPORT_SYMBOL(mmc_add_host); + +/** + * mmc_remove_host - remove host hardware + * @host: mmc host + * + * Unregister and remove all cards associated with this host, + * and power down the MMC bus. + */ +void mmc_remove_host(struct mmc_host *host) +{ + mmc_stop_host(host); + + device_del(&host->class_dev); + + spin_lock(&mmc_host_lock); + idr_remove(&mmc_host_idr, host->index); + spin_unlock(&mmc_host_lock); +} + +EXPORT_SYMBOL(mmc_remove_host); + +/** + * mmc_free_host - free the host structure + * @host: mmc host + * + * Free the host once all references to it have been dropped. + */ +void mmc_free_host(struct mmc_host *host) +{ + put_device(&host->class_dev); +} + +EXPORT_SYMBOL(mmc_free_host); + diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h new file mode 100644 index 00000000000..c2dc3d2d9f9 --- /dev/null +++ b/drivers/mmc/core/host.h @@ -0,0 +1,18 @@ +/* + * linux/drivers/mmc/core/host.h + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright 2007 Pierre Ossman + * + * 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 _MMC_CORE_HOST_H +#define _MMC_CORE_HOST_H + +int mmc_register_host_class(void); +void mmc_unregister_host_class(void); + +#endif + diff --git a/drivers/mmc/core/sysfs.c b/drivers/mmc/core/sysfs.c index a43a96c2281..fbf99f9a0b8 100644 --- a/drivers/mmc/core/sysfs.c +++ b/drivers/mmc/core/sysfs.c @@ -20,11 +20,9 @@ #include #include "bus.h" +#include "host.h" #include "sysfs.h" -#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) -#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) - int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs) { int error = 0; @@ -50,82 +48,6 @@ void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs) device_remove_file(&card->dev, &attrs[i]); } -static void mmc_host_classdev_release(struct device *dev) -{ - struct mmc_host *host = cls_dev_to_mmc_host(dev); - kfree(host); -} - -static struct class mmc_host_class = { - .name = "mmc_host", - .dev_release = mmc_host_classdev_release, -}; - -static DEFINE_IDR(mmc_host_idr); -static DEFINE_SPINLOCK(mmc_host_lock); - -/* - * Internal function. Allocate a new MMC host. - */ -struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev) -{ - struct mmc_host *host; - - host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); - if (host) { - memset(host, 0, sizeof(struct mmc_host) + extra); - - host->parent = dev; - host->class_dev.parent = dev; - host->class_dev.class = &mmc_host_class; - device_initialize(&host->class_dev); - } - - return host; -} - -/* - * Internal function. Register a new MMC host with the MMC class. - */ -int mmc_add_host_sysfs(struct mmc_host *host) -{ - int err; - - if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) - return -ENOMEM; - - spin_lock(&mmc_host_lock); - err = idr_get_new(&mmc_host_idr, host, &host->index); - spin_unlock(&mmc_host_lock); - if (err) - return err; - - snprintf(host->class_dev.bus_id, BUS_ID_SIZE, - "mmc%d", host->index); - - return device_add(&host->class_dev); -} - -/* - * Internal function. Unregister a MMC host with the MMC class. - */ -void mmc_remove_host_sysfs(struct mmc_host *host) -{ - device_del(&host->class_dev); - - spin_lock(&mmc_host_lock); - idr_remove(&mmc_host_idr, host->index); - spin_unlock(&mmc_host_lock); -} - -/* - * Internal function. Free a MMC host. - */ -void mmc_free_host_sysfs(struct mmc_host *host) -{ - put_device(&host->class_dev); -} - static struct workqueue_struct *workqueue; /* @@ -154,7 +76,7 @@ static int __init mmc_init(void) ret = mmc_register_bus(); if (ret == 0) { - ret = class_register(&mmc_host_class); + ret = mmc_register_host_class(); if (ret) mmc_unregister_bus(); } @@ -163,7 +85,7 @@ static int __init mmc_init(void) static void __exit mmc_exit(void) { - class_unregister(&mmc_host_class); + mmc_unregister_host_class(); mmc_unregister_bus(); destroy_workqueue(workqueue); } diff --git a/drivers/mmc/core/sysfs.h b/drivers/mmc/core/sysfs.h index 2a326a5b83c..2f60c79b203 100644 --- a/drivers/mmc/core/sysfs.h +++ b/drivers/mmc/core/sysfs.h @@ -23,11 +23,6 @@ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *a int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs); void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs); -struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); -int mmc_add_host_sysfs(struct mmc_host *host); -void mmc_remove_host_sysfs(struct mmc_host *host); -void mmc_free_host_sysfs(struct mmc_host *host); - int mmc_schedule_work(struct work_struct *work); int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); void mmc_flush_scheduled_work(void); -- cgit v1.2.3 From ffce2e7e7060c949ccd703dacc9b3dd81b377373 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 19 May 2007 14:32:22 +0200 Subject: mmc: move layer init and workqueue to core file Signed-off-by: Pierre Ossman --- drivers/mmc/core/core.c | 49 +++++++++++++++++++++++++++++++++++++++++++++- drivers/mmc/core/sysfs.c | 51 ------------------------------------------------ drivers/mmc/core/sysfs.h | 4 ---- 3 files changed, 48 insertions(+), 56 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 66e463d100c..b5d8a6d90cc 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -27,7 +27,8 @@ #include #include "core.h" -#include "sysfs.h" +#include "bus.h" +#include "host.h" #include "mmc_ops.h" #include "sd_ops.h" @@ -35,6 +36,25 @@ extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); +static struct workqueue_struct *workqueue; + +/* + * Internal function. Schedule delayed work in the MMC work queue. + */ +static int mmc_schedule_delayed_work(struct delayed_work *work, + unsigned long delay) +{ + return queue_delayed_work(workqueue, work, delay); +} + +/* + * Internal function. Flush all scheduled work from the MMC work queue. + */ +static void mmc_flush_scheduled_work(void) +{ + flush_workqueue(workqueue); +} + /** * mmc_request_done - finish processing an MMC request * @host: MMC host which completed request @@ -638,4 +658,31 @@ EXPORT_SYMBOL(mmc_resume_host); #endif +static int __init mmc_init(void) +{ + int ret; + + workqueue = create_singlethread_workqueue("kmmcd"); + if (!workqueue) + return -ENOMEM; + + ret = mmc_register_bus(); + if (ret == 0) { + ret = mmc_register_host_class(); + if (ret) + mmc_unregister_bus(); + } + return ret; +} + +static void __exit mmc_exit(void) +{ + mmc_unregister_host_class(); + mmc_unregister_bus(); + destroy_workqueue(workqueue); +} + +module_init(mmc_init); +module_exit(mmc_exit); + MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/core/sysfs.c b/drivers/mmc/core/sysfs.c index fbf99f9a0b8..00a97e70f91 100644 --- a/drivers/mmc/core/sysfs.c +++ b/drivers/mmc/core/sysfs.c @@ -10,17 +10,10 @@ * * MMC sysfs/driver model support. */ -#include -#include #include -#include -#include #include -#include -#include "bus.h" -#include "host.h" #include "sysfs.h" int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs) @@ -48,47 +41,3 @@ void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs) device_remove_file(&card->dev, &attrs[i]); } -static struct workqueue_struct *workqueue; - -/* - * Internal function. Schedule delayed work in the MMC work queue. - */ -int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) -{ - return queue_delayed_work(workqueue, work, delay); -} - -/* - * Internal function. Flush all scheduled work from the MMC work queue. - */ -void mmc_flush_scheduled_work(void) -{ - flush_workqueue(workqueue); -} - -static int __init mmc_init(void) -{ - int ret; - - workqueue = create_singlethread_workqueue("kmmcd"); - if (!workqueue) - return -ENOMEM; - - ret = mmc_register_bus(); - if (ret == 0) { - ret = mmc_register_host_class(); - if (ret) - mmc_unregister_bus(); - } - return ret; -} - -static void __exit mmc_exit(void) -{ - mmc_unregister_host_class(); - mmc_unregister_bus(); - destroy_workqueue(workqueue); -} - -module_init(mmc_init); -module_exit(mmc_exit); diff --git a/drivers/mmc/core/sysfs.h b/drivers/mmc/core/sysfs.h index 2f60c79b203..4b8f670bd10 100644 --- a/drivers/mmc/core/sysfs.h +++ b/drivers/mmc/core/sysfs.h @@ -23,8 +23,4 @@ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *a int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs); void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs); -int mmc_schedule_work(struct work_struct *work); -int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); -void mmc_flush_scheduled_work(void); - #endif -- cgit v1.2.3 From 8c75deae1ab99661975da098f8b721bafbb247c4 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 19 May 2007 16:14:43 +0200 Subject: mmc: fix silly copy-and-paste error Signed-off-by: Pierre Ossman --- drivers/mmc/core/mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index e4371555160..66f85bfa8db 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -237,7 +237,7 @@ out: * In the case of a resume, "curcard" will contain the card * we're trying to reinitialise. */ -static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, +static int mmc_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *oldcard) { struct mmc_card *card; @@ -500,7 +500,7 @@ static void mmc_resume(struct mmc_host *host) mmc_claim_host(host); - err = mmc_sd_init_card(host, host->ocr, host->card); + err = mmc_init_card(host, host->ocr, host->card); if (err != MMC_ERR_NONE) { mmc_remove(host); mmc_detach_bus(host); @@ -559,7 +559,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) /* * Detect and init the card. */ - err = mmc_sd_init_card(host, host->ocr, NULL); + err = mmc_init_card(host, host->ocr, NULL); if (err != MMC_ERR_NONE) goto err; -- cgit v1.2.3 From 9d26a5d3f2b9c4fe4b2ba491683c6989ecd6ae04 Mon Sep 17 00:00:00 2001 From: Rolf Eike Beer Date: Tue, 26 Jun 2007 13:31:16 +0200 Subject: sdhci: Fix "Unexpected interrupt" handling Whenever a power interrupt is signaled it is also reported as an unexpected one. All other unexpected interrupts get lost. Cause is a not inversed bitmask to remove power interrupts from the status. Signed-off-by: Rolf Eike Beer Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5e9a6ce8355..10d15c39d00 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1030,7 +1030,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS); } - intmask &= SDHCI_INT_BUS_POWER; + intmask &= ~SDHCI_INT_BUS_POWER; if (intmask) { printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n", -- cgit v1.2.3 From e8d04d3dba60bdc139644350fcc88f82e40129dc Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Tue, 19 Jun 2007 18:32:34 +0200 Subject: mmc: at91_mci typo Typo fix in at91_mci driver : standardized the typo (at91_mci everywhere) Signed-off-by: Nicolas Ferre Signed-off-by: Pierre Ossman --- drivers/mmc/host/at91_mci.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 5b00c194b62..647f29473cf 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -131,7 +131,7 @@ struct at91mci_host /* * Copy from sg to a dma block - used for transfers */ -static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) +static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) { unsigned int len, i, size; unsigned *dmabuf = host->buffer; @@ -180,7 +180,7 @@ static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data /* * Prepare a dma read */ -static void at91mci_pre_dma_read(struct at91mci_host *host) +static void at91_mci_pre_dma_read(struct at91mci_host *host) { int i; struct scatterlist *sg; @@ -248,7 +248,7 @@ static void at91mci_pre_dma_read(struct at91mci_host *host) /* * Handle after a dma read */ -static void at91mci_post_dma_read(struct at91mci_host *host) +static void at91_mci_post_dma_read(struct at91mci_host *host) { struct mmc_command *cmd; struct mmc_data *data; @@ -299,7 +299,7 @@ static void at91mci_post_dma_read(struct at91mci_host *host) /* Is there another transfer to trigger? */ if (host->transfer_index < data->sg_len) - at91mci_pre_dma_read(host); + at91_mci_pre_dma_read(host); else { at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); @@ -464,7 +464,7 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ host->buffer = NULL; host->total_length = 0; - at91mci_pre_dma_read(host); + at91_mci_pre_dma_read(host); ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; } else { @@ -476,7 +476,7 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ host->total_length, &host->physical_address, GFP_KERNEL); - at91mci_sg_to_dma(host, data); + at91_mci_sg_to_dma(host, data); pr_debug("Transmitting %d bytes\n", host->total_length); @@ -506,7 +506,7 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ /* * Wait for a command to complete */ -static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) +static void at91_mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) { unsigned int ier; @@ -521,15 +521,15 @@ static void at91mci_process_command(struct at91mci_host *host, struct mmc_comman /* * Process the next step in the request */ -static void at91mci_process_next(struct at91mci_host *host) +static void at91_mci_process_next(struct at91mci_host *host) { if (!(host->flags & FL_SENT_COMMAND)) { host->flags |= FL_SENT_COMMAND; - at91mci_process_command(host, host->request->cmd); + at91_mci_process_command(host, host->request->cmd); } else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { host->flags |= FL_SENT_STOP; - at91mci_process_command(host, host->request->stop); + at91_mci_process_command(host, host->request->stop); } else mmc_request_done(host->mmc, host->request); @@ -538,7 +538,7 @@ static void at91mci_process_next(struct at91mci_host *host) /* * Handle a command that has been completed */ -static void at91mci_completed_command(struct at91mci_host *host) +static void at91_mci_completed_command(struct at91mci_host *host) { struct mmc_command *cmd = host->cmd; unsigned int status; @@ -583,7 +583,7 @@ static void at91mci_completed_command(struct at91mci_host *host) else cmd->error = MMC_ERR_NONE; - at91mci_process_next(host); + at91_mci_process_next(host); } /* @@ -595,7 +595,7 @@ static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->request = mrq; host->flags = 0; - at91mci_process_next(host); + at91_mci_process_next(host); } /* @@ -708,7 +708,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid) if (int_status & AT91_MCI_ENDRX) { pr_debug("Receive has ended\n"); - at91mci_post_dma_read(host); + at91_mci_post_dma_read(host); } if (int_status & AT91_MCI_NOTBUSY) { @@ -737,7 +737,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid) if (completed) { pr_debug("Completed command\n"); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); - at91mci_completed_command(host); + at91_mci_completed_command(host); } else at91_mci_write(host, AT91_MCI_IDR, int_status); -- cgit v1.2.3 From ed99c541e0a15281c57530d54a4a5e3272f74fb9 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Mon, 9 Jul 2007 14:58:16 +0200 Subject: mmc: at91_mci: fix hanging and rework to match flowcharts Fixes hanging using multi block operations (seen during CMD25). Follows closely the datasheet flowcharts. This piece of code handles better big file writing. I had to take care of the notbusy signal during write (at91_mci_handle_cmdrdy function) and to rearrange the AT91_MCI_ENDRX and AT91_MCI_RXBUFF flag usage. Signed-off-by: Nicolas Ferre Signed-off-by: Pierre Ossman --- drivers/mmc/host/at91_mci.c | 199 ++++++++++++++++++++++++-------------------- 1 file changed, 108 insertions(+), 91 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 647f29473cf..28c881895ab 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -78,8 +78,6 @@ #define DRIVER_NAME "at91_mci" -#undef SUPPORT_4WIRE - #define FL_SENT_COMMAND (1 << 0) #define FL_SENT_STOP (1 << 1) @@ -268,8 +266,6 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) } while (host->in_use_index < host->transfer_index) { - unsigned int *buffer; - struct scatterlist *sg; pr_debug("finishing index %d\n", host->in_use_index); @@ -280,20 +276,22 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); - /* Swap the contents of the buffer */ - buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; - pr_debug("buffer = %p, length = %d\n", buffer, sg->length); - data->bytes_xfered += sg->length; if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ + unsigned int *buffer; int index; + /* Swap the contents of the buffer */ + buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; + pr_debug("buffer = %p, length = %d\n", buffer, sg->length); + for (index = 0; index < (sg->length / 4); index++) buffer[index] = swab32(buffer[index]); + + kunmap_atomic(buffer, KM_BIO_SRC_IRQ); } - kunmap_atomic(buffer, KM_BIO_SRC_IRQ); flush_dcache_page(sg->page); } @@ -301,8 +299,8 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) if (host->transfer_index < data->sg_len) at91_mci_pre_dma_read(host); else { + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); } pr_debug("post dma read done\n"); @@ -323,7 +321,6 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host) /* Now wait for cmd ready */ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); cmd = host->cmd; if (!cmd) return; @@ -331,18 +328,53 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host) data = cmd->data; if (!data) return; + if (cmd->data->flags & MMC_DATA_MULTI) { + pr_debug("multiple write : wait for BLKE...\n"); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); + } else + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); + data->bytes_xfered = host->total_length; } +/*Handle after command sent ready*/ +static int at91_mci_handle_cmdrdy(struct at91mci_host *host) +{ + if (!host->cmd) + return 1; + else if (!host->cmd->data) { + if (host->flags & FL_SENT_STOP) { + /*After multi block write, we must wait for NOTBUSY*/ + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); + } else return 1; + } else if (host->cmd->data->flags & MMC_DATA_WRITE) { + /*After sendding multi-block-write command, start DMA transfer*/ + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); + } + + /* command not completed, have to wait */ + return 0; +} + + /* * Enable the controller */ static void at91_mci_enable(struct at91mci_host *host) { + unsigned int mr; + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); - at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a); + mr = AT91_MCI_PDCMODE | 0x34a; + + if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) + mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; + + at91_mci_write(host, AT91_MCI_MR, mr); /* use Slot A or B (only one at same time) */ at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); @@ -358,9 +390,8 @@ static void at91_mci_disable(struct at91mci_host *host) /* * Send a command - * return the interrupts to enable */ -static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) +static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) { unsigned int cmdr, mr; unsigned int block_length; @@ -371,8 +402,7 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ host->cmd = cmd; - /* Not sure if this is needed */ -#if 0 + /* Needed for leaving busy state before CMD1 */ if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { pr_debug("Clearing timeout\n"); at91_mci_write(host, AT91_MCI_ARGR, 0); @@ -382,7 +412,7 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); } } -#endif + cmdr = cmd->opcode; if (mmc_resp_type(cmd) == MMC_RSP_NONE) @@ -439,50 +469,48 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ at91_mci_write(host, ATMEL_PDC_TCR, 0); at91_mci_write(host, ATMEL_PDC_TNPR, 0); at91_mci_write(host, ATMEL_PDC_TNCR, 0); + ier = AT91_MCI_CMDRDY; + } else { + /* zero block length and PDC mode */ + mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; + at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); - at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); - at91_mci_write(host, AT91_MCI_CMDR, cmdr); - return AT91_MCI_CMDRDY; - } - - mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */ - at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); - - /* - * Disable the PDC controller - */ - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); - - if (cmdr & AT91_MCI_TRCMD_START) { - data->bytes_xfered = 0; - host->transfer_index = 0; - host->in_use_index = 0; - if (cmdr & AT91_MCI_TRDIR) { - /* - * Handle a read - */ - host->buffer = NULL; - host->total_length = 0; - - at91_mci_pre_dma_read(host); - ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; - } - else { - /* - * Handle a write - */ - host->total_length = block_length * blocks; - host->buffer = dma_alloc_coherent(NULL, - host->total_length, - &host->physical_address, GFP_KERNEL); - - at91_mci_sg_to_dma(host, data); - - pr_debug("Transmitting %d bytes\n", host->total_length); + /* + * Disable the PDC controller + */ + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); - at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); - at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); - ier = AT91_MCI_TXBUFE; + if (cmdr & AT91_MCI_TRCMD_START) { + data->bytes_xfered = 0; + host->transfer_index = 0; + host->in_use_index = 0; + if (cmdr & AT91_MCI_TRDIR) { + /* + * Handle a read + */ + host->buffer = NULL; + host->total_length = 0; + + at91_mci_pre_dma_read(host); + ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; + } + else { + /* + * Handle a write + */ + host->total_length = block_length * blocks; + host->buffer = dma_alloc_coherent(NULL, + host->total_length, + &host->physical_address, GFP_KERNEL); + + at91_mci_sg_to_dma(host, data); + + pr_debug("Transmitting %d bytes\n", host->total_length); + + at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); + at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); + ier = AT91_MCI_CMDRDY; + } } } @@ -497,24 +525,9 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ if (cmdr & AT91_MCI_TRCMD_START) { if (cmdr & AT91_MCI_TRDIR) at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); - else - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); } - return ier; -} -/* - * Wait for a command to complete - */ -static void at91_mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) -{ - unsigned int ier; - - ier = at91_mci_send_command(host, cmd); - - pr_debug("setting ier to %08X\n", ier); - - /* Stop on errors or the required value */ + /* Enable selected interrupts */ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); } @@ -525,11 +538,11 @@ static void at91_mci_process_next(struct at91mci_host *host) { if (!(host->flags & FL_SENT_COMMAND)) { host->flags |= FL_SENT_COMMAND; - at91_mci_process_command(host, host->request->cmd); + at91_mci_send_command(host, host->request->cmd); } else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { host->flags |= FL_SENT_STOP; - at91_mci_process_command(host, host->request->stop); + at91_mci_send_command(host, host->request->stop); } else mmc_request_done(host->mmc, host->request); @@ -698,29 +711,33 @@ static irqreturn_t at91_mci_irq(int irq, void *devid) at91_mci_handle_transmitted(host); } + if (int_status & AT91_MCI_ENDRX) { + pr_debug("ENDRX\n"); + at91_mci_post_dma_read(host); + } + if (int_status & AT91_MCI_RXBUFF) { pr_debug("RX buffer full\n"); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX); + completed = 1; } if (int_status & AT91_MCI_ENDTX) pr_debug("Transmit has ended\n"); - if (int_status & AT91_MCI_ENDRX) { - pr_debug("Receive has ended\n"); - at91_mci_post_dma_read(host); - } - if (int_status & AT91_MCI_NOTBUSY) { pr_debug("Card is ready\n"); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); + completed = 1; } if (int_status & AT91_MCI_DTIP) pr_debug("Data transfer in progress\n"); - if (int_status & AT91_MCI_BLKE) + if (int_status & AT91_MCI_BLKE) { pr_debug("Block transfer has ended\n"); + completed = 1; + } if (int_status & AT91_MCI_TXRDY) pr_debug("Ready to transmit\n"); @@ -730,7 +747,7 @@ static irqreturn_t at91_mci_irq(int irq, void *devid) if (int_status & AT91_MCI_CMDRDY) { pr_debug("Command ready\n"); - completed = 1; + completed = at91_mci_handle_cmdrdy(host); } } @@ -830,11 +847,11 @@ static int __init at91_mci_probe(struct platform_device *pdev) host->bus_mode = 0; host->board = pdev->dev.platform_data; if (host->board->wire4) { -#ifdef SUPPORT_4WIRE - mmc->caps |= MMC_CAP_4_BIT_DATA; -#else - printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n"); -#endif + if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) + mmc->caps |= MMC_CAP_4_BIT_DATA; + else + printk("AT91 MMC: 4 wire bus mode not supported" + " - using 1 wire\n"); } /* -- cgit v1.2.3 From 7a2b94bc39915041304578188441f0f21aa5532a Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 16 May 2007 15:44:37 +0100 Subject: [ARM] pxa: remove MMC register defines from pxa-regs.h pxamci.h redefines the MMC registers differently so they can be used with ioremap. Remove the incompatible definitions from pxa-regs.h. Signed-off-by: Russell King --- drivers/mmc/host/pxamci.h | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/pxamci.h b/drivers/mmc/host/pxamci.h index 1b163220df2..df17c281278 100644 --- a/drivers/mmc/host/pxamci.h +++ b/drivers/mmc/host/pxamci.h @@ -1,25 +1,3 @@ -#undef MMC_STRPCL -#undef MMC_STAT -#undef MMC_CLKRT -#undef MMC_SPI -#undef MMC_CMDAT -#undef MMC_RESTO -#undef MMC_RDTO -#undef MMC_BLKLEN -#undef MMC_NOB -#undef MMC_PRTBUF -#undef MMC_I_MASK -#undef END_CMD_RES -#undef PRG_DONE -#undef DATA_TRAN_DONE -#undef MMC_I_REG -#undef MMC_CMD -#undef MMC_ARGH -#undef MMC_ARGL -#undef MMC_RES -#undef MMC_RXFIFO -#undef MMC_TXFIFO - #define MMC_STRPCL 0x0000 #define STOP_CLOCK (1 << 0) #define START_CLOCK (2 << 0) -- cgit v1.2.3 From 831441862956fffa17b9801db37e6ea1650b0f69 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Jul 2007 04:03:35 -0700 Subject: Freezer: make kernel threads nonfreezable by default Currently, the freezer treats all tasks as freezable, except for the kernel threads that explicitly set the PF_NOFREEZE flag for themselves. This approach is problematic, since it requires every kernel thread to either set PF_NOFREEZE explicitly, or call try_to_freeze(), even if it doesn't care for the freezing of tasks at all. It seems better to only require the kernel threads that want to or need to be frozen to use some freezer-related code and to remove any freezer-related code from the other (nonfreezable) kernel threads, which is done in this patch. The patch causes all kernel threads to be nonfreezable by default (ie. to have PF_NOFREEZE set by default) and introduces the set_freezable() function that should be called by the freezable kernel threads in order to unset PF_NOFREEZE. It also makes all of the currently freezable kernel threads call set_freezable(), so it shouldn't cause any (intentional) change of behaviour to appear. Additionally, it updates documentation to describe the freezing of tasks more accurately. [akpm@linux-foundation.org: build fixes] Signed-off-by: Rafael J. Wysocki Acked-by: Nigel Cunningham Cc: Pavel Machek Cc: Oleg Nesterov Cc: Gautham R Shenoy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/mmc/card/queue.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 4fb2089dc69..b53dac8d1b6 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -11,6 +11,7 @@ */ #include #include +#include #include #include @@ -44,11 +45,7 @@ static int mmc_queue_thread(void *d) struct mmc_queue *mq = d; struct request_queue *q = mq->queue; - /* - * Set iothread to ensure that we aren't put to sleep by - * the process freezing. We handle suspension ourselves. - */ - current->flags |= PF_MEMALLOC|PF_NOFREEZE; + current->flags |= PF_MEMALLOC; down(&mq->thread_sem); do { -- cgit v1.2.3