Commit 135cedad authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (26 commits)
  mmc: sdio_ops.c should #include "sdio_ops.h"
  mmc: proper prototypes for mmc_attach_*()
  mmc: make __mmc_release_bus() static
  sdhci: improve no card, no reset quirk
  MMC: OMAP: Do not busy wait for end of command for ever
  MMC: OMAP: Start new commands from work queue instead of irq
  MMC: OMAP: Lazy clock shutdown
  MMC: OMAP: Move failing command abortion to workqueue
  MMC: OMAP: Use tasklet instead of workqueue for cover switch notification
  MMC: OMAP: Check the get_cover_state function pointer if not set
  MMC: OMAP: Using setup_timer instead of init_timer
  MMC: OMAP: Abort stuck commands
  MMC: OMAP: General cleanup for MMC multislot support
  MMC: OMAP: Power functions modified to MMC multislot support
  MMC: OMAP: Fix timeout calculation for MMC multislot support
  MMC: OMAP: New release dma and abort xfer functions
  MMC: OMAP: Add back cover switch support
  MMC: OMAP: Introduce new multislot structure and change driver to use it
  MMC: OMAP: Remove cover switch handling to allow adding multislot support
  MMC: OMAP: Fix the BYTEBLOCK capability removal
  ...
parents 8a322726 e70aa3fa
...@@ -35,10 +35,6 @@ ...@@ -35,10 +35,6 @@
#include "sd_ops.h" #include "sd_ops.h"
#include "sdio_ops.h" #include "sdio_ops.h"
extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
static struct workqueue_struct *workqueue; static struct workqueue_struct *workqueue;
/* /*
...@@ -516,7 +512,7 @@ static void mmc_power_off(struct mmc_host *host) ...@@ -516,7 +512,7 @@ static void mmc_power_off(struct mmc_host *host)
/* /*
* Cleanup when the last reference to the bus operator is dropped. * Cleanup when the last reference to the bus operator is dropped.
*/ */
void __mmc_release_bus(struct mmc_host *host) static void __mmc_release_bus(struct mmc_host *host)
{ {
BUG_ON(!host); BUG_ON(!host);
BUG_ON(host->bus_refs); BUG_ON(host->bus_refs);
......
...@@ -46,6 +46,10 @@ void mmc_rescan(struct work_struct *work); ...@@ -46,6 +46,10 @@ void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host); void mmc_start_host(struct mmc_host *host);
void mmc_stop_host(struct mmc_host *host); void mmc_stop_host(struct mmc_host *host);
int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
int mmc_attach_sd(struct mmc_host *host, u32 ocr);
int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
extern int use_spi_crc; extern int use_spi_crc;
#endif #endif
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* linux/drivers/mmc/core/host.c * linux/drivers/mmc/core/host.c
* *
* Copyright (C) 2003 Russell King, All Rights Reserved. * Copyright (C) 2003 Russell King, All Rights Reserved.
* Copyright (C) 2007 Pierre Ossman * Copyright (C) 2007-2008 Pierre Ossman
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -57,12 +57,25 @@ static DEFINE_SPINLOCK(mmc_host_lock); ...@@ -57,12 +57,25 @@ static DEFINE_SPINLOCK(mmc_host_lock);
*/ */
struct mmc_host *mmc_alloc_host(int extra, struct device *dev) struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{ {
int err;
struct mmc_host *host; struct mmc_host *host;
if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
return NULL;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
if (!host) if (!host)
return NULL; return NULL;
spin_lock(&mmc_host_lock);
err = idr_get_new(&mmc_host_idr, host, &host->index);
spin_unlock(&mmc_host_lock);
if (err)
goto free;
snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
"mmc%d", host->index);
host->parent = dev; host->parent = dev;
host->class_dev.parent = dev; host->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class; host->class_dev.class = &mmc_host_class;
...@@ -85,6 +98,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) ...@@ -85,6 +98,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->max_blk_count = PAGE_CACHE_SIZE / 512; host->max_blk_count = PAGE_CACHE_SIZE / 512;
return host; return host;
free:
kfree(host);
return NULL;
} }
EXPORT_SYMBOL(mmc_alloc_host); EXPORT_SYMBOL(mmc_alloc_host);
...@@ -104,18 +121,6 @@ int mmc_add_host(struct mmc_host *host) ...@@ -104,18 +121,6 @@ int mmc_add_host(struct mmc_host *host)
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
!host->ops->enable_sdio_irq); !host->ops->enable_sdio_irq);
if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
return -ENOMEM;
spin_lock(&mmc_host_lock);
err = idr_get_new(&mmc_host_idr, host, &host->index);
spin_unlock(&mmc_host_lock);
if (err)
return err;
snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
"mmc%d", host->index);
led_trigger_register_simple(host->class_dev.bus_id, &host->led); led_trigger_register_simple(host->class_dev.bus_id, &host->led);
err = device_add(&host->class_dev); err = device_add(&host->class_dev);
...@@ -144,10 +149,6 @@ void mmc_remove_host(struct mmc_host *host) ...@@ -144,10 +149,6 @@ void mmc_remove_host(struct mmc_host *host)
device_del(&host->class_dev); device_del(&host->class_dev);
led_trigger_unregister_simple(host->led); led_trigger_unregister_simple(host->led);
spin_lock(&mmc_host_lock);
idr_remove(&mmc_host_idr, host->index);
spin_unlock(&mmc_host_lock);
} }
EXPORT_SYMBOL(mmc_remove_host); EXPORT_SYMBOL(mmc_remove_host);
...@@ -160,6 +161,10 @@ EXPORT_SYMBOL(mmc_remove_host); ...@@ -160,6 +161,10 @@ EXPORT_SYMBOL(mmc_remove_host);
*/ */
void mmc_free_host(struct mmc_host *host) void mmc_free_host(struct mmc_host *host)
{ {
spin_lock(&mmc_host_lock);
idr_remove(&mmc_host_idr, host->index);
spin_unlock(&mmc_host_lock);
put_device(&host->class_dev); put_device(&host->class_dev);
} }
......
...@@ -128,12 +128,12 @@ static int sdio_irq_thread(void *_host) ...@@ -128,12 +128,12 @@ static int sdio_irq_thread(void *_host)
} }
} }
set_task_state(current, TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if (host->caps & MMC_CAP_SDIO_IRQ) if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 1); host->ops->enable_sdio_irq(host, 1);
if (!kthread_should_stop()) if (!kthread_should_stop())
schedule_timeout(period); schedule_timeout(period);
set_task_state(current, TASK_RUNNING); set_current_state(TASK_RUNNING);
} while (!kthread_should_stop()); } while (!kthread_should_stop());
if (host->caps & MMC_CAP_SDIO_IRQ) if (host->caps & MMC_CAP_SDIO_IRQ)
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
#include "core.h" #include "core.h"
#include "sdio_ops.h"
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{ {
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/arch/board.h> #include <asm/arch/board.h>
#include <asm/arch/mmc.h>
#include <asm/arch/gpio.h> #include <asm/arch/gpio.h>
#include <asm/arch/dma.h> #include <asm/arch/dma.h>
#include <asm/arch/mux.h> #include <asm/arch/mux.h>
...@@ -93,9 +94,27 @@ ...@@ -93,9 +94,27 @@
/* Specifies how often in millisecs to poll for card status changes /* Specifies how often in millisecs to poll for card status changes
* when the cover switch is open */ * when the cover switch is open */
#define OMAP_MMC_SWITCH_POLL_DELAY 500 #define OMAP_MMC_COVER_POLL_DELAY 500
static int mmc_omap_enable_poll = 1; 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 tasklet_struct cover_tasklet;
struct timer_list cover_timer;
unsigned cover_open;
struct mmc_request *mrq;
struct mmc_omap_host *host;
struct mmc_host *mmc;
struct omap_mmc_slot_data *pdata;
};
struct mmc_omap_host { struct mmc_omap_host {
int initialized; int initialized;
...@@ -115,6 +134,15 @@ struct mmc_omap_host { ...@@ -115,6 +134,15 @@ struct mmc_omap_host {
unsigned char bus_mode; unsigned char bus_mode;
unsigned char hw_bus_mode; unsigned char hw_bus_mode;
struct work_struct cmd_abort_work;
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; unsigned int sg_len;
int sg_idx; int sg_idx;
u16 * buffer; u16 * buffer;
...@@ -131,63 +159,178 @@ struct mmc_omap_host { ...@@ -131,63 +159,178 @@ struct mmc_omap_host {
unsigned dma_len; unsigned dma_len;
short power_pin; short power_pin;
short wp_pin;
int switch_pin; struct mmc_omap_slot *slots[OMAP_MMC_MAX_SLOTS];
struct work_struct switch_work; struct mmc_omap_slot *current_slot;
struct timer_list switch_timer; spinlock_t slot_lock;
int switch_last_state; 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;
}; };
static inline int void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)
mmc_omap_cover_is_open(struct mmc_omap_host *host)
{ {
if (host->switch_pin < 0) unsigned long tick_ns;
return 0;
return omap_get_gpio_datain(host->switch_pin); 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);
}
} }
static ssize_t void mmc_omap_fclk_enable(struct mmc_omap_host *host, unsigned int enable)
mmc_omap_show_cover_switch(struct device *dev,
struct device_attribute *attr, char *buf)
{ {
struct mmc_omap_host *host = dev_get_drvdata(dev); unsigned long flags;
return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" : spin_lock_irqsave(&host->clk_lock, flags);
"closed"); 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 DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed)
{
struct mmc_omap_host *host = slot->host;
unsigned long flags;
static ssize_t if (claimed)
mmc_omap_show_enable_poll(struct device *dev, goto no_claim;
struct device_attribute *attr, char *buf) 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:
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;
}
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);
} 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_slot_release_work(struct work_struct *work)
{ {
return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll); 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 ssize_t static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
mmc_omap_store_enable_poll(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t size)
{ {
int enable_poll; struct mmc_omap_host *host = slot->host;
unsigned long flags;
int i;
BUG_ON(slot == NULL || host->mmc == NULL);
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);
}
if (sscanf(buf, "%10d", &enable_poll) != 1) spin_lock_irqsave(&host->slot_lock, flags);
return -EINVAL; /* Check for any pending requests */
for (i = 0; i < host->nr_slots; i++) {
struct mmc_omap_slot *new_slot;
if (enable_poll != mmc_omap_enable_poll) { if (host->slots[i] == NULL || host->slots[i]->mrq == NULL)
struct mmc_omap_host *host = dev_get_drvdata(dev); continue;
mmc_omap_enable_poll = enable_poll; BUG_ON(host->next_slot != NULL);
if (enable_poll && host->switch_pin >= 0) new_slot = host->slots[i];
schedule_work(&host->switch_work); /* 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);
schedule_work(&host->slot_release_work);
return;
} }
return size;
host->mmc = NULL;
wake_up(&host->slot_wq);
spin_unlock_irqrestore(&host->slot_lock, flags);
}
static inline
int mmc_omap_cover_is_open(struct mmc_omap_slot *slot)
{
if (slot->pdata->get_cover_state)
return slot->pdata->get_cover_state(mmc_dev(slot->mmc),
slot->id);
return 0;
}
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(enable_poll, 0664, static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
mmc_omap_show_enable_poll, mmc_omap_store_enable_poll);
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 static void
mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
...@@ -233,7 +376,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) ...@@ -233,7 +376,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); 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; cmdreg |= 1 << 6;
if (cmd->flags & MMC_RSP_BUSY) if (cmd->flags & MMC_RSP_BUSY)
...@@ -242,7 +385,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) ...@@ -242,7 +385,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
if (host->data && !(host->data->flags & MMC_DATA_WRITE)) if (host->data && !(host->data->flags & MMC_DATA_WRITE))
cmdreg |= 1 << 15; cmdreg |= 1 << 15;
clk_enable(host->fclk); mod_timer(&host->cmd_abort_timer, jiffies + HZ/2);
OMAP_MMC_WRITE(host, CTO, 200); OMAP_MMC_WRITE(host, CTO, 200);
OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
...@@ -256,27 +399,47 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) ...@@ -256,27 +399,47 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
OMAP_MMC_WRITE(host, CMD, cmdreg); OMAP_MMC_WRITE(host, CMD, cmdreg);
} }
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_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 static void
mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
{ {
if (host->dma_in_use) { if (host->dma_in_use)
enum dma_data_direction dma_data_dir; mmc_omap_release_dma(host, data, data->error);
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);
}
host->data = NULL; host->data = NULL;
host->sg_len = 0; host->sg_len = 0;
clk_disable(host->fclk);
/* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
* dozens of requests until the card finishes writing data. * dozens of requests until the card finishes writing data.
...@@ -284,12 +447,58 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) ...@@ -284,12 +447,58 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
*/ */
if (!data->stop) { if (!data->stop) {
struct mmc_host *mmc;
host->mrq = NULL; host->mrq = NULL;
mmc_request_done(host->mmc, data->mrq); mmc = host->mmc;
mmc_omap_release_slot(host->current_slot, 1);
mmc_request_done(mmc, data->mrq);
return; return;
} }
mmc_omap_start_command(host, data->stop); host->stop_data = data;
schedule_work(&host->send_stop_work);
}
static void
mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops)
{
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 < maxloops) {
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)
{
if (host->dma_in_use)
mmc_omap_release_dma(host, data, 1);
host->data = NULL;
host->sg_len = 0;
mmc_omap_send_abort(host, 10000);
} }
static void static void
...@@ -345,6 +554,8 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) ...@@ -345,6 +554,8 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
{ {
host->cmd = NULL; host->cmd = NULL;
del_timer(&host->cmd_abort_timer);
if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) { if (cmd->flags & MMC_RSP_136) {
/* response type 2 */ /* response type 2 */
...@@ -369,10 +580,66 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) ...@@ -369,10 +580,66 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
} }
if (host->data == NULL || cmd->error) { if (host->data == NULL || cmd->error) {
struct mmc_host *mmc;
if (host->data != NULL)
mmc_omap_abort_xfer(host, host->data);
host->mrq = NULL; host->mrq = NULL;
clk_disable(host->fclk); mmc = host->mmc;
mmc_request_done(host->mmc, cmd->mrq); mmc_omap_release_slot(host->current_slot, 1);
mmc_request_done(mmc, cmd->mrq);
}
}
/*
* 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_work);
BUG_ON(!host->cmd);
dev_dbg(mmc_dev(host->mmc), "Aborting stuck command CMD%d\n",
host->cmd->opcode);
if (host->cmd->error == 0)
host->cmd->error = -ETIMEDOUT;
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, 1);
mmc_request_done(mmc, cmd->mrq);
} else
mmc_omap_cmd_done(host, host->cmd);
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;
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 */ /* PIO only */
...@@ -388,6 +655,14 @@ mmc_omap_sg_to_buf(struct mmc_omap_host *host) ...@@ -388,6 +655,14 @@ mmc_omap_sg_to_buf(struct mmc_omap_host *host)
host->buffer_bytes_left = host->total_bytes_left; 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 */ /* PIO only */
static void static void
mmc_omap_xfer_data(struct mmc_omap_host *host, int write) mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
...@@ -436,11 +711,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -436,11 +711,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
u16 status; u16 status;
int end_command; int end_command;
int end_transfer; int end_transfer;
int transfer_error; int transfer_error, cmd_error;
if (host->cmd == NULL && host->data == NULL) { if (host->cmd == NULL && host->data == NULL) {
status = OMAP_MMC_READ(host, STAT); 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) { if (status != 0) {
OMAP_MMC_WRITE(host, STAT, status); OMAP_MMC_WRITE(host, STAT, status);
OMAP_MMC_WRITE(host, IE, 0); OMAP_MMC_WRITE(host, IE, 0);
...@@ -451,12 +727,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -451,12 +727,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
end_command = 0; end_command = 0;
end_transfer = 0; end_transfer = 0;
transfer_error = 0; transfer_error = 0;
cmd_error = 0;
while ((status = OMAP_MMC_READ(host, STAT)) != 0) { while ((status = OMAP_MMC_READ(host, STAT)) != 0) {
int cmd;
OMAP_MMC_WRITE(host, STAT, status); OMAP_MMC_WRITE(host, STAT, status);
if (host->cmd != NULL)
cmd = host->cmd->opcode;
else
cmd = -1;
#ifdef CONFIG_MMC_DEBUG #ifdef CONFIG_MMC_DEBUG
dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", 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); mmc_omap_report_irq(status);
printk("\n"); printk("\n");
#endif #endif
...@@ -468,12 +751,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -468,12 +751,12 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
mmc_omap_xfer_data(host, 1); 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; end_transfer = 1;
}
if (status & OMAP_MMC_STAT_DATA_TOUT) { 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) { if (host->data) {
host->data->error = -ETIMEDOUT; host->data->error = -ETIMEDOUT;
transfer_error = 1; transfer_error = 1;
...@@ -495,17 +778,16 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -495,17 +778,16 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
if (status & OMAP_MMC_STAT_CMD_TOUT) { if (status & OMAP_MMC_STAT_CMD_TOUT) {
/* Timeouts are routine with some commands */ /* Timeouts are routine with some commands */
if (host->cmd) { if (host->cmd) {
if (host->cmd->opcode != MMC_ALL_SEND_CID && struct mmc_omap_slot *slot =
host->cmd->opcode != host->current_slot;
MMC_SEND_OP_COND && if (slot == NULL ||
host->cmd->opcode != !mmc_omap_cover_is_open(slot))
MMC_APP_CMD &&
!mmc_omap_cover_is_open(host))
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"command timeout, CMD %d\n", "command timeout (CMD%d)\n",
host->cmd->opcode); cmd);
host->cmd->error = -ETIMEDOUT; host->cmd->error = -ETIMEDOUT;
end_command = 1; end_command = 1;
cmd_error = 1;
} }
} }
...@@ -513,9 +795,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -513,9 +795,10 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
if (host->cmd) { if (host->cmd) {
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"command CRC error (CMD%d, arg 0x%08x)\n", "command CRC error (CMD%d, arg 0x%08x)\n",
host->cmd->opcode, host->cmd->arg); cmd, host->cmd->arg);
host->cmd->error = -EILSEQ; host->cmd->error = -EILSEQ;
end_command = 1; end_command = 1;
cmd_error = 1;
} else } else
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"command CRC error without cmd?\n"); "command CRC error without cmd?\n");
...@@ -524,13 +807,13 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -524,13 +807,13 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
if (status & OMAP_MMC_STAT_CARD_ERR) { if (status & OMAP_MMC_STAT_CARD_ERR) {
dev_dbg(mmc_dev(host->mmc), dev_dbg(mmc_dev(host->mmc),
"ignoring card status error (CMD%d)\n", "ignoring card status error (CMD%d)\n",
host->cmd->opcode); cmd);
end_command = 1; end_command = 1;
} }
/* /*
* NOTE: On 1610 the END_OF_CMD may come too early when * 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) && if ((status & OMAP_MMC_STAT_END_OF_CMD) &&
(!(status & OMAP_MMC_STAT_A_EMPTY))) { (!(status & OMAP_MMC_STAT_A_EMPTY))) {
...@@ -538,63 +821,72 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) ...@@ -538,63 +821,72 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
} }
} }
if (end_command) { 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); 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; return IRQ_HANDLED;
} }
static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id) void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
{ {
struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; int cover_open;
struct mmc_omap_host *host = dev_get_drvdata(dev);
struct mmc_omap_slot *slot = host->slots[num];
schedule_work(&host->switch_work); BUG_ON(num >= host->nr_slots);
return IRQ_HANDLED; /* Other subsystems can call in here before we're initialised. */
if (host->nr_slots == 0 || !host->slots[num])
return;
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_host *host = (struct mmc_omap_host *) arg; struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg;
tasklet_schedule(&slot->cover_tasklet);
schedule_work(&host->switch_work);
} }
static void mmc_omap_switch_handler(struct work_struct *work) static void mmc_omap_cover_handler(unsigned long param)
{ {
struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, switch_work); struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
struct mmc_card *card; int cover_open = mmc_omap_cover_is_open(slot);
static int complained = 0;
int cards = 0, cover_open;
if (host->switch_pin == -1) mmc_detect_change(slot->mmc, 0);
if (!cover_open)
return; return;
cover_open = mmc_omap_cover_is_open(host);
if (cover_open != host->switch_last_state) { /*
kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); * If no card is inserted, we postpone polling until
host->switch_last_state = cover_open; * the cover has been closed.
} */
mmc_detect_change(host->mmc, 0); if (slot->mmc->card == NULL || !mmc_card_present(slot->mmc->card))
list_for_each_entry(card, &host->mmc->cards, node) { return;
if (mmc_card_present(card))
cards++; mod_timer(&slot->cover_timer,
} jiffies + msecs_to_jiffies(OMAP_MMC_COVER_POLL_DELAY));
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 */ /* Prepare to transfer the next segment of a scatterlist */
...@@ -765,13 +1057,12 @@ static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_reques ...@@ -765,13 +1057,12 @@ static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_reques
static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req)
{ {
int timeout; unsigned int timeout, cycle_ns;
u16 reg; u16 reg;
/* Convert ns to clock cycles by assuming 20MHz frequency cycle_ns = 1000000000 / host->current_slot->fclk_freq;
* 1 cycle at 20MHz = 500 ns timeout = req->data->timeout_ns / cycle_ns;
*/ timeout += req->data->timeout_clks;
timeout = req->data->timeout_clks + req->data->timeout_ns / 500;
/* Check if we need to use timeout multiplier register */ /* Check if we need to use timeout multiplier register */
reg = OMAP_MMC_READ(host, SDIO); reg = OMAP_MMC_READ(host, SDIO);
...@@ -854,11 +1145,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) ...@@ -854,11 +1145,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); BUG_ON(host->mrq != NULL);
WARN_ON(host->mrq != NULL);
host->mrq = req; host->mrq = req;
...@@ -867,60 +1157,56 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) ...@@ -867,60 +1157,56 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
mmc_omap_start_command(host, req->cmd); mmc_omap_start_command(host, req->cmd);
if (host->dma_in_use) if (host->dma_in_use)
omap_start_dma(host->dma_ch); omap_start_dma(host->dma_ch);
BUG_ON(irqs_disabled());
} }
static void innovator_fpga_socket_power(int on) static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
{ {
#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) struct mmc_omap_slot *slot = mmc_priv(mmc);
if (on) { struct mmc_omap_host *host = slot->host;
fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), unsigned long flags;
OMAP1510_FPGA_POWER);
} else { spin_lock_irqsave(&host->slot_lock, flags);
fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3), if (host->mmc != NULL) {
OMAP1510_FPGA_POWER); BUG_ON(slot->mrq != NULL);
} slot->mrq = req;
#endif 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 mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on,
* Turn the socket power on/off. Innovator uses FPGA, most boards int vdd)
* probably use GPIO.
*/
static void mmc_omap_power(struct mmc_omap_host *host, int on)
{ {
if (on) { struct mmc_omap_host *host;
if (machine_is_omap_innovator())
innovator_fpga_socket_power(1); host = slot->host;
else if (machine_is_omap_h2())
tps65010_set_gpio_out_value(GPIO3, HIGH); if (slot->pdata->set_power != NULL)
else if (machine_is_omap_h3()) slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on,
/* GPIO 4 of TPS65010 sends SD_EN signal */ vdd);
tps65010_set_gpio_out_value(GPIO4, HIGH);
else if (cpu_is_omap24xx()) { if (cpu_is_omap24xx()) {
u16 reg = OMAP_MMC_READ(host, CON); u16 w;
OMAP_MMC_WRITE(host, CON, reg | (1 << 11));
} else if (power_on) {
if (host->power_pin >= 0) w = OMAP_MMC_READ(host, CON);
omap_set_gpio_dataout(host->power_pin, 1); OMAP_MMC_WRITE(host, CON, w | (1 << 11));
} else { } else {
if (machine_is_omap_innovator()) w = OMAP_MMC_READ(host, CON);
innovator_fpga_socket_power(0); OMAP_MMC_WRITE(host, CON, w & ~(1 << 11));
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);
} }
} }
static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) 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 func_clk_rate = clk_get_rate(host->fclk);
int dsor; int dsor;
...@@ -936,7 +1222,8 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -936,7 +1222,8 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios)
if (dsor > 250) if (dsor > 250)
dsor = 250; dsor = 250;
dsor++;
slot->fclk_freq = func_clk_rate / dsor;
if (ios->bus_width == MMC_BUS_WIDTH_4) if (ios->bus_width == MMC_BUS_WIDTH_4)
dsor |= 1 << 15; dsor |= 1 << 15;
...@@ -946,28 +1233,40 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -946,28 +1233,40 @@ 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) static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{ {
struct mmc_omap_host *host = mmc_priv(mmc); struct mmc_omap_slot *slot = mmc_priv(mmc);
int dsor; struct mmc_omap_host *host = slot->host;
int i; int i, dsor;
int clk_enabled;
mmc_omap_select_slot(slot, 0);
dsor = mmc_omap_calc_divisor(mmc, ios); dsor = mmc_omap_calc_divisor(mmc, ios);
host->bus_mode = ios->bus_mode;
host->hw_bus_mode = host->bus_mode;
if (ios->vdd != slot->vdd)
slot->vdd = ios->vdd;
clk_enabled = 0;
switch (ios->power_mode) { switch (ios->power_mode) {
case MMC_POWER_OFF: case MMC_POWER_OFF:
mmc_omap_power(host, 0); mmc_omap_set_power(slot, 0, ios->vdd);
break; break;
case MMC_POWER_UP: case MMC_POWER_UP:
/* Cannot touch dsor yet, just power up MMC */ /* Cannot touch dsor yet, just power up MMC */
mmc_omap_power(host, 1); mmc_omap_set_power(slot, 1, ios->vdd);
return; goto exit;
case MMC_POWER_ON: case MMC_POWER_ON:
mmc_omap_fclk_enable(host, 1);
clk_enabled = 1;
dsor |= 1 << 11; dsor |= 1 << 11;
break; 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 /* On insanely high arm_per frequencies something sometimes
* goes somehow out of sync, and the POW bit is not being set, * goes somehow out of sync, and the POW bit is not being set,
...@@ -975,43 +1274,143 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -975,43 +1274,143 @@ 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. */ * Writing to the CON register twice seems to do the trick. */
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
OMAP_MMC_WRITE(host, CON, dsor); OMAP_MMC_WRITE(host, CON, dsor);
slot->saved_con = dsor;
if (ios->power_mode == MMC_POWER_ON) { if (ios->power_mode == MMC_POWER_ON) {
/* worst case at 400kHz, 80 cycles makes 200 microsecs */
int usecs = 250;
/* Send clock cycles, poll completion */ /* Send clock cycles, poll completion */
OMAP_MMC_WRITE(host, IE, 0); OMAP_MMC_WRITE(host, IE, 0);
OMAP_MMC_WRITE(host, STAT, 0xffff); OMAP_MMC_WRITE(host, STAT, 0xffff);
OMAP_MMC_WRITE(host, CMD, 1 << 7); 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); OMAP_MMC_WRITE(host, STAT, 1);
} }
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); exit:
mmc_omap_release_slot(slot, clk_enabled);
} }
static const struct mmc_host_ops mmc_omap_ops = { static const struct mmc_host_ops mmc_omap_ops = {
.request = mmc_omap_request, .request = mmc_omap_request,
.set_ios = mmc_omap_set_ios, .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; 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;
}
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;
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;
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);
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);
if (slot->pdata->get_cover_state != NULL)
device_remove_file(&mmc->class_dev, &dev_attr_cover_switch);
tasklet_kill(&slot->cover_tasklet);
del_timer_sync(&slot->cover_timer);
flush_scheduled_work();
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 mmc_omap_host *host = NULL;
struct resource *res; struct resource *res;
int ret = 0; int i, ret = 0;
int irq; int irq;
if (minfo == NULL) { if (pdata == NULL) {
dev_err(&pdev->dev, "platform data missing\n"); dev_err(&pdev->dev, "platform data missing\n");
return -ENXIO; return -ENXIO;
} }
if (pdata->nr_slots == 0) {
dev_err(&pdev->dev, "no slots\n");
return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
...@@ -1019,28 +1418,46 @@ static int __init mmc_omap_probe(struct platform_device *pdev) ...@@ -1019,28 +1418,46 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
res = request_mem_region(res->start, res->end - res->start + 1, res = request_mem_region(res->start, res->end - res->start + 1,
pdev->name); pdev->name);
if (res == NULL) if (res == NULL)
return -EBUSY; return -EBUSY;
mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL);
if (mmc == NULL) { if (host == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_free_mem_region; goto err_free_mem_region;
} }
host = mmc_priv(mmc); INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work);
host->mmc = mmc; 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);
spin_lock_init(&host->clk_lock);
setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
spin_lock_init(&host->dma_lock); spin_lock_init(&host->dma_lock);
init_timer(&host->dma_timer); setup_timer(&host->dma_timer, mmc_omap_dma_timer, (unsigned long) host);
host->dma_timer.function = mmc_omap_dma_timer; spin_lock_init(&host->slot_lock);
host->dma_timer.data = (unsigned long) host; init_waitqueue_head(&host->slot_wq);
host->pdata = pdata;
host->dev = &pdev->dev;
platform_set_drvdata(pdev, host);
host->id = pdev->id; host->id = pdev->id;
host->mem_res = res; host->mem_res = res;
host->irq = irq; 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()) { if (cpu_is_omap24xx()) {
host->iclk = clk_get(&pdev->dev, "mmc_ick"); host->iclk = clk_get(&pdev->dev, "mmc_ick");
if (IS_ERR(host->iclk)) if (IS_ERR(host->iclk))
...@@ -1058,109 +1475,34 @@ static int __init mmc_omap_probe(struct platform_device *pdev) ...@@ -1058,109 +1475,34 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
goto err_free_iclk; 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->switch_pin = minfo->switch_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 | MMC_CAP_BYTEBLOCK;
if (minfo->wire4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
/* 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;
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;
}
omap_set_gpio_direction(host->power_pin, 0);
}
ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
if (ret) if (ret)
goto err_free_power_gpio; goto err_free_fclk;
host->dev = &pdev->dev; if (pdata->init != NULL) {
platform_set_drvdata(pdev, host); ret = pdata->init(&pdev->dev);
if (ret < 0)
goto err_free_irq;
}
if (host->switch_pin >= 0) { host->nr_slots = pdata->nr_slots;
INIT_WORK(&host->switch_work, mmc_omap_switch_handler); for (i = 0; i < pdata->nr_slots; i++) {
init_timer(&host->switch_timer); ret = mmc_omap_new_slot(host, i);
host->switch_timer.function = mmc_omap_switch_timer; if (ret < 0) {
host->switch_timer.data = (unsigned long) host; while (--i >= 0)
if (omap_request_gpio(host->switch_pin) != 0) { mmc_omap_remove_slot(host->slots[i]);
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); goto err_plat_cleanup;
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; return 0;
no_switch: err_plat_cleanup:
/* FIXME: Free other resources too. */ if (pdata->cleanup)
if (host) { pdata->cleanup(&pdev->dev);
if (host->iclk && !IS_ERR(host->iclk)) err_free_irq:
clk_put(host->iclk); free_irq(host->irq, host);
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_free_fclk: err_free_fclk:
clk_put(host->fclk); clk_put(host->fclk);
err_free_iclk: err_free_iclk:
...@@ -1169,7 +1511,7 @@ err_free_iclk: ...@@ -1169,7 +1511,7 @@ err_free_iclk:
clk_put(host->iclk); clk_put(host->iclk);
} }
err_free_mmc_host: err_free_mmc_host:
mmc_free_host(host->mmc); kfree(host);
err_free_mem_region: err_free_mem_region:
release_mem_region(res->start, res->end - res->start + 1); release_mem_region(res->start, res->end - res->start + 1);
return ret; return ret;
...@@ -1178,25 +1520,18 @@ err_free_mem_region: ...@@ -1178,25 +1520,18 @@ err_free_mem_region:
static int mmc_omap_remove(struct platform_device *pdev) static int mmc_omap_remove(struct platform_device *pdev)
{ {
struct mmc_omap_host *host = platform_get_drvdata(pdev); struct mmc_omap_host *host = platform_get_drvdata(pdev);
int i;
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
BUG_ON(host == NULL); BUG_ON(host == NULL);
mmc_remove_host(host->mmc); for (i = 0; i < host->nr_slots; i++)
free_irq(host->irq, host); 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->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)) if (host->iclk && !IS_ERR(host->iclk))
clk_put(host->iclk); clk_put(host->iclk);
if (host->fclk && !IS_ERR(host->fclk)) if (host->fclk && !IS_ERR(host->fclk))
...@@ -1205,7 +1540,7 @@ static int mmc_omap_remove(struct platform_device *pdev) ...@@ -1205,7 +1540,7 @@ static int mmc_omap_remove(struct platform_device *pdev)
release_mem_region(pdev->resource[0].start, release_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1); pdev->resource[0].end - pdev->resource[0].start + 1);
mmc_free_host(host->mmc); kfree(host);
return 0; return 0;
} }
...@@ -1213,35 +1548,47 @@ static int mmc_omap_remove(struct platform_device *pdev) ...@@ -1213,35 +1548,47 @@ static int mmc_omap_remove(struct platform_device *pdev)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) 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); struct mmc_omap_host *host = platform_get_drvdata(pdev);
if (host && host->suspended) if (host == NULL || host->suspended)
return 0; return 0;
if (host) { for (i = 0; i < host->nr_slots; i++) {
ret = mmc_suspend_host(host->mmc, mesg); struct mmc_omap_slot *slot;
if (ret == 0)
host->suspended = 1; 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) 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); struct mmc_omap_host *host = platform_get_drvdata(pdev);
if (host && !host->suspended) if (host == NULL || !host->suspended)
return 0; return 0;
if (host) { for (i = 0; i < host->nr_slots; i++) {
ret = mmc_resume_host(host->mmc); struct mmc_omap_slot *slot;
if (ret == 0) slot = host->slots[i];
host->suspended = 0; ret = mmc_resume_host(slot->mmc);
} if (ret < 0)
return ret;
return ret; host->suspended = 0;
}
return 0;
} }
#else #else
#define mmc_omap_suspend NULL #define mmc_omap_suspend NULL
......
/* /*
* linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver * linux/drivers/mmc/host/sdhci.c - Secure Digital Host Controller Interface driver
* *
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/leds.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include "sdhci.h" #include "sdhci.h"
...@@ -30,10 +32,6 @@ ...@@ -30,10 +32,6 @@
static unsigned int debug_quirks = 0; static unsigned int debug_quirks = 0;
/* For multi controllers in one platform case */
static u16 chip_index = 0;
static spinlock_t index_lock;
/* /*
* Different quirks to handle when the hardware deviates from a strict * Different quirks to handle when the hardware deviates from a strict
* interpretation of the SDHCI specification. * interpretation of the SDHCI specification.
...@@ -43,7 +41,7 @@ static spinlock_t index_lock; ...@@ -43,7 +41,7 @@ static spinlock_t index_lock;
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) #define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
/* Controller has bad caps bits, but really supports DMA */ /* Controller has bad caps bits, but really supports DMA */
#define SDHCI_QUIRK_FORCE_DMA (1<<1) #define SDHCI_QUIRK_FORCE_DMA (1<<1)
/* Controller doesn't like some resets when there is no card inserted. */ /* Controller doesn't like to be reset when there is no card inserted. */
#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) #define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
/* Controller doesn't like clearing the power reg before a change */ /* Controller doesn't like clearing the power reg before a change */
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) #define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
...@@ -71,12 +69,20 @@ static const struct pci_device_id pci_ids[] __devinitdata = { ...@@ -71,12 +69,20 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
{ {
.vendor = PCI_VENDOR_ID_RICOH, .vendor = PCI_VENDOR_ID_RICOH,
.device = PCI_DEVICE_ID_RICOH_R5C822, .device = PCI_DEVICE_ID_RICOH_R5C822,
.subvendor = PCI_ANY_ID, .subvendor = PCI_VENDOR_ID_SAMSUNG,
.subdevice = PCI_ANY_ID, .subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_FORCE_DMA | .driver_data = SDHCI_QUIRK_FORCE_DMA |
SDHCI_QUIRK_NO_CARD_NO_RESET, SDHCI_QUIRK_NO_CARD_NO_RESET,
}, },
{
.vendor = PCI_VENDOR_ID_RICOH,
.device = PCI_DEVICE_ID_RICOH_R5C822,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_FORCE_DMA,
},
{ {
.vendor = PCI_VENDOR_ID_TI, .vendor = PCI_VENDOR_ID_TI,
.device = PCI_DEVICE_ID_TI_XX21_XX11_SD, .device = PCI_DEVICE_ID_TI_XX21_XX11_SD,
...@@ -256,6 +262,24 @@ static void sdhci_deactivate_led(struct sdhci_host *host) ...@@ -256,6 +262,24 @@ static void sdhci_deactivate_led(struct sdhci_host *host)
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
} }
#ifdef CONFIG_LEDS_CLASS
static void sdhci_led_control(struct led_classdev *led,
enum led_brightness brightness)
{
struct sdhci_host *host = container_of(led, struct sdhci_host, led);
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (brightness == LED_OFF)
sdhci_deactivate_led(host);
else
sdhci_activate_led(host);
spin_unlock_irqrestore(&host->lock, flags);
}
#endif
/*****************************************************************************\ /*****************************************************************************\
* * * *
* Core functions * * Core functions *
...@@ -773,7 +797,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) ...@@ -773,7 +797,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(host->mrq != NULL); WARN_ON(host->mrq != NULL);
#ifndef CONFIG_LEDS_CLASS
sdhci_activate_led(host); sdhci_activate_led(host);
#endif
host->mrq = mrq; host->mrq = mrq;
...@@ -965,7 +991,9 @@ static void sdhci_tasklet_finish(unsigned long param) ...@@ -965,7 +991,9 @@ static void sdhci_tasklet_finish(unsigned long param)
host->cmd = NULL; host->cmd = NULL;
host->data = NULL; host->data = NULL;
#ifndef CONFIG_LEDS_CLASS
sdhci_deactivate_led(host); sdhci_deactivate_led(host);
#endif
mmiowb(); mmiowb();
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
...@@ -1105,7 +1133,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) ...@@ -1105,7 +1133,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
goto out; goto out;
} }
DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask); DBG("*** %s got interrupt: 0x%08x\n",
mmc_hostname(host->mmc), intmask);
if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
...@@ -1235,7 +1264,7 @@ static int sdhci_resume (struct pci_dev *pdev) ...@@ -1235,7 +1264,7 @@ static int sdhci_resume (struct pci_dev *pdev)
if (chip->hosts[i]->flags & SDHCI_USE_DMA) if (chip->hosts[i]->flags & SDHCI_USE_DMA)
pci_set_master(pdev); pci_set_master(pdev);
ret = request_irq(chip->hosts[i]->irq, sdhci_irq, ret = request_irq(chip->hosts[i]->irq, sdhci_irq,
IRQF_SHARED, chip->hosts[i]->slot_descr, IRQF_SHARED, mmc_hostname(chip->hosts[i]->mmc),
chip->hosts[i]); chip->hosts[i]);
if (ret) if (ret)
return ret; return ret;
...@@ -1324,9 +1353,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1324,9 +1353,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq); DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq);
snprintf(host->slot_descr, 20, "sdhc%d:slot%d", chip->index, slot); ret = pci_request_region(pdev, host->bar, mmc_hostname(mmc));
ret = pci_request_region(pdev, host->bar, host->slot_descr);
if (ret) if (ret)
goto free; goto free;
...@@ -1343,7 +1370,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1343,7 +1370,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
if (version > 1) { if (version > 1) {
printk(KERN_ERR "%s: Unknown controller version (%d). " printk(KERN_ERR "%s: Unknown controller version (%d). "
"You may experience problems.\n", host->slot_descr, "You may experience problems.\n", mmc_hostname(mmc),
version); version);
} }
...@@ -1366,13 +1393,13 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1366,13 +1393,13 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
(host->flags & SDHCI_USE_DMA)) { (host->flags & SDHCI_USE_DMA)) {
printk(KERN_WARNING "%s: Will use DMA " printk(KERN_WARNING "%s: Will use DMA "
"mode even though HW doesn't fully " "mode even though HW doesn't fully "
"claim to support it.\n", host->slot_descr); "claim to support it.\n", mmc_hostname(mmc));
} }
if (host->flags & SDHCI_USE_DMA) { if (host->flags & SDHCI_USE_DMA) {
if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
printk(KERN_WARNING "%s: No suitable DMA available. " printk(KERN_WARNING "%s: No suitable DMA available. "
"Falling back to PIO.\n", host->slot_descr); "Falling back to PIO.\n", mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_DMA; host->flags &= ~SDHCI_USE_DMA;
} }
} }
...@@ -1386,7 +1413,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1386,7 +1413,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
if (host->max_clk == 0) { if (host->max_clk == 0) {
printk(KERN_ERR "%s: Hardware doesn't specify base clock " printk(KERN_ERR "%s: Hardware doesn't specify base clock "
"frequency.\n", host->slot_descr); "frequency.\n", mmc_hostname(mmc));
ret = -ENODEV; ret = -ENODEV;
goto unmap; goto unmap;
} }
...@@ -1396,7 +1423,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1396,7 +1423,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
(caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
if (host->timeout_clk == 0) { if (host->timeout_clk == 0) {
printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
"frequency.\n", host->slot_descr); "frequency.\n", mmc_hostname(mmc));
ret = -ENODEV; ret = -ENODEV;
goto unmap; goto unmap;
} }
...@@ -1424,7 +1451,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1424,7 +1451,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
if (mmc->ocr_avail == 0) { if (mmc->ocr_avail == 0) {
printk(KERN_ERR "%s: Hardware doesn't report any " printk(KERN_ERR "%s: Hardware doesn't report any "
"support voltages.\n", host->slot_descr); "support voltages.\n", mmc_hostname(mmc));
ret = -ENODEV; ret = -ENODEV;
goto unmap; goto unmap;
} }
...@@ -1458,8 +1485,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1458,8 +1485,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
*/ */
mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
if (mmc->max_blk_size >= 3) { if (mmc->max_blk_size >= 3) {
printk(KERN_WARNING "%s: Invalid maximum block size, assuming 512\n", printk(KERN_WARNING "%s: Invalid maximum block size, "
host->slot_descr); "assuming 512 bytes\n", mmc_hostname(mmc));
mmc->max_blk_size = 512; mmc->max_blk_size = 512;
} else } else
mmc->max_blk_size = 512 << mmc->max_blk_size; mmc->max_blk_size = 512 << mmc->max_blk_size;
...@@ -1480,7 +1507,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1480,7 +1507,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
host->slot_descr, host); mmc_hostname(mmc), host);
if (ret) if (ret)
goto untasklet; goto untasklet;
...@@ -1490,16 +1517,32 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) ...@@ -1490,16 +1517,32 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
sdhci_dumpregs(host); sdhci_dumpregs(host);
#endif #endif
#ifdef CONFIG_LEDS_CLASS
host->led.name = mmc_hostname(mmc);
host->led.brightness = LED_OFF;
host->led.default_trigger = mmc_hostname(mmc);
host->led.brightness_set = sdhci_led_control;
ret = led_classdev_register(&pdev->dev, &host->led);
if (ret)
goto reset;
#endif
mmiowb(); mmiowb();
mmc_add_host(mmc); mmc_add_host(mmc);
printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", mmc_hostname(mmc), printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n",
host->addr, host->irq, mmc_hostname(mmc), host->addr, host->irq,
(host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); (host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
return 0; return 0;
#ifdef CONFIG_LEDS_CLASS
reset:
sdhci_reset(host, SDHCI_RESET_ALL);
free_irq(host->irq, host);
#endif
untasklet: untasklet:
tasklet_kill(&host->card_tasklet); tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet); tasklet_kill(&host->finish_tasklet);
...@@ -1527,6 +1570,10 @@ static void sdhci_remove_slot(struct pci_dev *pdev, int slot) ...@@ -1527,6 +1570,10 @@ static void sdhci_remove_slot(struct pci_dev *pdev, int slot)
mmc_remove_host(mmc); mmc_remove_host(mmc);
#ifdef CONFIG_LEDS_CLASS
led_classdev_unregister(&host->led);
#endif
sdhci_reset(host, SDHCI_RESET_ALL); sdhci_reset(host, SDHCI_RESET_ALL);
free_irq(host->irq, host); free_irq(host->irq, host);
...@@ -1589,11 +1636,6 @@ static int __devinit sdhci_probe(struct pci_dev *pdev, ...@@ -1589,11 +1636,6 @@ static int __devinit sdhci_probe(struct pci_dev *pdev,
chip->num_slots = slots; chip->num_slots = slots;
pci_set_drvdata(pdev, chip); pci_set_drvdata(pdev, chip);
/* Add for multi controller case */
spin_lock(&index_lock);
chip->index = chip_index++;
spin_unlock(&index_lock);
for (i = 0;i < slots;i++) { for (i = 0;i < slots;i++) {
ret = sdhci_probe_slot(pdev, i); ret = sdhci_probe_slot(pdev, i);
if (ret) { if (ret) {
...@@ -1654,8 +1696,6 @@ static int __init sdhci_drv_init(void) ...@@ -1654,8 +1696,6 @@ static int __init sdhci_drv_init(void)
": Secure Digital Host Controller Interface driver\n"); ": Secure Digital Host Controller Interface driver\n");
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
spin_lock_init(&index_lock);
return pci_register_driver(&sdhci_driver); return pci_register_driver(&sdhci_driver);
} }
......
/* /*
* linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver * linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
* *
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -168,6 +168,10 @@ struct sdhci_host { ...@@ -168,6 +168,10 @@ struct sdhci_host {
struct sdhci_chip *chip; struct sdhci_chip *chip;
struct mmc_host *mmc; /* MMC structure */ struct mmc_host *mmc; /* MMC structure */
#ifdef CONFIG_LEDS_CLASS
struct led_classdev led; /* LED control */
#endif
spinlock_t lock; /* Mutex */ spinlock_t lock; /* Mutex */
int flags; /* Host attributes */ int flags; /* Host attributes */
...@@ -190,8 +194,6 @@ struct sdhci_host { ...@@ -190,8 +194,6 @@ struct sdhci_host {
int offset; /* Offset into current sg */ int offset; /* Offset into current sg */
int remain; /* Bytes left in current */ int remain; /* Bytes left in current */
char slot_descr[20]; /* Name for reservations */
int irq; /* Device IRQ */ int irq; /* Device IRQ */
int bar; /* PCI BAR index */ int bar; /* PCI BAR index */
unsigned long addr; /* Bus address */ unsigned long addr; /* Bus address */
...@@ -208,7 +210,6 @@ struct sdhci_chip { ...@@ -208,7 +210,6 @@ struct sdhci_chip {
unsigned long quirks; unsigned long quirks;
int index; /* Index for chip0, chip1 ...*/
int num_slots; /* Slots on controller */ int num_slots; /* Slots on controller */
struct sdhci_host *hosts[0]; /* Pointers to hosts */ struct sdhci_host *hosts[0]; /* Pointers to hosts */
}; };
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#define OMAP_MMC_MAX_SLOTS 2 #define OMAP_MMC_MAX_SLOTS 2
struct omap_mmc_platform_data { struct omap_mmc_platform_data {
struct omap_mmc_conf conf;
unsigned enabled:1; unsigned enabled:1;
/* number of slots on board */ /* number of slots on board */
unsigned nr_slots:2; unsigned nr_slots:2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment