Commit ed5dc237 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mmc-updates-for-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc

Pull MMC update from Chris Ball:
 "MMC highlights for 3.9:

  Core:
   - Support for packed commands in eMMC 4.5.  (This requires a host
     capability to be turned on.  It increases write throughput by 20%+,
     but may also increase average write latency; more testing needed.)
   - Add DT bindings for capability flags.
   - Add mmc_of_parse() for shared DT parsing between drivers.

  Drivers:
   - android-goldfish: New MMC driver for the Android Goldfish emulator.
   - mvsdio: Add DT bindings, pinctrl, use slot-gpio for card detection.
   - omap_hsmmc: Fix boot hangs with RPMB partitions.
   - sdhci-bcm2835: New driver for controller used by Raspberry Pi.
   - sdhci-esdhc-imx: Add 8-bit data, auto CMD23 support, use slot-gpio.
   - sh_mmcif: Add support for eMMC DDR, bundled MMCIF IRQs.
   - tmio_mmc: Add DT bindings, support for vccq regulator"

* tag 'mmc-updates-for-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (92 commits)
  mmc: tegra: assume CONFIG_OF, remove platform data
  mmc: add DT bindings for more MMC capability flags
  mmc: tmio: add support for the VccQ regulator
  mmc: tmio: remove unused and deprecated symbols
  mmc: sh_mobile_sdhi: use managed resource allocations
  mmc: sh_mobile_sdhi: remove unused .pdata field
  mmc: tmio-mmc: parse device-tree bindings
  mmc: tmio-mmc: define device-tree bindings
  mmc: sh_mmcif: use mmc_of_parse() to parse standard MMC DT bindings
  mmc: (cosmetic) remove "extern" from function declarations
  mmc: provide a standard MMC device-tree binding parser centrally
  mmc: detailed definition of CD and WP MMC line polarities in DT
  mmc: sdhi, tmio: only check flags in tmio-mmc driver proper
  mmc: sdhci: Fix parameter of sdhci_do_start_signal_voltage_switch()
  mmc: sdhci: check voltage range only on regulators aware of voltage value
  mmc: bcm2835: set SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK
  mmc: support packed write command for eMMC4.5 devices
  mmc: add packed command feature of eMMC4.5
  mmc: rtsx: remove driving adjustment
  mmc: use regulator_can_change_voltage() instead of regulator_count_voltages
  ...
