[MMC] Add MMC class devices
Create a mmc_host class to allow enumeration of MMC host controllers even though they have no card(s) inserted. Patch based on work by Pierre Ossman. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
d366b64363
commit
00b137cfda
4 changed files with 87 additions and 14 deletions
|
@ -796,17 +796,13 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||||
{
|
{
|
||||||
struct mmc_host *host;
|
struct mmc_host *host;
|
||||||
|
|
||||||
host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
|
host = mmc_alloc_host_sysfs(extra, dev);
|
||||||
if (host) {
|
if (host) {
|
||||||
memset(host, 0, sizeof(struct mmc_host) + extra);
|
|
||||||
|
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
init_waitqueue_head(&host->wq);
|
init_waitqueue_head(&host->wq);
|
||||||
INIT_LIST_HEAD(&host->cards);
|
INIT_LIST_HEAD(&host->cards);
|
||||||
INIT_WORK(&host->detect, mmc_rescan, host);
|
INIT_WORK(&host->detect, mmc_rescan, host);
|
||||||
|
|
||||||
host->dev = dev;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* By default, hosts do not support SGIO or large requests.
|
* By default, hosts do not support SGIO or large requests.
|
||||||
* They have to set these according to their abilities.
|
* They have to set these according to their abilities.
|
||||||
|
@ -828,15 +824,15 @@ EXPORT_SYMBOL(mmc_alloc_host);
|
||||||
*/
|
*/
|
||||||
int mmc_add_host(struct mmc_host *host)
|
int mmc_add_host(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
static unsigned int host_num;
|
int ret;
|
||||||
|
|
||||||
snprintf(host->host_name, sizeof(host->host_name),
|
ret = mmc_add_host_sysfs(host);
|
||||||
"mmc%d", host_num++);
|
if (ret == 0) {
|
||||||
|
mmc_power_off(host);
|
||||||
|
mmc_detect_change(host);
|
||||||
|
}
|
||||||
|
|
||||||
mmc_power_off(host);
|
return ret;
|
||||||
mmc_detect_change(host);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_add_host);
|
EXPORT_SYMBOL(mmc_add_host);
|
||||||
|
@ -859,6 +855,7 @@ void mmc_remove_host(struct mmc_host *host)
|
||||||
}
|
}
|
||||||
|
|
||||||
mmc_power_off(host);
|
mmc_power_off(host);
|
||||||
|
mmc_remove_host_sysfs(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_remove_host);
|
EXPORT_SYMBOL(mmc_remove_host);
|
||||||
|
@ -872,7 +869,7 @@ EXPORT_SYMBOL(mmc_remove_host);
|
||||||
void mmc_free_host(struct mmc_host *host)
|
void mmc_free_host(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
flush_scheduled_work();
|
flush_scheduled_work();
|
||||||
kfree(host);
|
mmc_free_host_sysfs(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_free_host);
|
EXPORT_SYMBOL(mmc_free_host);
|
||||||
|
|
|
@ -13,4 +13,9 @@
|
||||||
void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
|
void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
|
||||||
int mmc_register_card(struct mmc_card *card);
|
int mmc_register_card(struct mmc_card *card);
|
||||||
void mmc_remove_card(struct mmc_card *card);
|
void mmc_remove_card(struct mmc_card *card);
|
||||||
|
|
||||||
|
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);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
|
#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 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...) \
|
#define MMC_ATTR(name, fmt, args...) \
|
||||||
static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
|
static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \
|
||||||
|
@ -224,13 +225,82 @@ void mmc_remove_card(struct mmc_card *card)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void mmc_host_classdev_release(struct class_device *dev)
|
||||||
|
{
|
||||||
|
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||||
|
kfree(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct class mmc_host_class = {
|
||||||
|
.name = "mmc_host",
|
||||||
|
.release = mmc_host_classdev_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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->dev = dev;
|
||||||
|
host->class_dev.dev = host->dev;
|
||||||
|
host->class_dev.class = &mmc_host_class;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
static unsigned int host_num;
|
||||||
|
|
||||||
|
snprintf(host->host_name, sizeof(host->host_name),
|
||||||
|
"mmc%d", host_num++);
|
||||||
|
|
||||||
|
strlcpy(host->class_dev.class_id, host->host_name, BUS_ID_SIZE);
|
||||||
|
return class_device_add(&host->class_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal function. Unregister a MMC host with the MMC class.
|
||||||
|
*/
|
||||||
|
void mmc_remove_host_sysfs(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
class_device_del(&host->class_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal function. Free a MMC host.
|
||||||
|
*/
|
||||||
|
void mmc_free_host_sysfs(struct mmc_host *host)
|
||||||
|
{
|
||||||
|
class_device_put(&host->class_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int __init mmc_init(void)
|
static int __init mmc_init(void)
|
||||||
{
|
{
|
||||||
return bus_register(&mmc_bus_type);
|
int ret = bus_register(&mmc_bus_type);
|
||||||
|
if (ret == 0) {
|
||||||
|
ret = class_register(&mmc_host_class);
|
||||||
|
if (ret)
|
||||||
|
bus_unregister(&mmc_bus_type);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit mmc_exit(void)
|
static void __exit mmc_exit(void)
|
||||||
{
|
{
|
||||||
|
class_unregister(&mmc_host_class);
|
||||||
bus_unregister(&mmc_bus_type);
|
bus_unregister(&mmc_bus_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ struct device;
|
||||||
|
|
||||||
struct mmc_host {
|
struct mmc_host {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
struct class_device class_dev;
|
||||||
struct mmc_host_ops *ops;
|
struct mmc_host_ops *ops;
|
||||||
unsigned int f_min;
|
unsigned int f_min;
|
||||||
unsigned int f_max;
|
unsigned int f_max;
|
||||||
|
|
Loading…
Reference in a new issue