diff options
Diffstat (limited to 'drivers/mmc/core')
-rw-r--r-- | drivers/mmc/core/Makefile | 3 | ||||
-rw-r--r-- | drivers/mmc/core/bus.c | 253 | ||||
-rw-r--r-- | drivers/mmc/core/bus.h | 22 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 147 | ||||
-rw-r--r-- | drivers/mmc/core/core.h | 8 | ||||
-rw-r--r-- | drivers/mmc/core/host.c | 156 | ||||
-rw-r--r-- | drivers/mmc/core/host.h | 18 | ||||
-rw-r--r-- | drivers/mmc/core/mmc.c | 65 | ||||
-rw-r--r-- | drivers/mmc/core/sd.c | 63 | ||||
-rw-r--r-- | drivers/mmc/core/sysfs.c | 347 | ||||
-rw-r--r-- | drivers/mmc/core/sysfs.h | 19 |
11 files changed, 645 insertions, 456 deletions
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 1075b02ae75..3fdd08c7f14 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile | |||
@@ -7,5 +7,6 @@ ifeq ($(CONFIG_MMC_DEBUG),y) | |||
7 | endif | 7 | endif |
8 | 8 | ||
9 | obj-$(CONFIG_MMC) += mmc_core.o | 9 | obj-$(CONFIG_MMC) += mmc_core.o |
10 | mmc_core-y := core.o sysfs.o mmc.o mmc_ops.o sd.o sd_ops.o | 10 | mmc_core-y := core.o sysfs.o bus.o host.o \ |
11 | mmc.o mmc_ops.o sd.o sd_ops.o | ||
11 | 12 | ||
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 @@ | |||
1 | /* | ||
2 | * linux/drivers/mmc/core/bus.c | ||
3 | * | ||
4 | * Copyright (C) 2003 Russell King, All Rights Reserved. | ||
5 | * Copyright (C) 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 card bus driver model | ||
12 | */ | ||
13 | |||
14 | #include <linux/device.h> | ||
15 | #include <linux/err.h> | ||
16 | |||
17 | #include <linux/mmc/card.h> | ||
18 | #include <linux/mmc/host.h> | ||
19 | |||
20 | #include "sysfs.h" | ||
21 | #include "core.h" | ||
22 | #include "bus.h" | ||
23 | |||
24 | #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) | ||
25 | #define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) | ||
26 | |||
27 | static ssize_t mmc_type_show(struct device *dev, | ||
28 | struct device_attribute *attr, char *buf) | ||
29 | { | ||
30 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
31 | |||
32 | switch (card->type) { | ||
33 | case MMC_TYPE_MMC: | ||
34 | return sprintf(buf, "MMC\n"); | ||
35 | case MMC_TYPE_SD: | ||
36 | return sprintf(buf, "SD\n"); | ||
37 | default: | ||
38 | return -EFAULT; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | static struct device_attribute mmc_dev_attrs[] = { | ||
43 | MMC_ATTR_RO(type), | ||
44 | __ATTR_NULL, | ||
45 | }; | ||
46 | |||
47 | /* | ||
48 | * This currently matches any MMC driver to any MMC card - drivers | ||
49 | * themselves make the decision whether to drive this card in their | ||
50 | * probe method. | ||
51 | */ | ||
52 | static int mmc_bus_match(struct device *dev, struct device_driver *drv) | ||
53 | { | ||
54 | return 1; | ||
55 | } | ||
56 | |||
57 | static int | ||
58 | mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, | ||
59 | int buf_size) | ||
60 | { | ||
61 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
62 | int retval = 0, i = 0, length = 0; | ||
63 | |||
64 | #define add_env(fmt,val) do { \ | ||
65 | retval = add_uevent_var(envp, num_envp, &i, \ | ||
66 | buf, buf_size, &length, \ | ||
67 | fmt, val); \ | ||
68 | if (retval) \ | ||
69 | return retval; \ | ||
70 | } while (0); | ||
71 | |||
72 | switch (card->type) { | ||
73 | case MMC_TYPE_MMC: | ||
74 | add_env("MMC_TYPE=%s", "MMC"); | ||
75 | break; | ||
76 | case MMC_TYPE_SD: | ||
77 | add_env("MMC_TYPE=%s", "SD"); | ||
78 | break; | ||
79 | } | ||
80 | |||
81 | add_env("MMC_NAME=%s", mmc_card_name(card)); | ||
82 | |||
83 | #undef add_env | ||
84 | |||
85 | envp[i] = NULL; | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static int mmc_bus_probe(struct device *dev) | ||
91 | { | ||
92 | struct mmc_driver *drv = to_mmc_driver(dev->driver); | ||
93 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
94 | |||
95 | return drv->probe(card); | ||
96 | } | ||
97 | |||
98 | static int mmc_bus_remove(struct device *dev) | ||
99 | { | ||
100 | struct mmc_driver *drv = to_mmc_driver(dev->driver); | ||
101 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
102 | |||
103 | drv->remove(card); | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int mmc_bus_suspend(struct device *dev, pm_message_t state) | ||
109 | { | ||
110 | struct mmc_driver *drv = to_mmc_driver(dev->driver); | ||
111 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
112 | int ret = 0; | ||
113 | |||
114 | if (dev->driver && drv->suspend) | ||
115 | ret = drv->suspend(card, state); | ||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | static int mmc_bus_resume(struct device *dev) | ||
120 | { | ||
121 | struct mmc_driver *drv = to_mmc_driver(dev->driver); | ||
122 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
123 | int ret = 0; | ||
124 | |||
125 | if (dev->driver && drv->resume) | ||
126 | ret = drv->resume(card); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | static struct bus_type mmc_bus_type = { | ||
131 | .name = "mmc", | ||
132 | .dev_attrs = mmc_dev_attrs, | ||
133 | .match = mmc_bus_match, | ||
134 | .uevent = mmc_bus_uevent, | ||
135 | .probe = mmc_bus_probe, | ||
136 | .remove = mmc_bus_remove, | ||
137 | .suspend = mmc_bus_suspend, | ||
138 | .resume = mmc_bus_resume, | ||
139 | }; | ||
140 | |||
141 | int mmc_register_bus(void) | ||
142 | { | ||
143 | return bus_register(&mmc_bus_type); | ||
144 | } | ||
145 | |||
146 | void mmc_unregister_bus(void) | ||
147 | { | ||
148 | bus_unregister(&mmc_bus_type); | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * mmc_register_driver - register a media driver | ||
153 | * @drv: MMC media driver | ||
154 | */ | ||
155 | int mmc_register_driver(struct mmc_driver *drv) | ||
156 | { | ||
157 | drv->drv.bus = &mmc_bus_type; | ||
158 | return driver_register(&drv->drv); | ||
159 | } | ||
160 | |||
161 | EXPORT_SYMBOL(mmc_register_driver); | ||
162 | |||
163 | /** | ||
164 | * mmc_unregister_driver - unregister a media driver | ||
165 | * @drv: MMC media driver | ||
166 | */ | ||
167 | void mmc_unregister_driver(struct mmc_driver *drv) | ||
168 | { | ||
169 | drv->drv.bus = &mmc_bus_type; | ||
170 | driver_unregister(&drv->drv); | ||
171 | } | ||
172 | |||
173 | EXPORT_SYMBOL(mmc_unregister_driver); | ||
174 | |||
175 | static void mmc_release_card(struct device *dev) | ||
176 | { | ||
177 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
178 | |||
179 | kfree(card); | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * Allocate and initialise a new MMC card structure. | ||
184 | */ | ||
185 | struct mmc_card *mmc_alloc_card(struct mmc_host *host) | ||
186 | { | ||
187 | struct mmc_card *card; | ||
188 | |||
189 | card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); | ||
190 | if (!card) | ||
191 | return ERR_PTR(-ENOMEM); | ||
192 | |||
193 | memset(card, 0, sizeof(struct mmc_card)); | ||
194 | |||
195 | card->host = host; | ||
196 | |||
197 | device_initialize(&card->dev); | ||
198 | |||
199 | card->dev.parent = mmc_classdev(host); | ||
200 | card->dev.bus = &mmc_bus_type; | ||
201 | card->dev.release = mmc_release_card; | ||
202 | |||
203 | return card; | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * Register a new MMC card with the driver model. | ||
208 | */ | ||
209 | int mmc_add_card(struct mmc_card *card) | ||
210 | { | ||
211 | int ret; | ||
212 | |||
213 | snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), | ||
214 | "%s:%04x", mmc_hostname(card->host), card->rca); | ||
215 | |||
216 | card->dev.uevent_suppress = 1; | ||
217 | |||
218 | ret = device_add(&card->dev); | ||
219 | if (ret) | ||
220 | return ret; | ||
221 | |||
222 | if (card->host->bus_ops->sysfs_add) { | ||
223 | ret = card->host->bus_ops->sysfs_add(card->host, card); | ||
224 | if (ret) { | ||
225 | device_del(&card->dev); | ||
226 | return ret; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | card->dev.uevent_suppress = 0; | ||
231 | |||
232 | kobject_uevent(&card->dev.kobj, KOBJ_ADD); | ||
233 | |||
234 | mmc_card_set_present(card); | ||
235 | |||
236 | return 0; | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * Unregister a new MMC card with the driver model, and | ||
241 | * (eventually) free it. | ||
242 | */ | ||
243 | void mmc_remove_card(struct mmc_card *card) | ||
244 | { | ||
245 | if (mmc_card_present(card)) { | ||
246 | if (card->host->bus_ops->sysfs_remove) | ||
247 | card->host->bus_ops->sysfs_remove(card->host, card); | ||
248 | device_del(&card->dev); | ||
249 | } | ||
250 | |||
251 | put_device(&card->dev); | ||
252 | } | ||
253 | |||
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 @@ | |||
1 | /* | ||
2 | * linux/drivers/mmc/core/bus.h | ||
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 | #ifndef _MMC_CORE_BUS_H | ||
12 | #define _MMC_CORE_BUS_H | ||
13 | |||
14 | struct mmc_card *mmc_alloc_card(struct mmc_host *host); | ||
15 | int mmc_add_card(struct mmc_card *card); | ||
16 | void mmc_remove_card(struct mmc_card *card); | ||
17 | |||
18 | int mmc_register_bus(void); | ||
19 | void mmc_unregister_bus(void); | ||
20 | |||
21 | #endif | ||
22 | |||
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7385acfa1dd..b5d8a6d90cc 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c | |||
@@ -27,7 +27,8 @@ | |||
27 | #include <linux/mmc/sd.h> | 27 | #include <linux/mmc/sd.h> |
28 | 28 | ||
29 | #include "core.h" | 29 | #include "core.h" |
30 | #include "sysfs.h" | 30 | #include "bus.h" |
31 | #include "host.h" | ||
31 | 32 | ||
32 | #include "mmc_ops.h" | 33 | #include "mmc_ops.h" |
33 | #include "sd_ops.h" | 34 | #include "sd_ops.h" |
@@ -35,6 +36,25 @@ | |||
35 | extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); | 36 | extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); |
36 | extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); | 37 | extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); |
37 | 38 | ||
39 | static struct workqueue_struct *workqueue; | ||
40 | |||
41 | /* | ||
42 | * Internal function. Schedule delayed work in the MMC work queue. | ||
43 | */ | ||
44 | static int mmc_schedule_delayed_work(struct delayed_work *work, | ||
45 | unsigned long delay) | ||
46 | { | ||
47 | return queue_delayed_work(workqueue, work, delay); | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * Internal function. Flush all scheduled work from the MMC work queue. | ||
52 | */ | ||
53 | static void mmc_flush_scheduled_work(void) | ||
54 | { | ||
55 | flush_workqueue(workqueue); | ||
56 | } | ||
57 | |||
38 | /** | 58 | /** |
39 | * mmc_request_done - finish processing an MMC request | 59 | * mmc_request_done - finish processing an MMC request |
40 | * @host: MMC host which completed request | 60 | * @host: MMC host which completed request |
@@ -369,22 +389,6 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing) | |||
369 | } | 389 | } |
370 | 390 | ||
371 | /* | 391 | /* |
372 | * Allocate a new MMC card | ||
373 | */ | ||
374 | struct mmc_card *mmc_alloc_card(struct mmc_host *host) | ||
375 | { | ||
376 | struct mmc_card *card; | ||
377 | |||
378 | card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); | ||
379 | if (!card) | ||
380 | return ERR_PTR(-ENOMEM); | ||
381 | |||
382 | mmc_init_card(card, host); | ||
383 | |||
384 | return card; | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * Apply power to the MMC stack. This is a two-stage process. | 392 | * Apply power to the MMC stack. This is a two-stage process. |
389 | * First, we enable power to the card without the clock running. | 393 | * First, we enable power to the card without the clock running. |
390 | * We then wait a bit for the power to stabilise. Finally, | 394 | * We then wait a bit for the power to stabilise. Finally, |
@@ -512,7 +516,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) | |||
512 | EXPORT_SYMBOL(mmc_detect_change); | 516 | EXPORT_SYMBOL(mmc_detect_change); |
513 | 517 | ||
514 | 518 | ||
515 | static void mmc_rescan(struct work_struct *work) | 519 | void mmc_rescan(struct work_struct *work) |
516 | { | 520 | { |
517 | struct mmc_host *host = | 521 | struct mmc_host *host = |
518 | container_of(work, struct mmc_host, detect.work); | 522 | container_of(work, struct mmc_host, detect.work); |
@@ -561,69 +565,13 @@ static void mmc_rescan(struct work_struct *work) | |||
561 | } | 565 | } |
562 | } | 566 | } |
563 | 567 | ||
564 | 568 | void mmc_start_host(struct mmc_host *host) | |
565 | /** | ||
566 | * mmc_alloc_host - initialise the per-host structure. | ||
567 | * @extra: sizeof private data structure | ||
568 | * @dev: pointer to host device model structure | ||
569 | * | ||
570 | * Initialise the per-host structure. | ||
571 | */ | ||
572 | struct mmc_host *mmc_alloc_host(int extra, struct device *dev) | ||
573 | { | 569 | { |
574 | struct mmc_host *host; | 570 | mmc_power_off(host); |
575 | 571 | mmc_detect_change(host, 0); | |
576 | host = mmc_alloc_host_sysfs(extra, dev); | ||
577 | if (host) { | ||
578 | spin_lock_init(&host->lock); | ||
579 | init_waitqueue_head(&host->wq); | ||
580 | INIT_DELAYED_WORK(&host->detect, mmc_rescan); | ||
581 | |||
582 | /* | ||
583 | * By default, hosts do not support SGIO or large requests. | ||
584 | * They have to set these according to their abilities. | ||
585 | */ | ||
586 | host->max_hw_segs = 1; | ||
587 | host->max_phys_segs = 1; | ||
588 | host->max_seg_size = PAGE_CACHE_SIZE; | ||
589 | |||
590 | host->max_req_size = PAGE_CACHE_SIZE; | ||
591 | host->max_blk_size = 512; | ||
592 | host->max_blk_count = PAGE_CACHE_SIZE / 512; | ||
593 | } | ||
594 | |||
595 | return host; | ||
596 | } | ||
597 | |||
598 | EXPORT_SYMBOL(mmc_alloc_host); | ||
599 | |||
600 | /** | ||
601 | * mmc_add_host - initialise host hardware | ||
602 | * @host: mmc host | ||
603 | */ | ||
604 | int mmc_add_host(struct mmc_host *host) | ||
605 | { | ||
606 | int ret; | ||
607 | |||
608 | ret = mmc_add_host_sysfs(host); | ||
609 | if (ret == 0) { | ||
610 | mmc_power_off(host); | ||
611 | mmc_detect_change(host, 0); | ||
612 | } | ||
613 | |||
614 | return ret; | ||
615 | } | 572 | } |
616 | 573 | ||
617 | EXPORT_SYMBOL(mmc_add_host); | 574 | void mmc_stop_host(struct mmc_host *host) |
618 | |||
619 | /** | ||
620 | * mmc_remove_host - remove host hardware | ||
621 | * @host: mmc host | ||
622 | * | ||
623 | * Unregister and remove all cards associated with this host, | ||
624 | * and power down the MMC bus. | ||
625 | */ | ||
626 | void mmc_remove_host(struct mmc_host *host) | ||
627 | { | 575 | { |
628 | #ifdef CONFIG_MMC_DEBUG | 576 | #ifdef CONFIG_MMC_DEBUG |
629 | unsigned long flags; | 577 | unsigned long flags; |
@@ -648,24 +596,8 @@ void mmc_remove_host(struct mmc_host *host) | |||
648 | BUG_ON(host->card); | 596 | BUG_ON(host->card); |
649 | 597 | ||
650 | mmc_power_off(host); | 598 | mmc_power_off(host); |
651 | mmc_remove_host_sysfs(host); | ||
652 | } | 599 | } |
653 | 600 | ||
654 | EXPORT_SYMBOL(mmc_remove_host); | ||
655 | |||
656 | /** | ||
657 | * mmc_free_host - free the host structure | ||
658 | * @host: mmc host | ||
659 | * | ||
660 | * Free the host once all references to it have been dropped. | ||
661 | */ | ||
662 | void mmc_free_host(struct mmc_host *host) | ||
663 | { | ||
664 | mmc_free_host_sysfs(host); | ||
665 | } | ||
666 | |||
667 | EXPORT_SYMBOL(mmc_free_host); | ||
668 | |||
669 | #ifdef CONFIG_PM | 601 | #ifdef CONFIG_PM |
670 | 602 | ||
671 | /** | 603 | /** |
@@ -726,4 +658,31 @@ EXPORT_SYMBOL(mmc_resume_host); | |||
726 | 658 | ||
727 | #endif | 659 | #endif |
728 | 660 | ||
661 | static int __init mmc_init(void) | ||
662 | { | ||
663 | int ret; | ||
664 | |||
665 | workqueue = create_singlethread_workqueue("kmmcd"); | ||
666 | if (!workqueue) | ||
667 | return -ENOMEM; | ||
668 | |||
669 | ret = mmc_register_bus(); | ||
670 | if (ret == 0) { | ||
671 | ret = mmc_register_host_class(); | ||
672 | if (ret) | ||
673 | mmc_unregister_bus(); | ||
674 | } | ||
675 | return ret; | ||
676 | } | ||
677 | |||
678 | static void __exit mmc_exit(void) | ||
679 | { | ||
680 | mmc_unregister_host_class(); | ||
681 | mmc_unregister_bus(); | ||
682 | destroy_workqueue(workqueue); | ||
683 | } | ||
684 | |||
685 | module_init(mmc_init); | ||
686 | module_exit(mmc_exit); | ||
687 | |||
729 | MODULE_LICENSE("GPL"); | 688 | MODULE_LICENSE("GPL"); |
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 177264d090a..ae006b30dd8 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h | |||
@@ -18,6 +18,8 @@ | |||
18 | struct mmc_bus_ops { | 18 | struct mmc_bus_ops { |
19 | void (*remove)(struct mmc_host *); | 19 | void (*remove)(struct mmc_host *); |
20 | void (*detect)(struct mmc_host *); | 20 | void (*detect)(struct mmc_host *); |
21 | int (*sysfs_add)(struct mmc_host *, struct mmc_card *card); | ||
22 | void (*sysfs_remove)(struct mmc_host *, struct mmc_card *card); | ||
21 | void (*suspend)(struct mmc_host *); | 23 | void (*suspend)(struct mmc_host *); |
22 | void (*resume)(struct mmc_host *); | 24 | void (*resume)(struct mmc_host *); |
23 | }; | 25 | }; |
@@ -54,8 +56,6 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width); | |||
54 | u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); | 56 | u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); |
55 | void mmc_set_timing(struct mmc_host *host, unsigned int timing); | 57 | void mmc_set_timing(struct mmc_host *host, unsigned int timing); |
56 | 58 | ||
57 | struct mmc_card *mmc_alloc_card(struct mmc_host *host); | ||
58 | |||
59 | static inline void mmc_delay(unsigned int ms) | 59 | static inline void mmc_delay(unsigned int ms) |
60 | { | 60 | { |
61 | if (ms < 1000 / HZ) { | 61 | if (ms < 1000 / HZ) { |
@@ -66,5 +66,9 @@ static inline void mmc_delay(unsigned int ms) | |||
66 | } | 66 | } |
67 | } | 67 | } |
68 | 68 | ||
69 | void mmc_rescan(struct work_struct *work); | ||
70 | void mmc_start_host(struct mmc_host *host); | ||
71 | void mmc_stop_host(struct mmc_host *host); | ||
72 | |||
69 | #endif | 73 | #endif |
70 | 74 | ||
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 @@ | |||
1 | /* | ||
2 | * linux/drivers/mmc/core/host.c | ||
3 | * | ||
4 | * Copyright (C) 2003 Russell King, All Rights Reserved. | ||
5 | * Copyright (C) 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 host class device management | ||
12 | */ | ||
13 | |||
14 | #include <linux/device.h> | ||
15 | #include <linux/err.h> | ||
16 | #include <linux/idr.h> | ||
17 | #include <linux/pagemap.h> | ||
18 | |||
19 | #include <linux/mmc/host.h> | ||
20 | |||
21 | #include "core.h" | ||
22 | #include "host.h" | ||
23 | |||
24 | #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) | ||
25 | |||
26 | static void mmc_host_classdev_release(struct device *dev) | ||
27 | { | ||
28 | struct mmc_host *host = cls_dev_to_mmc_host(dev); | ||
29 | kfree(host); | ||
30 | } | ||
31 | |||
32 | static struct class mmc_host_class = { | ||
33 | .name = "mmc_host", | ||
34 | .dev_release = mmc_host_classdev_release, | ||
35 | }; | ||
36 | |||
37 | int mmc_register_host_class(void) | ||
38 | { | ||
39 | return class_register(&mmc_host_class); | ||
40 | } | ||
41 | |||
42 | void mmc_unregister_host_class(void) | ||
43 | { | ||
44 | class_unregister(&mmc_host_class); | ||
45 | } | ||
46 | |||
47 | static DEFINE_IDR(mmc_host_idr); | ||
48 | static DEFINE_SPINLOCK(mmc_host_lock); | ||
49 | |||
50 | /** | ||
51 | * mmc_alloc_host - initialise the per-host structure. | ||
52 | * @extra: sizeof private data structure | ||
53 | * @dev: pointer to host device model structure | ||
54 | * | ||
55 | * Initialise the per-host structure. | ||
56 | */ | ||
57 | struct mmc_host *mmc_alloc_host(int extra, struct device *dev) | ||
58 | { | ||
59 | struct mmc_host *host; | ||
60 | |||
61 | host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); | ||
62 | if (!host) | ||
63 | return NULL; | ||
64 | |||
65 | memset(host, 0, sizeof(struct mmc_host) + extra); | ||
66 | |||
67 | host->parent = dev; | ||
68 | host->class_dev.parent = dev; | ||
69 | host->class_dev.class = &mmc_host_class; | ||
70 | device_initialize(&host->class_dev); | ||
71 | |||
72 | spin_lock_init(&host->lock); | ||
73 | init_waitqueue_head(&host->wq); | ||
74 | INIT_DELAYED_WORK(&host->detect, mmc_rescan); | ||
75 | |||
76 | /* | ||
77 | * By default, hosts do not support SGIO or large requests. | ||
78 | * They have to set these according to their abilities. | ||
79 | */ | ||
80 | host->max_hw_segs = 1; | ||
81 | host->max_phys_segs = 1; | ||
82 | host->max_seg_size = PAGE_CACHE_SIZE; | ||
83 | |||
84 | host->max_req_size = PAGE_CACHE_SIZE; | ||
85 | host->max_blk_size = 512; | ||
86 | host->max_blk_count = PAGE_CACHE_SIZE / 512; | ||
87 | |||
88 | return host; | ||
89 | } | ||
90 | |||
91 | EXPORT_SYMBOL(mmc_alloc_host); | ||
92 | |||
93 | /** | ||
94 | * mmc_add_host - initialise host hardware | ||
95 | * @host: mmc host | ||
96 | */ | ||
97 | int mmc_add_host(struct mmc_host *host) | ||
98 | { | ||
99 | int err; | ||
100 | |||
101 | if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) | ||
102 | return -ENOMEM; | ||
103 | |||
104 | spin_lock(&mmc_host_lock); | ||
105 | err = idr_get_new(&mmc_host_idr, host, &host->index); | ||
106 | spin_unlock(&mmc_host_lock); | ||
107 | if (err) | ||
108 | return err; | ||
109 | |||
110 | snprintf(host->class_dev.bus_id, BUS_ID_SIZE, | ||
111 | "mmc%d", host->index); | ||
112 | |||
113 | err = device_add(&host->class_dev); | ||
114 | if (err) | ||
115 | return err; | ||
116 | |||
117 | mmc_start_host(host); | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | EXPORT_SYMBOL(mmc_add_host); | ||
123 | |||
124 | /** | ||
125 | * mmc_remove_host - remove host hardware | ||
126 | * @host: mmc host | ||
127 | * | ||
128 | * Unregister and remove all cards associated with this host, | ||
129 | * and power down the MMC bus. | ||
130 | */ | ||
131 | void mmc_remove_host(struct mmc_host *host) | ||
132 | { | ||
133 | mmc_stop_host(host); | ||
134 | |||
135 | device_del(&host->class_dev); | ||
136 | |||
137 | spin_lock(&mmc_host_lock); | ||
138 | idr_remove(&mmc_host_idr, host->index); | ||
139 | spin_unlock(&mmc_host_lock); | ||
140 | } | ||
141 | |||
142 | EXPORT_SYMBOL(mmc_remove_host); | ||
143 | |||
144 | /** | ||
145 | * mmc_free_host - free the host structure | ||
146 | * @host: mmc host | ||
147 | * | ||
148 | * Free the host once all references to it have been dropped. | ||
149 | */ | ||
150 | void mmc_free_host(struct mmc_host *host) | ||
151 | { | ||
152 | put_device(&host->class_dev); | ||
153 | } | ||
154 | |||
155 | EXPORT_SYMBOL(mmc_free_host); | ||
156 | |||
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 @@ | |||
1 | /* | ||
2 | * linux/drivers/mmc/core/host.h | ||
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 | #ifndef _MMC_CORE_HOST_H | ||
12 | #define _MMC_CORE_HOST_H | ||
13 | |||
14 | int mmc_register_host_class(void); | ||
15 | void mmc_unregister_host_class(void); | ||
16 | |||
17 | #endif | ||
18 | |||
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 42cc2867ed7..66f85bfa8db 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
@@ -18,6 +18,7 @@ | |||
18 | 18 | ||
19 | #include "core.h" | 19 | #include "core.h" |
20 | #include "sysfs.h" | 20 | #include "sysfs.h" |
21 | #include "bus.h" | ||
21 | #include "mmc_ops.h" | 22 | #include "mmc_ops.h" |
22 | 23 | ||
23 | static const unsigned int tran_exp[] = { | 24 | static const unsigned int tran_exp[] = { |
@@ -236,7 +237,7 @@ out: | |||
236 | * In the case of a resume, "curcard" will contain the card | 237 | * In the case of a resume, "curcard" will contain the card |
237 | * we're trying to reinitialise. | 238 | * we're trying to reinitialise. |
238 | */ | 239 | */ |
239 | static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, | 240 | static int mmc_init_card(struct mmc_host *host, u32 ocr, |
240 | struct mmc_card *oldcard) | 241 | struct mmc_card *oldcard) |
241 | { | 242 | { |
242 | struct mmc_card *card; | 243 | struct mmc_card *card; |
@@ -413,8 +414,7 @@ static void mmc_detect(struct mmc_host *host) | |||
413 | mmc_release_host(host); | 414 | mmc_release_host(host); |
414 | 415 | ||
415 | if (err != MMC_ERR_NONE) { | 416 | if (err != MMC_ERR_NONE) { |
416 | mmc_remove_card(host->card); | 417 | mmc_remove(host); |
417 | host->card = NULL; | ||
418 | 418 | ||
419 | mmc_claim_host(host); | 419 | mmc_claim_host(host); |
420 | mmc_detach_bus(host); | 420 | mmc_detach_bus(host); |
@@ -422,6 +422,53 @@ static void mmc_detect(struct mmc_host *host) | |||
422 | } | 422 | } |
423 | } | 423 | } |
424 | 424 | ||
425 | MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], | ||
426 | card->raw_cid[2], card->raw_cid[3]); | ||
427 | MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], | ||
428 | card->raw_csd[2], card->raw_csd[3]); | ||
429 | MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year); | ||
430 | MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev); | ||
431 | MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev); | ||
432 | MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid); | ||
433 | MMC_ATTR_FN(name, "%s\n", card->cid.prod_name); | ||
434 | MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid); | ||
435 | MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial); | ||
436 | |||
437 | static struct device_attribute mmc_dev_attrs[] = { | ||
438 | MMC_ATTR_RO(cid), | ||
439 | MMC_ATTR_RO(csd), | ||
440 | MMC_ATTR_RO(date), | ||
441 | MMC_ATTR_RO(fwrev), | ||
442 | MMC_ATTR_RO(hwrev), | ||
443 | MMC_ATTR_RO(manfid), | ||
444 | MMC_ATTR_RO(name), | ||
445 | MMC_ATTR_RO(oemid), | ||
446 | MMC_ATTR_RO(serial), | ||
447 | __ATTR_NULL, | ||
448 | }; | ||
449 | |||
450 | /* | ||
451 | * Adds sysfs entries as relevant. | ||
452 | */ | ||
453 | static int mmc_sysfs_add(struct mmc_host *host, struct mmc_card *card) | ||
454 | { | ||
455 | int ret; | ||
456 | |||
457 | ret = mmc_add_attrs(card, mmc_dev_attrs); | ||
458 | if (ret < 0) | ||
459 | return ret; | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | /* | ||
465 | * Removes the sysfs entries added by mmc_sysfs_add(). | ||
466 | */ | ||
467 | static void mmc_sysfs_remove(struct mmc_host *host, struct mmc_card *card) | ||
468 | { | ||
469 | mmc_remove_attrs(card, mmc_dev_attrs); | ||
470 | } | ||
471 | |||
425 | #ifdef CONFIG_MMC_UNSAFE_RESUME | 472 | #ifdef CONFIG_MMC_UNSAFE_RESUME |
426 | 473 | ||
427 | /* | 474 | /* |
@@ -453,11 +500,9 @@ static void mmc_resume(struct mmc_host *host) | |||
453 | 500 | ||
454 | mmc_claim_host(host); | 501 | mmc_claim_host(host); |
455 | 502 | ||
456 | err = mmc_sd_init_card(host, host->ocr, host->card); | 503 | err = mmc_init_card(host, host->ocr, host->card); |
457 | if (err != MMC_ERR_NONE) { | 504 | if (err != MMC_ERR_NONE) { |
458 | mmc_remove_card(host->card); | 505 | mmc_remove(host); |
459 | host->card = NULL; | ||
460 | |||
461 | mmc_detach_bus(host); | 506 | mmc_detach_bus(host); |
462 | } | 507 | } |
463 | 508 | ||
@@ -474,6 +519,8 @@ static void mmc_resume(struct mmc_host *host) | |||
474 | static const struct mmc_bus_ops mmc_ops = { | 519 | static const struct mmc_bus_ops mmc_ops = { |
475 | .remove = mmc_remove, | 520 | .remove = mmc_remove, |
476 | .detect = mmc_detect, | 521 | .detect = mmc_detect, |
522 | .sysfs_add = mmc_sysfs_add, | ||
523 | .sysfs_remove = mmc_sysfs_remove, | ||
477 | .suspend = mmc_suspend, | 524 | .suspend = mmc_suspend, |
478 | .resume = mmc_resume, | 525 | .resume = mmc_resume, |
479 | }; | 526 | }; |
@@ -512,13 +559,13 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) | |||
512 | /* | 559 | /* |
513 | * Detect and init the card. | 560 | * Detect and init the card. |
514 | */ | 561 | */ |
515 | err = mmc_sd_init_card(host, host->ocr, NULL); | 562 | err = mmc_init_card(host, host->ocr, NULL); |
516 | if (err != MMC_ERR_NONE) | 563 | if (err != MMC_ERR_NONE) |
517 | goto err; | 564 | goto err; |
518 | 565 | ||
519 | mmc_release_host(host); | 566 | mmc_release_host(host); |
520 | 567 | ||
521 | err = mmc_register_card(host->card); | 568 | err = mmc_add_card(host->card); |
522 | if (err) | 569 | if (err) |
523 | goto reclaim_host; | 570 | goto reclaim_host; |
524 | 571 | ||
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 @@ | |||
19 | 19 | ||
20 | #include "core.h" | 20 | #include "core.h" |
21 | #include "sysfs.h" | 21 | #include "sysfs.h" |
22 | #include "bus.h" | ||
22 | #include "mmc_ops.h" | 23 | #include "mmc_ops.h" |
23 | #include "sd_ops.h" | 24 | #include "sd_ops.h" |
24 | 25 | ||
25 | #include "core.h" | ||
26 | |||
27 | static const unsigned int tran_exp[] = { | 26 | static const unsigned int tran_exp[] = { |
28 | 10000, 100000, 1000000, 10000000, | 27 | 10000, 100000, 1000000, 10000000, |
29 | 0, 0, 0, 0 | 28 | 0, 0, 0, 0 |
@@ -487,8 +486,7 @@ static void mmc_sd_detect(struct mmc_host *host) | |||
487 | mmc_release_host(host); | 486 | mmc_release_host(host); |
488 | 487 | ||
489 | if (err != MMC_ERR_NONE) { | 488 | if (err != MMC_ERR_NONE) { |
490 | mmc_remove_card(host->card); | 489 | mmc_sd_remove(host); |
491 | host->card = NULL; | ||
492 | 490 | ||
493 | mmc_claim_host(host); | 491 | mmc_claim_host(host); |
494 | mmc_detach_bus(host); | 492 | mmc_detach_bus(host); |
@@ -496,6 +494,55 @@ static void mmc_sd_detect(struct mmc_host *host) | |||
496 | } | 494 | } |
497 | } | 495 | } |
498 | 496 | ||
497 | MMC_ATTR_FN(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], | ||
498 | card->raw_cid[2], card->raw_cid[3]); | ||
499 | MMC_ATTR_FN(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], | ||
500 | card->raw_csd[2], card->raw_csd[3]); | ||
501 | MMC_ATTR_FN(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); | ||
502 | MMC_ATTR_FN(date, "%02d/%04d\n", card->cid.month, card->cid.year); | ||
503 | MMC_ATTR_FN(fwrev, "0x%x\n", card->cid.fwrev); | ||
504 | MMC_ATTR_FN(hwrev, "0x%x\n", card->cid.hwrev); | ||
505 | MMC_ATTR_FN(manfid, "0x%06x\n", card->cid.manfid); | ||
506 | MMC_ATTR_FN(name, "%s\n", card->cid.prod_name); | ||
507 | MMC_ATTR_FN(oemid, "0x%04x\n", card->cid.oemid); | ||
508 | MMC_ATTR_FN(serial, "0x%08x\n", card->cid.serial); | ||
509 | |||
510 | static struct device_attribute mmc_sd_dev_attrs[] = { | ||
511 | MMC_ATTR_RO(cid), | ||
512 | MMC_ATTR_RO(csd), | ||
513 | MMC_ATTR_RO(scr), | ||
514 | MMC_ATTR_RO(date), | ||
515 | MMC_ATTR_RO(fwrev), | ||
516 | MMC_ATTR_RO(hwrev), | ||
517 | MMC_ATTR_RO(manfid), | ||
518 | MMC_ATTR_RO(name), | ||
519 | MMC_ATTR_RO(oemid), | ||
520 | MMC_ATTR_RO(serial), | ||
521 | __ATTR_NULL, | ||
522 | }; | ||
523 | |||
524 | /* | ||
525 | * Adds sysfs entries as relevant. | ||
526 | */ | ||
527 | static int mmc_sd_sysfs_add(struct mmc_host *host, struct mmc_card *card) | ||
528 | { | ||
529 | int ret; | ||
530 | |||
531 | ret = mmc_add_attrs(card, mmc_sd_dev_attrs); | ||
532 | if (ret < 0) | ||
533 | return ret; | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | /* | ||
539 | * Removes the sysfs entries added by mmc_sysfs_add(). | ||
540 | */ | ||
541 | static void mmc_sd_sysfs_remove(struct mmc_host *host, struct mmc_card *card) | ||
542 | { | ||
543 | mmc_remove_attrs(card, mmc_sd_dev_attrs); | ||
544 | } | ||
545 | |||
499 | #ifdef CONFIG_MMC_UNSAFE_RESUME | 546 | #ifdef CONFIG_MMC_UNSAFE_RESUME |
500 | 547 | ||
501 | /* | 548 | /* |
@@ -529,9 +576,7 @@ static void mmc_sd_resume(struct mmc_host *host) | |||
529 | 576 | ||
530 | err = mmc_sd_init_card(host, host->ocr, host->card); | 577 | err = mmc_sd_init_card(host, host->ocr, host->card); |
531 | if (err != MMC_ERR_NONE) { | 578 | if (err != MMC_ERR_NONE) { |
532 | mmc_remove_card(host->card); | 579 | mmc_sd_remove(host); |
533 | host->card = NULL; | ||
534 | |||
535 | mmc_detach_bus(host); | 580 | mmc_detach_bus(host); |
536 | } | 581 | } |
537 | 582 | ||
@@ -548,6 +593,8 @@ static void mmc_sd_resume(struct mmc_host *host) | |||
548 | static const struct mmc_bus_ops mmc_sd_ops = { | 593 | static const struct mmc_bus_ops mmc_sd_ops = { |
549 | .remove = mmc_sd_remove, | 594 | .remove = mmc_sd_remove, |
550 | .detect = mmc_sd_detect, | 595 | .detect = mmc_sd_detect, |
596 | .sysfs_add = mmc_sd_sysfs_add, | ||
597 | .sysfs_remove = mmc_sd_sysfs_remove, | ||
551 | .suspend = mmc_sd_suspend, | 598 | .suspend = mmc_sd_suspend, |
552 | .resume = mmc_sd_resume, | 599 | .resume = mmc_sd_resume, |
553 | }; | 600 | }; |
@@ -599,7 +646,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) | |||
599 | 646 | ||
600 | mmc_release_host(host); | 647 | mmc_release_host(host); |
601 | 648 | ||
602 | err = mmc_register_card(host->card); | 649 | err = mmc_add_card(host->card); |
603 | if (err) | 650 | if (err) |
604 | goto reclaim_host; | 651 | goto reclaim_host; |
605 | 652 | ||
diff --git a/drivers/mmc/core/sysfs.c b/drivers/mmc/core/sysfs.c index 843b1fbba55..00a97e70f91 100644 --- a/drivers/mmc/core/sysfs.c +++ b/drivers/mmc/core/sysfs.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * linux/drivers/mmc/core/sysfs.c | 2 | * linux/drivers/mmc/core/sysfs.c |
3 | * | 3 | * |
4 | * Copyright (C) 2003 Russell King, All Rights Reserved. | 4 | * Copyright (C) 2003 Russell King, All Rights Reserved. |
5 | * Copyright 2007 Pierre Ossman | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -9,352 +10,34 @@ | |||
9 | * | 10 | * |
10 | * MMC sysfs/driver model support. | 11 | * MMC sysfs/driver model support. |
11 | */ | 12 | */ |
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/device.h> | 13 | #include <linux/device.h> |
15 | #include <linux/idr.h> | ||
16 | #include <linux/workqueue.h> | ||
17 | 14 | ||
18 | #include <linux/mmc/card.h> | 15 | #include <linux/mmc/card.h> |
19 | #include <linux/mmc/host.h> | ||
20 | 16 | ||
21 | #include "sysfs.h" | 17 | #include "sysfs.h" |
22 | 18 | ||
23 | #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) | 19 | int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs) |
24 | #define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) | ||
25 | #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) | ||
26 | |||
27 | #define MMC_ATTR(name, fmt, args...) \ | ||
28 | static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ | ||
29 | { \ | ||
30 | struct mmc_card *card = dev_to_mmc_card(dev); \ | ||
31 | return sprintf(buf, fmt, args); \ | ||
32 | } | ||
33 | |||
34 | MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], | ||
35 | card->raw_cid[2], card->raw_cid[3]); | ||
36 | MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], | ||
37 | card->raw_csd[2], card->raw_csd[3]); | ||
38 | MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); | ||
39 | MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); | ||
40 | MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); | ||
41 | MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); | ||
42 | MMC_ATTR(manfid, "0x%06x\n", card->cid.manfid); | ||
43 | MMC_ATTR(name, "%s\n", card->cid.prod_name); | ||
44 | MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid); | ||
45 | MMC_ATTR(serial, "0x%08x\n", card->cid.serial); | ||
46 | |||
47 | #define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL) | ||
48 | |||
49 | static struct device_attribute mmc_dev_attrs[] = { | ||
50 | MMC_ATTR_RO(cid), | ||
51 | MMC_ATTR_RO(csd), | ||
52 | MMC_ATTR_RO(date), | ||
53 | MMC_ATTR_RO(fwrev), | ||
54 | MMC_ATTR_RO(hwrev), | ||
55 | MMC_ATTR_RO(manfid), | ||
56 | MMC_ATTR_RO(name), | ||
57 | MMC_ATTR_RO(oemid), | ||
58 | MMC_ATTR_RO(serial), | ||
59 | __ATTR_NULL | ||
60 | }; | ||
61 | |||
62 | static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr); | ||
63 | |||
64 | |||
65 | static void mmc_release_card(struct device *dev) | ||
66 | { | ||
67 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
68 | |||
69 | kfree(card); | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * This currently matches any MMC driver to any MMC card - drivers | ||
74 | * themselves make the decision whether to drive this card in their | ||
75 | * probe method. | ||
76 | */ | ||
77 | static int mmc_bus_match(struct device *dev, struct device_driver *drv) | ||
78 | { | ||
79 | return 1; | ||
80 | } | ||
81 | |||
82 | static int | ||
83 | mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, | ||
84 | int buf_size) | ||
85 | { | ||
86 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
87 | char ccc[13]; | ||
88 | int retval = 0, i = 0, length = 0; | ||
89 | |||
90 | #define add_env(fmt,val) do { \ | ||
91 | retval = add_uevent_var(envp, num_envp, &i, \ | ||
92 | buf, buf_size, &length, \ | ||
93 | fmt, val); \ | ||
94 | if (retval) \ | ||
95 | return retval; \ | ||
96 | } while (0); | ||
97 | |||
98 | for (i = 0; i < 12; i++) | ||
99 | ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; | ||
100 | ccc[12] = '\0'; | ||
101 | |||
102 | add_env("MMC_CCC=%s", ccc); | ||
103 | add_env("MMC_MANFID=%06x", card->cid.manfid); | ||
104 | add_env("MMC_NAME=%s", mmc_card_name(card)); | ||
105 | add_env("MMC_OEMID=%04x", card->cid.oemid); | ||
106 | #undef add_env | ||
107 | envp[i] = NULL; | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int mmc_bus_suspend(struct device *dev, pm_message_t state) | ||
113 | { | ||
114 | struct mmc_driver *drv = to_mmc_driver(dev->driver); | ||
115 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
116 | int ret = 0; | ||
117 | |||
118 | if (dev->driver && drv->suspend) | ||
119 | ret = drv->suspend(card, state); | ||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | static int mmc_bus_resume(struct device *dev) | ||
124 | { | ||
125 | struct mmc_driver *drv = to_mmc_driver(dev->driver); | ||
126 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
127 | int ret = 0; | ||
128 | |||
129 | if (dev->driver && drv->resume) | ||
130 | ret = drv->resume(card); | ||
131 | return ret; | ||
132 | } | ||
133 | |||
134 | static int mmc_bus_probe(struct device *dev) | ||
135 | { | ||
136 | struct mmc_driver *drv = to_mmc_driver(dev->driver); | ||
137 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
138 | |||
139 | return drv->probe(card); | ||
140 | } | ||
141 | |||
142 | static int mmc_bus_remove(struct device *dev) | ||
143 | { | ||
144 | struct mmc_driver *drv = to_mmc_driver(dev->driver); | ||
145 | struct mmc_card *card = dev_to_mmc_card(dev); | ||
146 | |||
147 | drv->remove(card); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | static struct bus_type mmc_bus_type = { | ||
153 | .name = "mmc", | ||
154 | .dev_attrs = mmc_dev_attrs, | ||
155 | .match = mmc_bus_match, | ||
156 | .uevent = mmc_bus_uevent, | ||
157 | .probe = mmc_bus_probe, | ||
158 | .remove = mmc_bus_remove, | ||
159 | .suspend = mmc_bus_suspend, | ||
160 | .resume = mmc_bus_resume, | ||
161 | }; | ||
162 | |||
163 | /** | ||
164 | * mmc_register_driver - register a media driver | ||
165 | * @drv: MMC media driver | ||
166 | */ | ||
167 | int mmc_register_driver(struct mmc_driver *drv) | ||
168 | { | ||
169 | drv->drv.bus = &mmc_bus_type; | ||
170 | return driver_register(&drv->drv); | ||
171 | } | ||
172 | |||
173 | EXPORT_SYMBOL(mmc_register_driver); | ||
174 | |||
175 | /** | ||
176 | * mmc_unregister_driver - unregister a media driver | ||
177 | * @drv: MMC media driver | ||
178 | */ | ||
179 | void mmc_unregister_driver(struct mmc_driver *drv) | ||
180 | { | ||
181 | drv->drv.bus = &mmc_bus_type; | ||
182 | driver_unregister(&drv->drv); | ||
183 | } | ||
184 | |||
185 | EXPORT_SYMBOL(mmc_unregister_driver); | ||
186 | |||
187 | |||
188 | /* | ||
189 | * Internal function. Initialise a MMC card structure. | ||
190 | */ | ||
191 | void mmc_init_card(struct mmc_card *card, struct mmc_host *host) | ||
192 | { | ||
193 | memset(card, 0, sizeof(struct mmc_card)); | ||
194 | card->host = host; | ||
195 | device_initialize(&card->dev); | ||
196 | card->dev.parent = mmc_classdev(host); | ||
197 | card->dev.bus = &mmc_bus_type; | ||
198 | card->dev.release = mmc_release_card; | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Internal function. Register a new MMC card with the driver model. | ||
203 | */ | ||
204 | int mmc_register_card(struct mmc_card *card) | ||
205 | { | 20 | { |
206 | int ret; | 21 | int error = 0; |
22 | int i; | ||
207 | 23 | ||
208 | snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), | 24 | for (i = 0; attr_name(attrs[i]); i++) { |
209 | "%s:%04x", mmc_hostname(card->host), card->rca); | 25 | error = device_create_file(&card->dev, &attrs[i]); |
210 | 26 | if (error) { | |
211 | ret = device_add(&card->dev); | 27 | while (--i >= 0) |
212 | if (ret == 0) { | 28 | device_remove_file(&card->dev, &attrs[i]); |
213 | if (mmc_card_sd(card)) { | 29 | break; |
214 | ret = device_create_file(&card->dev, &mmc_dev_attr_scr); | ||
215 | if (ret) | ||
216 | device_del(&card->dev); | ||
217 | } | 30 | } |
218 | } | 31 | } |
219 | if (ret == 0) | ||
220 | mmc_card_set_present(card); | ||
221 | return ret; | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Internal function. Unregister a new MMC card with the | ||
226 | * driver model, and (eventually) free it. | ||
227 | */ | ||
228 | void mmc_remove_card(struct mmc_card *card) | ||
229 | { | ||
230 | if (mmc_card_present(card)) { | ||
231 | if (mmc_card_sd(card)) | ||
232 | device_remove_file(&card->dev, &mmc_dev_attr_scr); | ||
233 | |||
234 | device_del(&card->dev); | ||
235 | } | ||
236 | |||
237 | put_device(&card->dev); | ||
238 | } | ||
239 | |||
240 | |||
241 | static void mmc_host_classdev_release(struct device *dev) | ||
242 | { | ||
243 | struct mmc_host *host = cls_dev_to_mmc_host(dev); | ||
244 | kfree(host); | ||
245 | } | ||
246 | |||
247 | static struct class mmc_host_class = { | ||
248 | .name = "mmc_host", | ||
249 | .dev_release = mmc_host_classdev_release, | ||
250 | }; | ||
251 | |||
252 | static DEFINE_IDR(mmc_host_idr); | ||
253 | static DEFINE_SPINLOCK(mmc_host_lock); | ||
254 | |||
255 | /* | ||
256 | * Internal function. Allocate a new MMC host. | ||
257 | */ | ||
258 | struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev) | ||
259 | { | ||
260 | struct mmc_host *host; | ||
261 | |||
262 | host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); | ||
263 | if (host) { | ||
264 | memset(host, 0, sizeof(struct mmc_host) + extra); | ||
265 | |||
266 | host->parent = dev; | ||
267 | host->class_dev.parent = dev; | ||
268 | host->class_dev.class = &mmc_host_class; | ||
269 | device_initialize(&host->class_dev); | ||
270 | } | ||
271 | 32 | ||
272 | return host; | 33 | return error; |
273 | } | 34 | } |
274 | 35 | ||
275 | /* | 36 | void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs) |
276 | * Internal function. Register a new MMC host with the MMC class. | ||
277 | */ | ||
278 | int mmc_add_host_sysfs(struct mmc_host *host) | ||
279 | { | 37 | { |
280 | int err; | 38 | int i; |
281 | |||
282 | if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) | ||
283 | return -ENOMEM; | ||
284 | |||
285 | spin_lock(&mmc_host_lock); | ||
286 | err = idr_get_new(&mmc_host_idr, host, &host->index); | ||
287 | spin_unlock(&mmc_host_lock); | ||
288 | if (err) | ||
289 | return err; | ||
290 | |||
291 | snprintf(host->class_dev.bus_id, BUS_ID_SIZE, | ||
292 | "mmc%d", host->index); | ||
293 | |||
294 | return device_add(&host->class_dev); | ||
295 | } | ||
296 | 39 | ||
297 | /* | 40 | for (i = 0; attr_name(attrs[i]); i++) |
298 | * Internal function. Unregister a MMC host with the MMC class. | 41 | device_remove_file(&card->dev, &attrs[i]); |
299 | */ | ||
300 | void mmc_remove_host_sysfs(struct mmc_host *host) | ||
301 | { | ||
302 | device_del(&host->class_dev); | ||
303 | |||
304 | spin_lock(&mmc_host_lock); | ||
305 | idr_remove(&mmc_host_idr, host->index); | ||
306 | spin_unlock(&mmc_host_lock); | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Internal function. Free a MMC host. | ||
311 | */ | ||
312 | void mmc_free_host_sysfs(struct mmc_host *host) | ||
313 | { | ||
314 | put_device(&host->class_dev); | ||
315 | } | ||
316 | |||
317 | static struct workqueue_struct *workqueue; | ||
318 | |||
319 | /* | ||
320 | * Internal function. Schedule delayed work in the MMC work queue. | ||
321 | */ | ||
322 | int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) | ||
323 | { | ||
324 | return queue_delayed_work(workqueue, work, delay); | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * Internal function. Flush all scheduled work from the MMC work queue. | ||
329 | */ | ||
330 | void mmc_flush_scheduled_work(void) | ||
331 | { | ||
332 | flush_workqueue(workqueue); | ||
333 | } | ||
334 | |||
335 | static int __init mmc_init(void) | ||
336 | { | ||
337 | int ret; | ||
338 | |||
339 | workqueue = create_singlethread_workqueue("kmmcd"); | ||
340 | if (!workqueue) | ||
341 | return -ENOMEM; | ||
342 | |||
343 | ret = bus_register(&mmc_bus_type); | ||
344 | if (ret == 0) { | ||
345 | ret = class_register(&mmc_host_class); | ||
346 | if (ret) | ||
347 | bus_unregister(&mmc_bus_type); | ||
348 | } | ||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | static void __exit mmc_exit(void) | ||
353 | { | ||
354 | class_unregister(&mmc_host_class); | ||
355 | bus_unregister(&mmc_bus_type); | ||
356 | destroy_workqueue(workqueue); | ||
357 | } | 42 | } |
358 | 43 | ||
359 | module_init(mmc_init); | ||
360 | module_exit(mmc_exit); | ||
diff --git a/drivers/mmc/core/sysfs.h b/drivers/mmc/core/sysfs.h index 80e29b35828..4b8f670bd10 100644 --- a/drivers/mmc/core/sysfs.h +++ b/drivers/mmc/core/sysfs.h | |||
@@ -11,17 +11,16 @@ | |||
11 | #ifndef _MMC_CORE_SYSFS_H | 11 | #ifndef _MMC_CORE_SYSFS_H |
12 | #define _MMC_CORE_SYSFS_H | 12 | #define _MMC_CORE_SYSFS_H |
13 | 13 | ||
14 | void mmc_init_card(struct mmc_card *card, struct mmc_host *host); | 14 | #define MMC_ATTR_FN(name, fmt, args...) \ |
15 | int mmc_register_card(struct mmc_card *card); | 15 | static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ |
16 | void mmc_remove_card(struct mmc_card *card); | 16 | { \ |
17 | struct mmc_card *card = container_of(dev, struct mmc_card, dev);\ | ||
18 | return sprintf(buf, fmt, args); \ | ||
19 | } | ||
17 | 20 | ||
18 | struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); | 21 | #define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL) |
19 | int mmc_add_host_sysfs(struct mmc_host *host); | ||
20 | void mmc_remove_host_sysfs(struct mmc_host *host); | ||
21 | void mmc_free_host_sysfs(struct mmc_host *host); | ||
22 | 22 | ||
23 | int mmc_schedule_work(struct work_struct *work); | 23 | int mmc_add_attrs(struct mmc_card *card, struct device_attribute *attrs); |
24 | int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); | 24 | void mmc_remove_attrs(struct mmc_card *card, struct device_attribute *attrs); |
25 | void mmc_flush_scheduled_work(void); | ||
26 | 25 | ||
27 | #endif | 26 | #endif |