MMC: OMAP: Sync MMC OMAP driver with mainline tree
Subsystems (2)
cover MMC: OMAP: Sync MMC OMAP driver with mainline tree
Hi Pierre, Tony and folks,
The new version of the patch series that follows is a synchronization of MMC OMAP driver from Linux-OMAP tree into mainline tree.
Improvements and corrections are based on comments from Pierre Ossman and Roel Kluin, from LKML.
Just to remind you, basically it brings MMC multislot support for OMAP boards with one slot (like H2 1611, H3 1710) or two slots (like H4 2420 and N800). Others boards supported by such feature are: N770, Siemens SX1 and Apollon.
BR,
Carlos.
Signature · 7 lines
Carlos Eduardo Aguiar Nokia Institute of Technology - INdT Open Source Mobile Research Center - OSMRC - Manaus Core Team Phone: +55 92 2126-1079 Mobile: +55 92 8127-1797 E-mail: [email protected]
On Fri, 14 Mar 2008 15:35:56 -0400
Carlos Aguiar <[email protected]> wrote:
Except for the issue with the device attributes, the patch set looks fine. Send me a fix for that patch (and affected subsequent) and I'll queue it up.
Signature · 5 lines
-- Pierre Ossman Linux kernel, MMC maintainer http://www.kernel.org PulseAudio, core developer http://pulseaudio.org rdesktop, core developer http://www.rdesktop.org
Quoted 6 lines · ext Pierre Ossman
On Fri, 14 Mar 2008 15:35:56 -0400
Carlos Aguiar <[email protected]> wrote:Except for the issue with the device attributes, the patch set looks fine. Send me a fix for that patch (and affected subsequent) and I'll queue it up.
Hi Pierre,
I'm resending the whole series with the device attributes issues solved, as you mentioned.
Sorry for the delay - I promised to send you the series on Monday - but I have some mmc issues on Linux-OMAP mailing list to handle.
So, feel free to queue it up.
Thanks in advance,
Carlos.
Signature · 7 lines
Carlos Eduardo Aguiar Nokia Institute of Technology - INdT Open Source Mobile Research Center - OSMRC - Manaus Core Team Phone: +55 92 2126-1079 Mobile: +55 92 8127-1797 E-mail: [email protected]
PATCH 1/18 MMC: OMAP: Remove some opcodes from host driver
In reply to Pierre OssmanFrom: Carlos Eduardo Aguiar <[email protected]>
This patch removes some opcodes from host driver so there's no need on putting '#include <linux/mmc/mmc.h>', that should not be needed in host drivers.
--- drivers/mmc/host/omap.c | 11 +++-------- 1 files changed, 3 insertions(+), 8 deletions(-)
drivers/mmc/host/omap.c +3 -8
@@ -495,15 +495,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)if (status &OMAP_MMC_STAT_CMD_TOUT ) {/* Timeouts are routine with some commands */ if (host->cmd) { -if (host->cmd->opcode !=MMC_ALL_SEND_CID && - host->cmd->opcode != -MMC_SEND_OP_COND && - host->cmd->opcode != -MMC_APP_CMD && - !mmc_omap_cover_is_open (host)) +if (!mmc_omap_cover_is_open (host))dev_err (mmc_dev (host->mmc), -"command timeout, CMD %d\n" , - host->cmd->opcode); +"command timeout, CMD %d\n" , + host->cmd->opcode); host->cmd->error = -ETIMEDOUT ; end_command =1 ; }
-- 1.5.3.GIT
PATCH 2/18 MMC: OMAP: Remove extra divisor increase
In reply to Pierre OssmanFrom: Tony Lindgren <[email protected]>
As noted by Kyungmin Park, the divisor calculation has an unnecessary increase.
--- drivers/mmc/host/omap.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)
drivers/mmc/host/omap.c +0 -1
@@ -931,7 +931,6 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios)if (dsor >250 ) dsor =250 ; - dsor++;if (ios->bus_width ==MMC_BUS_WIDTH_4 ) dsor |=1 <<15 ;
-- 1.5.3.GIT
PATCH 3/18 MMC: OMAP: Fix the BYTEBLOCK capability removal
In reply to Pierre OssmanFrom: Francisco Alecrim <[email protected]>
According with commit 255d01af9a990fd5166f04ed0cc0b30b7b67e81e from Linux-OMAP tree, the BYTEBLOCK capability was removed by Pierre Ossman. MMC_CAP_BYTEBLOCK is not defined causing the compile error:
drivers/mmc/host/omap.c: In function `mmc_omap_probe':drivers/mmc/host/omap.c:1077: error: `MMC_CAP_BYTEBLOCK' undeclared (first use in this function) drivers/mmc/host/omap.c:1077: error: (Each undeclared identifier is reported only once drivers/mmc/host/omap.c:1077: error: for each function it appears in.)
--- drivers/mmc/host/omap.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/mmc/host/omap.c +1 -1
@@ -1070,7 +1070,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev) mmc->f_min =400000 ; mmc->f_max =24000000 ; mmc->ocr_avail =MMC_VDD_32_33 |MMC_VDD_33_34 ; - mmc->caps =MMC_CAP_MULTIWRITE |MMC_CAP_BYTEBLOCK ; + mmc->caps =MMC_CAP_MULTIWRITE ;if (minfo->wire4) mmc->caps |=MMC_CAP_4_BIT_DATA ;
-- 1.5.3.GIT
PATCH 4/18 MMC: OMAP: Remove cover switch handling to allow adding multislot support
In reply to Pierre OssmanFrom: Tony Lindgren <[email protected]>
This patch removes the MMC cover switch handling temporarily to make following multislot patches cleaner.
MMC cover switch handling will be added back in later patches after adding basic multislot support.
--- drivers/mmc/host/omap.c | 152 +---------------------------------------------- 1 files changed, 1 insertions(+), 151 deletions(-)
drivers/mmc/host/omap.c +1 -151
@@ -95,8 +95,6 @@* when the cover switch is open */ #define OMAP_MMC_SWITCH_POLL_DELAY 500 -static int mmc_omap_enable_poll =1 ; -struct mmc_omap_host {int initialized;int suspended;
@@ -133,62 +131,11 @@ struct mmc_omap_host {short power_pin;short wp_pin; -int switch_pin;struct work_struct switch_work;struct timer_list switch_timer;int switch_last_state; }; -static inline int -mmc_omap_cover_is_open (struct mmc_omap_host *host) -{ -if (host->switch_pin <0 ) -return 0 ; -return omap_get_gpio_datain (host->switch_pin); -} - -static ssize_t -mmc_omap_show_cover_switch (struct device *dev, -struct device_attribute *attr,char *buf) -{ -struct mmc_omap_host *host =dev_get_drvdata (dev); - -return sprintf (buf,"%s\n" ,mmc_omap_cover_is_open (host) ?"open" : -"closed" ); -} - -static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); - -static ssize_t -mmc_omap_show_enable_poll (struct device *dev, -struct device_attribute *attr,char *buf) -{ -return snprintf (buf,PAGE_SIZE ,"%d\n" , mmc_omap_enable_poll); -} - -static ssize_t -mmc_omap_store_enable_poll (struct device *dev, -struct device_attribute *attr,const char *buf, -size_t size) -{ -int enable_poll; - -if (sscanf (buf,"%10d" , &enable_poll) !=1 ) -return -EINVAL ; - -if (enable_poll != mmc_omap_enable_poll) { -struct mmc_omap_host *host =dev_get_drvdata (dev); - - mmc_omap_enable_poll = enable_poll; -if (enable_poll && host->switch_pin >=0 ) -schedule_work (&host->switch_work); - } -return size; -} - -static DEVICE_ATTR(enable_poll, 0664, - mmc_omap_show_enable_poll, mmc_omap_store_enable_poll); -static void mmc_omap_start_command (struct mmc_omap_host *host,struct mmc_command *cmd) {
@@ -495,8 +442,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)if (status &OMAP_MMC_STAT_CMD_TOUT ) {/* Timeouts are routine with some commands */ if (host->cmd) { -if (!mmc_omap_cover_is_open (host)) -dev_err (mmc_dev (host->mmc), +dev_err (mmc_dev (host->mmc),"command timeout, CMD %d\n" , host->cmd->opcode); host->cmd->error = -ETIMEDOUT ;
@@ -544,54 +490,6 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)return IRQ_HANDLED ; } -static irqreturn_t mmc_omap_switch_irq (int irq,void *dev_id) -{ -struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; - -schedule_work (&host->switch_work); - -return IRQ_HANDLED ; -} - -static void mmc_omap_switch_timer (unsigned long arg) -{ -struct mmc_omap_host *host = (struct mmc_omap_host *) arg; - -schedule_work (&host->switch_work); -} - -static void mmc_omap_switch_handler (struct work_struct *work) -{ - struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, switch_work); -struct mmc_card *card; -static int complained =0 ; -int cards =0 , cover_open; - -if (host->switch_pin ==-1 ) -return ; - cover_open =mmc_omap_cover_is_open (host); -if (cover_open != host->switch_last_state) { -kobject_uevent (&host->dev->kobj,KOBJ_CHANGE ); - host->switch_last_state = cover_open; - } -mmc_detect_change (host->mmc,0 ); -list_for_each_entry (card, &host->mmc->cards, node) { -if (mmc_card_present (card)) - cards++; - } -if (mmc_omap_cover_is_open (host)) { -if (!complained) { -dev_info (mmc_dev (host->mmc),"cover is open\n" ); - complained =1 ; - } -if (mmc_omap_enable_poll) -mod_timer (&host->switch_timer, jiffies + -msecs_to_jiffies (OMAP_MMC_SWITCH_POLL_DELAY )); - }else { - complained =0 ; - } -} -/* Prepare to transfer the next segment of a scatterlist */ static void mmc_omap_prepare_dma (struct mmc_omap_host *host,struct mmc_data *data)
@@ -1057,7 +955,6 @@ static int __init mmc_omap_probe(struct platform_device *pdev)* the card detect sensing. */ host->power_pin = minfo->power_pin; - host->switch_pin = minfo->switch_pin; host->wp_pin = minfo->wp_pin; host->use_dma =1 ; host->dma_ch =-1 ;
@@ -1102,48 +999,10 @@ static int __init mmc_omap_probe(struct platform_device *pdev) host->dev = &pdev->dev;platform_set_drvdata (pdev, host); -if (host->switch_pin >=0 ) { -INIT_WORK (&host->switch_work, mmc_omap_switch_handler); -init_timer (&host->switch_timer); - host->switch_timer.function = mmc_omap_switch_timer; - host->switch_timer.data = (unsigned long ) host; -if (omap_request_gpio (host->switch_pin) !=0 ) { -dev_warn (mmc_dev (host->mmc),"Unable to get GPIO pin for MMC cover switch\n" ); - host->switch_pin =-1 ; - goto no_switch; - } - -omap_set_gpio_direction (host->switch_pin,1 ); - ret =request_irq (OMAP_GPIO_IRQ (host->switch_pin), - mmc_omap_switch_irq,IRQF_TRIGGER_RISING ,DRIVER_NAME , host); -if (ret) { -dev_warn (mmc_dev (host->mmc),"Unable to get IRQ for MMC cover switch\n" ); -omap_free_gpio (host->switch_pin); - host->switch_pin =-1 ; - goto no_switch; - } - ret =device_create_file (&pdev->dev, &dev_attr_cover_switch); -if (ret ==0 ) { - ret =device_create_file (&pdev->dev, &dev_attr_enable_poll); -if (ret !=0 ) -device_remove_file (&pdev->dev, &dev_attr_cover_switch); - } -if (ret) { -dev_warn (mmc_dev (host->mmc),"Unable to create sysfs attributes\n" ); -free_irq (OMAP_GPIO_IRQ (host->switch_pin), host); -omap_free_gpio (host->switch_pin); - host->switch_pin =-1 ; - goto no_switch; - } -if (mmc_omap_enable_poll &&mmc_omap_cover_is_open (host)) -schedule_work (&host->switch_work); - } -mmc_add_host (mmc);return 0 ; -no_switch:/* FIXME: Free other resources too. */ if (host) {if (host->iclk && !IS_ERR (host->iclk))
@@ -1182,15 +1041,6 @@ static int mmc_omap_remove(struct platform_device *pdev)if (host->power_pin >=0 )omap_free_gpio (host->power_pin); -if (host->switch_pin >=0 ) { -device_remove_file (&pdev->dev, &dev_attr_enable_poll); -device_remove_file (&pdev->dev, &dev_attr_cover_switch); -free_irq (OMAP_GPIO_IRQ (host->switch_pin), host); -omap_free_gpio (host->switch_pin); - host->switch_pin =-1 ; -del_timer_sync (&host->switch_timer); -flush_scheduled_work (); - }if (host->iclk && !IS_ERR (host->iclk))clk_put (host->iclk);if (host->fclk && !IS_ERR (host->fclk))
-- 1.5.3.GIT
PATCH 5/18 MMC: OMAP: Introduce new multislot structure and change driver to use it
In reply to Pierre OssmanFrom: Juha Yrjola <[email protected]>
Introduce new MMC multislot structure and change driver to use it.
Note that MMC clocking is now enabled in mmc_omap_select_slot() and disabled in mmc_omap_release_slot().
--- drivers/mmc/host/omap.c | 402 ++++++++++++++++++++++++++++----------- include/asm-arm/arch-omap/mmc.h | 2 + 2 files changed, 294 insertions(+), 110 deletions(-)
drivers/mmc/host/omap.c +292 -110
@@ -32,6 +32,7 @@#include <asm/mach-types.h> #include <asm/arch/board.h> +#include <asm/arch/mmc.h> #include <asm/arch/gpio.h> #include <asm/arch/dma.h> #include <asm/arch/mux.h>
@@ -95,6 +96,22 @@* when the cover switch is open */ #define OMAP_MMC_SWITCH_POLL_DELAY 500 +struct mmc_omap_host ; + +struct mmc_omap_slot { +int id; +unsigned int vdd; +u16 saved_con; +u16 bus_mode; +unsigned int fclk_freq; +unsigned powered:1 ; + +struct mmc_request *mrq; +struct mmc_omap_host *host; +struct mmc_host *mmc; +struct omap_mmc_slot_data *pdata; +}; +struct mmc_omap_host {int initialized;int suspended;
@@ -129,13 +146,98 @@ struct mmc_omap_host {unsigned dma_len;short power_pin; -short wp_pin; -struct work_struct switch_work; -struct timer_list switch_timer; -int switch_last_state; +struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS ]; +struct mmc_omap_slot *current_slot; +spinlock_t slot_lock; +wait_queue_head_t slot_wq; +int nr_slots; + +struct omap_mmc_platform_data *pdata; }; +static void mmc_omap_select_slot (struct mmc_omap_slot *slot,int claimed) +{ +struct mmc_omap_host *host = slot->host; +unsigned long flags; + +if (claimed) + goto no_claim; +spin_lock_irqsave (&host->slot_lock, flags); +while (host->mmc !=NULL ) { +spin_unlock_irqrestore (&host->slot_lock, flags); +wait_event (host->slot_wq, host->mmc ==NULL ); +spin_lock_irqsave (&host->slot_lock, flags); + } + host->mmc = slot->mmc; +spin_unlock_irqrestore (&host->slot_lock, flags); +no_claim: +clk_enable (host->fclk); +if (host->current_slot != slot) { +if (host->pdata->switch_slot !=NULL ) + host->pdata->switch_slot (mmc_dev (slot->mmc), slot->id); + host->current_slot = slot; + } + +/* Doing the dummy read here seems to work around some bug +* at least in OMAP24xx silicon where the command would not +* start after writing the CMD register. Sigh. */ +OMAP_MMC_READ (host,CON ); + +OMAP_MMC_WRITE (host,CON , slot->saved_con); +} + +static void mmc_omap_start_request (struct mmc_omap_host *host, +struct mmc_request *req); + +static void mmc_omap_release_slot (struct mmc_omap_slot *slot) +{ +struct mmc_omap_host *host = slot->host; +unsigned long flags; +int i; + +BUG_ON (slot ==NULL || host->mmc ==NULL ); +clk_disable (host->fclk); + +spin_lock_irqsave (&host->slot_lock, flags); +/* Check for any pending requests */ +for (i =0 ; i < host->nr_slots; i++) { +struct mmc_omap_slot *new_slot; +struct mmc_request *rq; + +if (host->slots[i] ==NULL || host->slots[i]->mrq ==NULL ) +continue ; + + new_slot = host->slots[i]; +/* The current slot should not have a request in queue */ +BUG_ON (new_slot == host->current_slot); + + host->mmc = new_slot->mmc; +spin_unlock_irqrestore (&host->slot_lock, flags); +mmc_omap_select_slot (new_slot,1 ); + rq = new_slot->mrq; + new_slot->mrq =NULL ; +mmc_omap_start_request (host, rq); +return ; + } + + host->mmc =NULL ; +wake_up (&host->slot_wq); +spin_unlock_irqrestore (&host->slot_lock, flags); +} + +static ssize_t +mmc_omap_show_slot_name (struct device *dev,struct device_attribute *attr, +char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); +struct mmc_omap_slot *slot =mmc_priv (mmc); + +return sprintf (buf,"%s\n" , slot->pdata->name); +} + +static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); +static void mmc_omap_start_command (struct mmc_omap_host *host,struct mmc_command *cmd) {
@@ -180,7 +282,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) cmdreg = cmd->opcode | (resptype <<8 ) | (cmdtype <<12 ); -if (host->bus_mode ==MMC_BUSMODE_OPENDRAIN ) +if (host->current_slot->bus_mode ==MMC_BUSMODE_OPENDRAIN ) cmdreg |=1 <<6 ;if (cmd->flags &MMC_RSP_BUSY )
@@ -189,8 +291,6 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)if (host->data && !(host->data->flags &MMC_DATA_WRITE )) cmdreg |=1 <<15 ; -clk_enable (host->fclk); -OMAP_MMC_WRITE (host,CTO ,200 );OMAP_MMC_WRITE (host,ARGL , cmd->arg &0xffff );OMAP_MMC_WRITE (host,ARGH , cmd->arg >>16 );
@@ -442,6 +542,8 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)if (status &OMAP_MMC_STAT_CMD_TOUT ) {/* Timeouts are routine with some commands */ if (host->cmd) { +struct mmc_omap_slot *slot = + host->current_slot;dev_err (mmc_dev (host->mmc),"command timeout, CMD %d\n" , host->cmd->opcode);
@@ -747,11 +849,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) } } -static void mmc_omap_request (struct mmc_host *mmc,struct mmc_request *req) +static void mmc_omap_start_request (struct mmc_omap_host *host, +struct mmc_request *req) { -struct mmc_omap_host *host =mmc_priv (mmc); - -WARN_ON (host->mrq !=NULL ); +BUG_ON (host->mrq !=NULL ); host->mrq = req;
@@ -760,6 +861,26 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)mmc_omap_start_command (host, req->cmd);if (host->dma_in_use)omap_start_dma (host->dma_ch); +BUG_ON (irqs_disabled ()); +} + +static void mmc_omap_request (struct mmc_host *mmc,struct mmc_request *req) +{ +struct mmc_omap_slot *slot =mmc_priv (mmc); +struct mmc_omap_host *host = slot->host; +unsigned long flags; + +spin_lock_irqsave (&host->slot_lock, flags); +if (host->mmc !=NULL ) { +BUG_ON (slot->mrq !=NULL ); + slot->mrq = req; +spin_unlock_irqrestore (&host->slot_lock, flags); +return ; + }else + host->mmc = mmc; +spin_unlock_irqrestore (&host->slot_lock, flags); +mmc_omap_select_slot (slot,1 ); +mmc_omap_start_request (host, req); }static void innovator_fpga_socket_power (int on)
@@ -813,7 +934,8 @@ static void mmc_omap_power(struct mmc_omap_host *host, int on)static int mmc_omap_calc_divisor (struct mmc_host *mmc,struct mmc_ios *ios) { -struct mmc_omap_host *host =mmc_priv (mmc); +struct mmc_omap_slot *slot =mmc_priv (mmc); +struct mmc_omap_host *host = slot->host;int func_clk_rate =clk_get_rate (host->fclk);int dsor;
@@ -830,6 +952,8 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios)if (dsor >250 ) dsor =250 ; + slot->fclk_freq = func_clk_rate / dsor; +if (ios->bus_width ==MMC_BUS_WIDTH_4 ) dsor |=1 <<15 ;
@@ -838,9 +962,9 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios)static void mmc_omap_set_ios (struct mmc_host *mmc,struct mmc_ios *ios) { -struct mmc_omap_host *host =mmc_priv (mmc); -int dsor; -int i; +struct mmc_omap_slot *slot =mmc_priv (mmc); +struct mmc_omap_host *host = slot->host; +int i, dsor; dsor =mmc_omap_calc_divisor (mmc, ios); host->bus_mode = ios->bus_mode;
@@ -878,32 +1002,101 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)clk_disable (host->fclk); } -static int mmc_omap_get_ro (struct mmc_host *mmc) -{ -struct mmc_omap_host *host =mmc_priv (mmc); - -return host->wp_pin &&omap_get_gpio_datain (host->wp_pin); -} -static const struct mmc_host_ops mmc_omap_ops = { .request = mmc_omap_request, .set_ios = mmc_omap_set_ios, - .get_ro = mmc_omap_get_ro, }; -static int __init mmc_omap_probe (struct platform_device *pdev) +static int __init mmc_omap_new_slot (struct mmc_omap_host *host,int id) { -struct omap_mmc_conf *minfo = pdev->dev.platform_data; +struct mmc_omap_slot *slot =NULL ;struct mmc_host *mmc; +int r; + + mmc =mmc_alloc_host (sizeof (struct mmc_omap_slot ), host->dev); +if (mmc ==NULL ) +return -ENOMEM ; + + slot =mmc_priv (mmc); + slot->host = host; + slot->mmc = mmc; + slot->id = id; + slot->pdata = &host->pdata->slots[id]; + + host->slots[id] = slot; + + mmc->caps =MMC_CAP_MULTIWRITE ; +if (host->pdata->conf.wire4) + mmc->caps |=MMC_CAP_4_BIT_DATA ; + + mmc->ops = &mmc_omap_ops; + mmc->f_min =400000 ; + +if (cpu_class_is_omap2 ()) + mmc->f_max =48000000 ; +else + mmc->f_max =24000000 ; +if (host->pdata->max_freq) + mmc->f_max =min (host->pdata->max_freq, mmc->f_max); + mmc->ocr_avail = slot->pdata->ocr_mask; + +/* Use scatterlist DMA to reduce per-transfer costs. +* NOTE max_seg_size assumption that small blocks aren't +* normally used (except e.g. for reading SD registers). +*/ + mmc->max_phys_segs =32 ; + mmc->max_hw_segs =32 ; + mmc->max_blk_size =2048 ;/* BLEN is 11 bits (+1) */ + mmc->max_blk_count =2048 ;/* NBLK is 11 bits (+1) */ + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; + + r =mmc_add_host (mmc); +if (r <0 ) + goto err_remove_host; + +if (slot->pdata->name !=NULL ) { + r =device_create_file (&mmc->class_dev, + &dev_attr_slot_name); +if (r <0 ) + goto err_remove_host; + } + +return 0 ; + +err_remove_host: +mmc_remove_host (mmc); +mmc_free_host (mmc); +return r; +} + +static void mmc_omap_remove_slot (struct mmc_omap_slot *slot) +{ +struct mmc_host *mmc = slot->mmc; + +if (slot->pdata->name !=NULL ) +device_remove_file (&mmc->class_dev, &dev_attr_slot_name); + +mmc_remove_host (mmc); +mmc_free_host (mmc); +} + +static int __init mmc_omap_probe (struct platform_device *pdev) +{ +struct omap_mmc_platform_data *pdata = pdev->dev.platform_data;struct mmc_omap_host *host =NULL ;struct resource *res; -int ret =0 ; +int i, ret =0 ;int irq; -if (minfo ==NULL ) { +if (pdata ==NULL ) {dev_err (&pdev->dev,"platform data missing\n" );return -ENXIO ; } +if (pdata->nr_slots ==0 ) { +dev_err (&pdev->dev,"no slots\n" ); +return -ENXIO ; + } res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); irq =platform_get_irq (pdev,0 );
@@ -911,28 +1104,39 @@ static int __init mmc_omap_probe(struct platform_device *pdev)return -ENXIO ; res =request_mem_region (res->start, res->end - res->start +1 , - pdev->name); + pdev->name);if (res ==NULL )return -EBUSY ; - mmc =mmc_alloc_host (sizeof (struct mmc_omap_host ), &pdev->dev); -if (mmc ==NULL ) { + host =kzalloc (sizeof (struct mmc_omap_host ),GFP_KERNEL ); +if (host ==NULL ) { ret = -ENOMEM ; goto err_free_mem_region; } - host =mmc_priv (mmc); - host->mmc = mmc; -spin_lock_init (&host->dma_lock);init_timer (&host->dma_timer); +spin_lock_init (&host->slot_lock); +init_waitqueue_head (&host->slot_wq); + host->dma_timer.function = mmc_omap_dma_timer; host->dma_timer.data = (unsigned long ) host; + host->pdata = pdata; + host->dev = &pdev->dev; +platform_set_drvdata (pdev, host); + host->id = pdev->id; host->mem_res = res; host->irq = irq; + host->use_dma =1 ; + host->dma_ch =-1 ; + + host->irq = irq; + host->phys_base = host->mem_res->start; + host->virt_base = (void __iomem *)IO_ADDRESS (host->phys_base); +if (cpu_is_omap24xx ()) { host->iclk =clk_get (&pdev->dev,"mmc_ick" );if (IS_ERR (host->iclk))
@@ -950,70 +1154,34 @@ static int __init mmc_omap_probe(struct platform_device *pdev) goto err_free_iclk; } -/* REVISIT: -* Also, use minfo->cover to decide how to manage -* the card detect sensing. -*/ - host->power_pin = minfo->power_pin; - host->wp_pin = minfo->wp_pin; - host->use_dma =1 ; - host->dma_ch =-1 ; - - host->irq = irq; - host->phys_base = host->mem_res->start; - host->virt_base = (void __iomem *)IO_ADDRESS (host->phys_base); - - mmc->ops = &mmc_omap_ops; - mmc->f_min =400000 ; - mmc->f_max =24000000 ; - mmc->ocr_avail =MMC_VDD_32_33 |MMC_VDD_33_34 ; - mmc->caps =MMC_CAP_MULTIWRITE ; + ret =request_irq (host->irq, mmc_omap_irq,0 ,DRIVER_NAME , host); +if (ret) + goto err_free_fclk; -if (minfo->wire4) - mmc->caps |=MMC_CAP_4_BIT_DATA ; +if (pdata->init !=NULL ) { + ret = pdata->init (&pdev->dev); +if (ret <0 ) + goto err_free_irq; + } -/* Use scatterlist DMA to reduce per-transfer costs. -* NOTE max_seg_size assumption that small blocks aren't -* normally used (except e.g. for reading SD registers). -*/ - mmc->max_phys_segs =32 ; - mmc->max_hw_segs =32 ; - mmc->max_blk_size =2048 ;/* BLEN is 11 bits (+1) */ - mmc->max_blk_count =2048 ;/* NBLK is 11 bits (+1) */ - mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - mmc->max_seg_size = mmc->max_req_size; + host->nr_slots = pdata->nr_slots; +for (i =0 ; i < pdata->nr_slots; i++) { + ret =mmc_omap_new_slot (host, i); +if (ret <0 ) { +while (--i >=0 ) +mmc_omap_remove_slot (host->slots[i]); -if (host->power_pin >=0 ) { -if ((ret =omap_request_gpio (host->power_pin)) !=0 ) { -dev_err (mmc_dev (host->mmc), -"Unable to get GPIO pin for MMC power\n" ); - goto err_free_fclk; + goto err_plat_cleanup; } -omap_set_gpio_direction (host->power_pin,0 ); } - ret =request_irq (host->irq, mmc_omap_irq,0 ,DRIVER_NAME , host); -if (ret) - goto err_free_power_gpio; - - host->dev = &pdev->dev; -platform_set_drvdata (pdev, host); - -mmc_add_host (mmc); -return 0 ; -/* FIXME: Free other resources too. */ -if (host) { -if (host->iclk && !IS_ERR (host->iclk)) -clk_put (host->iclk); -if (host->fclk && !IS_ERR (host->fclk)) -clk_put (host->fclk); -mmc_free_host (host->mmc); - } -err_free_power_gpio: -if (host->power_pin >=0 ) -omap_free_gpio (host->power_pin); +err_plat_cleanup: +if (pdata->cleanup) + pdata->cleanup (&pdev->dev); +err_free_irq: +free_irq (host->irq, host); err_free_fclk:clk_put (host->fclk); err_free_iclk:
@@ -1022,7 +1190,7 @@ err_free_iclk:clk_put (host->iclk); } err_free_mmc_host: -mmc_free_host (host->mmc); +kfree (host); err_free_mem_region:release_mem_region (res->start, res->end - res->start +1 );return ret;
@@ -1031,16 +1199,18 @@ err_free_mem_region:static int mmc_omap_remove (struct platform_device *pdev) {struct mmc_omap_host *host =platform_get_drvdata (pdev); +int i;platform_set_drvdata (pdev,NULL );BUG_ON (host ==NULL ); -mmc_remove_host (host->mmc); -free_irq (host->irq, host); +for (i =0 ; i < host->nr_slots; i++) +mmc_omap_remove_slot (host->slots[i]); + +if (host->pdata->cleanup) + host->pdata->cleanup (&pdev->dev); -if (host->power_pin >=0 ) -omap_free_gpio (host->power_pin);if (host->iclk && !IS_ERR (host->iclk))clk_put (host->iclk);if (host->fclk && !IS_ERR (host->fclk))
@@ -1049,7 +1219,7 @@ static int mmc_omap_remove(struct platform_device *pdev)release_mem_region (pdev->resource[0 ].start, pdev->resource[0 ].end - pdev->resource[0 ].start +1 ); -mmc_free_host (host->mmc); +kfree (host);return 0 ; }
@@ -1057,35 +1227,47 @@ static int mmc_omap_remove(struct platform_device *pdev)#ifdef CONFIG_PM static int mmc_omap_suspend (struct platform_device *pdev,pm_message_t mesg) { -int ret =0 ; +int i, ret =0 ;struct mmc_omap_host *host =platform_get_drvdata (pdev); -if (host && host->suspended) +if (host ==NULL || host->suspended)return 0 ; -if (host) { - ret =mmc_suspend_host (host->mmc, mesg); -if (ret ==0 ) - host->suspended =1 ; +for (i =0 ; i < host->nr_slots; i++) { +struct mmc_omap_slot *slot; + + slot = host->slots[i]; + ret =mmc_suspend_host (slot->mmc, mesg); +if (ret <0 ) { +while (--i >=0 ) { + slot = host->slots[i]; +mmc_resume_host (slot->mmc); + } +return ret; + } } -return ret; + host->suspended =1 ; +return 0 ; }static int mmc_omap_resume (struct platform_device *pdev) { -int ret =0 ; +int i, ret =0 ;struct mmc_omap_host *host =platform_get_drvdata (pdev); -if (host && !host->suspended) +if (host ==NULL || !host->suspended)return 0 ; -if (host) { - ret =mmc_resume_host (host->mmc); -if (ret ==0 ) - host->suspended =0 ; - } +for (i =0 ; i < host->nr_slots; i++) { +struct mmc_omap_slot *slot; + slot = host->slots[i]; + ret =mmc_resume_host (slot->mmc); +if (ret <0 ) +return ret; -return ret; + host->suspended =0 ; + } +return 0 ; }#else #define mmc_omap_suspend NULL
include/asm-arm/arch-omap/mmc.h +2 -0
@@ -18,6 +18,8 @@#define OMAP_MMC_MAX_SLOTS 2struct omap_mmc_platform_data { +struct omap_mmc_conf conf; +unsigned enabled:1 ;/* number of slots on board */ unsigned nr_slots:2 ;
-- 1.5.3.GIT
PATCH 6/18 MMC: OMAP: Add back cover switch support
In reply to Pierre OssmanFrom: Juha Yrjola <[email protected]>
This patch adds back MMC cover switch support in a way that supports multiple slots.
--- drivers/mmc/host/omap.c | 87 +++++++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 84 insertions(+), 3 deletions(-)
drivers/mmc/host/omap.c +84 -3
@@ -106,6 +106,10 @@ struct mmc_omap_slot {unsigned int fclk_freq; unsigned powered:1; +struct work_struct switch_work; +struct timer_list switch_timer; +unsigned cover_open; +struct mmc_request *mrq;struct mmc_omap_host *host;struct mmc_host *mmc;
@@ -226,6 +230,25 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot)spin_unlock_irqrestore (&host->slot_lock, flags); } +static inline +int mmc_omap_cover_is_open (struct mmc_omap_slot *slot) +{ +return slot->pdata->get_cover_state (mmc_dev (slot->mmc), slot->id); +} + +static ssize_t +mmc_omap_show_cover_switch (struct device *dev,struct device_attribute *attr, +char *buf) +{ + struct mmc_host *mmc = container_of(dev, struct mmc_host, class_dev); +struct mmc_omap_slot *slot =mmc_priv (mmc); + +return sprintf (buf,"%s\n" ,mmc_omap_cover_is_open (slot) ?"open" : +"closed" ); +} + +static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); +static ssize_t mmc_omap_show_slot_name (struct device *dev,struct device_attribute *attr,char *buf)
@@ -544,9 +567,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)if (host->cmd) {struct mmc_omap_slot *slot = host->current_slot; -dev_err (mmc_dev (host->mmc), -"command timeout, CMD %d\n" , - host->cmd->opcode); +if (!mmc_omap_cover_is_open (slot)) +dev_err (mmc_dev (host->mmc), +"command timeout, CMD %d\n" , + host->cmd->opcode); host->cmd->error = -ETIMEDOUT ; end_command =1 ; }
@@ -592,6 +616,42 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)return IRQ_HANDLED ; } +void omap_mmc_notify_cover_event (struct device *dev,int slot,int is_closed) +{ +struct mmc_omap_host *host =dev_get_drvdata (dev); + +BUG_ON (slot >= host->nr_slots); + +/* Other subsystems can call in here before we're initialised. */ +if (host->nr_slots ==0 || !host->slots[slot]) +return ; + +schedule_work (&host->slots[slot]->switch_work); +} + +static void mmc_omap_switch_timer (unsigned long arg) +{ +struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg; + +schedule_work (&slot->switch_work); +} + +static void mmc_omap_cover_handler (struct work_struct *work) +{ + struct mmc_omap_slot *slot = container_of(work, struct mmc_omap_slot, + switch_work); +int cover_open; + + cover_open =mmc_omap_cover_is_open (slot); +if (cover_open != slot->cover_open) { +sysfs_notify (&slot->mmc->class_dev.kobj,NULL ,"cover_switch" ); + slot->cover_open = cover_open; +dev_info (mmc_dev (slot->mmc),"cover is now %s\n" , + cover_open ?"open" :"closed" ); + } +mmc_detect_change (slot->mmc, slot->id); +} +/* Prepare to transfer the next segment of a scatterlist */ static void mmc_omap_prepare_dma (struct mmc_omap_host *host,struct mmc_data *data)
@@ -1062,8 +1122,24 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) goto err_remove_host; } +if (slot->pdata->get_cover_state !=NULL ) { + r =device_create_file (&mmc->class_dev, + &dev_attr_cover_switch); +if (r <0 ) + goto err_remove_slot_name; + +INIT_WORK (&slot->switch_work, mmc_omap_cover_handler); +init_timer (&slot->switch_timer); + slot->switch_timer.function = mmc_omap_switch_timer; + slot->switch_timer.data = (unsigned long ) slot; +schedule_work (&slot->switch_work); + } +return 0 ; +err_remove_slot_name: +if (slot->pdata->name !=NULL ) +device_remove_file (&mmc->class_dev, &dev_attr_slot_name); err_remove_host:mmc_remove_host (mmc);mmc_free_host (mmc);
@@ -1076,6 +1152,11 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)if (slot->pdata->name !=NULL )device_remove_file (&mmc->class_dev, &dev_attr_slot_name); +if (slot->pdata->get_cover_state !=NULL ) +device_remove_file (&mmc->class_dev, &dev_attr_cover_switch); + +del_timer_sync (&slot->switch_timer); +flush_scheduled_work ();mmc_remove_host (mmc);mmc_free_host (mmc);
-- 1.5.3.GIT
PATCH 10/18 MMC: OMAP: General cleanup for MMC multislot support
In reply to Pierre OssmanFrom: Juha Yrjola <[email protected]>
General code cleanup, modifications at some dev_* functions and other hacks at mmc_omap_irq() for MMC multislot support.
--- drivers/mmc/host/omap.c | 46 +++++++++++++++++++++++++++++----------------- 1 files changed, 29 insertions(+), 17 deletions(-)
drivers/mmc/host/omap.c +29 -17
@@ -547,11 +547,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)u16 status;int end_command;int end_transfer; -int transfer_error; +int transfer_error, cmd_error;if (host->cmd ==NULL && host->data ==NULL ) { status =OMAP_MMC_READ (host,STAT ); -dev_info (mmc_dev (host->mmc),"spurious irq 0x%04x\n" , status); +dev_info (mmc_dev (host->slots[0 ]->mmc), +"Spurious IRQ 0x%04x\n" , status);if (status !=0 ) {OMAP_MMC_WRITE (host,STAT , status);OMAP_MMC_WRITE (host,IE ,0 );
@@ -562,12 +563,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) end_command =0 ; end_transfer =0 ; transfer_error =0 ; + cmd_error =0 ;while ((status =OMAP_MMC_READ (host,STAT )) !=0 ) { +int cmd; +OMAP_MMC_WRITE (host,STAT , status); +if (host->cmd !=NULL ) + cmd = host->cmd->opcode; +else + cmd =-1 ;#ifdef CONFIG_MMC_DEBUG dev_dbg (mmc_dev (host->mmc),"MMC IRQ %04x (CMD %d): " , - status, host->cmd !=NULL ? host->cmd->opcode :-1 ); + status, cmd);mmc_omap_report_irq (status);printk ("\n" );#endif
@@ -579,12 +587,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)mmc_omap_xfer_data (host,1 ); } -if (status &OMAP_MMC_STAT_END_OF_DATA ) { +if (status &OMAP_MMC_STAT_END_OF_DATA ) end_transfer =1 ; - }if (status &OMAP_MMC_STAT_DATA_TOUT ) { -dev_dbg (mmc_dev (host->mmc),"data timeout\n" ); +dev_dbg (mmc_dev (host->mmc),"data timeout (CMD%d)\n" , + cmd);if (host->data) { host->data->error = -ETIMEDOUT ; transfer_error =1 ;
@@ -608,12 +616,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)if (host->cmd) {struct mmc_omap_slot *slot = host->current_slot; -if (!mmc_omap_cover_is_open (slot)) +if (slot ==NULL || + !mmc_omap_cover_is_open (slot))dev_err (mmc_dev (host->mmc), -"command timeout, CMD %d\n" , - host->cmd->opcode); +"command timeout (CMD%d)\n" , + cmd); host->cmd->error = -ETIMEDOUT ; end_command =1 ; + cmd_error =1 ; } }
@@ -621,9 +631,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)if (host->cmd) {dev_err (mmc_dev (host->mmc),"command CRC error (CMD%d, arg 0x%08x)\n" , - host->cmd->opcode, host->cmd->arg); + cmd, host->cmd->arg); host->cmd->error = -EILSEQ ; end_command =1 ; + cmd_error =1 ; }else dev_err (mmc_dev (host->mmc),"command CRC error without cmd?\n" );
@@ -632,13 +643,13 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)if (status &OMAP_MMC_STAT_CARD_ERR ) {dev_dbg (mmc_dev (host->mmc),"ignoring card status error (CMD%d)\n" , - host->cmd->opcode); + cmd); end_command =1 ; }/* * NOTE: On 1610 the END_OF_CMD may come too early when -* starting a write +* starting a write */ if ((status &OMAP_MMC_STAT_END_OF_CMD ) && (!(status &OMAP_MMC_STAT_A_EMPTY ))) {
@@ -646,13 +657,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } } -if (end_command) { +if (end_command)mmc_omap_cmd_done (host, host->cmd); +if (host->data !=NULL ) { +if (transfer_error) +mmc_omap_xfer_done (host, host->data); +else if (end_transfer) +mmc_omap_end_of_data (host, host->data); } -if (transfer_error) -mmc_omap_xfer_done (host, host->data); -else if (end_transfer) -mmc_omap_end_of_data (host, host->data);return IRQ_HANDLED ; }
-- 1.5.3.GIT
PATCH 7/18 MMC: OMAP: New release dma and abort xfer functions
In reply to Pierre OssmanFrom: Juha Yrjola <[email protected]>
New functions to support MMC multislot:mmc_omap_release_dma() and mmc_omap_abort_xfer().
--- drivers/mmc/host/omap.c | 79 +++++++++++++++++++++++++++++++++++----------- 1 files changed, 60 insertions(+), 19 deletions(-)
drivers/mmc/host/omap.c +60 -19
@@ -327,26 +327,32 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) }static void +mmc_omap_release_dma (struct mmc_omap_host *host,struct mmc_data *data, +int abort) +{ +enum dma_data_direction dma_data_dir; + +BUG_ON (host->dma_ch <0 ); +if (data->error) +omap_stop_dma (host->dma_ch); +/* Release DMA channel lazily */ +mod_timer (&host->dma_timer, jiffies +HZ ); +if (data->flags &MMC_DATA_WRITE ) + dma_data_dir =DMA_TO_DEVICE ; +else + dma_data_dir =DMA_FROM_DEVICE ; +dma_unmap_sg (mmc_dev (host->mmc), data->sg, host->sg_len, + dma_data_dir); +} + +static void mmc_omap_xfer_done (struct mmc_omap_host *host,struct mmc_data *data) { -if (host->dma_in_use) { -enum dma_data_direction dma_data_dir; - -BUG_ON (host->dma_ch <0 ); -if (data->error) -omap_stop_dma (host->dma_ch); -/* Release DMA channel lazily */ -mod_timer (&host->dma_timer, jiffies +HZ ); -if (data->flags &MMC_DATA_WRITE ) - dma_data_dir =DMA_TO_DEVICE ; -else - dma_data_dir =DMA_FROM_DEVICE ; -dma_unmap_sg (mmc_dev (host->mmc), data->sg, host->sg_len, - dma_data_dir); - } +if (host->dma_in_use) +mmc_omap_release_dma (host, data, data->error); + host->data =NULL ; host->sg_len =0 ; -clk_disable (host->fclk);/* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing * dozens of requests until the card finishes writing data.
@@ -354,8 +360,12 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)*/ if (!data->stop) { +struct mmc_host *mmc; + host->mrq =NULL ; -mmc_request_done (host->mmc, data->mrq); + mmc = host->mmc; +mmc_omap_release_slot (host->current_slot); +mmc_request_done (mmc, data->mrq);return ; }
@@ -363,6 +373,32 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) }static void +mmc_omap_abort_xfer (struct mmc_omap_host *host,struct mmc_data *data) +{ +int loops; +u16 ie; + +if (host->dma_in_use) +mmc_omap_release_dma (host, data,1 ); + + host->data =NULL ; + host->sg_len =0 ; + + ie =OMAP_MMC_READ (host,IE ); +OMAP_MMC_WRITE (host,IE ,0 ); +OMAP_MMC_WRITE (host,CMD ,1 <<7 ); + loops =0 ; +while (!(OMAP_MMC_READ (host,STAT ) &OMAP_MMC_STAT_END_OF_CMD )) { +udelay (1 ); + loops++; +if (loops ==100000 ) +break ; + } +OMAP_MMC_WRITE (host,STAT ,OMAP_MMC_STAT_END_OF_CMD ); +OMAP_MMC_WRITE (host,IE , ie); +} + +static void mmc_omap_end_of_data (struct mmc_omap_host *host,struct mmc_data *data) {unsigned long flags;
@@ -439,9 +475,14 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) }if (host->data ==NULL || cmd->error) { +struct mmc_host *mmc; + +if (host->data !=NULL ) +mmc_omap_abort_xfer (host, host->data); host->mrq =NULL ; -clk_disable (host->fclk); -mmc_request_done (host->mmc, cmd->mrq); + mmc = host->mmc; +mmc_omap_release_slot (host->current_slot); +mmc_request_done (mmc, cmd->mrq); } }
-- 1.5.3.GIT
PATCH 8/18 MMC: OMAP: Fix timeout calculation for MMC multislot support
In reply to Pierre OssmanFrom: Juha Yrjola <[email protected]>
Fix the data timeout calculation for MMC multislot support.
--- drivers/mmc/host/omap.c | 9 ++++----- 1 files changed, 4 insertions(+), 5 deletions(-)
drivers/mmc/host/omap.c +4 -5
@@ -861,13 +861,12 @@ static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_requesstatic inline void set_data_timeout (struct mmc_omap_host *host,struct mmc_request *req) { -int timeout; +unsigned int timeout, cycle_ns;u16 reg; -/* Convert ns to clock cycles by assuming 20MHz frequency -* 1 cycle at 20MHz = 500 ns -*/ - timeout = req->data->timeout_clks + req->data->timeout_ns /500 ; + cycle_ns =1000000000 / host->current_slot->fclk_freq; + timeout = req->data->timeout_ns / cycle_ns; + timeout += req->data->timeout_clks;/* Check if we need to use timeout multiplier register */ reg =OMAP_MMC_READ (host,SDIO );
-- 1.5.3.GIT
PATCH 9/18 MMC: OMAP: Power functions modified to MMC multislot support
In reply to Pierre OssmanFrom: Juha Yrjola <[email protected]>
Modifications at power functions to MMC multislot support. This patch also move board-specific code out of MMC OMAP driver.
--- drivers/mmc/host/omap.c | 88 ++++++++++++++++++++--------------------------- 1 files changed, 37 insertions(+), 51 deletions(-)
drivers/mmc/host/omap.c +37 -51
@@ -983,52 +983,27 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)mmc_omap_start_request (host, req); } -static void innovator_fpga_socket_power (int on) +static void mmc_omap_set_power (struct mmc_omap_slot *slot,int power_on, +int vdd) { -#if defined(CONFIG_MACH_OMAP_INNOVATOR ) && defined(CONFIG_ARCH_OMAP15XX ) -if (on) { -fpga_write (fpga_read (OMAP1510_FPGA_POWER ) | (1 <<3 ), -OMAP1510_FPGA_POWER ); - }else { -fpga_write (fpga_read (OMAP1510_FPGA_POWER ) & ~(1 <<3 ), -OMAP1510_FPGA_POWER ); - } -#endif -} +struct mmc_omap_host *host; -/* -* Turn the socket power on/off. Innovator uses FPGA, most boards -* probably use GPIO. -*/ -static void mmc_omap_power (struct mmc_omap_host *host,int on) -{ -if (on) { -if (machine_is_omap_innovator ()) -innovator_fpga_socket_power (1 ); -else if (machine_is_omap_h2 ()) -tps65010_set_gpio_out_value (GPIO3 ,HIGH ); -else if (machine_is_omap_h3 ()) -/* GPIO 4 of TPS65010 sends SD_EN signal */ -tps65010_set_gpio_out_value (GPIO4 ,HIGH ); -else if (cpu_is_omap24xx ()) { -u16 reg =OMAP_MMC_READ (host,CON ); -OMAP_MMC_WRITE (host,CON , reg | (1 <<11 )); - }else -if (host->power_pin >=0 ) -omap_set_gpio_dataout (host->power_pin,1 ); - }else { -if (machine_is_omap_innovator ()) -innovator_fpga_socket_power (0 ); -else if (machine_is_omap_h2 ()) -tps65010_set_gpio_out_value (GPIO3 ,LOW ); -else if (machine_is_omap_h3 ()) -tps65010_set_gpio_out_value (GPIO4 ,LOW ); -else if (cpu_is_omap24xx ()) { -u16 reg =OMAP_MMC_READ (host,CON ); -OMAP_MMC_WRITE (host,CON , reg & ~(1 <<11 )); - }else -if (host->power_pin >=0 ) -omap_set_gpio_dataout (host->power_pin,0 ); + host = slot->host; + +if (slot->pdata->set_power !=NULL ) + slot->pdata->set_power (mmc_dev (slot->mmc), slot->id, power_on, + vdd); + +if (cpu_is_omap24xx ()) { +u16 w; + +if (power_on) { + w =OMAP_MMC_READ (host,CON ); +OMAP_MMC_WRITE (host,CON , w | (1 <<11 )); + }else { + w =OMAP_MMC_READ (host,CON ); +OMAP_MMC_WRITE (host,CON , w & ~(1 <<11 )); + } } }
@@ -1067,23 +1042,31 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)int i, dsor; dsor =mmc_omap_calc_divisor (mmc, ios); - host->bus_mode = ios->bus_mode; - host->hw_bus_mode = host->bus_mode; + +mmc_omap_select_slot (slot,0 ); + +if (ios->vdd != slot->vdd) + slot->vdd = ios->vdd;switch (ios->power_mode) {case MMC_POWER_OFF : -mmc_omap_power (host,0 ); +mmc_omap_set_power (slot,0 , ios->vdd);break ;case MMC_POWER_UP :/* Cannot touch dsor yet, just power up MMC */ -mmc_omap_power (host,1 ); -return ; +mmc_omap_set_power (slot,1 , ios->vdd); + goto exit;case MMC_POWER_ON : dsor |=1 <<11 ;break ; } -clk_enable (host->fclk); +if (slot->bus_mode != ios->bus_mode) { +if (slot->pdata->set_bus_mode !=NULL ) + slot->pdata->set_bus_mode (mmc_dev (mmc), slot->id, + ios->bus_mode); + slot->bus_mode = ios->bus_mode; + }/* On insanely high arm_per frequencies something sometimes * goes somehow out of sync, and the POW bit is not being set,
@@ -1091,6 +1074,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)* Writing to the CON register twice seems to do the trick. */ for (i =0 ; i <2 ; i++)OMAP_MMC_WRITE (host,CON , dsor); + slot->saved_con = dsor;if (ios->power_mode ==MMC_POWER_ON ) {/* Send clock cycles, poll completion */ OMAP_MMC_WRITE (host,IE ,0 );
@@ -1099,7 +1083,9 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)while ((OMAP_MMC_READ (host,STAT ) &1 ) ==0 );OMAP_MMC_WRITE (host,STAT ,1 ); } -clk_disable (host->fclk); + +exit: +mmc_omap_release_slot (slot); }static const struct mmc_host_ops mmc_omap_ops = {
-- 1.5.3.GIT
PATCH 11/18 MMC: OMAP: Abort stuck commands
In reply to Pierre OssmanFrom: Jarkko Lavinen <[email protected]>
When a card is removed while it is being accessed, a command can get stuck so that no timeout or end of command interrupt ever occurs. The command getting stuck is almost always CDM12, but also the other commands can get stuck. Catch a stuck command with a timer and try sending the initialization stream until the controller starts running again and responds with the end of command status.
--- drivers/mmc/host/omap.c | 93 ++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 83 insertions(+), 10 deletions(-)
drivers/mmc/host/omap.c +83 -10
@@ -134,6 +134,9 @@ struct mmc_omap_host {unsigned char bus_mode;unsigned char hw_bus_mode; +struct work_struct cmd_abort; +struct timer_list cmd_timer; +unsigned int sg_len;int sg_idx;u16 * buffer;
@@ -314,6 +317,8 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)if (host->data && !(host->data->flags &MMC_DATA_WRITE )) cmdreg |=1 <<15 ; +mod_timer (&host->cmd_timer, jiffies +HZ /2 ); +OMAP_MMC_WRITE (host,CTO ,200 );OMAP_MMC_WRITE (host,ARGL , cmd->arg &0xffff );OMAP_MMC_WRITE (host,ARGH , cmd->arg >>16 );
@@ -373,9 +378,37 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) }static void +mmc_omap_send_abort (struct mmc_omap_host *host) +{ +struct mmc_omap_slot *slot = host->current_slot; +unsigned int restarts, passes, timeout; +u16 stat =0 ; + +/* Sending abort takes 80 clocks. Have some extra and round up */ + timeout = (120 *1000000 + slot->fclk_freq -1 )/slot->fclk_freq; + restarts =0 ; +while (restarts <10000 ) { +OMAP_MMC_WRITE (host,STAT ,0xFFFF ); +OMAP_MMC_WRITE (host,CMD , (3 <<12 ) | (1 <<7 )); + + passes =0 ; +while (passes < timeout) { + stat =OMAP_MMC_READ (host,STAT ); +if (stat &OMAP_MMC_STAT_END_OF_CMD ) + goto out; +udelay (1 ); + passes++; + } + + restarts++; + } +out: +OMAP_MMC_WRITE (host,STAT , stat); +} + +static void mmc_omap_abort_xfer (struct mmc_omap_host *host,struct mmc_data *data) { -int loops;u16 ie;if (host->dma_in_use)
@@ -386,16 +419,8 @@ mmc_omap_abort_xfer(struct mmc_omap_host *host, struct mmc_data *data) ie =OMAP_MMC_READ (host,IE );OMAP_MMC_WRITE (host,IE ,0 ); -OMAP_MMC_WRITE (host,CMD ,1 <<7 ); - loops =0 ; -while (!(OMAP_MMC_READ (host,STAT ) &OMAP_MMC_STAT_END_OF_CMD )) { -udelay (1 ); - loops++; -if (loops ==100000 ) -break ; - } -OMAP_MMC_WRITE (host,STAT ,OMAP_MMC_STAT_END_OF_CMD );OMAP_MMC_WRITE (host,IE , ie); +mmc_omap_send_abort (host); } static void
@@ -451,6 +476,8 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) { host->cmd =NULL ; +del_timer (&host->cmd_timer); +if (cmd->flags &MMC_RSP_PRESENT ) {if (cmd->flags &MMC_RSP_136 ) {/* response type 2 */
@@ -486,6 +513,47 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) } } +/* +* Abort stuck command. Can occur when card is removed while it is being +* read. +*/ +static void mmc_omap_abort_command (struct work_struct *work) +{ + struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, + cmd_abort); +u16 ie; + + ie =OMAP_MMC_READ (host,IE ); +OMAP_MMC_WRITE (host,IE ,0 ); + +if (!host->cmd) { +OMAP_MMC_WRITE (host,IE , ie); +return ; + } + +dev_dbg (mmc_dev (host->mmc),"Aborting stuck command CMD%d\n" , + host->cmd->opcode); + +if (host->data && host->dma_in_use) +mmc_omap_release_dma (host, host->data,1 ); + + host->data =NULL ; + host->sg_len =0 ; + +mmc_omap_send_abort (host); + host->cmd->error = -ETIMEDOUT ; +mmc_omap_cmd_done (host, host->cmd); +OMAP_MMC_WRITE (host,IE , ie); +} + +static void +mmc_omap_cmd_timer (unsigned long data) +{ +struct mmc_omap_host *host = (struct mmc_omap_host *) data; + +schedule_work (&host->cmd_abort); +} +/* PIO only */ static void mmc_omap_sg_to_buf (struct mmc_omap_host *host)
@@ -1233,6 +1301,11 @@ static int __init mmc_omap_probe(struct platform_device *pdev) goto err_free_mem_region; } +INIT_WORK (&host->cmd_abort, mmc_omap_abort_command); +init_timer (&host->cmd_timer); + host->cmd_timer.function = mmc_omap_cmd_timer; + host->cmd_timer.data = (unsigned long ) host; +spin_lock_init (&host->dma_lock);init_timer (&host->dma_timer);spin_lock_init (&host->slot_lock);
-- 1.5.3.GIT
PATCH 14/18 MMC: OMAP: Use tasklet instead of workqueue for cover switch notification
In reply to Pierre OssmanFrom: Jarkko Lavinen <[email protected]>
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.
--- drivers/mmc/host/omap.c | 67 ++++++++++++++++++++++++++++------------------- 1 files changed, 40 insertions(+), 27 deletions(-)
drivers/mmc/host/omap.c +40 -27
@@ -94,7 +94,7 @@/* Specifies how often in millisecs to poll for card status changes * when the cover switch is open */ -#define OMAP_MMC_SWITCH_POLL_DELAY 500 +#define OMAP_MMC_COVER_POLL_DELAY 500struct mmc_omap_host ;
@@ -106,8 +106,8 @@ struct mmc_omap_slot {unsigned int fclk_freq; unsigned powered:1; -struct work_struct switch_work; -struct timer_list switch_timer; +struct tasklet_struct cover_tasklet; +struct timer_list cover_timer;unsigned cover_open;struct mmc_request *mrq;
@@ -740,40 +740,51 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)return IRQ_HANDLED ; } -void omap_mmc_notify_cover_event (struct device *dev,int slot,int is_closed) +void omap_mmc_notify_cover_event (struct device *dev,int num,int is_closed) { +int cover_open;struct mmc_omap_host *host =dev_get_drvdata (dev); +struct mmc_omap_slot *slot = host->slots[num]; -BUG_ON (slot >= host->nr_slots); +BUG_ON (num >= host->nr_slots);/* Other subsystems can call in here before we're initialised. */ -if (host->nr_slots ==0 || !host->slots[slot]) +if (host->nr_slots ==0 || !host->slots[num])return ; -schedule_work (&host->slots[slot]->switch_work); + cover_open =mmc_omap_cover_is_open (slot); +if (cover_open != slot->cover_open) { + slot->cover_open = cover_open; +sysfs_notify (&slot->mmc->class_dev.kobj,NULL ,"cover_switch" ); + } + +tasklet_hi_schedule (&slot->cover_tasklet); } -static void mmc_omap_switch_timer (unsigned long arg) +static void mmc_omap_cover_timer (unsigned long arg) {struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg; - -schedule_work (&slot->switch_work); +tasklet_schedule (&slot->cover_tasklet); } -static void mmc_omap_cover_handler (struct work_struct *work) +static void mmc_omap_cover_handler (unsigned long param) { - struct mmc_omap_slot *slot = container_of(work, struct mmc_omap_slot, - switch_work); -int cover_open; +struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param; +int cover_open =mmc_omap_cover_is_open (slot); - cover_open =mmc_omap_cover_is_open (slot); -if (cover_open != slot->cover_open) { -sysfs_notify (&slot->mmc->class_dev.kobj,NULL ,"cover_switch" ); - slot->cover_open = cover_open; -dev_info (mmc_dev (slot->mmc),"cover is now %s\n" , - cover_open ?"open" :"closed" ); - } -mmc_detect_change (slot->mmc, slot->id); +mmc_detect_change (slot->mmc,0 ); +if (!cover_open) +return ; + +/* +* If no card is inserted, we postpone polling until +* the cover has been closed. +*/ +if (slot->mmc->card ==NULL || !mmc_card_present (slot->mmc->card)) +return ; + +mod_timer (&slot->cover_timer, + jiffies +msecs_to_jiffies (OMAP_MMC_COVER_POLL_DELAY )); }/* 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)if (r <0 ) goto err_remove_slot_name; -INIT_WORK (&slot->switch_work, mmc_omap_cover_handler); -setup_timer (&slot->switch_timer, mmc_omap_switch_timer, - (unsigned long ) slot); -schedule_work (&slot->switch_work); +setup_timer (&slot->cover_timer, mmc_omap_cover_timer, + (unsigned long )slot); +tasklet_init (&slot->cover_tasklet, mmc_omap_cover_handler, + (unsigned long )slot); +tasklet_schedule (&slot->cover_tasklet); }return 0 ;
@@ -1263,7 +1275,8 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot)if (slot->pdata->get_cover_state !=NULL )device_remove_file (&mmc->class_dev, &dev_attr_cover_switch); -del_timer_sync (&slot->switch_timer); +tasklet_kill (&slot->cover_tasklet); +del_timer_sync (&slot->cover_timer);flush_scheduled_work ();mmc_remove_host (mmc);
-- 1.5.3.GIT
PATCH 15/18 MMC: OMAP: Move failing command abortion to workqueue
In reply to Pierre OssmanFrom: Jarkko Lavinen <[email protected]>
Abort failed command from workqueue rather than from an interrupt, allowing longer delays in abortion.
--- drivers/mmc/host/omap.c | 82 ++++++++++++++++++++++++++++------------------- 1 files changed, 49 insertions(+), 33 deletions(-)
drivers/mmc/host/omap.c +49 -33
@@ -134,8 +134,9 @@ struct mmc_omap_host {unsigned char bus_mode;unsigned char hw_bus_mode; -struct work_struct cmd_abort; -struct timer_list cmd_timer; +struct work_struct cmd_abort_work; + unsigned abort:1; +struct timer_list cmd_abort_timer;unsigned int sg_len;int sg_idx;
@@ -320,7 +321,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)if (host->data && !(host->data->flags &MMC_DATA_WRITE )) cmdreg |=1 <<15 ; -mod_timer (&host->cmd_timer, jiffies +HZ /2 ); +mod_timer (&host->cmd_abort_timer, jiffies +HZ /2 );OMAP_MMC_WRITE (host,CTO ,200 );OMAP_MMC_WRITE (host,ARGL , cmd->arg &0xffff );
@@ -381,7 +382,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) }static void -mmc_omap_send_abort (struct mmc_omap_host *host) +mmc_omap_send_abort (struct mmc_omap_host *host,int maxloops) {struct mmc_omap_slot *slot = host->current_slot;unsigned int restarts, passes, timeout;
@@ -390,7 +391,7 @@ mmc_omap_send_abort(struct mmc_omap_host *host)/* Sending abort takes 80 clocks. Have some extra and round up */ timeout = (120 *1000000 + slot->fclk_freq -1 )/slot->fclk_freq; restarts =0 ; -while (restarts <10000 ) { +while (restarts < maxloops) {OMAP_MMC_WRITE (host,STAT ,0xFFFF );OMAP_MMC_WRITE (host,CMD , (3 <<12 ) | (1 <<7 ));
@@ -412,18 +413,13 @@ out:static void mmc_omap_abort_xfer (struct mmc_omap_host *host,struct mmc_data *data) { -u16 ie; -if (host->dma_in_use)mmc_omap_release_dma (host, data,1 ); host->data =NULL ; host->sg_len =0 ; - ie =OMAP_MMC_READ (host,IE ); -OMAP_MMC_WRITE (host,IE ,0 ); -OMAP_MMC_WRITE (host,IE , ie); -mmc_omap_send_abort (host); +mmc_omap_send_abort (host,10000 ); } static void
@@ -479,7 +475,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) { host->cmd =NULL ; -del_timer (&host->cmd_timer); +del_timer (&host->cmd_abort_timer);if (cmd->flags &MMC_RSP_PRESENT ) {if (cmd->flags &MMC_RSP_136 ) {
@@ -523,38 +519,48 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)static void mmc_omap_abort_command (struct work_struct *work) { struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, - cmd_abort); -u16 ie; - - ie =OMAP_MMC_READ (host,IE ); -OMAP_MMC_WRITE (host,IE ,0 ); - -if (!host->cmd) { -OMAP_MMC_WRITE (host,IE , ie); -return ; - } + cmd_abort_work); +BUG_ON (!host->cmd);dev_dbg (mmc_dev (host->mmc),"Aborting stuck command CMD%d\n" , host->cmd->opcode); -if (host->data && host->dma_in_use) -mmc_omap_release_dma (host, host->data,1 ); +if (host->cmd->error ==0 ) + host->cmd->error = -ETIMEDOUT ; - host->data =NULL ; - host->sg_len =0 ; +if (host->data ==NULL ) { +struct mmc_command *cmd; +struct mmc_host *mmc; + + cmd = host->cmd; + host->cmd =NULL ; +mmc_omap_send_abort (host,10000 ); + + host->mrq =NULL ; + mmc = host->mmc; +mmc_omap_release_slot (host->current_slot); +mmc_request_done (mmc, cmd->mrq); + }else +mmc_omap_cmd_done (host, host->cmd); -mmc_omap_send_abort (host); - host->cmd->error = -ETIMEDOUT ; -mmc_omap_cmd_done (host, host->cmd); -OMAP_MMC_WRITE (host,IE , ie); + host->abort =0 ; +enable_irq (host->irq); }static void mmc_omap_cmd_timer (unsigned long data) {struct mmc_omap_host *host = (struct mmc_omap_host *) data; +unsigned long flags; -schedule_work (&host->cmd_abort); +spin_lock_irqsave (&host->slot_lock, flags); +if (host->cmd !=NULL && !host->abort) { +OMAP_MMC_WRITE (host,IE ,0 ); +disable_irq (host->irq); + host->abort =1 ; +schedule_work (&host->cmd_abort_work); + } +spin_unlock_irqrestore (&host->slot_lock, flags); }/* PIO only */
@@ -728,6 +734,15 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } } +if (cmd_error && host->data) { +del_timer (&host->cmd_abort_timer); + host->abort =1 ; +OMAP_MMC_WRITE (host,IE ,0 ); +disable_irq (host->irq); +schedule_work (&host->cmd_abort_work); +return IRQ_HANDLED ; + } +if (end_command)mmc_omap_cmd_done (host, host->cmd);if (host->data !=NULL ) {
@@ -1316,8 +1331,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev) goto err_free_mem_region; } -INIT_WORK (&host->cmd_abort, mmc_omap_abort_command); -setup_timer (&host->cmd_timer, mmc_omap_cmd_timer, (unsigned long ) host); +INIT_WORK (&host->cmd_abort_work, mmc_omap_abort_command); +setup_timer (&host->cmd_abort_timer, mmc_omap_cmd_timer, + (unsigned long ) host);spin_lock_init (&host->dma_lock);setup_timer (&host->dma_timer, mmc_omap_dma_timer, (unsigned long ) host);
-- 1.5.3.GIT
PATCH 16/18 MMC: OMAP: Lazy clock shutdown
In reply to Pierre OssmanFrom: Jarkko Lavinen <[email protected]>
MMCA spec says the mmc clock should be kept running for at least 8 cycles after the last RW request. Ensure this with lazy clock disable after a request, or with an explicit delay before switching a slot.
--- drivers/mmc/host/omap.c | 89 +++++++++++++++++++++++++++++++++++++++------- 1 files changed, 75 insertions(+), 14 deletions(-)
drivers/mmc/host/omap.c +75 -14
@@ -161,9 +161,38 @@ struct mmc_omap_host {wait_queue_head_t slot_wq;int nr_slots; +struct timer_list clk_timer; +spinlock_t clk_lock;/* for changing enabled state */ + unsigned int fclk_enabled:1; +struct omap_mmc_platform_data *pdata; }; +void mmc_omap_fclk_offdelay (struct mmc_omap_slot *slot) +{ +unsigned long tick_ns; + +if (slot !=NULL && slot->host->fclk_enabled && slot->fclk_freq >0 ) { + tick_ns = (1000000000 + slot->fclk_freq -1 ) / slot->fclk_freq; +ndelay (8 * tick_ns); + } +} + +void mmc_omap_fclk_enable (struct mmc_omap_host *host,unsigned int enable) +{ +unsigned long flags; + +spin_lock_irqsave (&host->clk_lock, flags); +if (host->fclk_enabled != enable) { + host->fclk_enabled = enable; +if (enable) +clk_enable (host->fclk); +else +clk_disable (host->fclk); + } +spin_unlock_irqrestore (&host->clk_lock, flags); +} +static void mmc_omap_select_slot (struct mmc_omap_slot *slot,int claimed) {struct mmc_omap_host *host = slot->host;
@@ -180,32 +209,49 @@ static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) host->mmc = slot->mmc;spin_unlock_irqrestore (&host->slot_lock, flags); no_claim: -clk_enable (host->fclk); +del_timer (&host->clk_timer); +if (host->current_slot != slot || !claimed) +mmc_omap_fclk_offdelay (host->current_slot); +if (host->current_slot != slot) { +OMAP_MMC_WRITE (host,CON , slot->saved_con &0xFC00 );if (host->pdata->switch_slot !=NULL ) host->pdata->switch_slot (mmc_dev (slot->mmc), slot->id); host->current_slot = slot; } -/* Doing the dummy read here seems to work around some bug -* at least in OMAP24xx silicon where the command would not -* start after writing the CMD register. Sigh. */ -OMAP_MMC_READ (host,CON ); +if (claimed) { +mmc_omap_fclk_enable (host,1 ); + +/* Doing the dummy read here seems to work around some bug +* at least in OMAP24xx silicon where the command would not +* start after writing the CMD register. Sigh. */ +OMAP_MMC_READ (host,CON ); -OMAP_MMC_WRITE (host,CON , slot->saved_con); +OMAP_MMC_WRITE (host,CON , slot->saved_con); + }else +mmc_omap_fclk_enable (host,0 ); }static void mmc_omap_start_request (struct mmc_omap_host *host,struct mmc_request *req); -static void mmc_omap_release_slot (struct mmc_omap_slot *slot) +static void mmc_omap_release_slot (struct mmc_omap_slot *slot,int clk_enabled) {struct mmc_omap_host *host = slot->host;unsigned long flags;int i;BUG_ON (slot ==NULL || host->mmc ==NULL ); -clk_disable (host->fclk); + +if (clk_enabled) +/* Keeps clock running for at least 8 cycles on valid freq */ +mod_timer (&host->clk_timer, jiffies +HZ /10 ); +else { +del_timer (&host->clk_timer); +mmc_omap_fclk_offdelay (slot); +mmc_omap_fclk_enable (host,0 ); + }spin_lock_irqsave (&host->slot_lock, flags);/* Check for any pending requests */
@@ -373,7 +419,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) host->mrq =NULL ; mmc = host->mmc; -mmc_omap_release_slot (host->current_slot); +mmc_omap_release_slot (host->current_slot,1 );mmc_request_done (mmc, data->mrq);return ; }
@@ -507,7 +553,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)mmc_omap_abort_xfer (host, host->data); host->mrq =NULL ; mmc = host->mmc; -mmc_omap_release_slot (host->current_slot); +mmc_omap_release_slot (host->current_slot,1 );mmc_request_done (mmc, cmd->mrq); } }
@@ -538,7 +584,7 @@ static void mmc_omap_abort_command(struct work_struct *work) host->mrq =NULL ; mmc = host->mmc; -mmc_omap_release_slot (host->current_slot); +mmc_omap_release_slot (host->current_slot,1 );mmc_request_done (mmc, cmd->mrq); }else mmc_omap_cmd_done (host, host->cmd);
@@ -576,6 +622,14 @@ mmc_omap_sg_to_buf(struct mmc_omap_host *host) host->buffer_bytes_left = host->total_bytes_left; } +static void +mmc_omap_clk_timer (unsigned long data) +{ +struct mmc_omap_host *host = (struct mmc_omap_host *) data; + +mmc_omap_fclk_enable (host,0 ); +} +/* PIO only */ static void mmc_omap_xfer_data (struct mmc_omap_host *host,int write)
@@ -1149,14 +1203,16 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)struct mmc_omap_slot *slot =mmc_priv (mmc);struct mmc_omap_host *host = slot->host;int i, dsor; - - dsor =mmc_omap_calc_divisor (mmc, ios); +int clk_enabled;mmc_omap_select_slot (slot,0 ); + dsor =mmc_omap_calc_divisor (mmc, ios); +if (ios->vdd != slot->vdd) slot->vdd = ios->vdd; + clk_enabled =0 ;switch (ios->power_mode) {case MMC_POWER_OFF :mmc_omap_set_power (slot,0 , ios->vdd);
@@ -1166,6 +1222,8 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)mmc_omap_set_power (slot,1 , ios->vdd); goto exit;case MMC_POWER_ON : +mmc_omap_fclk_enable (host,1 ); + clk_enabled =1 ; dsor |=1 <<11 ;break ; }
@@ -1194,7 +1252,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } exit: -mmc_omap_release_slot (slot); +mmc_omap_release_slot (slot, clk_enabled); }static const struct mmc_host_ops mmc_omap_ops = {
@@ -1335,6 +1393,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev)setup_timer (&host->cmd_abort_timer, mmc_omap_cmd_timer, (unsigned long ) host); +spin_lock_init (&host->clk_lock); +setup_timer (&host->clk_timer, mmc_omap_clk_timer, (unsigned long ) host); +spin_lock_init (&host->dma_lock);setup_timer (&host->dma_timer, mmc_omap_dma_timer, (unsigned long ) host);spin_lock_init (&host->slot_lock);
-- 1.5.3.GIT
PATCH 12/18 MMC: OMAP: Using setup_timer instead of init_timer
In reply to Pierre OssmanFrom: Carlos Eduardo Aguiar <[email protected]>
Using setup_timer() instead of init_timer() on omap.c file.
--- drivers/mmc/host/omap.c | 14 ++++---------- 1 files changed, 4 insertions(+), 10 deletions(-)
drivers/mmc/host/omap.c +4 -10
@@ -1235,9 +1235,8 @@ static int __init mmc_omap_new_slot(struct mmc_omap_host *host, int id) goto err_remove_slot_name;INIT_WORK (&slot->switch_work, mmc_omap_cover_handler); -init_timer (&slot->switch_timer); - slot->switch_timer.function = mmc_omap_switch_timer; - slot->switch_timer.data = (unsigned long ) slot; +setup_timer (&slot->switch_timer, mmc_omap_switch_timer, + (unsigned long ) slot);schedule_work (&slot->switch_work); }
@@ -1302,18 +1301,13 @@ static int __init mmc_omap_probe(struct platform_device *pdev) }INIT_WORK (&host->cmd_abort, mmc_omap_abort_command); -init_timer (&host->cmd_timer); - host->cmd_timer.function = mmc_omap_cmd_timer; - host->cmd_timer.data = (unsigned long ) host; +setup_timer (&host->cmd_timer, mmc_omap_cmd_timer, (unsigned long ) host);spin_lock_init (&host->dma_lock); -init_timer (&host->dma_timer); +setup_timer (&host->dma_timer, mmc_omap_dma_timer, (unsigned long ) host);spin_lock_init (&host->slot_lock);init_waitqueue_head (&host->slot_wq); - host->dma_timer.function = mmc_omap_dma_timer; - host->dma_timer.data = (unsigned long ) host; - host->pdata = pdata; host->dev = &pdev->dev;platform_set_drvdata (pdev, host);
-- 1.5.3.GIT
PATCH 13/18 MMC: OMAP: Check the get_cover_state function pointer if not set
In reply to Pierre OssmanFrom: Kyungmin Park <[email protected]>
If the get_cover_state is not set, it occurs the oops.
--- drivers/mmc/host/omap.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-)
drivers/mmc/host/omap.c +4 -1
@@ -236,7 +236,10 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot)static inline int mmc_omap_cover_is_open (struct mmc_omap_slot *slot) { -return slot->pdata->get_cover_state (mmc_dev (slot->mmc), slot->id); +if (slot->pdata->get_cover_state) +return slot->pdata->get_cover_state (mmc_dev (slot->mmc), + slot->id); +return 0 ; } static ssize_t
-- 1.5.3.GIT
PATCH 17/18 MMC: OMAP: Start new commands from work queue instead of irq
In reply to Pierre OssmanFrom: Jarkko Lavinen <[email protected]>
Use work queues for starting new commands instead of starting them directly from irq handler. The command scheduling needs to be delayed a bit for some cards which should not be done from an interrupt.
--- drivers/mmc/host/omap.c | 48 +++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 42 insertions(+), 6 deletions(-)
drivers/mmc/host/omap.c +42 -6
@@ -138,6 +138,11 @@ struct mmc_omap_host { unsigned abort:1;struct timer_list cmd_abort_timer; +struct work_struct slot_release_work; +struct mmc_omap_slot *next_slot; +struct work_struct send_stop_work; +struct mmc_data *stop_data; +unsigned int sg_len;int sg_idx;u16 * buffer;
@@ -236,6 +241,21 @@ no_claim:static void mmc_omap_start_request (struct mmc_omap_host *host,struct mmc_request *req); +static void mmc_omap_slot_release_work (struct work_struct *work) +{ + struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, + slot_release_work); +struct mmc_omap_slot *next_slot = host->next_slot; +struct mmc_request *rq; + + host->next_slot =NULL ; +mmc_omap_select_slot (next_slot,1 ); + + rq = next_slot->mrq; + next_slot->mrq =NULL ; +mmc_omap_start_request (host, rq); +} +static void mmc_omap_release_slot (struct mmc_omap_slot *slot,int clk_enabled) {struct mmc_omap_host *host = slot->host;
@@ -257,21 +277,19 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)/* Check for any pending requests */ for (i =0 ; i < host->nr_slots; i++) {struct mmc_omap_slot *new_slot; -struct mmc_request *rq;if (host->slots[i] ==NULL || host->slots[i]->mrq ==NULL )continue ; +BUG_ON (host->next_slot !=NULL ); new_slot = host->slots[i];/* The current slot should not have a request in queue */ BUG_ON (new_slot == host->current_slot); + host->next_slot = new_slot; host->mmc = new_slot->mmc;spin_unlock_irqrestore (&host->slot_lock, flags); -mmc_omap_select_slot (new_slot,1 ); - rq = new_slot->mrq; - new_slot->mrq =NULL ; -mmc_omap_start_request (host, rq); +schedule_work (&host->slot_release_work);return ; }
@@ -400,6 +418,20 @@ mmc_omap_release_dma(struct mmc_omap_host *host, struct mmc_data *data, dma_data_dir); } +static void mmc_omap_send_stop_work (struct work_struct *work) +{ + struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, + send_stop_work); +struct mmc_omap_slot *slot = host->current_slot; +struct mmc_data *data = host->stop_data; +unsigned long tick_ns; + + tick_ns = (1000000000 + slot->fclk_freq -1 )/slot->fclk_freq; +ndelay (8 *tick_ns); + +mmc_omap_start_command (host, data->stop); +} +static void mmc_omap_xfer_done (struct mmc_omap_host *host,struct mmc_data *data) {
@@ -424,7 +456,8 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)return ; } -mmc_omap_start_command (host, data->stop); + host->stop_data = data; +schedule_work (&host->send_stop_work); } static void
@@ -1389,6 +1422,9 @@ static int __init mmc_omap_probe(struct platform_device *pdev) goto err_free_mem_region; } +INIT_WORK (&host->slot_release_work, mmc_omap_slot_release_work); +INIT_WORK (&host->send_stop_work, mmc_omap_send_stop_work); +INIT_WORK (&host->cmd_abort_work, mmc_omap_abort_command);setup_timer (&host->cmd_abort_timer, mmc_omap_cmd_timer, (unsigned long ) host);
-- 1.5.3.GIT
PATCH 18/18 MMC: OMAP: Do not busy wait for end of command for ever
In reply to Pierre OssmanFrom: Jarkko Lavinen <[email protected]>
The limit was a fixed 100k limit in the busy loop, which is not accurate. It would better to have time limit for the worst case which occurs when sending 80 cycles at 400 kHz and takes about 200 microseconds, so limit the max time spend in the busy loop for some 250 microseconds.
--- drivers/mmc/host/omap.c | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-)
drivers/mmc/host/omap.c +7 -1
@@ -1276,11 +1276,17 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)OMAP_MMC_WRITE (host,CON , dsor); slot->saved_con = dsor;if (ios->power_mode ==MMC_POWER_ON ) { +/* worst case at 400kHz, 80 cycles makes 200 microsecs */ +int usecs =250 ; +/* Send clock cycles, poll completion */ OMAP_MMC_WRITE (host,IE ,0 );OMAP_MMC_WRITE (host,STAT ,0xffff );OMAP_MMC_WRITE (host,CMD ,1 <<7 ); -while ((OMAP_MMC_READ (host,STAT ) &1 ) ==0 ); +while (usecs >0 && (OMAP_MMC_READ (host,STAT ) &1 ) ==0 ) { +udelay (1 ); + usecs--; + }OMAP_MMC_WRITE (host,STAT ,1 ); }
-- 1.5.3.GIT
Quoted 3 lines · On Wed, 26 Mar 2008 16:08:36 -0400 Carlos Aguiar <[email protected]>…
So, feel free to queue it up.
All done. Will be in 2.6.26.
Signature · 5 lines
-- Pierre Ossman Linux kernel, MMC maintainer http://www.kernel.org PulseAudio, core developer http://pulseaudio.org rdesktop, core developer http://www.rdesktop.org