| /* | |
| * linux/drivers/mmc/core/host.c | |
| * | |
| * Copyright (C) 2003 Russell King, All Rights Reserved. | |
| * Copyright (C) 2007-2008 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 <linux/device.h> | |
| #include <linux/err.h> | |
| #include <linux/idr.h> | |
| #include <linux/pagemap.h> | |
| #include <linux/leds.h> | |
| #include <linux/mmc/host.h> | |
| #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) | |
| { | |
| int err; | |
| struct mmc_host *host; | |
| if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) | |
| return NULL; | |
| host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); | |
| if (!host) | |
| return NULL; | |
| spin_lock(&mmc_host_lock); | |
| err = idr_get_new(&mmc_host_idr, host, &host->index); | |
| spin_unlock(&mmc_host_lock); | |
| if (err) | |
| goto free; | |
| snprintf(host->class_dev.bus_id, BUS_ID_SIZE, | |
| "mmc%d", host->index); | |
| 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; | |
| free: | |
| kfree(host); | |
| return NULL; | |
| } | |
| EXPORT_SYMBOL(mmc_alloc_host); | |
| /** | |
| * mmc_add_host - initialise host hardware | |
| * @host: mmc host | |
| * | |
| * Register the host with the driver model. The host must be | |
| * prepared to start servicing requests before this function | |
| * completes. | |
| */ | |
| int mmc_add_host(struct mmc_host *host) | |
| { | |
| int err; | |
| WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && | |
| !host->ops->enable_sdio_irq); | |
| led_trigger_register_simple(host->class_dev.bus_id, &host->led); | |
| 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. No new requests will be issued | |
| * after this function has returned. | |
| */ | |
| void mmc_remove_host(struct mmc_host *host) | |
| { | |
| mmc_stop_host(host); | |
| device_del(&host->class_dev); | |
| led_trigger_unregister_simple(host->led); | |
| } | |
| 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) | |
| { | |
| spin_lock(&mmc_host_lock); | |
| idr_remove(&mmc_host_idr, host->index); | |
| spin_unlock(&mmc_host_lock); | |
| put_device(&host->class_dev); | |
| } | |
| EXPORT_SYMBOL(mmc_free_host); | |