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 @@
#include "sd_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;
/*
......@@ -516,7 +512,7 @@ static void mmc_power_off(struct mmc_host *host)
/*
* 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->bus_refs);
......
......@@ -46,6 +46,10 @@ void mmc_rescan(struct work_struct *work);
void mmc_start_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;
#endif
......
......@@ -2,7 +2,7 @@
* linux/drivers/mmc/core/host.c
*
* 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
* it under the terms of the GNU General Public License version 2 as
......@@ -57,12 +57,25 @@ static DEFINE_SPINLOCK(mmc_host_lock);
*/
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
int err;
struct mmc_host *host;
if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
return NULL;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
if (!host)
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->class_dev.parent = dev;
host->class_dev.class = &mmc_host_class;
......@@ -85,6 +98,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->max_blk_count = PAGE_CACHE_SIZE / 512;
return host;
free:
kfree(host);
return NULL;
}
EXPORT_SYMBOL(mmc_alloc_host);
......@@ -104,18 +121,6 @@ int mmc_add_host(struct mmc_host *host)
WARN_ON((host->caps & MMC_CAP_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);
err = device_add(&host->class_dev);
......@@ -144,10 +149,6 @@ void mmc_remove_host(struct mmc_host *host)
device_del(&host->class_dev);
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);
......@@ -160,6 +161,10 @@ EXPORT_SYMBOL(mmc_remove_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);
}
......
......@@ -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)
host->ops->enable_sdio_irq(host, 1);
if (!kthread_should_stop())
schedule_timeout(period);
set_task_state(current, TASK_RUNNING);
set_current_state(TASK_RUNNING);
} while (!kthread_should_stop());
if (host->caps & MMC_CAP_SDIO_IRQ)
......
......@@ -17,6 +17,7 @@
#include <linux/mmc/sdio.h>
#include "core.h"
#include "sdio_ops.h"
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
......
......@@ -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>
......@@ -93,9 +94,27 @@
/* Specifies how often in millisecs to poll for card status changes
* when the cover switch is open */
#define OMAP_MMC_SWITCH_POLL_DELAY 500
static int mmc_omap_enable_poll = 1;
#define OMAP_MMC_COVER_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 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 {
int initialized;
......@@ -115,6 +134,15 @@ struct mmc_omap_host {
unsigned char 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;
int sg_idx;
u16 * buffer;
......@@ -131,63 +159,178 @@ struct mmc_omap_host {
unsigned dma_len;
short power_pin;
short wp_pin;
int switch_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 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
mmc_omap_cover_is_open(struct mmc_omap_host *host)
void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)
{
if (host->switch_pin < 0)
return 0;
return omap_get_gpio_datain(host->switch_pin);
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);
}
}
static ssize_t
mmc_omap_show_cover_switch(struct device *dev,
struct device_attribute *attr, char *buf)
void mmc_omap_fclk_enable(struct mmc_omap_host *host, unsigned int enable)
{
struct mmc_omap_host *host = dev_get_drvdata(dev);
unsigned long flags;
return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" :
"closed");
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 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
mmc_omap_show_enable_poll(struct device *dev,
struct device_attribute *attr, char *buf)
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:
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
mmc_omap_store_enable_poll(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t size)
static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled)
{
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)
return -EINVAL;
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;
if (enable_poll != mmc_omap_enable_poll) {
struct mmc_omap_host *host = dev_get_drvdata(dev);
if (host->slots[i] == NULL || host->slots[i]->mrq == NULL)
continue;
mmc_omap_enable_poll = enable_poll;
if (enable_poll && host->switch_pin >= 0)
schedule_work(&host->switch_work);
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);
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,
mmc_omap_show_enable_poll, mmc_omap_store_enable_poll);
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)
{
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)
......@@ -233,7 +376,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)
......@@ -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))
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, ARGL, cmd->arg & 0xffff);
......@@ -256,27 +399,47 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
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
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.
......@@ -284,12 +447,58 @@ 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, 1);
mmc_request_done(mmc, data->mrq);
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
......@@ -345,6 +554,8 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
{
host->cmd = NULL;
del_timer(&host->cmd_abort_timer);
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
/* response type 2 */
......@@ -369,10 +580,66 @@ 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, 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 */
......@@ -388,6 +655,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)
......@@ -436,11 +711,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);
......@@ -451,12 +727,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
......@@ -468,12 +751,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;
......@@ -495,17 +778,16 @@ 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))
struct mmc_omap_slot *slot =
host->current_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;
}
}
......@@ -513,9 +795,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");
......@@ -524,13 +807,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))) {
......@@ -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);
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;
}
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;
schedule_work(&host->switch_work);
struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg;
tasklet_schedule(&slot->cover_tasklet);
}
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_card *card;
static int complained = 0;
int cards = 0, cover_open;
struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param;
int cover_open = mmc_omap_cover_is_open(slot);
if (host->switch_pin == -1)
mmc_detect_change(slot->mmc, 0);
if (!cover_open)
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;
}
/*
* 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 */
......@@ -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)
{
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);
......@@ -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);
WARN_ON(host->mrq != NULL);
BUG_ON(host->mrq != NULL);
host->mrq = 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);
if (host->dma_in_use)
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)
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_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);
}
/*
* 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)
static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on,
int vdd)
{
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);
struct mmc_omap_host *host;
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));
}
}
}
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;
......@@ -936,7 +1222,8 @@ static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios)
if (dsor > 250)
dsor = 250;
dsor++;
slot->fclk_freq = func_clk_rate / dsor;
if (ios->bus_width == MMC_BUS_WIDTH_4)
dsor |= 1 << 15;
......@@ -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)
{
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;
int clk_enabled;
mmc_omap_select_slot(slot, 0);
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) {
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:
mmc_omap_fclk_enable(host, 1);
clk_enabled = 1;
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,
......@@ -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. */
for (i = 0; i < 2; i++)
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);
}
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 = {
.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;
}
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 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);
......@@ -1019,28 +1418,46 @@ 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;
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);
spin_lock_init(&host->clk_lock);
setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
spin_lock_init(&host->dma_lock);
init_timer(&host->dma_timer);
host->dma_timer.function = mmc_omap_dma_timer;
host->dma_timer.data = (unsigned long) host;
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->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))
......@@ -1058,109 +1475,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->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);
if (ret)
goto err_free_power_gpio;
goto err_free_fclk;
host->dev = &pdev->dev;
platform_set_drvdata(pdev, host);
if (pdata->init != NULL) {
ret = pdata->init(&pdev->dev);
if (ret < 0)
goto err_free_irq;
}
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;
}
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]);
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);
goto err_plat_cleanup;
}
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))
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:
......@@ -1169,7 +1511,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;
......@@ -1178,25 +1520,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->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))
......@@ -1205,7 +1540,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;
}
......@@ -1213,35 +1548,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
......
/*
* 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
* it under the terms of the GNU General Public License as published by
......@@ -19,6 +19,8 @@
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/leds.h>
#include <linux/mmc/host.h>
#include "sdhci.h"
......@@ -30,10 +32,6 @@
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
* interpretation of the SDHCI specification.
......@@ -43,7 +41,7 @@ static spinlock_t index_lock;
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
/* Controller has bad caps bits, but really supports DMA */
#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)
/* Controller doesn't like clearing the power reg before a change */
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
......@@ -71,12 +69,20 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_RICOH,
.device = PCI_DEVICE_ID_RICOH_R5C822,
.subvendor = PCI_ANY_ID,
.subvendor = PCI_VENDOR_ID_SAMSUNG,
.subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_FORCE_DMA |
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,
.device = PCI_DEVICE_ID_TI_XX21_XX11_SD,
......@@ -256,6 +262,24 @@ static void sdhci_deactivate_led(struct sdhci_host *host)
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 *
......@@ -773,7 +797,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(host->mrq != NULL);
#ifndef CONFIG_LEDS_CLASS
sdhci_activate_led(host);
#endif
host->mrq = mrq;
......@@ -965,7 +991,9 @@ static void sdhci_tasklet_finish(unsigned long param)
host->cmd = NULL;
host->data = NULL;
#ifndef CONFIG_LEDS_CLASS
sdhci_deactivate_led(host);
#endif
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
......@@ -1105,7 +1133,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
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)) {
writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
......@@ -1235,7 +1264,7 @@ static int sdhci_resume (struct pci_dev *pdev)
if (chip->hosts[i]->flags & SDHCI_USE_DMA)
pci_set_master(pdev);
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]);
if (ret)
return ret;
......@@ -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);
snprintf(host->slot_descr, 20, "sdhc%d:slot%d", chip->index, slot);
ret = pci_request_region(pdev, host->bar, host->slot_descr);
ret = pci_request_region(pdev, host->bar, mmc_hostname(mmc));
if (ret)
goto free;
......@@ -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;
if (version > 1) {
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);
}
......@@ -1366,13 +1393,13 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
(host->flags & SDHCI_USE_DMA)) {
printk(KERN_WARNING "%s: Will use DMA "
"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 (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
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;
}
}
......@@ -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;
if (host->max_clk == 0) {
printk(KERN_ERR "%s: Hardware doesn't specify base clock "
"frequency.\n", host->slot_descr);
"frequency.\n", mmc_hostname(mmc));
ret = -ENODEV;
goto unmap;
}
......@@ -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;
if (host->timeout_clk == 0) {
printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
"frequency.\n", host->slot_descr);
"frequency.\n", mmc_hostname(mmc));
ret = -ENODEV;
goto unmap;
}
......@@ -1424,7 +1451,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
if (mmc->ocr_avail == 0) {
printk(KERN_ERR "%s: Hardware doesn't report any "
"support voltages.\n", host->slot_descr);
"support voltages.\n", mmc_hostname(mmc));
ret = -ENODEV;
goto unmap;
}
......@@ -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;
if (mmc->max_blk_size >= 3) {
printk(KERN_WARNING "%s: Invalid maximum block size, assuming 512\n",
host->slot_descr);
printk(KERN_WARNING "%s: Invalid maximum block size, "
"assuming 512 bytes\n", mmc_hostname(mmc));
mmc->max_blk_size = 512;
} else
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)
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
host->slot_descr, host);
mmc_hostname(mmc), host);
if (ret)
goto untasklet;
......@@ -1490,16 +1517,32 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
sdhci_dumpregs(host);
#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();
mmc_add_host(mmc);
printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", mmc_hostname(mmc),
host->addr, host->irq,
printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n",
mmc_hostname(mmc), host->addr, host->irq,
(host->flags & SDHCI_USE_DMA)?"DMA":"PIO");
return 0;
#ifdef CONFIG_LEDS_CLASS
reset:
sdhci_reset(host, SDHCI_RESET_ALL);
free_irq(host->irq, host);
#endif
untasklet:
tasklet_kill(&host->card_tasklet);
tasklet_kill(&host->finish_tasklet);
......@@ -1527,6 +1570,10 @@ static void sdhci_remove_slot(struct pci_dev *pdev, int slot)
mmc_remove_host(mmc);
#ifdef CONFIG_LEDS_CLASS
led_classdev_unregister(&host->led);
#endif
sdhci_reset(host, SDHCI_RESET_ALL);
free_irq(host->irq, host);
......@@ -1589,11 +1636,6 @@ static int __devinit sdhci_probe(struct pci_dev *pdev,
chip->num_slots = slots;
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++) {
ret = sdhci_probe_slot(pdev, i);
if (ret) {
......@@ -1654,8 +1696,6 @@ static int __init sdhci_drv_init(void)
": Secure Digital Host Controller Interface driver\n");
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");
spin_lock_init(&index_lock);
return pci_register_driver(&sdhci_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
* it under the terms of the GNU General Public License as published by
......@@ -168,6 +168,10 @@ struct sdhci_host {
struct sdhci_chip *chip;
struct mmc_host *mmc; /* MMC structure */
#ifdef CONFIG_LEDS_CLASS
struct led_classdev led; /* LED control */
#endif
spinlock_t lock; /* Mutex */
int flags; /* Host attributes */
......@@ -190,8 +194,6 @@ struct sdhci_host {
int offset; /* Offset into current sg */
int remain; /* Bytes left in current */
char slot_descr[20]; /* Name for reservations */
int irq; /* Device IRQ */
int bar; /* PCI BAR index */
unsigned long addr; /* Bus address */
......@@ -208,7 +210,6 @@ struct sdhci_chip {
unsigned long quirks;
int index; /* Index for chip0, chip1 ...*/
int num_slots; /* Slots on controller */
struct sdhci_host *hosts[0]; /* Pointers to hosts */
};
......@@ -18,6 +18,8 @@
#define OMAP_MMC_MAX_SLOTS 2
struct omap_mmc_platform_data {
struct omap_mmc_conf conf;
unsigned enabled:1;
/* number of slots on board */
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