]> Pileus Git - ~andy/linux/blob - drivers/mmc/core/sysfs.c
mmc: refactor bus operations
[~andy/linux] / drivers / mmc / core / sysfs.c
1 /*
2  *  linux/drivers/mmc/core/sysfs.c
3  *
4  *  Copyright (C) 2003 Russell King, All Rights Reserved.
5  *  Copyright 2007 Pierre Ossman
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  *
11  *  MMC sysfs/driver model support.
12  */
13 #include <linux/module.h>
14 #include <linux/init.h>
15 #include <linux/device.h>
16 #include <linux/idr.h>
17 #include <linux/workqueue.h>
18
19 #include <linux/mmc/card.h>
20 #include <linux/mmc/host.h>
21
22 #include "bus.h"
23 #include "sysfs.h"
24
25 #define to_mmc_driver(d)        container_of(d, struct mmc_driver, drv)
26 #define cls_dev_to_mmc_host(d)  container_of(d, struct mmc_host, class_dev)
27
28 int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs)
29 {
30         int error = 0;
31         int i;
32
33         for (i = 0; attr_name(attrs[i]); i++) {
34                 error = device_create_file(&card->dev, &attrs[i]);
35                 if (error) {
36                         while (--i >= 0)
37                                 device_remove_file(&card->dev, &attrs[i]);
38                         break;
39                 }
40         }
41
42         return error;
43 }
44
45 void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs)
46 {
47         int i;
48
49         for (i = 0; attr_name(attrs[i]); i++)
50                 device_remove_file(&card->dev, &attrs[i]);
51 }
52
53 static void mmc_host_classdev_release(struct device *dev)
54 {
55         struct mmc_host *host = cls_dev_to_mmc_host(dev);
56         kfree(host);
57 }
58
59 static struct class mmc_host_class = {
60         .name           = "mmc_host",
61         .dev_release    = mmc_host_classdev_release,
62 };
63
64 static DEFINE_IDR(mmc_host_idr);
65 static DEFINE_SPINLOCK(mmc_host_lock);
66
67 /*
68  * Internal function. Allocate a new MMC host.
69  */
70 struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev)
71 {
72         struct mmc_host *host;
73
74         host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
75         if (host) {
76                 memset(host, 0, sizeof(struct mmc_host) + extra);
77
78                 host->parent = dev;
79                 host->class_dev.parent = dev;
80                 host->class_dev.class = &mmc_host_class;
81                 device_initialize(&host->class_dev);
82         }
83
84         return host;
85 }
86
87 /*
88  * Internal function. Register a new MMC host with the MMC class.
89  */
90 int mmc_add_host_sysfs(struct mmc_host *host)
91 {
92         int err;
93
94         if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
95                 return -ENOMEM;
96
97         spin_lock(&mmc_host_lock);
98         err = idr_get_new(&mmc_host_idr, host, &host->index);
99         spin_unlock(&mmc_host_lock);
100         if (err)
101                 return err;
102
103         snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
104                  "mmc%d", host->index);
105
106         return device_add(&host->class_dev);
107 }
108
109 /*
110  * Internal function. Unregister a MMC host with the MMC class.
111  */
112 void mmc_remove_host_sysfs(struct mmc_host *host)
113 {
114         device_del(&host->class_dev);
115
116         spin_lock(&mmc_host_lock);
117         idr_remove(&mmc_host_idr, host->index);
118         spin_unlock(&mmc_host_lock);
119 }
120
121 /*
122  * Internal function. Free a MMC host.
123  */
124 void mmc_free_host_sysfs(struct mmc_host *host)
125 {
126         put_device(&host->class_dev);
127 }
128
129 static struct workqueue_struct *workqueue;
130
131 /*
132  * Internal function. Schedule delayed work in the MMC work queue.
133  */
134 int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
135 {
136         return queue_delayed_work(workqueue, work, delay);
137 }
138
139 /*
140  * Internal function. Flush all scheduled work from the MMC work queue.
141  */
142 void mmc_flush_scheduled_work(void)
143 {
144         flush_workqueue(workqueue);
145 }
146
147 static int __init mmc_init(void)
148 {
149         int ret;
150
151         workqueue = create_singlethread_workqueue("kmmcd");
152         if (!workqueue)
153                 return -ENOMEM;
154
155         ret = mmc_register_bus();
156         if (ret == 0) {
157                 ret = class_register(&mmc_host_class);
158                 if (ret)
159                         mmc_unregister_bus();
160         }
161         return ret;
162 }
163
164 static void __exit mmc_exit(void)
165 {
166         class_unregister(&mmc_host_class);
167         mmc_unregister_bus();
168         destroy_workqueue(workqueue);
169 }
170
171 module_init(mmc_init);
172 module_exit(mmc_exit);