aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/omap.c
diff options
context:
space:
mode:
authorJarkko Lavinen <jarkko.lavinen@nokia.com>2008-03-26 16:09:42 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-04-18 14:05:30 -0400
commit7584d276d47a55afaeb614ed16cf306cbe2d6117 (patch)
tree85f43e35e299312f45a9be8206f0075a33a28045 /drivers/mmc/host/omap.c
parent8348f0029d85828671e3a1d11db41fe53afbdc0d (diff)
MMC: OMAP: Use tasklet instead of workqueue for cover switch notification
The cover waitqueue is occasionally scheduled twice from timer and the interrupt and oops follows. It would have been possible to fix this problem with spinlocks but using tasklet was a dropin solution with no need for locking. This path also adds some cleanups. Signed-off-by: Jarkko Lavinen <jarkko.lavinen@nokia.com> Signed-off-by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc/host/omap.c')
-rw-r--r--drivers/mmc/host/omap.c67
1 files changed, 40 insertions, 27 deletions
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index f7fb97802827..8f393e8ed42f 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -94,7 +94,7 @@
94 94
95/* Specifies how often in millisecs to poll for card status changes 95/* Specifies how often in millisecs to poll for card status changes
96 * when the cover switch is open */ 96 * when the cover switch is open */
97#define OMAP_MMC_SWITCH_POLL_DELAY 500 97#define OMAP_MMC_COVER_POLL_DELAY 500
98 98
99struct mmc_omap_host; 99struct mmc_omap_host;
100 100
@@ -106,8 +106,8 @@ struct mmc_omap_slot {
106 unsigned int fclk_freq; 106 unsigned int fclk_freq;
107 unsigned powered:1; 107 unsigned powered:1;
108 108
109 struct work_struct switch_work; 109 struct tasklet_struct cover_tasklet;
110 struct timer_list switch_timer; 110 struct timer_list cover_timer;
111 unsigned cover_open; 111 unsigned cover_open;
112 112
113 struct mmc_request *mrq; 113 struct mmc_request *mrq;
@@ -740,40 +740,51 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
740 return IRQ_HANDLED; 740 return IRQ_HANDLED;
741} 741}
742 742
743void omap_mmc_notify_cover_event(struct device *dev, int slot, int is_closed) 743void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
744{ 744{
745 int cover_open;
745 struct mmc_omap_host *host = dev_get_drvdata(dev); 746 struct mmc_omap_host *host = dev_get_drvdata(dev);
747 struct mmc_omap_slot *slot = host->slots[num];
746 748
747 BUG_ON(slot >= host->nr_slots); 749 BUG_ON(num >= host->nr_slots);
748 750
749 /* Other subsystems can call in here before we're initialised. */ 751 /* Other subsystems can call in here before we're initialised. */
750 if (host->nr_slots == 0 || !host->slots[slot]) 752 if (host->nr_slots == 0 || !host->slots[num])
751 return; 753 return;
752 754
753 schedule_work(&host->slots[slot]->switch_work); 755 cover_open = mmc_omap_cover_is_open(slot);
756 if (cover_open != slot->cover_open) {
757 slot->cover_open = cover_open;
758 sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch");
759 }
760
761 tasklet_hi_schedule(&slot->cover_tasklet);
754} 762}
755 763
756static void mmc_omap_switch_timer(unsigned long arg) 764static void mmc_omap_cover_timer(unsigned long arg)
757{ 765{
758 struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg; 766 struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg;
759 767 tasklet_schedule(&slot->cover_tasklet);
760 schedule_work(&slot->switch_work);
761} 768}
762 769
763static void mmc_omap_cover_handler(struct work_struct *work) 770static void mmc_omap_cover_handler(unsigned long param)
764{ 771{
765 struct mmc_omap_slot *slot = container_of(work, struct mmc_omap_slot, 772 struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
766 switch_work); 773 int cover_open = mmc_omap_cover_is_open(slot);
767 int cover_open;
768 774
769 cover_open = mmc_omap_cover_is_open(slot); 775 mmc_detect_change(slot->mmc, 0);
770 if (cover_open != slot->cover_open) { 776 if (!cover_open)
771 sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch"); 777 return;
772 slot->cover_open = cover_open; 778
773 dev_info(mmc_dev(slot->mmc), "cover is now %s\n", 779 /*
774 cover_open ? "open" : "closed"); 780 * If no card is inserted, we postpone polling until
775 } 781 * the cover has been closed.
776 mmc_detect_change(slot->mmc, slot->id); 782 */
783 if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card))
784 return;
785
786 mod_timer(&slot->cover_timer,
787 jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
777} 788}
778 789
779/* Prepare to transfer the next segment of a scatterlist */ 790/* Prepare to transfer the next segment of a scatterlist */
@@ -1237,10 +1248,11 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id)
1237 if (r < 0) 1248 if (r < 0)
1238 goto err_remove_slot_name; 1249 goto err_remove_slot_name;
1239 1250
1240 INIT_WORK(&slot->switch_work, mmc_omap_cover_handler); 1251 setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
1241 setup_timer(&slot->switch_timer, mmc_omap_switch_timer, 1252 (unsigned long)slot);
1242 (unsigned long) slot); 1253 tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
1243 schedule_work(&slot->switch_work); 1254 (unsigned long)slot);
1255 tasklet_schedule(&slot->cover_tasklet);
1244 } 1256 }
1245 1257
1246 return 0; 1258 return 0;
@@ -1263,7 +1275,8 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)
1263 if (slot->pdata->get_cover_state != NULL) 1275 if (slot->pdata->get_cover_state != NULL)
1264 device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); 1276 device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
1265 1277
1266 del_timer_sync(&slot->switch_timer); 1278 tasklet_kill(&slot->cover_tasklet);
1279 del_timer_sync(&slot->cover_timer);
1267 flush_scheduled_work(); 1280 flush_scheduled_work();
1268 1281
1269 mmc_remove_host(mmc); 1282 mmc_remove_host(mmc);