parents 0512c04a 0e786102
Broadcom BCM2835 SDHCI controller
This file documents differences between the core properties described
by mmc.txt and the properties that represent the BCM2835 controller.
Required properties:
- compatible : Should be "brcm,bcm2835-sdhci".
- clocks : The clock feeding the SDHCI controller.
Example:
sdhci: sdhci {
compatible = "brcm,bcm2835-sdhci";
reg = <0x7e300000 0x100>;
interrupts = <2 30>;
clocks = <&clk_mmc>;
bus-width = <4>;
};
...@@ -6,23 +6,45 @@ Interpreted by the OF core: ...@@ -6,23 +6,45 @@ Interpreted by the OF core:
- reg: Registers location and length. - reg: Registers location and length.
- interrupts: Interrupts used by the MMC controller. - interrupts: Interrupts used by the MMC controller.
Required properties:
- bus-width: Number of data lines, can be <1>, <4>, or <8>
Card detection: Card detection:
If no property below is supplied, standard SDHCI card detect is used. If no property below is supplied, host native card detect is used.
Only one of the properties in this section should be supplied: Only one of the properties in this section should be supplied:
- broken-cd: There is no card detection available; polling must be used. - broken-cd: There is no card detection available; polling must be used.
- cd-gpios: Specify GPIOs for card detection, see gpio binding - cd-gpios: Specify GPIOs for card detection, see gpio binding
- non-removable: non-removable slot (like eMMC); assume always present. - non-removable: non-removable slot (like eMMC); assume always present.
Optional properties: Optional properties:
- bus-width: Number of data lines, can be <1>, <4>, or <8>. The default
will be <1> if the property is absent.
- wp-gpios: Specify GPIOs for write protection, see gpio binding - wp-gpios: Specify GPIOs for write protection, see gpio binding
- cd-inverted: when present, polarity on the cd gpio line is inverted - cd-inverted: when present, polarity on the CD line is inverted. See the note
- wp-inverted: when present, polarity on the wp gpio line is inverted below for the case, when a GPIO is used for the CD line
- wp-inverted: when present, polarity on the WP line is inverted. See the note
below for the case, when a GPIO is used for the WP line
- max-frequency: maximum operating clock frequency - max-frequency: maximum operating clock frequency
- no-1-8-v: when present, denotes that 1.8v card voltage is not supported on - no-1-8-v: when present, denotes that 1.8v card voltage is not supported on
this system, even if the controller claims it is. this system, even if the controller claims it is.
- cap-sd-highspeed: SD high-speed timing is supported
- cap-mmc-highspeed: MMC high-speed timing is supported
- cap-power-off-card: powering off the card is safe
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted"
line levels. We choose to follow the SDHCI standard, which specifies both those
lines as "active low." Therefore, using the "cd-inverted" property means, that
the CD line is active high, i.e. it is high, when a card is inserted. Similar
logic applies to the "wp-inverted" property.
CD and WP lines can be implemented on the hardware in one of two ways: as GPIOs,
specified in cd-gpios and wp-gpios properties, or as dedicated pins. Polarity of
dedicated pins can be specified, using *-inverted properties. GPIO polarity can
also be specified using the OF_GPIO_ACTIVE_LOW flag. This creates an ambiguity
in the latter case. We choose to use the XOR logic for GPIO CD and WP lines.
This means, the two properties are "superimposed," for example leaving the
OF_GPIO_ACTIVE_LOW flag clear and specifying the respective *-inverted
property results in a double-inversion and actually means the "normal" line
polarity is in effect.
Optional SDIO properties: Optional SDIO properties:
- keep-power-in-suspend: Preserves card power during a suspend/resume cycle - keep-power-in-suspend: Preserves card power during a suspend/resume cycle
......
* Marvell orion-sdio controller
This file documents differences between the core properties in mmc.txt
and the properties used by the orion-sdio driver.
- compatible: Should be "marvell,orion-sdio"
- clocks: reference to the clock of the SDIO interface
Example:
mvsdio@d00d4000 {
compatible = "marvell,orion-sdio";
reg = <0xd00d4000 0x200>;
interrupts = <54>;
clocks = <&gateclk 17>;
status = "disabled";
};
...@@ -26,8 +26,16 @@ Required Properties: ...@@ -26,8 +26,16 @@ Required Properties:
* bus-width: as documented in mmc core bindings. * bus-width: as documented in mmc core bindings.
* wp-gpios: specifies the write protect gpio line. The format of the * wp-gpios: specifies the write protect gpio line. The format of the
gpio specifier depends on the gpio controller. If the write-protect gpio specifier depends on the gpio controller. If a GPIO is not used
line is not available, this property is optional. for write-protect, this property is optional.
* disable-wp: If the wp-gpios property isn't present then (by default)
we'd assume that the write protect is hooked up directly to the
controller's special purpose write protect line (accessible via
the WRTPRT register). However, it's possible that we simply don't
want write protect. In that case specify 'disable-wp'.
NOTE: This property is not required for slots known to always
connect to eMMC or SDIO cards.
Optional properties: Optional properties:
......
* Toshiba Mobile IO SD/MMC controller
The tmio-mmc driver doesn't probe its devices actively, instead its binding to
devices is managed by either MFD drivers or by the sh_mobile_sdhi platform
driver. Those drivers supply the tmio-mmc driver with platform data, that either
describe hardware capabilities, known to them, or are obtained by them from
their own platform data or from their DT information. In the latter case all
compulsory and any optional properties, common to all SD/MMC drivers, as
described in mmc.txt, can be used. Additionally the following tmio_mmc-specific
optional bindings can be used.
Optional properties:
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
When used with Renesas SDHI hardware, the following compatibility strings
configure various model-specific properties:
"renesas,sh7372-sdhi": (default) compatible with SH7372
"renesas,r8a7740-sdhi": compatible with R8A7740: certain MMC/SD commands have to
wait for the interface to become idle.
...@@ -5692,7 +5692,7 @@ S: Maintained ...@@ -5692,7 +5692,7 @@ S: Maintained
F: drivers/mmc/host/omap.c F: drivers/mmc/host/omap.c
OMAP HS MMC SUPPORT OMAP HS MMC SUPPORT
M: Venkatraman S <svenkatr@ti.com> M: Balaji T K <balajitk@ti.com>
L: linux-mmc@vger.kernel.org L: linux-mmc@vger.kernel.org
L: linux-omap@vger.kernel.org L: linux-omap@vger.kernel.org
S: Maintained S: Maintained
...@@ -6804,6 +6804,14 @@ F: include/linux/dw_dmac.h ...@@ -6804,6 +6804,14 @@ F: include/linux/dw_dmac.h
F: drivers/dma/dw_dmac_regs.h F: drivers/dma/dw_dmac_regs.h
F: drivers/dma/dw_dmac.c F: drivers/dma/dw_dmac.c
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
M: Seungwon Jeon <tgih.jun@samsung.com>
M: Jaehoon Chung <jh80.chung@samsung.com>
L: linux-mmc@vger.kernel.org
S: Maintained
F: include/linux/mmc/dw_mmc.h
F: drivers/mmc/host/dw_mmc*
TIMEKEEPING, NTP TIMEKEEPING, NTP
M: John Stultz <john.stultz@linaro.org> M: John Stultz <john.stultz@linaro.org>
M: Thomas Gleixner <tglx@linutronix.de> M: Thomas Gleixner <tglx@linutronix.de>
......
...@@ -151,6 +151,7 @@ slot@0 { ...@@ -151,6 +151,7 @@ slot@0 {
reg = <0>; reg = <0>;
bus-width = <4>; bus-width = <4>;
samsung,cd-pinmux-gpio = <&gpc3 2 2 3 3>; samsung,cd-pinmux-gpio = <&gpc3 2 2 3 3>;
disable-wp;
gpios = <&gpc3 0 2 0 3>, <&gpc3 1 2 0 3>, gpios = <&gpc3 0 2 0 3>, <&gpc3 1 2 0 3>,
<&gpc3 3 2 3 3>, <&gpc3 4 2 3 3>, <&gpc3 3 2 3 3>, <&gpc3 4 2 3 3>,
<&gpc3 5 2 3 3>, <&gpc3 6 2 3 3>, <&gpc3 5 2 3 3>, <&gpc3 6 2 3 3>,
......
This diff is collapsed.
...@@ -22,7 +22,8 @@ ...@@ -22,7 +22,8 @@
#define MMC_QUEUE_BOUNCESZ 65536 #define MMC_QUEUE_BOUNCESZ 65536
#define MMC_QUEUE_SUSPENDED (1 << 0)
#define MMC_REQ_SPECIAL_MASK (REQ_DISCARD | REQ_FLUSH)
/* /*
* Prepare a MMC request. This just filters out odd stuff. * Prepare a MMC request. This just filters out odd stuff.
...@@ -58,6 +59,7 @@ static int mmc_queue_thread(void *d) ...@@ -58,6 +59,7 @@ static int mmc_queue_thread(void *d)
do { do {
struct request *req = NULL; struct request *req = NULL;
struct mmc_queue_req *tmp; struct mmc_queue_req *tmp;
unsigned int cmd_flags = 0;
spin_lock_irq(q->queue_lock); spin_lock_irq(q->queue_lock);
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
...@@ -67,12 +69,23 @@ static int mmc_queue_thread(void *d) ...@@ -67,12 +69,23 @@ static int mmc_queue_thread(void *d)
if (req || mq->mqrq_prev->req) { if (req || mq->mqrq_prev->req) {
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
cmd_flags = req ? req->cmd_flags : 0;
mq->issue_fn(mq, req); mq->issue_fn(mq, req);
if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
continue; /* fetch again */
}
/* /*
* Current request becomes previous request * Current request becomes previous request
* and vice versa. * and vice versa.
* In case of special requests, current request
* has been finished. Do not assign it to previous
* request.
*/ */
if (cmd_flags & MMC_REQ_SPECIAL_MASK)
mq->mqrq_cur->req = NULL;
mq->mqrq_prev->brq.mrq.data = NULL; mq->mqrq_prev->brq.mrq.data = NULL;
mq->mqrq_prev->req = NULL; mq->mqrq_prev->req = NULL;
tmp = mq->mqrq_prev; tmp = mq->mqrq_prev;
...@@ -103,6 +116,8 @@ static void mmc_request_fn(struct request_queue *q) ...@@ -103,6 +116,8 @@ static void mmc_request_fn(struct request_queue *q)
{ {
struct mmc_queue *mq = q->queuedata; struct mmc_queue *mq = q->queuedata;
struct request *req; struct request *req;
unsigned long flags;
struct mmc_context_info *cntx;
if (!mq) { if (!mq) {
while ((req = blk_fetch_request(q)) != NULL) { while ((req = blk_fetch_request(q)) != NULL) {
...@@ -112,7 +127,20 @@ static void mmc_request_fn(struct request_queue *q) ...@@ -112,7 +127,20 @@ static void mmc_request_fn(struct request_queue *q)
return; return;
} }
if (!mq->mqrq_cur->req && !mq->mqrq_prev->req) cntx = &mq->card->host->context_info;
if (!mq->mqrq_cur->req && mq->mqrq_prev->req) {
/*
* New MMC request arrived when MMC thread may be
* blocked on the previous request to be complete
* with no current request fetched
*/
spin_lock_irqsave(&cntx->lock, flags);
if (cntx->is_waiting_last_req) {
cntx->is_new_req = true;
wake_up_interruptible(&cntx->wait);
}
spin_unlock_irqrestore(&cntx->lock, flags);
} else if (!mq->mqrq_cur->req && !mq->mqrq_prev->req)
wake_up_process(mq->thread); wake_up_process(mq->thread);
} }
...@@ -334,6 +362,49 @@ void mmc_cleanup_queue(struct mmc_queue *mq) ...@@ -334,6 +362,49 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
} }
EXPORT_SYMBOL(mmc_cleanup_queue); EXPORT_SYMBOL(mmc_cleanup_queue);
int mmc_packed_init(struct mmc_queue *mq, struct mmc_card *card)
{
struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
int ret = 0;
mqrq_cur->packed = kzalloc(sizeof(struct mmc_packed), GFP_KERNEL);
if (!mqrq_cur->packed) {
pr_warn("%s: unable to allocate packed cmd for mqrq_cur\n",
mmc_card_name(card));
ret = -ENOMEM;
goto out;
}
mqrq_prev->packed = kzalloc(sizeof(struct mmc_packed), GFP_KERNEL);
if (!mqrq_prev->packed) {
pr_warn("%s: unable to allocate packed cmd for mqrq_prev\n",
mmc_card_name(card));
kfree(mqrq_cur->packed);
mqrq_cur->packed = NULL;
ret = -ENOMEM;
goto out;
}
INIT_LIST_HEAD(&mqrq_cur->packed->list);
INIT_LIST_HEAD(&mqrq_prev->packed->list);
out:
return ret;
}
void mmc_packed_clean(struct mmc_queue *mq)
{
struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
kfree(mqrq_cur->packed);
mqrq_cur->packed = NULL;
kfree(mqrq_prev->packed);
mqrq_prev->packed = NULL;
}
/** /**
* mmc_queue_suspend - suspend a MMC request queue * mmc_queue_suspend - suspend a MMC request queue
* @mq: MMC queue to suspend * @mq: MMC queue to suspend
...@@ -378,6 +449,41 @@ void mmc_queue_resume(struct mmc_queue *mq) ...@@ -378,6 +449,41 @@ void mmc_queue_resume(struct mmc_queue *mq)
} }
} }
static unsigned int mmc_queue_packed_map_sg(struct mmc_queue *mq,
struct mmc_packed *packed,
struct scatterlist *sg,
enum mmc_packed_type cmd_type)
{
struct scatterlist *__sg = sg;
unsigned int sg_len = 0;
struct request *req;
if (mmc_packed_wr(cmd_type)) {
unsigned int hdr_sz = mmc_large_sector(mq->card) ? 4096 : 512;
unsigned int max_seg_sz = queue_max_segment_size(mq->queue);
unsigned int len, remain, offset = 0;
u8 *buf = (u8 *)packed->cmd_hdr;
remain = hdr_sz;
do {
len = min(remain, max_seg_sz);
sg_set_buf(__sg, buf + offset, len);
offset += len;
remain -= len;
(__sg++)->page_link &= ~0x02;
sg_len++;
} while (remain);
}
list_for_each_entry(req, &packed->list, queuelist) {
sg_len += blk_rq_map_sg(mq->queue, req, __sg);
__sg = sg + (sg_len - 1);
(__sg++)->page_link &= ~0x02;
}
sg_mark_end(sg + (sg_len - 1));
return sg_len;
}
/* /*
* Prepare the sg list(s) to be handed of to the host driver * Prepare the sg list(s) to be handed of to the host driver
*/ */
...@@ -386,14 +492,26 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq) ...@@ -386,14 +492,26 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq, struct mmc_queue_req *mqrq)
unsigned int sg_len; unsigned int sg_len;
size_t buflen; size_t buflen;
struct scatterlist *sg; struct scatterlist *sg;
enum mmc_packed_type cmd_type;
int i; int i;
if (!mqrq->bounce_buf) cmd_type = mqrq->cmd_type;
return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
if (!mqrq->bounce_buf) {
if (mmc_packed_cmd(cmd_type))
return mmc_queue_packed_map_sg(mq, mqrq->packed,
mqrq->sg, cmd_type);
else
return blk_rq_map_sg(mq->queue, mqrq->req, mqrq->sg);
}
BUG_ON(!mqrq->bounce_sg); BUG_ON(!mqrq->bounce_sg);
sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg); if (mmc_packed_cmd(cmd_type))
sg_len = mmc_queue_packed_map_sg(mq, mqrq->packed,
mqrq->bounce_sg, cmd_type);
else
sg_len = blk_rq_map_sg(mq->queue, mqrq->req, mqrq->bounce_sg);
mqrq->bounce_sg_len = sg_len; mqrq->bounce_sg_len = sg_len;
......
...@@ -12,6 +12,23 @@ struct mmc_blk_request { ...@@ -12,6 +12,23 @@ struct mmc_blk_request {
struct mmc_data data; struct mmc_data data;
}; };
enum mmc_packed_type {
MMC_PACKED_NONE = 0,
MMC_PACKED_WRITE,
};
#define mmc_packed_cmd(type) ((type) != MMC_PACKED_NONE)
#define mmc_packed_wr(type) ((type) == MMC_PACKED_WRITE)
struct mmc_packed {
struct list_head list;
u32 cmd_hdr[1024];
unsigned int blocks;
u8 nr_entries;
u8 retries;
s16 idx_failure;
};
struct mmc_queue_req { struct mmc_queue_req {
struct request *req; struct request *req;
struct mmc_blk_request brq; struct mmc_blk_request brq;
...@@ -20,6 +37,8 @@ struct mmc_queue_req { ...@@ -20,6 +37,8 @@ struct mmc_queue_req {
struct scatterlist *bounce_sg; struct scatterlist *bounce_sg;
unsigned int bounce_sg_len; unsigned int bounce_sg_len;
struct mmc_async_req mmc_active; struct mmc_async_req mmc_active;
enum mmc_packed_type cmd_type;
struct mmc_packed *packed;
}; };
struct mmc_queue { struct mmc_queue {
...@@ -27,6 +46,9 @@ struct mmc_queue { ...@@ -27,6 +46,9 @@ struct mmc_queue {
struct task_struct *thread; struct task_struct *thread;
struct semaphore thread_sem; struct semaphore thread_sem;
unsigned int flags; unsigned int flags;
#define MMC_QUEUE_SUSPENDED (1 << 0)
#define MMC_QUEUE_NEW_REQUEST (1 << 1)
int (*issue_fn)(struct mmc_queue *, struct request *); int (*issue_fn)(struct mmc_queue *, struct request *);
void *data; void *data;
struct request_queue *queue; struct request_queue *queue;
...@@ -46,4 +68,7 @@ extern unsigned int mmc_queue_map_sg(struct mmc_queue *, ...@@ -46,4 +68,7 @@ extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
extern void mmc_queue_bounce_pre(struct mmc_queue_req *); extern void mmc_queue_bounce_pre(struct mmc_queue_req *);
extern void mmc_queue_bounce_post(struct mmc_queue_req *); extern void mmc_queue_bounce_post(struct mmc_queue_req *);
extern int mmc_packed_init(struct mmc_queue *, struct mmc_card *);
extern void mmc_packed_clean(struct mmc_queue *);
#endif #endif
...@@ -321,6 +321,7 @@ int mmc_add_card(struct mmc_card *card) ...@@ -321,6 +321,7 @@ int mmc_add_card(struct mmc_card *card)
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card); mmc_add_card_debugfs(card);
#endif #endif
mmc_init_context_info(card->host);
ret = device_add(&card->dev); ret = device_add(&card->dev);
if (ret) if (ret)
......
...@@ -319,11 +319,45 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) ...@@ -319,11 +319,45 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
} }
EXPORT_SYMBOL(mmc_start_bkops); EXPORT_SYMBOL(mmc_start_bkops);
/*
* mmc_wait_data_done() - done callback for data request
* @mrq: done data request
*
* Wakes up mmc context, passed as a callback to host controller driver
*/
static void mmc_wait_data_done(struct mmc_request *mrq)
{
mrq->host->context_info.is_done_rcv = true;
wake_up_interruptible(&mrq->host->context_info.wait);
}
static void mmc_wait_done(struct mmc_request *mrq) static void mmc_wait_done(struct mmc_request *mrq)
{ {
complete(&mrq->completion); complete(&mrq->completion);
} }
/*
*__mmc_start_data_req() - starts data request
* @host: MMC host to start the request
* @mrq: data request to start
*
* Sets the done callback to be called when request is completed by the card.
* Starts data mmc request execution
*/
static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq)
{
mrq->done = mmc_wait_data_done;
mrq->host = host;
if (mmc_card_removed(host->card)) {
mrq->cmd->error = -ENOMEDIUM;
mmc_wait_data_done(mrq);
return -ENOMEDIUM;
}
mmc_start_request(host, mrq);
return 0;
}
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{ {
init_completion(&mrq->completion); init_completion(&mrq->completion);
...@@ -337,6 +371,62 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) ...@@ -337,6 +371,62 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
return 0; return 0;
} }
/*
* mmc_wait_for_data_req_done() - wait for request completed
* @host: MMC host to prepare the command.
* @mrq: MMC request to wait for
*
* Blocks MMC context till host controller will ack end of data request
* execution or new request notification arrives from the block layer.
* Handles command retries.
*
* Returns enum mmc_blk_status after checking errors.
*/
static int mmc_wait_for_data_req_done(struct mmc_host *host,
struct mmc_request *mrq,
struct mmc_async_req *next_req)
{
struct mmc_command *cmd;
struct mmc_context_info *context_info = &host->context_info;
int err;
unsigned long flags;
while (1) {
wait_event_interruptible(context_info->wait,
(context_info->is_done_rcv ||
context_info->is_new_req));
spin_lock_irqsave(&context_info->lock, flags);
context_info->is_waiting_last_req = false;
spin_unlock_irqrestore(&context_info->lock, flags);
if (context_info->is_done_rcv) {
context_info->is_done_rcv = false;
context_info->is_new_req = false;
cmd = mrq->cmd;
if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card)) {
err = host->areq->err_check(host->card,
host->areq);
break; /* return err */
} else {
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host),
cmd->opcode, cmd->error);
cmd->retries--;
cmd->error = 0;
host->ops->request(host, mrq);
continue; /* wait for done/new event again */
}
} else if (context_info->is_new_req) {
context_info->is_new_req = false;
if (!next_req) {
err = MMC_BLK_NEW_REQUEST;
break; /* return err */
}
}
}
return err;
}
static void mmc_wait_for_req_done(struct mmc_host *host, static void mmc_wait_for_req_done(struct mmc_host *host,
struct mmc_request *mrq) struct mmc_request *mrq)
{ {
...@@ -426,8 +516,16 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, ...@@ -426,8 +516,16 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
mmc_pre_req(host, areq->mrq, !host->areq); mmc_pre_req(host, areq->mrq, !host->areq);
if (host->areq) { if (host->areq) {
mmc_wait_for_req_done(host, host->areq->mrq); err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq);
err = host->areq->err_check(host->card, host->areq); if (err == MMC_BLK_NEW_REQUEST) {
if (error)
*error = err;
/*
* The previous request was not completed,
* nothing to return
*/
return NULL;
}
/* /*
* Check BKOPS urgency for each R1 response * Check BKOPS urgency for each R1 response
*/ */
...@@ -439,14 +537,14 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, ...@@ -439,14 +537,14 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
} }
if (!err && areq) if (!err && areq)
start_err = __mmc_start_req(host, areq->mrq); start_err = __mmc_start_data_req(host, areq->mrq);
if (host->areq) if (host->areq)
mmc_post_req(host, host->areq->mrq, 0); mmc_post_req(host, host->areq->mrq, 0);
/* Cancel a prepared request if it was not started. */ /* Cancel a prepared request if it was not started. */
if ((err || start_err) && areq) if ((err || start_err) && areq)
mmc_post_req(host, areq->mrq, -EINVAL); mmc_post_req(host, areq->mrq, -EINVAL);
if (err) if (err)
host->areq = NULL; host->areq = NULL;
...@@ -1137,7 +1235,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc, ...@@ -1137,7 +1235,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
*/ */
voltage = regulator_get_voltage(supply); voltage = regulator_get_voltage(supply);
if (regulator_count_voltages(supply) == 1) if (!regulator_can_change_voltage(supply))
min_uV = max_uV = voltage; min_uV = max_uV = voltage;
if (voltage < 0) if (voltage < 0)
...@@ -1219,10 +1317,30 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) ...@@ -1219,10 +1317,30 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
return ocr; return ocr;
} }
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11) int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
{
int err = 0;
int old_signal_voltage = host->ios.signal_voltage;
host->ios.signal_voltage = signal_voltage;
if (host->ops->start_signal_voltage_switch) {
mmc_host_clk_hold(host);
err = host->ops->start_signal_voltage_switch(host, &host->ios);
mmc_host_clk_release(host);
}
if (err)
host->ios.signal_voltage = old_signal_voltage;
return err;
}
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
{ {
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
int err = 0; int err = 0;
u32 clock;
BUG_ON(!host); BUG_ON(!host);
...@@ -1230,27 +1348,81 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11 ...@@ -1230,27 +1348,81 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11
* Send CMD11 only if the request is to switch the card to * Send CMD11 only if the request is to switch the card to
* 1.8V signalling. * 1.8V signalling.
*/ */
if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) { if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
cmd.opcode = SD_SWITCH_VOLTAGE; return __mmc_set_signal_voltage(host, signal_voltage);
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0); /*
if (err) * If we cannot switch voltages, return failure so the caller
return err; * can continue without UHS mode
*/
if (!host->ops->start_signal_voltage_switch)
return -EPERM;
if (!host->ops->card_busy)
pr_warning("%s: cannot verify signal voltage switch\n",
mmc_hostname(host));
cmd.opcode = SD_SWITCH_VOLTAGE;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) err = mmc_wait_for_cmd(host, &cmd, 0);
return -EIO; if (err)
return err;
if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
return -EIO;
mmc_host_clk_hold(host);
/*
* The card should drive cmd and dat[0:3] low immediately
* after the response of cmd11, but wait 1 ms to be sure
*/
mmc_delay(1);
if (host->ops->card_busy && !host->ops->card_busy(host)) {
err = -EAGAIN;
goto power_cycle;
} }
/*
* During a signal voltage level switch, the clock must be gated
* for 5 ms according to the SD spec
*/
clock = host->ios.clock;
host->ios.clock = 0;
mmc_set_ios(host);
host->ios.signal_voltage = signal_voltage; if (__mmc_set_signal_voltage(host, signal_voltage)) {
/*
* Voltages may not have been switched, but we've already
* sent CMD11, so a power cycle is required anyway
*/
err = -EAGAIN;
goto power_cycle;
}
if (host->ops->start_signal_voltage_switch) { /* Keep clock gated for at least 5 ms */
mmc_host_clk_hold(host); mmc_delay(5);
err = host->ops->start_signal_voltage_switch(host, &host->ios); host->ios.clock = clock;
mmc_host_clk_release(host); mmc_set_ios(host);
/* Wait for at least 1 ms according to spec */
mmc_delay(1);
/*
* Failure to switch is indicated by the card holding
* dat[0:3] low
*/
if (host->ops->card_busy && host->ops->card_busy(host))
err = -EAGAIN;
power_cycle:
if (err) {
pr_debug("%s: Signal voltage switch failed, "
"power cycling card\n", mmc_hostname(host));
mmc_power_cycle(host);
} }
mmc_host_clk_release(host);
return err; return err;
} }
...@@ -1314,7 +1486,7 @@ static void mmc_power_up(struct mmc_host *host) ...@@ -1314,7 +1486,7 @@ static void mmc_power_up(struct mmc_host *host)
mmc_set_ios(host); mmc_set_ios(host);
/* Set signal voltage to 3.3V */ /* Set signal voltage to 3.3V */
mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, false); __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
/* /*
* This delay should be sufficient to allow the power supply * This delay should be sufficient to allow the power supply
...@@ -1372,6 +1544,14 @@ void mmc_power_off(struct mmc_host *host) ...@@ -1372,6 +1544,14 @@ void mmc_power_off(struct mmc_host *host)
mmc_host_clk_release(host); mmc_host_clk_release(host);
} }
void mmc_power_cycle(struct mmc_host *host)
{
mmc_power_off(host);
/* Wait at least 1 ms according to SD spec */
mmc_delay(1);
mmc_power_up(host);
}
/* /*
* Cleanup when the last reference to the bus operator is dropped. * Cleanup when the last reference to the bus operator is dropped.
*/ */
...@@ -2388,6 +2568,7 @@ EXPORT_SYMBOL(mmc_flush_cache); ...@@ -2388,6 +2568,7 @@ EXPORT_SYMBOL(mmc_flush_cache);
* Turn the cache ON/OFF. * Turn the cache ON/OFF.
* Turning the cache OFF shall trigger flushing of the data * Turning the cache OFF shall trigger flushing of the data
* to the non-volatile storage. * to the non-volatile storage.
* This function should be called with host claimed
*/ */
int mmc_cache_ctrl(struct mmc_host *host, u8 enable) int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
{ {
...@@ -2399,7 +2580,6 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable) ...@@ -2399,7 +2580,6 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
mmc_card_is_removable(host)) mmc_card_is_removable(host))
return err; return err;
mmc_claim_host(host);
if (card && mmc_card_mmc(card) && if (card && mmc_card_mmc(card) &&
(card->ext_csd.cache_size > 0)) { (card->ext_csd.cache_size > 0)) {
enable = !!enable; enable = !!enable;
...@@ -2417,7 +2597,6 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable) ...@@ -2417,7 +2597,6 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
card->ext_csd.cache_ctrl = enable; card->ext_csd.cache_ctrl = enable;
} }
} }
mmc_release_host(host);
return err; return err;
} }
...@@ -2436,10 +2615,6 @@ int mmc_suspend_host(struct mmc_host *host) ...@@ -2436,10 +2615,6 @@ int mmc_suspend_host(struct mmc_host *host)
cancel_delayed_work(&host->detect); cancel_delayed_work(&host->detect);
mmc_flush_scheduled_work(); mmc_flush_scheduled_work();
err = mmc_cache_ctrl(host, 0);
if (err)
goto out;
mmc_bus_get(host); mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) { if (host->bus_ops && !host->bus_dead) {
if (host->bus_ops->suspend) { if (host->bus_ops->suspend) {
...@@ -2581,6 +2756,23 @@ int mmc_pm_notify(struct notifier_block *notify_block, ...@@ -2581,6 +2756,23 @@ int mmc_pm_notify(struct notifier_block *notify_block,
} }
#endif #endif
/**
* mmc_init_context_info() - init synchronization context
* @host: mmc host
*
* Init struct context_info needed to implement asynchronous
* request mechanism, used by mmc core, host driver and mmc requests
* supplier.
*/
void mmc_init_context_info(struct mmc_host *host)
{
spin_lock_init(&host->context_info.lock);
host->context_info.is_new_req = false;
host->context_info.is_done_rcv = false;
host->context_info.is_waiting_last_req = false;
init_waitqueue_head(&host->context_info.wait);
}
static int __init mmc_init(void) static int __init mmc_init(void)
{ {
int ret; int ret;
......
...@@ -40,11 +40,12 @@ void mmc_set_ungated(struct mmc_host *host); ...@@ -40,11 +40,12 @@ void mmc_set_ungated(struct mmc_host *host);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width); void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
bool cmd11); int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
void mmc_power_off(struct mmc_host *host); void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host);
static inline void mmc_delay(unsigned int ms) static inline void mmc_delay(unsigned int ms)
{ {
...@@ -76,5 +77,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host); ...@@ -76,5 +77,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host);
void mmc_add_card_debugfs(struct mmc_card *card); void mmc_add_card_debugfs(struct mmc_card *card);
void mmc_remove_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card);
void mmc_init_context_info(struct mmc_host *host);
#endif #endif
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/leds.h> #include <linux/leds.h>
...@@ -23,6 +25,7 @@ ...@@ -23,6 +25,7 @@
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
#include <linux/mmc/slot-gpio.h>
#include "core.h" #include "core.h"
#include "host.h" #include "host.h"
...@@ -294,6 +297,126 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host) ...@@ -294,6 +297,126 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
#endif #endif
/**
* mmc_of_parse() - parse host's device-tree node
* @host: host whose node should be parsed.
*
* To keep the rest of the MMC subsystem unaware of whether DT has been
* used to to instantiate and configure this host instance or not, we
* parse the properties and set respective generic mmc-host flags and
* parameters.
*/
void mmc_of_parse(struct mmc_host *host)
{
struct device_node *np;
u32 bus_width;
bool explicit_inv_wp, gpio_inv_wp = false;
enum of_gpio_flags flags;
int len, ret, gpio;
if (!host->parent || !host->parent->of_node)
return;
np = host->parent->of_node;
/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
if (of_property_read_u32(np, "bus-width", &bus_width) < 0) {
dev_dbg(host->parent,
"\"bus-width\" property is missing, assuming 1 bit.\n");
bus_width = 1;
}
switch (bus_width) {
case 8:
host->caps |= MMC_CAP_8_BIT_DATA;
/* Hosts capable of 8-bit transfers can also do 4 bits */
case 4:
host->caps |= MMC_CAP_4_BIT_DATA;
break;
case 1:
break;
default:
dev_err(host->parent,
"Invalid \"bus-width\" value %ud!\n", bus_width);
}
/* f_max is obtained from the optional "max-frequency" property */
of_property_read_u32(np, "max-frequency", &host->f_max);
/*
* Configure CD and WP pins. They are both by default active low to
* match the SDHCI spec. If GPIOs are provided for CD and / or WP, the
* mmc-gpio helpers are used to attach, configure and use them. If
* polarity inversion is specified in DT, one of MMC_CAP2_CD_ACTIVE_HIGH
* and MMC_CAP2_RO_ACTIVE_HIGH capability-2 flags is set. If the
* "broken-cd" property is provided, the MMC_CAP_NEEDS_POLL capability
* is set. If the "non-removable" property is found, the
* MMC_CAP_NONREMOVABLE capability is set and no card-detection
* configuration is performed.
*/
/* Parse Card Detection */
if (of_find_property(np, "non-removable", &len)) {
host->caps |= MMC_CAP_NONREMOVABLE;
} else {
bool explicit_inv_cd, gpio_inv_cd = false;
explicit_inv_cd = of_property_read_bool(np, "cd-inverted");
if (of_find_property(np, "broken-cd", &len))
host->caps |= MMC_CAP_NEEDS_POLL;
gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
if (gpio_is_valid(gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_cd = true;
ret = mmc_gpio_request_cd(host, gpio);
if (ret < 0)
dev_err(host->parent,
"Failed to request CD GPIO #%d: %d!\n",
gpio, ret);
else
dev_info(host->parent, "Got CD GPIO #%d.\n",
gpio);
}
if (explicit_inv_cd ^ gpio_inv_cd)
host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
}
/* Parse Write Protection */
explicit_inv_wp = of_property_read_bool(np, "wp-inverted");
gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
if (gpio_is_valid(gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_wp = true;
ret = mmc_gpio_request_ro(host, gpio);
if (ret < 0)
dev_err(host->parent,
"Failed to request WP GPIO: %d!\n", ret);
}
if (explicit_inv_wp ^ gpio_inv_wp)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
if (of_find_property(np, "cap-sd-highspeed", &len))
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (of_find_property(np, "cap-mmc-highspeed", &len))
host->caps |= MMC_CAP_MMC_HIGHSPEED;
if (of_find_property(np, "cap-power-off-card", &len))
host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_find_property(np, "cap-sdio-irq", &len))
host->caps |= MMC_CAP_SDIO_IRQ;
if (of_find_property(np, "keep-power-in-suspend", &len))
host->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", &len))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}
EXPORT_SYMBOL(mmc_of_parse);
/** /**
* mmc_alloc_host - initialise the per-host structure. * mmc_alloc_host - initialise the per-host structure.
* @extra: sizeof private data structure * @extra: sizeof private data structure
......
...@@ -496,7 +496,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -496,7 +496,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
* RPMB regions are defined in multiples of 128K. * RPMB regions are defined in multiples of 128K.
*/ */
card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT]; card->ext_csd.raw_rpmb_size_mult = ext_csd[EXT_CSD_RPMB_MULT];
if (ext_csd[EXT_CSD_RPMB_MULT]) { if (ext_csd[EXT_CSD_RPMB_MULT] && mmc_host_cmd23(card->host)) {
mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17, mmc_part_add(card, ext_csd[EXT_CSD_RPMB_MULT] << 17,
EXT_CSD_PART_CONFIG_ACC_RPMB, EXT_CSD_PART_CONFIG_ACC_RPMB,
"rpmb", 0, false, "rpmb", 0, false,
...@@ -538,6 +538,11 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -538,6 +538,11 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
} else { } else {
card->ext_csd.data_tag_unit_size = 0; card->ext_csd.data_tag_unit_size = 0;
} }
card->ext_csd.max_packed_writes =
ext_csd[EXT_CSD_MAX_PACKED_WRITES];
card->ext_csd.max_packed_reads =
ext_csd[EXT_CSD_MAX_PACKED_READS];
} else { } else {
card->ext_csd.data_sector_size = 512; card->ext_csd.data_sector_size = 512;
} }
...@@ -769,11 +774,11 @@ static int mmc_select_hs200(struct mmc_card *card) ...@@ -769,11 +774,11 @@ static int mmc_select_hs200(struct mmc_card *card)
if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V && if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&
host->caps2 & MMC_CAP2_HS200_1_2V_SDR) host->caps2 & MMC_CAP2_HS200_1_2V_SDR)
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0); err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V && if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V &&
host->caps2 & MMC_CAP2_HS200_1_8V_SDR) host->caps2 & MMC_CAP2_HS200_1_8V_SDR)
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, 0); err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
/* If fails try again during next card power cycle */ /* If fails try again during next card power cycle */
if (err) if (err)
...@@ -1221,8 +1226,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -1221,8 +1226,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* WARNING: eMMC rules are NOT the same as SD DDR * WARNING: eMMC rules are NOT the same as SD DDR
*/ */
if (ddr == MMC_1_2V_DDR_MODE) { if (ddr == MMC_1_2V_DDR_MODE) {
err = mmc_set_signal_voltage(host, err = __mmc_set_signal_voltage(host,
MMC_SIGNAL_VOLTAGE_120, 0); MMC_SIGNAL_VOLTAGE_120);
if (err) if (err)
goto err; goto err;
} }
...@@ -1275,6 +1280,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -1275,6 +1280,29 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} }
} }
/*
* The mandatory minimum values are defined for packed command.
* read: 5, write: 3
*/
if (card->ext_csd.max_packed_writes >= 3 &&
card->ext_csd.max_packed_reads >= 5 &&
host->caps2 & MMC_CAP2_PACKED_CMD) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_EXP_EVENTS_CTRL,
EXT_CSD_PACKED_EVENT_EN,
card->ext_csd.generic_cmd6_time);
if (err && err != -EBADMSG)
goto free_card;
if (err) {
pr_warn("%s: Enabling packed event failed\n",
mmc_hostname(card->host));
card->ext_csd.packed_event_en = 0;
err = 0;
} else {
card->ext_csd.packed_event_en = 1;
}
}
if (!oldcard) if (!oldcard)
host->card = card; host->card = card;
...@@ -1379,6 +1407,11 @@ static int mmc_suspend(struct mmc_host *host) ...@@ -1379,6 +1407,11 @@ static int mmc_suspend(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
err = mmc_cache_ctrl(host, 0);
if (err)
goto out;
if (mmc_can_poweroff_notify(host->card)) if (mmc_can_poweroff_notify(host->card))
err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT); err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
else if (mmc_card_can_sleep(host)) else if (mmc_card_can_sleep(host))
...@@ -1386,8 +1419,9 @@ static int mmc_suspend(struct mmc_host *host) ...@@ -1386,8 +1419,9 @@ static int mmc_suspend(struct mmc_host *host)
else if (!mmc_host_is_spi(host)) else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host); err = mmc_deselect_cards(host);
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_release_host(host);
out:
mmc_release_host(host);
return err; return err;
} }
......
...@@ -363,6 +363,7 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -363,6 +363,7 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD, return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
ext_csd, 512); ext_csd, 512);
} }
EXPORT_SYMBOL_GPL(mmc_send_ext_csd);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp) int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
{ {
......
...@@ -444,8 +444,7 @@ static void sd_update_bus_speed_mode(struct mmc_card *card) ...@@ -444,8 +444,7 @@ static void sd_update_bus_speed_mode(struct mmc_card *card)
* If the host doesn't support any of the UHS-I modes, fallback on * If the host doesn't support any of the UHS-I modes, fallback on
* default speed. * default speed.
*/ */
if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | if (!mmc_host_uhs(card->host)) {
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))) {
card->sd_bus_speed = 0; card->sd_bus_speed = 0;
return; return;
} }
...@@ -713,6 +712,14 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) ...@@ -713,6 +712,14 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
{ {
int err; int err;
u32 max_current; u32 max_current;
int retries = 10;
try_again:
if (!retries) {
ocr &= ~SD_OCR_S18R;
pr_warning("%s: Skipping voltage switch\n",
mmc_hostname(host));
}
/* /*
* Since we're changing the OCR value, we seem to * Since we're changing the OCR value, we seem to
...@@ -734,10 +741,10 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) ...@@ -734,10 +741,10 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
/* /*
* If the host supports one of UHS-I modes, request the card * If the host supports one of UHS-I modes, request the card
* to switch to 1.8V signaling level. * to switch to 1.8V signaling level. If the card has failed
* repeatedly to switch however, skip this.
*/ */
if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | if (retries && mmc_host_uhs(host))
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))
ocr |= SD_OCR_S18R; ocr |= SD_OCR_S18R;
/* /*
...@@ -748,7 +755,6 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) ...@@ -748,7 +755,6 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
if (max_current > 150) if (max_current > 150)
ocr |= SD_OCR_XPC; ocr |= SD_OCR_XPC;
try_again:
err = mmc_send_app_op_cond(host, ocr, rocr); err = mmc_send_app_op_cond(host, ocr, rocr);
if (err) if (err)
return err; return err;
...@@ -759,9 +765,12 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) ...@@ -759,9 +765,12 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
*/ */
if (!mmc_host_is_spi(host) && rocr && if (!mmc_host_is_spi(host) && rocr &&
((*rocr & 0x41000000) == 0x41000000)) { ((*rocr & 0x41000000) == 0x41000000)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, true); err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
if (err) { if (err == -EAGAIN) {
ocr &= ~SD_OCR_S18R; retries--;
goto try_again;
} else if (err) {
retries = 0;
goto try_again; goto try_again;
} }
} }
...@@ -960,16 +969,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -960,16 +969,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
/* Card is an ultra-high-speed card */ /* Card is an ultra-high-speed card */
mmc_card_set_uhs(card); mmc_card_set_uhs(card);
/*
* Since initialization is now complete, enable preset
* value registers for UHS-I cards.
*/
if (host->ops->enable_preset_value) {
mmc_host_clk_hold(card->host);
host->ops->enable_preset_value(host, true);
mmc_host_clk_release(card->host);
}
} else { } else {
/* /*
* Attempt to change to high-speed (if supported) * Attempt to change to high-speed (if supported)
...@@ -1148,13 +1147,6 @@ int mmc_attach_sd(struct mmc_host *host) ...@@ -1148,13 +1147,6 @@ int mmc_attach_sd(struct mmc_host *host)
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
/* Disable preset value enable if already set since last time */
if (host->ops->enable_preset_value) {
mmc_host_clk_hold(host);
host->ops->enable_preset_value(host, false);
mmc_host_clk_release(host);
}
err = mmc_send_app_op_cond(host, 0, &ocr); err = mmc_send_app_op_cond(host, 0, &ocr);
if (err) if (err)
return err; return err;
......
...@@ -157,10 +157,7 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr) ...@@ -157,10 +157,7 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
if (ret) if (ret)
goto out; goto out;
if (card->host->caps & if (mmc_host_uhs(card->host)) {
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_DDR50)) {
if (data & SDIO_UHS_DDR50) if (data & SDIO_UHS_DDR50)
card->sw_caps.sd3_bus_mode card->sw_caps.sd3_bus_mode
|= SD_MODE_UHS_DDR50; |= SD_MODE_UHS_DDR50;
...@@ -478,8 +475,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) ...@@ -478,8 +475,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card)
* If the host doesn't support any of the UHS-I modes, fallback on * If the host doesn't support any of the UHS-I modes, fallback on
* default speed. * default speed.
*/ */
if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | if (!mmc_host_uhs(card->host))
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
return 0; return 0;
bus_speed = SDIO_SPEED_SDR12; bus_speed = SDIO_SPEED_SDR12;
...@@ -489,23 +485,27 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) ...@@ -489,23 +485,27 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card)
bus_speed = SDIO_SPEED_SDR104; bus_speed = SDIO_SPEED_SDR104;
timing = MMC_TIMING_UHS_SDR104; timing = MMC_TIMING_UHS_SDR104;
card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR; card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
card->sd_bus_speed = UHS_SDR104_BUS_SPEED;
} else if ((card->host->caps & MMC_CAP_UHS_DDR50) && } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) { (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
bus_speed = SDIO_SPEED_DDR50; bus_speed = SDIO_SPEED_DDR50;
timing = MMC_TIMING_UHS_DDR50; timing = MMC_TIMING_UHS_DDR50;
card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR; card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
card->sd_bus_speed = UHS_DDR50_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode & MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
SD_MODE_UHS_SDR50)) { SD_MODE_UHS_SDR50)) {
bus_speed = SDIO_SPEED_SDR50; bus_speed = SDIO_SPEED_SDR50;
timing = MMC_TIMING_UHS_SDR50; timing = MMC_TIMING_UHS_SDR50;
card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR; card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
card->sd_bus_speed = UHS_SDR50_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) && MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) { (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
bus_speed = SDIO_SPEED_SDR25; bus_speed = SDIO_SPEED_SDR25;
timing = MMC_TIMING_UHS_SDR25; timing = MMC_TIMING_UHS_SDR25;
card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR; card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
card->sd_bus_speed = UHS_SDR25_BUS_SPEED;
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 | } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode & MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
...@@ -513,6 +513,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) ...@@ -513,6 +513,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card)
bus_speed = SDIO_SPEED_SDR12; bus_speed = SDIO_SPEED_SDR12;
timing = MMC_TIMING_UHS_SDR12; timing = MMC_TIMING_UHS_SDR12;
card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR; card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
card->sd_bus_speed = UHS_SDR12_BUS_SPEED;
} }
err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed); err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
...@@ -583,10 +584,19 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -583,10 +584,19 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
{ {
struct mmc_card *card; struct mmc_card *card;
int err; int err;
int retries = 10;
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
try_again:
if (!retries) {
pr_warning("%s: Skipping voltage switch\n",
mmc_hostname(host));
ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
}
/* /*
* Inform the card of the voltage * Inform the card of the voltage
*/ */
...@@ -645,14 +655,16 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, ...@@ -645,14 +655,16 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
* systems that claim 1.8v signalling in fact do not support * systems that claim 1.8v signalling in fact do not support
* it. * it.
*/ */
if ((ocr & R4_18V_PRESENT) && if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) {
(host->caps & err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | if (err == -EAGAIN) {
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | sdio_reset(host);
MMC_CAP_UHS_DDR50))) { mmc_go_idle(host);
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, mmc_send_if_cond(host, host->ocr_avail);
true); mmc_remove_card(card);
if (err) { retries--;
goto try_again;
} else if (err) {
ocr &= ~R4_18V_PRESENT; ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT; host->ocr &= ~R4_18V_PRESENT;
} }
...@@ -937,10 +949,12 @@ static int mmc_sdio_resume(struct mmc_host *host) ...@@ -937,10 +949,12 @@ static int mmc_sdio_resume(struct mmc_host *host)
mmc_claim_host(host); mmc_claim_host(host);
/* No need to reinitialize powered-resumed nonremovable cards */ /* No need to reinitialize powered-resumed nonremovable cards */
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
sdio_reset(host);
mmc_go_idle(host);
err = mmc_sdio_init_card(host, host->ocr, host->card, err = mmc_sdio_init_card(host, host->ocr, host->card,
mmc_card_keep_power(host)); mmc_card_keep_power(host));
else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
/* We may have switched to 1-bit mode during suspend */ /* We may have switched to 1-bit mode during suspend */
err = sdio_enable_4bit_bus(host->card); err = sdio_enable_4bit_bus(host->card);
if (err > 0) { if (err > 0) {
...@@ -1020,6 +1034,10 @@ static int mmc_sdio_power_restore(struct mmc_host *host) ...@@ -1020,6 +1034,10 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
goto out; goto out;
} }
if (mmc_host_uhs(host))
/* to query card if 1.8V signalling is supported */
host->ocr |= R4_18V_PRESENT;
ret = mmc_sdio_init_card(host, host->ocr, host->card, ret = mmc_sdio_init_card(host, host->ocr, host->card,
mmc_card_keep_power(host)); mmc_card_keep_power(host));
if (!ret && host->sdio_irqs) if (!ret && host->sdio_irqs)
...@@ -1085,6 +1103,10 @@ int mmc_attach_sdio(struct mmc_host *host) ...@@ -1085,6 +1103,10 @@ int mmc_attach_sdio(struct mmc_host *host)
/* /*
* Detect and init the card. * Detect and init the card.
*/ */
if (mmc_host_uhs(host))
/* to query card if 1.8V signalling is supported */
host->ocr |= R4_18V_PRESENT;
err = mmc_sdio_init_card(host, host->ocr, NULL, 0); err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
if (err) { if (err) {
if (err == -EAGAIN) { if (err == -EAGAIN) {
......
...@@ -92,6 +92,20 @@ int mmc_gpio_get_cd(struct mmc_host *host) ...@@ -92,6 +92,20 @@ int mmc_gpio_get_cd(struct mmc_host *host)
} }
EXPORT_SYMBOL(mmc_gpio_get_cd); EXPORT_SYMBOL(mmc_gpio_get_cd);
/**
* mmc_gpio_request_ro - request a gpio for write-protection
* @host: mmc host
* @gpio: gpio number requested
*
* As devm_* managed functions are used in mmc_gpio_request_ro(), client
* drivers do not need to explicitly call mmc_gpio_free_ro() for freeing up,
* if the requesting and freeing are only needed at probing and unbinding time
* for once. However, if client drivers do something special like runtime
* switching for write-protection, they are responsible for calling
* mmc_gpio_request_ro() and mmc_gpio_free_ro() as a pair on their own.
*
* Returns zero on success, else an error.
*/
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
{ {
struct mmc_gpio *ctx; struct mmc_gpio *ctx;
...@@ -106,7 +120,8 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) ...@@ -106,7 +120,8 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
ctx = host->slot.handler_priv; ctx = host->slot.handler_priv;
ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->ro_label); ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
ctx->ro_label);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -116,6 +131,20 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) ...@@ -116,6 +131,20 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
} }
EXPORT_SYMBOL(mmc_gpio_request_ro); EXPORT_SYMBOL(mmc_gpio_request_ro);
/**
* mmc_gpio_request_cd - request a gpio for card-detection
* @host: mmc host
* @gpio: gpio number requested
*
* As devm_* managed functions are used in mmc_gpio_request_cd(), client
* drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up,
* if the requesting and freeing are only needed at probing and unbinding time
* for once. However, if client drivers do something special like runtime
* switching for card-detection, they are responsible for calling
* mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own.
*
* Returns zero on success, else an error.
*/
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio)
{ {
struct mmc_gpio *ctx; struct mmc_gpio *ctx;
...@@ -128,7 +157,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) ...@@ -128,7 +157,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio)
ctx = host->slot.handler_priv; ctx = host->slot.handler_priv;
ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label); ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN,
ctx->cd_label);
if (ret < 0) if (ret < 0)
/* /*
* don't bother freeing memory. It might still get used by other * don't bother freeing memory. It might still get used by other
...@@ -146,7 +176,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) ...@@ -146,7 +176,8 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio)
irq = -EINVAL; irq = -EINVAL;
if (irq >= 0) { if (irq >= 0) {
ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt, ret = devm_request_threaded_irq(&host->class_dev, irq,
NULL, mmc_gpio_cd_irqt,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
ctx->cd_label, host); ctx->cd_label, host);
if (ret < 0) if (ret < 0)
...@@ -164,6 +195,13 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio) ...@@ -164,6 +195,13 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio)
} }
EXPORT_SYMBOL(mmc_gpio_request_cd); EXPORT_SYMBOL(mmc_gpio_request_cd);
/**
* mmc_gpio_free_ro - free the write-protection gpio
* @host: mmc host
*
* It's provided only for cases that client drivers need to manually free
* up the write-protection gpio requested by mmc_gpio_request_ro().
*/
void mmc_gpio_free_ro(struct mmc_host *host) void mmc_gpio_free_ro(struct mmc_host *host)
{ {
struct mmc_gpio *ctx = host->slot.handler_priv; struct mmc_gpio *ctx = host->slot.handler_priv;
...@@ -175,10 +213,17 @@ void mmc_gpio_free_ro(struct mmc_host *host) ...@@ -175,10 +213,17 @@ void mmc_gpio_free_ro(struct mmc_host *host)
gpio = ctx->ro_gpio; gpio = ctx->ro_gpio;
ctx->ro_gpio = -EINVAL; ctx->ro_gpio = -EINVAL;
gpio_free(gpio); devm_gpio_free(&host->class_dev, gpio);
} }
EXPORT_SYMBOL(mmc_gpio_free_ro); EXPORT_SYMBOL(mmc_gpio_free_ro);
/**
* mmc_gpio_free_cd - free the card-detection gpio
* @host: mmc host
*
* It's provided only for cases that client drivers need to manually free
* up the card-detection gpio requested by mmc_gpio_request_cd().
*/
void mmc_gpio_free_cd(struct mmc_host *host) void mmc_gpio_free_cd(struct mmc_host *host)
{ {
struct mmc_gpio *ctx = host->slot.handler_priv; struct mmc_gpio *ctx = host->slot.handler_priv;
...@@ -188,13 +233,13 @@ void mmc_gpio_free_cd(struct mmc_host *host) ...@@ -188,13 +233,13 @@ void mmc_gpio_free_cd(struct mmc_host *host)
return; return;
if (host->slot.cd_irq >= 0) { if (host->slot.cd_irq >= 0) {
free_irq(host->slot.cd_irq, host); devm_free_irq(&host->class_dev, host->slot.cd_irq, host);
host->slot.cd_irq = -EINVAL; host->slot.cd_irq = -EINVAL;
} }
gpio = ctx->cd_gpio; gpio = ctx->cd_gpio;
ctx->cd_gpio = -EINVAL; ctx->cd_gpio = -EINVAL;
gpio_free(gpio); devm_gpio_free(&host->class_dev, gpio);
} }
EXPORT_SYMBOL(mmc_gpio_free_cd); EXPORT_SYMBOL(mmc_gpio_free_cd);
...@@ -238,6 +238,17 @@ config MMC_SDHCI_S3C_DMA ...@@ -238,6 +238,17 @@ config MMC_SDHCI_S3C_DMA
YMMV. YMMV.
config MMC_SDHCI_BCM2835
tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
depends on ARCH_BCM2835
depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
help
This selects the BCM2835 SD/MMC controller. If you have a BCM2835
platform with SD or MMC devices, say Y or M here.
If unsure, say N.
config MMC_OMAP config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support" tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP depends on ARCH_OMAP
...@@ -361,6 +372,13 @@ config MMC_DAVINCI ...@@ -361,6 +372,13 @@ config MMC_DAVINCI
If you have an DAVINCI board with a Multimedia Card slot, If you have an DAVINCI board with a Multimedia Card slot,
say Y or M here. If unsure, say N. say Y or M here. If unsure, say N.
config MMC_GOLDFISH
tristate "goldfish qemu Multimedia Card Interface support"
depends on GOLDFISH
help
This selects the Goldfish Multimedia card Interface emulation
found on the Goldfish Android virtual device emulation.
config MMC_SPI config MMC_SPI
tristate "MMC/SD/SDIO over SPI" tristate "MMC/SD/SDIO over SPI"
depends on SPI_MASTER && !HIGHMEM && HAS_DMA depends on SPI_MASTER && !HIGHMEM && HAS_DMA
......
...@@ -23,6 +23,7 @@ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o ...@@ -23,6 +23,7 @@ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_MSM) += msm_sdcc.o obj-$(CONFIG_MMC_MSM) += msm_sdcc.o
obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
obj-$(CONFIG_MMC_GOLDFISH) += android-goldfish.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o
ifeq ($(CONFIG_OF),y) ifeq ($(CONFIG_OF),y)
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
...@@ -58,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o ...@@ -58,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
ifeq ($(CONFIG_CB710_DEBUG),y) ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG CFLAGS-cb710-mmc += -DDEBUG
......
This diff is collapsed.
...@@ -175,16 +175,6 @@ static int dw_mci_exynos_setup_bus(struct dw_mci *host, ...@@ -175,16 +175,6 @@ static int dw_mci_exynos_setup_bus(struct dw_mci *host,
} }
} }
gpio = of_get_named_gpio(slot_np, "wp-gpios", 0);
if (gpio_is_valid(gpio)) {
if (devm_gpio_request(host->dev, gpio, "dw-mci-wp"))
dev_info(host->dev, "gpio [%d] request failed\n",
gpio);
} else {
dev_info(host->dev, "wp gpio not available");
host->pdata->quirks |= DW_MCI_QUIRK_NO_WRITE_PROTECT;
}
if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
return 0; return 0;
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h>
#include "dw_mmc.h" #include "dw_mmc.h"
...@@ -74,6 +75,8 @@ struct idmac_desc { ...@@ -74,6 +75,8 @@ struct idmac_desc {
* struct dw_mci_slot - MMC slot state * struct dw_mci_slot - MMC slot state
* @mmc: The mmc_host representing this slot. * @mmc: The mmc_host representing this slot.
* @host: The MMC controller this slot is using. * @host: The MMC controller this slot is using.
* @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
* @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
* @ctype: Card type for this slot. * @ctype: Card type for this slot.
* @mrq: mmc_request currently being processed or waiting to be * @mrq: mmc_request currently being processed or waiting to be
* processed, or NULL when the slot is idle. * processed, or NULL when the slot is idle.
...@@ -88,6 +91,9 @@ struct dw_mci_slot { ...@@ -88,6 +91,9 @@ struct dw_mci_slot {
struct mmc_host *mmc; struct mmc_host *mmc;
struct dw_mci *host; struct dw_mci *host;
int quirks;
int wp_gpio;
u32 ctype; u32 ctype;
struct mmc_request *mrq; struct mmc_request *mrq;
...@@ -825,10 +831,12 @@ static int dw_mci_get_ro(struct mmc_host *mmc) ...@@ -825,10 +831,12 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
struct dw_mci_board *brd = slot->host->pdata; struct dw_mci_board *brd = slot->host->pdata;
/* Use platform get_ro function, else try on board write protect */ /* Use platform get_ro function, else try on board write protect */
if (brd->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT) if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT)
read_only = 0; read_only = 0;
else if (brd->get_ro) else if (brd->get_ro)
read_only = brd->get_ro(slot->id); read_only = brd->get_ro(slot->id);
else if (gpio_is_valid(slot->wp_gpio))
read_only = gpio_get_value(slot->wp_gpio);
else else
read_only = read_only =
mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0; mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
...@@ -1785,6 +1793,30 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) ...@@ -1785,6 +1793,30 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
return NULL; return NULL;
} }
static struct dw_mci_of_slot_quirks {
char *quirk;
int id;
} of_slot_quirks[] = {
{
.quirk = "disable-wp",
.id = DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT,
},
};
static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
{
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
int quirks = 0;
int idx;
/* get quirks */
for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++)
if (of_get_property(np, of_slot_quirks[idx].quirk, NULL))
quirks |= of_slot_quirks[idx].id;
return quirks;
}
/* find out bus-width for a given slot */ /* find out bus-width for a given slot */
static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
{ {
...@@ -1799,7 +1831,34 @@ static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) ...@@ -1799,7 +1831,34 @@ static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
" as 1\n"); " as 1\n");
return bus_wd; return bus_wd;
} }
/* find the write protect gpio for a given slot; or -1 if none specified */
static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
{
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
int gpio;
if (!np)
return -EINVAL;
gpio = of_get_named_gpio(np, "wp-gpios", 0);
/* Having a missing entry is valid; return silently */
if (!gpio_is_valid(gpio))
return -EINVAL;
if (devm_gpio_request(dev, gpio, "dw-mci-wp")) {
dev_warn(dev, "gpio [%d] request failed\n", gpio);
return -EINVAL;
}
return gpio;
}
#else /* CONFIG_OF */ #else /* CONFIG_OF */
static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
{
return 0;
}
static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot) static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
{ {
return 1; return 1;
...@@ -1808,6 +1867,10 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) ...@@ -1808,6 +1867,10 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
{ {
return NULL; return NULL;
} }
static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
{
return -EINVAL;
}
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
...@@ -1828,6 +1891,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) ...@@ -1828,6 +1891,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
slot->host = host; slot->host = host;
host->slot[id] = slot; host->slot[id] = slot;
slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
mmc->ops = &dw_mci_ops; mmc->ops = &dw_mci_ops;
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
mmc->f_max = host->bus_hz; mmc->f_max = host->bus_hz;
...@@ -1923,6 +1988,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) ...@@ -1923,6 +1988,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
else else
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id);
mmc_add_host(mmc); mmc_add_host(mmc);
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
......
...@@ -21,7 +21,11 @@ ...@@ -21,7 +21,11 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/pinctrl/consumer.h>
#include <asm/sizes.h> #include <asm/sizes.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -51,8 +55,6 @@ struct mvsd_host { ...@@ -51,8 +55,6 @@ struct mvsd_host {
struct mmc_host *mmc; struct mmc_host *mmc;
struct device *dev; struct device *dev;
struct clk *clk; struct clk *clk;
int gpio_card_detect;
int gpio_write_protect;
}; };
#define mvsd_write(offs, val) writel(val, iobase + (offs)) #define mvsd_write(offs, val) writel(val, iobase + (offs))
...@@ -538,13 +540,6 @@ static void mvsd_timeout_timer(unsigned long data) ...@@ -538,13 +540,6 @@ static void mvsd_timeout_timer(unsigned long data)
mmc_request_done(host->mmc, mrq); mmc_request_done(host->mmc, mrq);
} }
static irqreturn_t mvsd_card_detect_irq(int irq, void *dev)
{
struct mvsd_host *host = dev;
mmc_detect_change(host->mmc, msecs_to_jiffies(100));
return IRQ_HANDLED;
}
static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable) static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
{ {
struct mvsd_host *host = mmc_priv(mmc); struct mvsd_host *host = mmc_priv(mmc);
...@@ -564,20 +559,6 @@ static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable) ...@@ -564,20 +559,6 @@ static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
} }
static int mvsd_get_ro(struct mmc_host *mmc)
{
struct mvsd_host *host = mmc_priv(mmc);
if (host->gpio_write_protect)
return gpio_get_value(host->gpio_write_protect);
/*
* Board doesn't support read only detection; let the mmc core
* decide what to do.
*/
return -ENOSYS;
}
static void mvsd_power_up(struct mvsd_host *host) static void mvsd_power_up(struct mvsd_host *host)
{ {
void __iomem *iobase = host->base; void __iomem *iobase = host->base;
...@@ -674,7 +655,7 @@ static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -674,7 +655,7 @@ static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static const struct mmc_host_ops mvsd_ops = { static const struct mmc_host_ops mvsd_ops = {
.request = mvsd_request, .request = mvsd_request,
.get_ro = mvsd_get_ro, .get_ro = mmc_gpio_get_ro,
.set_ios = mvsd_set_ios, .set_ios = mvsd_set_ios,
.enable_sdio_irq = mvsd_enable_sdio_irq, .enable_sdio_irq = mvsd_enable_sdio_irq,
}; };
...@@ -703,17 +684,18 @@ mv_conf_mbus_windows(struct mvsd_host *host, ...@@ -703,17 +684,18 @@ mv_conf_mbus_windows(struct mvsd_host *host,
static int __init mvsd_probe(struct platform_device *pdev) static int __init mvsd_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node;
struct mmc_host *mmc = NULL; struct mmc_host *mmc = NULL;
struct mvsd_host *host = NULL; struct mvsd_host *host = NULL;
const struct mvsdio_platform_data *mvsd_data;
const struct mbus_dram_target_info *dram; const struct mbus_dram_target_info *dram;
struct resource *r; struct resource *r;
int ret, irq; int ret, irq;
int gpio_card_detect, gpio_write_protect;
struct pinctrl *pinctrl;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
mvsd_data = pdev->dev.platform_data; if (!r || irq < 0)
if (!r || irq < 0 || !mvsd_data)
return -ENXIO; return -ENXIO;
mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
...@@ -725,8 +707,43 @@ static int __init mvsd_probe(struct platform_device *pdev) ...@@ -725,8 +707,43 @@ static int __init mvsd_probe(struct platform_device *pdev)
host = mmc_priv(mmc); host = mmc_priv(mmc);
host->mmc = mmc; host->mmc = mmc;
host->dev = &pdev->dev; host->dev = &pdev->dev;
host->base_clock = mvsd_data->clock / 2;
host->clk = ERR_PTR(-EINVAL); pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR(pinctrl))
dev_warn(&pdev->dev, "no pins associated\n");
/*
* Some non-DT platforms do not pass a clock, and the clock
* frequency is passed through platform_data. On DT platforms,
* a clock must always be passed, even if there is no gatable
* clock associated to the SDIO interface (it can simply be a
* fixed rate clock).
*/
host->clk = devm_clk_get(&pdev->dev, NULL);
if (!IS_ERR(host->clk))
clk_prepare_enable(host->clk);
if (np) {
if (IS_ERR(host->clk)) {
dev_err(&pdev->dev, "DT platforms must have a clock associated\n");
ret = -EINVAL;
goto out;
}
host->base_clock = clk_get_rate(host->clk) / 2;
gpio_card_detect = of_get_named_gpio(np, "cd-gpios", 0);
gpio_write_protect = of_get_named_gpio(np, "wp-gpios", 0);
} else {
const struct mvsdio_platform_data *mvsd_data;
mvsd_data = pdev->dev.platform_data;
if (!mvsd_data) {
ret = -ENXIO;
goto out;
}
host->base_clock = mvsd_data->clock / 2;
gpio_card_detect = mvsd_data->gpio_card_detect;
gpio_write_protect = mvsd_data->gpio_write_protect;
}
mmc->ops = &mvsd_ops; mmc->ops = &mvsd_ops;
...@@ -765,43 +782,14 @@ static int __init mvsd_probe(struct platform_device *pdev) ...@@ -765,43 +782,14 @@ static int __init mvsd_probe(struct platform_device *pdev)
goto out; goto out;
} }
/* Not all platforms can gate the clock, so it is not if (gpio_is_valid(gpio_card_detect)) {
an error if the clock does not exists. */ ret = mmc_gpio_request_cd(mmc, gpio_card_detect);
host->clk = devm_clk_get(&pdev->dev, NULL); if (ret)
if (!IS_ERR(host->clk)) goto out;
clk_prepare_enable(host->clk); } else
if (mvsd_data->gpio_card_detect) {
ret = devm_gpio_request_one(&pdev->dev,
mvsd_data->gpio_card_detect,
GPIOF_IN, DRIVER_NAME " cd");
if (ret == 0) {
irq = gpio_to_irq(mvsd_data->gpio_card_detect);
ret = devm_request_irq(&pdev->dev, irq,
mvsd_card_detect_irq,
IRQ_TYPE_EDGE_RISING |
IRQ_TYPE_EDGE_FALLING,
DRIVER_NAME " cd", host);
if (ret == 0)
host->gpio_card_detect =
mvsd_data->gpio_card_detect;
else
devm_gpio_free(&pdev->dev,
mvsd_data->gpio_card_detect);
}
}
if (!host->gpio_card_detect)
mmc->caps |= MMC_CAP_NEEDS_POLL; mmc->caps |= MMC_CAP_NEEDS_POLL;
if (mvsd_data->gpio_write_protect) { mmc_gpio_request_ro(mmc, gpio_write_protect);
ret = devm_gpio_request_one(&pdev->dev,
mvsd_data->gpio_write_protect,
GPIOF_IN, DRIVER_NAME " wp");
if (ret == 0) {
host->gpio_write_protect =
mvsd_data->gpio_write_protect;
}
}
setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host); setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
platform_set_drvdata(pdev, mmc); platform_set_drvdata(pdev, mmc);
...@@ -811,15 +799,17 @@ static int __init mvsd_probe(struct platform_device *pdev) ...@@ -811,15 +799,17 @@ static int __init mvsd_probe(struct platform_device *pdev)
pr_notice("%s: %s driver initialized, ", pr_notice("%s: %s driver initialized, ",
mmc_hostname(mmc), DRIVER_NAME); mmc_hostname(mmc), DRIVER_NAME);
if (host->gpio_card_detect) if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
printk("using GPIO %d for card detection\n", printk("using GPIO %d for card detection\n",
host->gpio_card_detect); gpio_card_detect);
else else
printk("lacking card detect (fall back to polling)\n"); printk("lacking card detect (fall back to polling)\n");
return 0; return 0;
out: out:
if (mmc) { if (mmc) {
mmc_gpio_free_cd(mmc);
mmc_gpio_free_ro(mmc);
if (!IS_ERR(host->clk)) if (!IS_ERR(host->clk))
clk_disable_unprepare(host->clk); clk_disable_unprepare(host->clk);
mmc_free_host(mmc); mmc_free_host(mmc);
...@@ -834,6 +824,8 @@ static int __exit mvsd_remove(struct platform_device *pdev) ...@@ -834,6 +824,8 @@ static int __exit mvsd_remove(struct platform_device *pdev)
struct mvsd_host *host = mmc_priv(mmc); struct mvsd_host *host = mmc_priv(mmc);
mmc_gpio_free_cd(mmc);
mmc_gpio_free_ro(mmc);
mmc_remove_host(mmc); mmc_remove_host(mmc);
del_timer_sync(&host->timer); del_timer_sync(&host->timer);
mvsd_power_down(host); mvsd_power_down(host);
...@@ -873,12 +865,19 @@ static int mvsd_resume(struct platform_device *dev) ...@@ -873,12 +865,19 @@ static int mvsd_resume(struct platform_device *dev)
#define mvsd_resume NULL #define mvsd_resume NULL
#endif #endif
static const struct of_device_id mvsdio_dt_ids[] = {
{ .compatible = "marvell,orion-sdio" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);
static struct platform_driver mvsd_driver = { static struct platform_driver mvsd_driver = {
.remove = __exit_p(mvsd_remove), .remove = __exit_p(mvsd_remove),
.suspend = mvsd_suspend, .suspend = mvsd_suspend,
.resume = mvsd_resume, .resume = mvsd_resume,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.of_match_table = mvsdio_dt_ids,
}, },
}; };
......
...@@ -354,7 +354,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host) ...@@ -354,7 +354,7 @@ static void mxs_mmc_adtc(struct mxs_mmc_host *host)
struct dma_async_tx_descriptor *desc; struct dma_async_tx_descriptor *desc;
struct scatterlist *sgl = data->sg, *sg; struct scatterlist *sgl = data->sg, *sg;
unsigned int sg_len = data->sg_len; unsigned int sg_len = data->sg_len;
int i; unsigned int i;
unsigned short dma_data_dir, timeout; unsigned short dma_data_dir, timeout;
enum dma_transfer_direction slave_dirn; enum dma_transfer_direction slave_dirn;
...@@ -804,3 +804,4 @@ module_platform_driver(mxs_mmc_driver); ...@@ -804,3 +804,4 @@ module_platform_driver(mxs_mmc_driver);
MODULE_DESCRIPTION("FREESCALE MXS MMC peripheral"); MODULE_DESCRIPTION("FREESCALE MXS MMC peripheral");
MODULE_AUTHOR("Freescale Semiconductor"); MODULE_AUTHOR("Freescale Semiconductor");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRIVER_NAME);
...@@ -146,7 +146,7 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) ...@@ -146,7 +146,7 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
oms->pdata.get_ro = of_mmc_spi_get_ro; oms->pdata.get_ro = of_mmc_spi_get_ro;
oms->detect_irq = irq_of_parse_and_map(np, 0); oms->detect_irq = irq_of_parse_and_map(np, 0);
if (oms->detect_irq != NO_IRQ) { if (oms->detect_irq != 0) {
oms->pdata.init = of_mmc_spi_init; oms->pdata.init = of_mmc_spi_init;
oms->pdata.exit = of_mmc_spi_exit; oms->pdata.exit = of_mmc_spi_exit;
} else { } else {
......
...@@ -1097,11 +1097,6 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) ...@@ -1097,11 +1097,6 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
voltage = OUTPUT_1V8; voltage = OUTPUT_1V8;
if (voltage == OUTPUT_1V8) { if (voltage == OUTPUT_1V8) {
err = rtsx_pci_write_register(pcr,
SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B);
if (err < 0)
goto out;
err = sd_wait_voltage_stable_1(host); err = sd_wait_voltage_stable_1(host);
if (err < 0) if (err < 0)
goto out; goto out;
......
/*
* BCM2835 SDHCI
* Copyright (C) 2012 Stephen Warren
* Based on U-Boot's MMC driver for the BCM2835 by Oleksandr Tymoshenko & me
* Portions of the code there were obviously based on the Linux kernel at:
* git://github.com/raspberrypi/linux.git rpi-3.6.y
* commit f5b930b "Main bcm2708 linux port" signed-off-by Dom Cobley.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/mmc/host.h>
#include "sdhci-pltfm.h"
/*
* 400KHz is max freq for card ID etc. Use that as min card clock. We need to
* know the min to enable static calculation of max BCM2835_SDHCI_WRITE_DELAY.
*/
#define MIN_FREQ 400000
/*
* The Arasan has a bugette whereby it may lose the content of successive
* writes to registers that are within two SD-card clock cycles of each other
* (a clock domain crossing problem). It seems, however, that the data
* register does not have this problem, which is just as well - otherwise we'd
* have to nobble the DMA engine too.
*
* This should probably be dynamically calculated based on the actual card
* frequency. However, this is the longest we'll have to wait, and doesn't
* seem to slow access down too much, so the added complexity doesn't seem
* worth it for now.
*
* 1/MIN_FREQ is (max) time per tick of eMMC clock.
* 2/MIN_FREQ is time for two ticks.
* Multiply by 1000000 to get uS per two ticks.
* *1000000 for uSecs.
* +1 for hack rounding.
*/
#define BCM2835_SDHCI_WRITE_DELAY (((2 * 1000000) / MIN_FREQ) + 1)
struct bcm2835_sdhci {
u32 shadow;
};
static void bcm2835_sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
writel(val, host->ioaddr + reg);
udelay(BCM2835_SDHCI_WRITE_DELAY);
}
static inline u32 bcm2835_sdhci_readl(struct sdhci_host *host, int reg)
{
u32 val = readl(host->ioaddr + reg);
if (reg == SDHCI_CAPABILITIES)
val |= SDHCI_CAN_VDD_330;
return val;
}
static void bcm2835_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct bcm2835_sdhci *bcm2835_host = pltfm_host->priv;
u32 oldval = (reg == SDHCI_COMMAND) ? bcm2835_host->shadow :
bcm2835_sdhci_readl(host, reg & ~3);
u32 word_num = (reg >> 1) & 1;
u32 word_shift = word_num * 16;
u32 mask = 0xffff << word_shift;
u32 newval = (oldval & ~mask) | (val << word_shift);
if (reg == SDHCI_TRANSFER_MODE)
bcm2835_host->shadow = newval;
else
bcm2835_sdhci_writel(host, newval, reg & ~3);
}
static u16 bcm2835_sdhci_readw(struct sdhci_host *host, int reg)
{
u32 val = bcm2835_sdhci_readl(host, (reg & ~3));
u32 word_num = (reg >> 1) & 1;
u32 word_shift = word_num * 16;
u32 word = (val >> word_shift) & 0xffff;
return word;
}
static void bcm2835_sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
u32 oldval = bcm2835_sdhci_readl(host, reg & ~3);
u32 byte_num = reg & 3;
u32 byte_shift = byte_num * 8;
u32 mask = 0xff << byte_shift;
u32 newval = (oldval & ~mask) | (val << byte_shift);
bcm2835_sdhci_writel(host, newval, reg & ~3);
}
static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg)
{
u32 val = bcm2835_sdhci_readl(host, (reg & ~3));
u32 byte_num = reg & 3;
u32 byte_shift = byte_num * 8;
u32 byte = (val >> byte_shift) & 0xff;
return byte;
}
unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host)
{
return MIN_FREQ;
}
static struct sdhci_ops bcm2835_sdhci_ops = {
.write_l = bcm2835_sdhci_writel,
.write_w = bcm2835_sdhci_writew,
.write_b = bcm2835_sdhci_writeb,
.read_l = bcm2835_sdhci_readl,
.read_w = bcm2835_sdhci_readw,
.read_b = bcm2835_sdhci_readb,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.get_min_clock = bcm2835_sdhci_get_min_clock,
};
static struct sdhci_pltfm_data bcm2835_sdhci_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
.ops = &bcm2835_sdhci_ops,
};
static int bcm2835_sdhci_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
struct bcm2835_sdhci *bcm2835_host;
struct sdhci_pltfm_host *pltfm_host;
int ret;
host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata);
if (IS_ERR(host))
return PTR_ERR(host);
bcm2835_host = devm_kzalloc(&pdev->dev, sizeof(*bcm2835_host),
GFP_KERNEL);
if (!bcm2835_host) {
dev_err(mmc_dev(host->mmc),
"failed to allocate bcm2835_sdhci\n");
return -ENOMEM;
}
pltfm_host = sdhci_priv(host);
pltfm_host->priv = bcm2835_host;
pltfm_host->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pltfm_host->clk)) {
ret = PTR_ERR(pltfm_host->clk);
goto err;
}
return sdhci_add_host(host);
err:
sdhci_pltfm_free(pdev);
return ret;
}
static int bcm2835_sdhci_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
sdhci_pltfm_free(pdev);
return 0;
}
static const struct of_device_id bcm2835_sdhci_of_match[] = {
{ .compatible = "brcm,bcm2835-sdhci" },
{ }
};
MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match);
static struct platform_driver bcm2835_sdhci_driver = {
.driver = {
.name = "sdhci-bcm2835",
.owner = THIS_MODULE,
.of_match_table = bcm2835_sdhci_of_match,
.pm = SDHCI_PLTFM_PMOPS,
},
.probe = bcm2835_sdhci_probe,
.remove = bcm2835_sdhci_remove,
};
module_platform_driver(bcm2835_sdhci_driver);
MODULE_DESCRIPTION("BCM2835 SDHCI driver");
MODULE_AUTHOR("Stephen Warren");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
...@@ -935,7 +935,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host) ...@@ -935,7 +935,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
return 0; return 0;
} }
static int sdhci_pci_8bit_width(struct sdhci_host *host, int width) static int sdhci_pci_bus_width(struct sdhci_host *host, int width)
{ {
u8 ctrl; u8 ctrl;
...@@ -977,7 +977,7 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) ...@@ -977,7 +977,7 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
static struct sdhci_ops sdhci_pci_ops = { static struct sdhci_ops sdhci_pci_ops = {
.enable_dma = sdhci_pci_enable_dma, .enable_dma = sdhci_pci_enable_dma,
.platform_8bit_width = sdhci_pci_8bit_width, .platform_bus_width = sdhci_pci_bus_width,
.hw_reset = sdhci_pci_hw_reset, .hw_reset = sdhci_pci_hw_reset,
}; };
......
...@@ -36,6 +36,14 @@ ...@@ -36,6 +36,14 @@
#endif #endif
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
return clk_get_rate(pltfm_host->clk);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);
static struct sdhci_ops sdhci_pltfm_ops = { static struct sdhci_ops sdhci_pltfm_ops = {
}; };
......
...@@ -98,6 +98,8 @@ extern int sdhci_pltfm_register(struct platform_device *pdev, ...@@ -98,6 +98,8 @@ extern int sdhci_pltfm_register(struct platform_device *pdev,
struct sdhci_pltfm_data *pdata); struct sdhci_pltfm_data *pdata);
extern int sdhci_pltfm_unregister(struct platform_device *pdev); extern int sdhci_pltfm_unregister(struct platform_device *pdev);
extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
#ifdef CONFIG_PM #ifdef CONFIG_PM
extern const struct dev_pm_ops sdhci_pltfm_pmops; extern const struct dev_pm_ops sdhci_pltfm_pmops;
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops) #define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
......
...@@ -111,17 +111,10 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width) ...@@ -111,17 +111,10 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width)
return 0; return 0;
} }
static u32 pxav2_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
return clk_get_rate(pltfm_host->clk);
}
static struct sdhci_ops pxav2_sdhci_ops = { static struct sdhci_ops pxav2_sdhci_ops = {
.get_max_clock = pxav2_get_max_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock,
.platform_reset_exit = pxav2_set_private_registers, .platform_reset_exit = pxav2_set_private_registers,
.platform_8bit_width = pxav2_mmc_set_width, .platform_bus_width = pxav2_mmc_set_width,
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
......
...@@ -32,10 +32,14 @@ ...@@ -32,10 +32,14 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include "sdhci.h" #include "sdhci.h"
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#define PXAV3_RPM_DELAY_MS 50
#define SD_CLOCK_BURST_SIZE_SETUP 0x10A #define SD_CLOCK_BURST_SIZE_SETUP 0x10A
#define SDCLK_SEL 0x100 #define SDCLK_SEL 0x100
#define SDCLK_DELAY_SHIFT 9 #define SDCLK_DELAY_SHIFT 9
...@@ -163,18 +167,11 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) ...@@ -163,18 +167,11 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
return 0; return 0;
} }
static u32 pxav3_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
return clk_get_rate(pltfm_host->clk);
}
static struct sdhci_ops pxav3_sdhci_ops = { static struct sdhci_ops pxav3_sdhci_ops = {
.platform_reset_exit = pxav3_set_private_registers, .platform_reset_exit = pxav3_set_private_registers,
.set_uhs_signaling = pxav3_set_uhs_signaling, .set_uhs_signaling = pxav3_set_uhs_signaling,
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
.get_max_clock = pxav3_get_max_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock,
}; };
#ifdef CONFIG_OF #ifdef CONFIG_OF
...@@ -303,20 +300,37 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) ...@@ -303,20 +300,37 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
sdhci_get_of_property(pdev); sdhci_get_of_property(pdev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
pm_runtime_use_autosuspend(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1);
pm_runtime_get_noresume(&pdev->dev);
ret = sdhci_add_host(host); ret = sdhci_add_host(host);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to add host\n"); dev_err(&pdev->dev, "failed to add host\n");
pm_runtime_forbid(&pdev->dev);
pm_runtime_disable(&pdev->dev);
goto err_add_host; goto err_add_host;
} }
platform_set_drvdata(pdev, host); platform_set_drvdata(pdev, host);
if (pdata->pm_caps & MMC_PM_KEEP_POWER) {
device_init_wakeup(&pdev->dev, 1);
host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ;
} else {
device_init_wakeup(&pdev->dev, 0);
}
pm_runtime_put_autosuspend(&pdev->dev);
return 0; return 0;
err_add_host: err_add_host:
clk_disable_unprepare(clk); clk_disable_unprepare(clk);
clk_put(clk); clk_put(clk);
mmc_gpio_free_cd(host->mmc);
err_cd_req: err_cd_req:
err_clk_get: err_clk_get:
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
...@@ -329,16 +343,14 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) ...@@ -329,16 +343,14 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_pxa *pxa = pltfm_host->priv; struct sdhci_pxa *pxa = pltfm_host->priv;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
pm_runtime_get_sync(&pdev->dev);
sdhci_remove_host(host, 1); sdhci_remove_host(host, 1);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk); clk_put(pltfm_host->clk);
if (gpio_is_valid(pdata->ext_cd_gpio))
mmc_gpio_free_cd(host->mmc);
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
kfree(pxa); kfree(pxa);
...@@ -347,6 +359,83 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) ...@@ -347,6 +359,83 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM_SLEEP
static int sdhci_pxav3_suspend(struct device *dev)
{
int ret;
struct sdhci_host *host = dev_get_drvdata(dev);
pm_runtime_get_sync(dev);
ret = sdhci_suspend_host(host);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
static int sdhci_pxav3_resume(struct device *dev)
{
int ret;
struct sdhci_host *host = dev_get_drvdata(dev);
pm_runtime_get_sync(dev);
ret = sdhci_resume_host(host);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
#endif
#ifdef CONFIG_PM_RUNTIME
static int sdhci_pxav3_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
unsigned long flags;
if (pltfm_host->clk) {
spin_lock_irqsave(&host->lock, flags);
host->runtime_suspended = true;
spin_unlock_irqrestore(&host->lock, flags);
clk_disable_unprepare(pltfm_host->clk);
}
return 0;
}
static int sdhci_pxav3_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
unsigned long flags;
if (pltfm_host->clk) {
clk_prepare_enable(pltfm_host->clk);
spin_lock_irqsave(&host->lock, flags);
host->runtime_suspended = false;
spin_unlock_irqrestore(&host->lock, flags);
}
return 0;
}
#endif
#ifdef CONFIG_PM
static const struct dev_pm_ops sdhci_pxav3_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume)
SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend,
sdhci_pxav3_runtime_resume, NULL)
};
#define SDHCI_PXAV3_PMOPS (&sdhci_pxav3_pmops)
#else
#define SDHCI_PXAV3_PMOPS NULL
#endif
static struct platform_driver sdhci_pxav3_driver = { static struct platform_driver sdhci_pxav3_driver = {
.driver = { .driver = {
.name = "sdhci-pxav3", .name = "sdhci-pxav3",
...@@ -354,7 +443,7 @@ static struct platform_driver sdhci_pxav3_driver = { ...@@ -354,7 +443,7 @@ static struct platform_driver sdhci_pxav3_driver = {
.of_match_table = sdhci_pxav3_of_match, .of_match_table = sdhci_pxav3_of_match,
#endif #endif
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = SDHCI_PLTFM_PMOPS, .pm = SDHCI_PXAV3_PMOPS,
}, },
.probe = sdhci_pxav3_probe, .probe = sdhci_pxav3_probe,
.remove = sdhci_pxav3_remove, .remove = sdhci_pxav3_remove,
......
...@@ -332,14 +332,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) ...@@ -332,14 +332,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
} }
/** /**
* sdhci_s3c_platform_8bit_width - support 8bit buswidth * sdhci_s3c_platform_bus_width - support 8bit buswidth
* @host: The SDHCI host being queried * @host: The SDHCI host being queried
* @width: MMC_BUS_WIDTH_ macro for the bus width being requested * @width: MMC_BUS_WIDTH_ macro for the bus width being requested
* *
* We have 8-bit width support but is not a v3 controller. * We have 8-bit width support but is not a v3 controller.
* So we add platform_8bit_width() and support 8bit width. * So we add platform_bus_width() and support 8bit width.
*/ */
static int sdhci_s3c_platform_8bit_width(struct sdhci_host *host, int width) static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width)
{ {
u8 ctrl; u8 ctrl;
...@@ -369,7 +369,7 @@ static struct sdhci_ops sdhci_s3c_ops = { ...@@ -369,7 +369,7 @@ static struct sdhci_ops sdhci_s3c_ops = {
.get_max_clock = sdhci_s3c_get_max_clk, .get_max_clock = sdhci_s3c_get_max_clk,
.set_clock = sdhci_s3c_set_clock, .set_clock = sdhci_s3c_set_clock,
.get_min_clock = sdhci_s3c_get_min_clock, .get_min_clock = sdhci_s3c_get_min_clock,
.platform_8bit_width = sdhci_s3c_platform_8bit_width, .platform_bus_width = sdhci_s3c_platform_bus_width,
}; };
static void sdhci_s3c_notify_change(struct platform_device *dev, int state) static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
......
...@@ -27,8 +27,6 @@ ...@@ -27,8 +27,6 @@
#include <asm/gpio.h> #include <asm/gpio.h>
#include <linux/platform_data/mmc-sdhci-tegra.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
/* Tegra SDHOST controller vendor register definitions */ /* Tegra SDHOST controller vendor register definitions */
...@@ -45,8 +43,11 @@ struct sdhci_tegra_soc_data { ...@@ -45,8 +43,11 @@ struct sdhci_tegra_soc_data {
}; };
struct sdhci_tegra { struct sdhci_tegra {
const struct tegra_sdhci_platform_data *plat;
const struct sdhci_tegra_soc_data *soc_data; const struct sdhci_tegra_soc_data *soc_data;
int cd_gpio;
int wp_gpio;
int power_gpio;
int is_8bit;
}; };
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
...@@ -108,12 +109,11 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host) ...@@ -108,12 +109,11 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv; struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
if (!gpio_is_valid(plat->wp_gpio)) if (!gpio_is_valid(tegra_host->wp_gpio))
return -1; return -1;
return gpio_get_value(plat->wp_gpio); return gpio_get_value(tegra_host->wp_gpio);
} }
static irqreturn_t carddetect_irq(int irq, void *data) static irqreturn_t carddetect_irq(int irq, void *data)
...@@ -143,15 +143,14 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask) ...@@ -143,15 +143,14 @@ static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
} }
} }
static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv; struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
u32 ctrl; u32 ctrl;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) { if (tegra_host->is_8bit && bus_width == MMC_BUS_WIDTH_8) {
ctrl &= ~SDHCI_CTRL_4BITBUS; ctrl &= ~SDHCI_CTRL_4BITBUS;
ctrl |= SDHCI_CTRL_8BITBUS; ctrl |= SDHCI_CTRL_8BITBUS;
} else { } else {
...@@ -170,7 +169,7 @@ static struct sdhci_ops tegra_sdhci_ops = { ...@@ -170,7 +169,7 @@ static struct sdhci_ops tegra_sdhci_ops = {
.read_l = tegra_sdhci_readl, .read_l = tegra_sdhci_readl,
.read_w = tegra_sdhci_readw, .read_w = tegra_sdhci_readw,
.write_l = tegra_sdhci_writel, .write_l = tegra_sdhci_writel,
.platform_8bit_width = tegra_sdhci_8bit, .platform_bus_width = tegra_sdhci_buswidth,
.platform_reset_exit = tegra_sdhci_reset_exit, .platform_reset_exit = tegra_sdhci_reset_exit,
}; };
...@@ -217,31 +216,19 @@ static const struct of_device_id sdhci_tegra_dt_match[] = { ...@@ -217,31 +216,19 @@ static const struct of_device_id sdhci_tegra_dt_match[] = {
}; };
MODULE_DEVICE_TABLE(of, sdhci_dt_ids); MODULE_DEVICE_TABLE(of, sdhci_dt_ids);
static struct tegra_sdhci_platform_data *sdhci_tegra_dt_parse_pdata( static void sdhci_tegra_parse_dt(struct device *dev,
struct platform_device *pdev) struct sdhci_tegra *tegra_host)
{ {
struct tegra_sdhci_platform_data *plat; struct device_node *np = dev->of_node;
struct device_node *np = pdev->dev.of_node;
u32 bus_width; u32 bus_width;
if (!np) tegra_host->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
return NULL; tegra_host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
if (!plat) {
dev_err(&pdev->dev, "Can't allocate platform data\n");
return NULL;
}
plat->cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
plat->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
plat->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
if (of_property_read_u32(np, "bus-width", &bus_width) == 0 && if (of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
bus_width == 8) bus_width == 8)
plat->is_8bit = 1; tegra_host->is_8bit = 1;
return plat;
} }
static int sdhci_tegra_probe(struct platform_device *pdev) static int sdhci_tegra_probe(struct platform_device *pdev)
...@@ -250,7 +237,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev) ...@@ -250,7 +237,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
const struct sdhci_tegra_soc_data *soc_data; const struct sdhci_tegra_soc_data *soc_data;
struct sdhci_host *host; struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host; struct sdhci_pltfm_host *pltfm_host;
struct tegra_sdhci_platform_data *plat;
struct sdhci_tegra *tegra_host; struct sdhci_tegra *tegra_host;
struct clk *clk; struct clk *clk;
int rc; int rc;
...@@ -263,52 +249,40 @@ static int sdhci_tegra_probe(struct platform_device *pdev) ...@@ -263,52 +249,40 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
host = sdhci_pltfm_init(pdev, soc_data->pdata); host = sdhci_pltfm_init(pdev, soc_data->pdata);
if (IS_ERR(host)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);
pltfm_host = sdhci_priv(host); pltfm_host = sdhci_priv(host);
plat = pdev->dev.platform_data;
if (plat == NULL)
plat = sdhci_tegra_dt_parse_pdata(pdev);
if (plat == NULL) {
dev_err(mmc_dev(host->mmc), "missing platform data\n");
rc = -ENXIO;
goto err_no_plat;
}
tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL); tegra_host = devm_kzalloc(&pdev->dev, sizeof(*tegra_host), GFP_KERNEL);
if (!tegra_host) { if (!tegra_host) {
dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n"); dev_err(mmc_dev(host->mmc), "failed to allocate tegra_host\n");
rc = -ENOMEM; rc = -ENOMEM;
goto err_no_plat; goto err_alloc_tegra_host;
} }
tegra_host->plat = plat;
tegra_host->soc_data = soc_data; tegra_host->soc_data = soc_data;
pltfm_host->priv = tegra_host; pltfm_host->priv = tegra_host;
if (gpio_is_valid(plat->power_gpio)) { sdhci_tegra_parse_dt(&pdev->dev, tegra_host);
rc = gpio_request(plat->power_gpio, "sdhci_power");
if (gpio_is_valid(tegra_host->power_gpio)) {
rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
if (rc) { if (rc) {
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"failed to allocate power gpio\n"); "failed to allocate power gpio\n");
goto err_power_req; goto err_power_req;
} }
gpio_direction_output(plat->power_gpio, 1); gpio_direction_output(tegra_host->power_gpio, 1);
} }
if (gpio_is_valid(plat->cd_gpio)) { if (gpio_is_valid(tegra_host->cd_gpio)) {
rc = gpio_request(plat->cd_gpio, "sdhci_cd"); rc = gpio_request(tegra_host->cd_gpio, "sdhci_cd");
if (rc) { if (rc) {
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"failed to allocate cd gpio\n"); "failed to allocate cd gpio\n");
goto err_cd_req; goto err_cd_req;
} }
gpio_direction_input(plat->cd_gpio); gpio_direction_input(tegra_host->cd_gpio);
rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq, rc = request_irq(gpio_to_irq(tegra_host->cd_gpio),
carddetect_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
mmc_hostname(host->mmc), host); mmc_hostname(host->mmc), host);
...@@ -319,14 +293,14 @@ static int sdhci_tegra_probe(struct platform_device *pdev) ...@@ -319,14 +293,14 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
} }
if (gpio_is_valid(plat->wp_gpio)) { if (gpio_is_valid(tegra_host->wp_gpio)) {
rc = gpio_request(plat->wp_gpio, "sdhci_wp"); rc = gpio_request(tegra_host->wp_gpio, "sdhci_wp");
if (rc) { if (rc) {
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"failed to allocate wp gpio\n"); "failed to allocate wp gpio\n");
goto err_wp_req; goto err_wp_req;
} }
gpio_direction_input(plat->wp_gpio); gpio_direction_input(tegra_host->wp_gpio);
} }
clk = clk_get(mmc_dev(host->mmc), NULL); clk = clk_get(mmc_dev(host->mmc), NULL);
...@@ -338,9 +312,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) ...@@ -338,9 +312,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
clk_prepare_enable(clk); clk_prepare_enable(clk);
pltfm_host->clk = clk; pltfm_host->clk = clk;
host->mmc->pm_caps = plat->pm_flags; if (tegra_host->is_8bit)
if (plat->is_8bit)
host->mmc->caps |= MMC_CAP_8_BIT_DATA; host->mmc->caps |= MMC_CAP_8_BIT_DATA;
rc = sdhci_add_host(host); rc = sdhci_add_host(host);
...@@ -353,19 +325,19 @@ static int sdhci_tegra_probe(struct platform_device *pdev) ...@@ -353,19 +325,19 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk); clk_put(pltfm_host->clk);
err_clk_get: err_clk_get:
if (gpio_is_valid(plat->wp_gpio)) if (gpio_is_valid(tegra_host->wp_gpio))
gpio_free(plat->wp_gpio); gpio_free(tegra_host->wp_gpio);
err_wp_req: err_wp_req:
if (gpio_is_valid(plat->cd_gpio)) if (gpio_is_valid(tegra_host->cd_gpio))
free_irq(gpio_to_irq(plat->cd_gpio), host); free_irq(gpio_to_irq(tegra_host->cd_gpio), host);
err_cd_irq_req: err_cd_irq_req:
if (gpio_is_valid(plat->cd_gpio)) if (gpio_is_valid(tegra_host->cd_gpio))
gpio_free(plat->cd_gpio); gpio_free(tegra_host->cd_gpio);
err_cd_req: err_cd_req:
if (gpio_is_valid(plat->power_gpio)) if (gpio_is_valid(tegra_host->power_gpio))
gpio_free(plat->power_gpio); gpio_free(tegra_host->power_gpio);
err_power_req: err_power_req:
err_no_plat: err_alloc_tegra_host:
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
return rc; return rc;
} }
...@@ -375,21 +347,20 @@ static int sdhci_tegra_remove(struct platform_device *pdev) ...@@ -375,21 +347,20 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_tegra *tegra_host = pltfm_host->priv; struct sdhci_tegra *tegra_host = pltfm_host->priv;
const struct tegra_sdhci_platform_data *plat = tegra_host->plat;
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead); sdhci_remove_host(host, dead);
if (gpio_is_valid(plat->wp_gpio)) if (gpio_is_valid(tegra_host->wp_gpio))
gpio_free(plat->wp_gpio); gpio_free(tegra_host->wp_gpio);
if (gpio_is_valid(plat->cd_gpio)) { if (gpio_is_valid(tegra_host->cd_gpio)) {
free_irq(gpio_to_irq(plat->cd_gpio), host); free_irq(gpio_to_irq(tegra_host->cd_gpio), host);
gpio_free(plat->cd_gpio); gpio_free(tegra_host->cd_gpio);
} }
if (gpio_is_valid(plat->power_gpio)) if (gpio_is_valid(tegra_host->power_gpio))
gpio_free(plat->power_gpio); gpio_free(tegra_host->power_gpio);
clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk); clk_put(pltfm_host->clk);
......
This diff is collapsed.
...@@ -229,6 +229,18 @@ ...@@ -229,6 +229,18 @@
/* 60-FB reserved */ /* 60-FB reserved */
#define SDHCI_PRESET_FOR_SDR12 0x66
#define SDHCI_PRESET_FOR_SDR25 0x68
#define SDHCI_PRESET_FOR_SDR50 0x6A
#define SDHCI_PRESET_FOR_SDR104 0x6C
#define SDHCI_PRESET_FOR_DDR50 0x6E
#define SDHCI_PRESET_DRV_MASK 0xC000
#define SDHCI_PRESET_DRV_SHIFT 14
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
#define SDHCI_PRESET_CLKGEN_SEL_SHIFT 10
#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF
#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0
#define SDHCI_SLOT_INT_STATUS 0xFC #define SDHCI_SLOT_INT_STATUS 0xFC
#define SDHCI_HOST_VERSION 0xFE #define SDHCI_HOST_VERSION 0xFE
...@@ -269,7 +281,7 @@ struct sdhci_ops { ...@@ -269,7 +281,7 @@ struct sdhci_ops {
unsigned int (*get_max_clock)(struct sdhci_host *host); unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_min_clock)(struct sdhci_host *host);
unsigned int (*get_timeout_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host);
int (*platform_8bit_width)(struct sdhci_host *host, int (*platform_bus_width)(struct sdhci_host *host,
int width); int width);
void (*platform_send_init_74_clocks)(struct sdhci_host *host, void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode); u8 power_mode);
......
This diff is collapsed.
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/sh_mobile_sdhi.h> #include <linux/mmc/sh_mobile_sdhi.h>
...@@ -32,6 +33,16 @@ ...@@ -32,6 +33,16 @@
#include "tmio_mmc.h" #include "tmio_mmc.h"
struct sh_mobile_sdhi_of_data {
unsigned long tmio_flags;
};
static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
{
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
},
};
struct sh_mobile_sdhi { struct sh_mobile_sdhi {
struct clk *clk; struct clk *clk;
struct tmio_mmc_data mmc_data; struct tmio_mmc_data mmc_data;
...@@ -117,8 +128,18 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = { ...@@ -117,8 +128,18 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = {
.cd_wakeup = sh_mobile_sdhi_cd_wakeup, .cd_wakeup = sh_mobile_sdhi_cd_wakeup,
}; };
static const struct of_device_id sh_mobile_sdhi_of_match[] = {
{ .compatible = "renesas,shmobile-sdhi" },
{ .compatible = "renesas,sh7372-sdhi" },
{ .compatible = "renesas,r8a7740-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], },
{},
};
MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
static int sh_mobile_sdhi_probe(struct platform_device *pdev) static int sh_mobile_sdhi_probe(struct platform_device *pdev)
{ {
const struct of_device_id *of_id =
of_match_device(sh_mobile_sdhi_of_match, &pdev->dev);
struct sh_mobile_sdhi *priv; struct sh_mobile_sdhi *priv;
struct tmio_mmc_data *mmc_data; struct tmio_mmc_data *mmc_data;
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
...@@ -126,7 +147,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -126,7 +147,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
int irq, ret, i = 0; int irq, ret, i = 0;
bool multiplexed_isr = true; bool multiplexed_isr = true;
priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL); priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
if (priv == NULL) { if (priv == NULL) {
dev_err(&pdev->dev, "kzalloc failed\n"); dev_err(&pdev->dev, "kzalloc failed\n");
return -ENOMEM; return -ENOMEM;
...@@ -135,15 +156,14 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -135,15 +156,14 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data = &priv->mmc_data; mmc_data = &priv->mmc_data;
if (p) { if (p) {
p->pdata = mmc_data;
if (p->init) { if (p->init) {
ret = p->init(pdev, &sdhi_ops); ret = p->init(pdev, &sdhi_ops);
if (ret) if (ret)
goto einit; return ret;
} }
} }
priv->clk = clk_get(&pdev->dev, NULL); priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk)) { if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk); ret = PTR_ERR(priv->clk);
dev_err(&pdev->dev, "cannot get clock: %d\n", ret); dev_err(&pdev->dev, "cannot get clock: %d\n", ret);
...@@ -153,10 +173,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -153,10 +173,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_enable = sh_mobile_sdhi_clk_enable;
mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
if (p) { if (p) {
mmc_data->flags = p->tmio_flags; mmc_data->flags = p->tmio_flags;
if (mmc_data->flags & TMIO_MMC_HAS_IDLE_WAIT)
mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
mmc_data->ocr_mask = p->tmio_ocr_mask; mmc_data->ocr_mask = p->tmio_ocr_mask;
mmc_data->capabilities |= p->tmio_caps; mmc_data->capabilities |= p->tmio_caps;
mmc_data->capabilities2 |= p->tmio_caps2; mmc_data->capabilities2 |= p->tmio_caps2;
...@@ -187,6 +206,11 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -187,6 +206,11 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
*/ */
mmc_data->flags |= TMIO_MMC_SDIO_IRQ; mmc_data->flags |= TMIO_MMC_SDIO_IRQ;
if (of_id && of_id->data) {
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
mmc_data->flags |= of_data->tmio_flags;
}
ret = tmio_mmc_host_probe(&host, pdev, mmc_data); ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
if (ret < 0) if (ret < 0)
goto eprobe; goto eprobe;
...@@ -199,33 +223,33 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -199,33 +223,33 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT); irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
if (irq >= 0) { if (irq >= 0) {
multiplexed_isr = false; multiplexed_isr = false;
ret = request_irq(irq, tmio_mmc_card_detect_irq, 0, ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_card_detect_irq, 0,
dev_name(&pdev->dev), host); dev_name(&pdev->dev), host);
if (ret) if (ret)
goto eirq_card_detect; goto eirq;
} }
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO); irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
if (irq >= 0) { if (irq >= 0) {
multiplexed_isr = false; multiplexed_isr = false;
ret = request_irq(irq, tmio_mmc_sdio_irq, 0, ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdio_irq, 0,
dev_name(&pdev->dev), host); dev_name(&pdev->dev), host);
if (ret) if (ret)
goto eirq_sdio; goto eirq;
} }
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD); irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDCARD);
if (irq >= 0) { if (irq >= 0) {
multiplexed_isr = false; multiplexed_isr = false;
ret = request_irq(irq, tmio_mmc_sdcard_irq, 0, ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_sdcard_irq, 0,
dev_name(&pdev->dev), host); dev_name(&pdev->dev), host);
if (ret) if (ret)
goto eirq_sdcard; goto eirq;
} else if (!multiplexed_isr) { } else if (!multiplexed_isr) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Principal SD-card IRQ is missing among named interrupts\n"); "Principal SD-card IRQ is missing among named interrupts\n");
ret = irq; ret = irq;
goto eirq_sdcard; goto eirq;
} }
if (multiplexed_isr) { if (multiplexed_isr) {
...@@ -234,15 +258,15 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -234,15 +258,15 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
if (irq < 0) if (irq < 0)
break; break;
i++; i++;
ret = request_irq(irq, tmio_mmc_irq, 0, ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0,
dev_name(&pdev->dev), host); dev_name(&pdev->dev), host);
if (ret) if (ret)
goto eirq_multiplexed; goto eirq;
} }
/* There must be at least one IRQ source */ /* There must be at least one IRQ source */
if (!i) if (!i)
goto eirq_multiplexed; goto eirq;
} }
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n", dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
...@@ -252,28 +276,12 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) ...@@ -252,28 +276,12 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
return ret; return ret;
eirq_multiplexed: eirq:
while (i--) {
irq = platform_get_irq(pdev, i);
free_irq(irq, host);
}
eirq_sdcard:
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_SDIO);
if (irq >= 0)
free_irq(irq, host);
eirq_sdio:
irq = platform_get_irq_byname(pdev, SH_MOBILE_SDHI_IRQ_CARD_DETECT);
if (irq >= 0)
free_irq(irq, host);
eirq_card_detect:
tmio_mmc_host_remove(host); tmio_mmc_host_remove(host);
eprobe: eprobe:
clk_put(priv->clk);
eclkget: eclkget:
if (p && p->cleanup) if (p && p->cleanup)
p->cleanup(pdev); p->cleanup(pdev);
einit:
kfree(priv);
return ret; return ret;
} }
...@@ -281,29 +289,13 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) ...@@ -281,29 +289,13 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
{ {
struct mmc_host *mmc = platform_get_drvdata(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev);
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
int i = 0, irq;
if (p)
p->pdata = NULL;
tmio_mmc_host_remove(host); tmio_mmc_host_remove(host);
while (1) {
irq = platform_get_irq(pdev, i++);
if (irq < 0)
break;
free_irq(irq, host);
}
clk_put(priv->clk);
if (p && p->cleanup) if (p && p->cleanup)
p->cleanup(pdev); p->cleanup(pdev);
kfree(priv);
return 0; return 0;
} }
...@@ -314,12 +306,6 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { ...@@ -314,12 +306,6 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
.runtime_resume = tmio_mmc_host_runtime_resume, .runtime_resume = tmio_mmc_host_runtime_resume,
}; };
static const struct of_device_id sh_mobile_sdhi_of_match[] = {
{ .compatible = "renesas,shmobile-sdhi" },
{ }
};
MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
static struct platform_driver sh_mobile_sdhi_driver = { static struct platform_driver sh_mobile_sdhi_driver = {
.driver = { .driver = {
.name = "sh_mobile_sdhi", .name = "sh_mobile_sdhi",
......
This diff is collapsed.
...@@ -1012,7 +1012,7 @@ static const struct dev_pm_ops wmt_mci_pm = { ...@@ -1012,7 +1012,7 @@ static const struct dev_pm_ops wmt_mci_pm = {
static struct platform_driver wmt_mci_driver = { static struct platform_driver wmt_mci_driver = {
.probe = wmt_mci_probe, .probe = wmt_mci_probe,
.remove = __exit_p(wmt_mci_remove), .remove = wmt_mci_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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