2007-05-19 20:06:24 +08:00
|
|
|
/*
|
|
|
|
* 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 <linux/device.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/idr.h>
|
|
|
|
#include <linux/pagemap.h>
|
2007-09-24 13:15:48 +08:00
|
|
|
#include <linux/leds.h>
|
2007-05-19 20:06:24 +08:00
|
|
|
|
|
|
|
#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)
|
|
|
|
{
|
|
|
|
struct mmc_host *host;
|
|
|
|
|
2007-08-11 05:00:50 +08:00
|
|
|
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
|
2007-05-19 20:06:24 +08:00
|
|
|
if (!host)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
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
|
2007-07-12 02:22:11 +08:00
|
|
|
*
|
|
|
|
* Register the host with the driver model. The host must be
|
|
|
|
* prepared to start servicing requests before this function
|
|
|
|
* completes.
|
2007-05-19 20:06:24 +08:00
|
|
|
*/
|
|
|
|
int mmc_add_host(struct mmc_host *host)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
2007-07-24 14:09:39 +08:00
|
|
|
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
|
|
|
|
!host->ops->enable_sdio_irq);
|
|
|
|
|
2007-05-19 20:06:24 +08:00
|
|
|
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);
|
|
|
|
|
2007-09-24 13:15:48 +08:00
|
|
|
led_trigger_register_simple(host->class_dev.bus_id, &host->led);
|
|
|
|
|
2007-05-19 20:06:24 +08:00
|
|
|
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,
|
2007-07-12 02:22:11 +08:00
|
|
|
* and power down the MMC bus. No new requests will be issued
|
|
|
|
* after this function has returned.
|
2007-05-19 20:06:24 +08:00
|
|
|
*/
|
|
|
|
void mmc_remove_host(struct mmc_host *host)
|
|
|
|
{
|
|
|
|
mmc_stop_host(host);
|
|
|
|
|
|
|
|
device_del(&host->class_dev);
|
|
|
|
|
2007-09-24 13:15:48 +08:00
|
|
|
led_trigger_unregister(host->led);
|
|
|
|
|
2007-05-19 20:06:24 +08:00
|
|
|
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);
|
|
|
|
|