MMC highlights for 3.6:
Core: - Rename cd-gpio to slot-gpio and extend it to support more slot GPIO functions, such as write-protect. - Add a function to get regulators (Vdd and Vccq) for a host. Drivers: - sdhci-pxav2, sdhci-pxav3: Add device tree support. - sdhi: Add device tree support. - sh_mmcif: Add support for regulators, device tree, slot-gpio. - tmio: Add regulator support, use slot-gpio. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJQDGaKAAoJEHNBYZ7TNxYMskYQAI0RsnncLyT8DXsnZw5jjNf2 sB7h3Sc2ExIQUIrqwxaRbzbAyVCDBAK4+FyBK7iN3KqyaL6G6mWAzXInxfw0hkpy kV66hIXzzTbGryvndlPzwRcswJrPKWwwTMOx68Cyw12UIPn1ZK7LRGYTeUOEBkYf QNGYo8Jcq+Kr+FnR6Ios5NH1t9EghCUDVquTzqOalhovN8QeMD2FxbgbegSS3Tu0 qqle1eCCd5uaDwC13Nf0kb8qvlXi164UlT367T+C0QXKoQiaB7/K6tr2BgzQBNgI chAi3JEGU70WnQ2LavfWxO/F7nEEp+In3g1uMb6kj1yWhBq2yJV7seMCCEt6gzqe djFm4JfRgEPBSUnUD0G6vP8SVJNHr/L6WXyGgtFyOa+kFVNF2rhN1Y+rzBoLdxt3 R0sZp5lhSQtuxT88GfEqG1bqOkGbiWWvuzQqLn1Z/32pwY/6/ZvN9K52xr4pY4Tn xZ7vNij8Vho0MaUSueVrrLHoTcwYqZjujMlDm0Uyu3eRHSH7ON7DC9rwPJ2PWXxR vxKgatXSIMpL9COQfoB9LWlRPrZLsKmt0WcBCRYS49zO6bkWmsZkzyUnJ0PuBIl2 NojLQNbaAAwtfhGmmxhaCNT9QeBfNg2U0jhCct0wLe3eCw0V1PFvb8HBlPBqrttc A1sqnOt5LHBxgh2uRB5a =OBTA -----END PGP SIGNATURE----- Merge tag 'mmc-merge-for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc Pull MMC updates from Chris Ball: "MMC highlights for 3.6: Core: - Rename cd-gpio to slot-gpio and extend it to support more slot GPIO functions, such as write-protect. - Add a function to get regulators (Vdd and Vccq) for a host. Drivers: - sdhci-pxav2, sdhci-pxav3: Add device tree support. - sdhi: Add device tree support. - sh_mmcif: Add support for regulators, device tree, slot-gpio. - tmio: Add regulator support, use slot-gpio." * tag 'mmc-merge-for-3.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (62 commits) mmc: sdhci-dove: Prepare for common clock framework mmc: sdhci-dove: Add SDHCI_QUIRK_NO_HISPD_BIT mmc: omap_hsmmc: ensure probe returns error upon resource failure mmc: mxs-mmc: Add wp-inverted property mmc: esdhc: Fix DMA_MASK to not break mx25 DMA access mmc: core: reset signal voltage on power up mmc: sd: Fix sd current limit setting mmc: omap_hsmmc: add clk_prepare and clk_unprepare mmc: sdhci: When a UHS switch fails, cycle power if regulator is used mmc: atmel-mci: modify CLKDIV displaying in debugfs mmc: atmel-mci: fix incorrect setting of host->data to NULL mmc: sdhci: poll for card even when card is logically unremovable mmc: sdhci: Introduce new flag SDHCI_USING_RETUNING_TIMER mmc: sdio: Change pr_warning to pr_warn_ratelimited mmc: core: Simplify and fix for SD switch processing mmc: sdhci: restore host settings when card is removed mmc: sdhci: fix incorrect command used in tuning mmc: sdhci-pci: CaFe has broken card detection mmc: sdhci: Report failure reasons for all cases in sdhci_add_host() mmc: s3cmci: Convert s3cmci driver to gpiolib API ...
This commit is contained in:
Коммит
a6be1fcbc5
|
@ -3,21 +3,22 @@
|
|||
The Enhanced Secure Digital Host Controller provides an interface
|
||||
for MMC, SD, and SDIO types of memory cards.
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the sdhci-esdhc driver.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be
|
||||
"fsl,<chip>-esdhc", "fsl,esdhc"
|
||||
- reg : should contain eSDHC registers location and length.
|
||||
- interrupts : should contain eSDHC interrupt.
|
||||
- interrupt-parent : interrupt source phandle.
|
||||
- clock-frequency : specifies eSDHC base clock frequency.
|
||||
- sdhci,wp-inverted : (optional) specifies that eSDHC controller
|
||||
reports inverted write-protect state; New devices should use
|
||||
the generic "wp-inverted" property.
|
||||
- sdhci,1-bit-only : (optional) specifies that a controller can
|
||||
only handle 1-bit data transfers. New devices should use the
|
||||
generic "bus-width = <1>" property.
|
||||
- sdhci,auto-cmd12: (optional) specifies that a controller can
|
||||
only handle auto CMD12.
|
||||
|
||||
Optional properties:
|
||||
- sdhci,wp-inverted : specifies that eSDHC controller reports
|
||||
inverted write-protect state; New devices should use the generic
|
||||
"wp-inverted" property.
|
||||
- sdhci,1-bit-only : specifies that a controller can only handle
|
||||
1-bit data transfers. New devices should use the generic
|
||||
"bus-width = <1>" property.
|
||||
- sdhci,auto-cmd12: specifies that a controller can only handle auto
|
||||
CMD12.
|
||||
|
||||
Example:
|
||||
|
||||
|
|
|
@ -3,17 +3,15 @@
|
|||
The Enhanced Secure Digital Host Controller on Freescale i.MX family
|
||||
provides an interface for MMC, SD, and SDIO types of memory cards.
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the sdhci-esdhc-imx driver.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "fsl,<chip>-esdhc"
|
||||
- reg : Should contain eSDHC registers location and length
|
||||
- interrupts : Should contain eSDHC interrupt
|
||||
|
||||
Optional properties:
|
||||
- non-removable : Indicate the card is wired to host permanently
|
||||
- fsl,cd-internal : Indicate to use controller internal card detection
|
||||
- fsl,wp-internal : Indicate to use controller internal write protection
|
||||
- cd-gpios : Specify GPIOs for card detection
|
||||
- wp-gpios : Specify GPIOs for write protection
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
MMC/SD/SDIO slot directly connected to a SPI bus
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the mmc_spi driver.
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "mmc-spi-slot".
|
||||
- reg : should specify SPI address (chip-select number).
|
||||
- spi-max-frequency : maximum frequency for this device (Hz).
|
||||
- voltage-ranges : two cells are required, first cell specifies minimum
|
||||
slot voltage (mV), second cell specifies maximum slot voltage (mV).
|
||||
|
@ -11,8 +12,7 @@ Required properties:
|
|||
Optional properties:
|
||||
- gpios : may specify GPIOs in this order: Card-Detect GPIO,
|
||||
Write-Protect GPIO. Note that this does not follow the
|
||||
binding from mmc.txt, for historic reasons.
|
||||
- interrupts : the interrupt of a card detect interrupt.
|
||||
binding from mmc.txt, for historical reasons.
|
||||
- interrupt-parent : the phandle for the interrupt controller that
|
||||
services interrupts for this device.
|
||||
|
||||
|
|
|
@ -2,13 +2,17 @@ These properties are common to multiple MMC host controllers. Any host
|
|||
that requires the respective functionality should implement them using
|
||||
these definitions.
|
||||
|
||||
Interpreted by the OF core:
|
||||
- reg: Registers location and length.
|
||||
- interrupts: Interrupts used by the MMC controller.
|
||||
|
||||
Required properties:
|
||||
- bus-width: Number of data lines, can be <1>, <4>, or <8>
|
||||
|
||||
Optional properties:
|
||||
- cd-gpios: Specify GPIOs for card detection, see gpio binding
|
||||
- wp-gpios: Specify GPIOs for write protection, see gpio binding
|
||||
- cd-inverted: when present, polarity on the wp gpio line is inverted
|
||||
- cd-inverted: when present, polarity on the cd gpio line is inverted
|
||||
- wp-inverted: when present, polarity on the wp gpio line is inverted
|
||||
- non-removable: non-removable slot (like eMMC)
|
||||
- max-frequency: maximum operating clock frequency
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
* ARM PrimeCell MultiMedia Card Interface (MMCI) PL180/1
|
||||
|
||||
The ARM PrimeCell MMCI PL180 and PL181 provides and interface for
|
||||
The ARM PrimeCell MMCI PL180 and PL181 provides an interface for
|
||||
reading and writing to MultiMedia and SD cards alike.
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the mmci driver.
|
||||
|
||||
Required properties:
|
||||
- compatible : contains "arm,pl18x", "arm,primecell".
|
||||
- reg : contains pl18x registers and length.
|
||||
- interrupts : contains the device IRQ(s).
|
||||
- arm,primecell-periphid : contains the PrimeCell Peripheral ID.
|
||||
|
||||
Optional properties:
|
||||
- wp-gpios : contains any write protect (ro) gpios
|
||||
- cd-gpios : contains any card detection gpios
|
||||
- cd-inverted : indicates whether the cd gpio is inverted
|
||||
- max-frequency : contains the maximum operating frequency
|
||||
- bus-width : number of data lines, can be <1>, <4>, or <8>
|
||||
- mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable
|
||||
- mmc-cap-sd-highspeed : indicates whether SD is high speed capable
|
||||
|
|
|
@ -3,16 +3,14 @@
|
|||
The Freescale MXS Synchronous Serial Ports (SSP) can act as a MMC controller
|
||||
to support MMC, SD, and SDIO types of memory cards.
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the mxsmmc driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,<chip>-mmc". The supported chips include
|
||||
imx23 and imx28.
|
||||
- reg: Should contain registers location and length
|
||||
- interrupts: Should contain ERROR and DMA interrupts
|
||||
- fsl,ssp-dma-channel: APBH DMA channel for the SSP
|
||||
- bus-width: Number of data lines, can be <1>, <4>, or <8>
|
||||
|
||||
Optional properties:
|
||||
- wp-gpios: Specify GPIOs for write protection
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
|
@ -3,15 +3,13 @@
|
|||
This controller on Tegra family SoCs provides an interface for MMC, SD,
|
||||
and SDIO types of memory cards.
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the sdhci-tegra driver.
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "nvidia,<chip>-sdhci"
|
||||
- reg : Should contain SD/MMC registers location and length
|
||||
- interrupts : Should contain SD/MMC interrupt
|
||||
- bus-width : Number of data lines, can be <1>, <4>, or <8>
|
||||
|
||||
Optional properties:
|
||||
- cd-gpios : Specify GPIOs for card detection
|
||||
- wp-gpios : Specify GPIOs for write protection
|
||||
- power-gpios : Specify GPIOs for power control
|
||||
|
||||
Example:
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
* Marvell sdhci-pxa v2/v3 controller
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-pxav2 and sdhci-pxav3 drivers.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "mrvl,pxav2-mmc" or "mrvl,pxav3-mmc".
|
||||
|
||||
Optional properties:
|
||||
- mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning.
|
||||
|
||||
Example:
|
||||
|
||||
sdhci@d4280800 {
|
||||
compatible = "mrvl,pxav3-mmc";
|
||||
reg = <0xd4280800 0x800>;
|
||||
bus-width = <8>;
|
||||
interrupts = <27>;
|
||||
non-removable;
|
||||
mrvl,clk-delay-cycles = <31>;
|
||||
};
|
|
@ -3,21 +3,20 @@
|
|||
The Highspeed MMC Host Controller on TI OMAP family
|
||||
provides an interface for MMC, SD, and SDIO types of memory cards.
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the omap_hsmmc driver.
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
Should be "ti,omap2-hsmmc", for OMAP2 controllers
|
||||
Should be "ti,omap3-hsmmc", for OMAP3 controllers
|
||||
Should be "ti,omap4-hsmmc", for OMAP4 controllers
|
||||
- ti,hwmods: Must be "mmc<n>", n is controller instance starting 1
|
||||
- reg : should contain hsmmc registers location and length
|
||||
|
||||
Optional properties:
|
||||
ti,dual-volt: boolean, supports dual voltage cards
|
||||
<supply-name>-supply: phandle to the regulator device tree node
|
||||
"supply-name" examples are "vmmc", "vmmc_aux" etc
|
||||
bus-width: Number of data lines, default assumed is 1 if the property is missing.
|
||||
cd-gpios: GPIOs for card detection
|
||||
wp-gpios: GPIOs for write protection
|
||||
ti,non-removable: non-removable slot (like eMMC)
|
||||
ti,needs-special-reset: Requires a special softreset sequence
|
||||
|
||||
|
|
|
@ -850,9 +850,7 @@ out:
|
|||
goto retry;
|
||||
if (!err)
|
||||
mmc_blk_reset_success(md, type);
|
||||
spin_lock_irq(&md->lock);
|
||||
__blk_end_request(req, err, blk_rq_bytes(req));
|
||||
spin_unlock_irq(&md->lock);
|
||||
blk_end_request(req, err, blk_rq_bytes(req));
|
||||
|
||||
return err ? 0 : 1;
|
||||
}
|
||||
|
@ -934,9 +932,7 @@ out_retry:
|
|||
if (!err)
|
||||
mmc_blk_reset_success(md, type);
|
||||
out:
|
||||
spin_lock_irq(&md->lock);
|
||||
__blk_end_request(req, err, blk_rq_bytes(req));
|
||||
spin_unlock_irq(&md->lock);
|
||||
blk_end_request(req, err, blk_rq_bytes(req));
|
||||
|
||||
return err ? 0 : 1;
|
||||
}
|
||||
|
@ -951,9 +947,7 @@ static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
|
|||
if (ret)
|
||||
ret = -EIO;
|
||||
|
||||
spin_lock_irq(&md->lock);
|
||||
__blk_end_request_all(req, ret);
|
||||
spin_unlock_irq(&md->lock);
|
||||
blk_end_request_all(req, ret);
|
||||
|
||||
return ret ? 0 : 1;
|
||||
}
|
||||
|
@ -1252,14 +1246,10 @@ static int mmc_blk_cmd_err(struct mmc_blk_data *md, struct mmc_card *card,
|
|||
|
||||
blocks = mmc_sd_num_wr_blocks(card);
|
||||
if (blocks != (u32)-1) {
|
||||
spin_lock_irq(&md->lock);
|
||||
ret = __blk_end_request(req, 0, blocks << 9);
|
||||
spin_unlock_irq(&md->lock);
|
||||
ret = blk_end_request(req, 0, blocks << 9);
|
||||
}
|
||||
} else {
|
||||
spin_lock_irq(&md->lock);
|
||||
ret = __blk_end_request(req, 0, brq->data.bytes_xfered);
|
||||
spin_unlock_irq(&md->lock);
|
||||
ret = blk_end_request(req, 0, brq->data.bytes_xfered);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -1311,10 +1301,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
|||
* A block was successfully transferred.
|
||||
*/
|
||||
mmc_blk_reset_success(md, type);
|
||||
spin_lock_irq(&md->lock);
|
||||
ret = __blk_end_request(req, 0,
|
||||
ret = blk_end_request(req, 0,
|
||||
brq->data.bytes_xfered);
|
||||
spin_unlock_irq(&md->lock);
|
||||
/*
|
||||
* If the blk_end_request function returns non-zero even
|
||||
* though all data has been transferred and no errors
|
||||
|
@ -1364,10 +1352,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
|||
* time, so we only reach here after trying to
|
||||
* read a single sector.
|
||||
*/
|
||||
spin_lock_irq(&md->lock);
|
||||
ret = __blk_end_request(req, -EIO,
|
||||
ret = blk_end_request(req, -EIO,
|
||||
brq->data.blksz);
|
||||
spin_unlock_irq(&md->lock);
|
||||
if (!ret)
|
||||
goto start_new_req;
|
||||
break;
|
||||
|
@ -1388,12 +1374,10 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
|||
return 1;
|
||||
|
||||
cmd_abort:
|
||||
spin_lock_irq(&md->lock);
|
||||
if (mmc_card_removed(card))
|
||||
req->cmd_flags |= REQ_QUIET;
|
||||
while (ret)
|
||||
ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
|
||||
spin_unlock_irq(&md->lock);
|
||||
ret = blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
|
||||
|
||||
start_new_req:
|
||||
if (rqc) {
|
||||
|
@ -1417,9 +1401,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
|||
ret = mmc_blk_part_switch(card, md);
|
||||
if (ret) {
|
||||
if (req) {
|
||||
spin_lock_irq(&md->lock);
|
||||
__blk_end_request_all(req, -EIO);
|
||||
spin_unlock_irq(&md->lock);
|
||||
blk_end_request_all(req, -EIO);
|
||||
}
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
|
|
@ -7,6 +7,6 @@ mmc_core-y := core.o bus.o host.o \
|
|||
mmc.o mmc_ops.o sd.o sd_ops.o \
|
||||
sdio.o sdio_ops.o sdio_bus.o \
|
||||
sdio_cis.o sdio_io.o sdio_irq.o \
|
||||
quirks.o cd-gpio.o
|
||||
quirks.o slot-gpio.o
|
||||
|
||||
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Generic GPIO card-detect helper
|
||||
*
|
||||
* Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mmc/cd-gpio.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct mmc_cd_gpio {
|
||||
unsigned int gpio;
|
||||
char label[0];
|
||||
};
|
||||
|
||||
static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
|
||||
{
|
||||
/* Schedule a card detection after a debounce timeout */
|
||||
mmc_detect_change(dev_id, msecs_to_jiffies(100));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio)
|
||||
{
|
||||
size_t len = strlen(dev_name(host->parent)) + 4;
|
||||
struct mmc_cd_gpio *cd;
|
||||
int irq = gpio_to_irq(gpio);
|
||||
int ret;
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
|
||||
if (!cd)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(cd->label, len, "%s cd", dev_name(host->parent));
|
||||
|
||||
ret = gpio_request_one(gpio, GPIOF_DIR_IN, cd->label);
|
||||
if (ret < 0)
|
||||
goto egpioreq;
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT, cd->label, host);
|
||||
if (ret < 0)
|
||||
goto eirqreq;
|
||||
|
||||
cd->gpio = gpio;
|
||||
host->hotplug.irq = irq;
|
||||
host->hotplug.handler_priv = cd;
|
||||
|
||||
return 0;
|
||||
|
||||
eirqreq:
|
||||
gpio_free(gpio);
|
||||
egpioreq:
|
||||
kfree(cd);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_cd_gpio_request);
|
||||
|
||||
void mmc_cd_gpio_free(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
|
||||
|
||||
if (!cd)
|
||||
return;
|
||||
|
||||
free_irq(host->hotplug.irq, host);
|
||||
gpio_free(cd->gpio);
|
||||
kfree(cd);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_cd_gpio_free);
|
|
@ -404,6 +404,7 @@ int mmc_interrupt_hpi(struct mmc_card *card)
|
|||
{
|
||||
int err;
|
||||
u32 status;
|
||||
unsigned long prg_wait;
|
||||
|
||||
BUG_ON(!card);
|
||||
|
||||
|
@ -419,30 +420,38 @@ int mmc_interrupt_hpi(struct mmc_card *card)
|
|||
goto out;
|
||||
}
|
||||
|
||||
switch (R1_CURRENT_STATE(status)) {
|
||||
case R1_STATE_IDLE:
|
||||
case R1_STATE_READY:
|
||||
case R1_STATE_STBY:
|
||||
/*
|
||||
* If the card status is in PRG-state, we can send the HPI command.
|
||||
*/
|
||||
if (R1_CURRENT_STATE(status) == R1_STATE_PRG) {
|
||||
do {
|
||||
/*
|
||||
* We don't know when the HPI command will finish
|
||||
* processing, so we need to resend HPI until out
|
||||
* of prg-state, and keep checking the card status
|
||||
* with SEND_STATUS. If a timeout error occurs when
|
||||
* sending the HPI command, we are already out of
|
||||
* prg-state.
|
||||
* In idle states, HPI is not needed and the caller
|
||||
* can issue the next intended command immediately
|
||||
*/
|
||||
goto out;
|
||||
case R1_STATE_PRG:
|
||||
break;
|
||||
default:
|
||||
/* In all other states, it's illegal to issue HPI */
|
||||
pr_debug("%s: HPI cannot be sent. Card state=%d\n",
|
||||
mmc_hostname(card->host), R1_CURRENT_STATE(status));
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mmc_send_hpi_cmd(card, &status);
|
||||
if (err)
|
||||
pr_debug("%s: abort HPI (%d error)\n",
|
||||
mmc_hostname(card->host), err);
|
||||
goto out;
|
||||
|
||||
prg_wait = jiffies + msecs_to_jiffies(card->ext_csd.out_of_int_time);
|
||||
do {
|
||||
err = mmc_send_status(card, &status);
|
||||
if (err)
|
||||
|
||||
if (!err && R1_CURRENT_STATE(status) == R1_STATE_TRAN)
|
||||
break;
|
||||
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
|
||||
} else
|
||||
pr_debug("%s: Left prg-state\n", mmc_hostname(card->host));
|
||||
if (time_after(jiffies, prg_wait))
|
||||
err = -ETIMEDOUT;
|
||||
} while (!err);
|
||||
|
||||
out:
|
||||
mmc_release_host(card->host);
|
||||
|
@ -941,7 +950,7 @@ int mmc_regulator_get_ocrmask(struct regulator *supply)
|
|||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_regulator_get_ocrmask);
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_get_ocrmask);
|
||||
|
||||
/**
|
||||
* mmc_regulator_set_ocr - set regulator to match host->ios voltage
|
||||
|
@ -1011,7 +1020,30 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
|||
"could not set regulator OCR (%d)\n", result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_regulator_set_ocr);
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr);
|
||||
|
||||
int mmc_regulator_get_supply(struct mmc_host *mmc)
|
||||
{
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
struct regulator *supply;
|
||||
int ret;
|
||||
|
||||
supply = devm_regulator_get(dev, "vmmc");
|
||||
mmc->supply.vmmc = supply;
|
||||
mmc->supply.vqmmc = devm_regulator_get(dev, "vqmmc");
|
||||
|
||||
if (IS_ERR(supply))
|
||||
return PTR_ERR(supply);
|
||||
|
||||
ret = mmc_regulator_get_ocrmask(supply);
|
||||
if (ret > 0)
|
||||
mmc->ocr_avail = ret;
|
||||
else
|
||||
dev_warn(mmc_dev(mmc), "Failed getting OCR mask: %d\n", ret);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);
|
||||
|
||||
#endif /* CONFIG_REGULATOR */
|
||||
|
||||
|
@ -1180,6 +1212,9 @@ static void mmc_power_up(struct mmc_host *host)
|
|||
host->ios.timing = MMC_TIMING_LEGACY;
|
||||
mmc_set_ios(host);
|
||||
|
||||
/* Set signal voltage to 3.3V */
|
||||
mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, false);
|
||||
|
||||
/*
|
||||
* This delay should be sufficient to allow the power supply
|
||||
* to reach the minimum voltage.
|
||||
|
@ -1931,9 +1966,6 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
|||
*/
|
||||
mmc_hw_reset_for_init(host);
|
||||
|
||||
/* Initialization should be done at 3.3 V I/O voltage. */
|
||||
mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
|
||||
|
||||
/*
|
||||
* sdio_reset sends CMD52 to reset card. Since we do not know
|
||||
* if the card is being re-initialized, just send it. CMD52
|
||||
|
@ -2075,6 +2107,7 @@ void mmc_rescan(struct work_struct *work)
|
|||
void mmc_start_host(struct mmc_host *host)
|
||||
{
|
||||
host->f_init = max(freqs[0], host->f_min);
|
||||
host->rescan_disable = 0;
|
||||
mmc_power_up(host);
|
||||
mmc_detect_change(host, 0);
|
||||
}
|
||||
|
@ -2088,6 +2121,7 @@ void mmc_stop_host(struct mmc_host *host)
|
|||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
|
||||
host->rescan_disable = 1;
|
||||
cancel_delayed_work_sync(&host->detect);
|
||||
mmc_flush_scheduled_work();
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
static void mmc_host_classdev_release(struct device *dev)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
mutex_destroy(&host->slot.lock);
|
||||
kfree(host);
|
||||
}
|
||||
|
||||
|
@ -312,6 +313,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
if (!host)
|
||||
return NULL;
|
||||
|
||||
/* scanning will be enabled when we're ready */
|
||||
host->rescan_disable = 1;
|
||||
spin_lock(&mmc_host_lock);
|
||||
err = idr_get_new(&mmc_host_idr, host, &host->index);
|
||||
spin_unlock(&mmc_host_lock);
|
||||
|
@ -327,6 +330,9 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
|||
|
||||
mmc_host_clk_init(host);
|
||||
|
||||
mutex_init(&host->slot.lock);
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
init_waitqueue_head(&host->wq);
|
||||
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
|
||||
|
|
|
@ -818,9 +818,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
if (!mmc_host_is_spi(host))
|
||||
mmc_set_bus_mode(host, MMC_BUSMODE_OPENDRAIN);
|
||||
|
||||
/* Initialization should be done at 3.3 V I/O voltage. */
|
||||
mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
|
||||
|
||||
/*
|
||||
* Since we're changing the OCR value, we seem to
|
||||
* need to tell some cards to go back to the idle
|
||||
|
|
|
@ -569,7 +569,6 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
|
|||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = card->rca << 16 | 1;
|
||||
cmd.cmd_timeout_ms = card->ext_csd.out_of_int_time;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||
if (err) {
|
||||
|
|
|
@ -244,7 +244,7 @@ static int mmc_read_ssr(struct mmc_card *card)
|
|||
* bitfield positions accordingly.
|
||||
*/
|
||||
au = UNSTUFF_BITS(ssr, 428 - 384, 4);
|
||||
if (au > 0 || au <= 9) {
|
||||
if (au > 0 && au <= 9) {
|
||||
card->ssr.au = 1 << (au + 4);
|
||||
es = UNSTUFF_BITS(ssr, 408 - 384, 16);
|
||||
et = UNSTUFF_BITS(ssr, 402 - 384, 6);
|
||||
|
@ -290,8 +290,12 @@ static int mmc_read_switch(struct mmc_card *card)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Find out the supported Bus Speed Modes. */
|
||||
err = mmc_sd_switch(card, 0, 0, 1, status);
|
||||
/*
|
||||
* Find out the card's support bits with a mode 0 operation.
|
||||
* The argument does not matter, as the support bits do not
|
||||
* change with the arguments.
|
||||
*/
|
||||
err = mmc_sd_switch(card, 0, 0, 0, status);
|
||||
if (err) {
|
||||
/*
|
||||
* If the host or the card can't do the switch,
|
||||
|
@ -312,46 +316,8 @@ static int mmc_read_switch(struct mmc_card *card)
|
|||
|
||||
if (card->scr.sda_spec3) {
|
||||
card->sw_caps.sd3_bus_mode = status[13];
|
||||
|
||||
/* Find out Driver Strengths supported by the card */
|
||||
err = mmc_sd_switch(card, 0, 2, 1, status);
|
||||
if (err) {
|
||||
/*
|
||||
* If the host or the card can't do the switch,
|
||||
* fail more gracefully.
|
||||
*/
|
||||
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
||||
goto out;
|
||||
|
||||
pr_warning("%s: problem reading "
|
||||
"Driver Strength.\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Driver Strengths supported by the card */
|
||||
card->sw_caps.sd3_drv_type = status[9];
|
||||
|
||||
/* Find out Current Limits supported by the card */
|
||||
err = mmc_sd_switch(card, 0, 3, 1, status);
|
||||
if (err) {
|
||||
/*
|
||||
* If the host or the card can't do the switch,
|
||||
* fail more gracefully.
|
||||
*/
|
||||
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
||||
goto out;
|
||||
|
||||
pr_warning("%s: problem reading "
|
||||
"Current Limit.\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
card->sw_caps.sd3_curr_limit = status[7];
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -551,53 +517,71 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Get host's max current setting at its current voltage */
|
||||
static u32 sd_get_host_max_current(struct mmc_host *host)
|
||||
{
|
||||
u32 voltage, max_current;
|
||||
|
||||
voltage = 1 << host->ios.vdd;
|
||||
switch (voltage) {
|
||||
case MMC_VDD_165_195:
|
||||
max_current = host->max_current_180;
|
||||
break;
|
||||
case MMC_VDD_29_30:
|
||||
case MMC_VDD_30_31:
|
||||
max_current = host->max_current_300;
|
||||
break;
|
||||
case MMC_VDD_32_33:
|
||||
case MMC_VDD_33_34:
|
||||
max_current = host->max_current_330;
|
||||
break;
|
||||
default:
|
||||
max_current = 0;
|
||||
}
|
||||
|
||||
return max_current;
|
||||
}
|
||||
|
||||
static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
||||
{
|
||||
int current_limit = 0;
|
||||
int current_limit = SD_SET_CURRENT_NO_CHANGE;
|
||||
int err;
|
||||
u32 max_current;
|
||||
|
||||
/*
|
||||
* Current limit switch is only defined for SDR50, SDR104, and DDR50
|
||||
* bus speed modes. For other bus speed modes, we set the default
|
||||
* current limit of 200mA.
|
||||
* bus speed modes. For other bus speed modes, we do not change the
|
||||
* current limit.
|
||||
*/
|
||||
if ((card->sd_bus_speed == UHS_SDR50_BUS_SPEED) ||
|
||||
(card->sd_bus_speed == UHS_SDR104_BUS_SPEED) ||
|
||||
(card->sd_bus_speed == UHS_DDR50_BUS_SPEED)) {
|
||||
if (card->host->caps & MMC_CAP_MAX_CURRENT_800) {
|
||||
if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_800)
|
||||
if ((card->sd_bus_speed != UHS_SDR50_BUS_SPEED) &&
|
||||
(card->sd_bus_speed != UHS_SDR104_BUS_SPEED) &&
|
||||
(card->sd_bus_speed != UHS_DDR50_BUS_SPEED))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Host has different current capabilities when operating at
|
||||
* different voltages, so find out its max current first.
|
||||
*/
|
||||
max_current = sd_get_host_max_current(card->host);
|
||||
|
||||
/*
|
||||
* We only check host's capability here, if we set a limit that is
|
||||
* higher than the card's maximum current, the card will be using its
|
||||
* maximum current, e.g. if the card's maximum current is 300ma, and
|
||||
* when we set current limit to 200ma, the card will draw 200ma, and
|
||||
* when we set current limit to 400/600/800ma, the card will draw its
|
||||
* maximum 300ma from the host.
|
||||
*/
|
||||
if (max_current >= 800)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_800;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_600)
|
||||
else if (max_current >= 600)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_600;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_400)
|
||||
else if (max_current >= 400)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_400;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
} else if (card->host->caps & MMC_CAP_MAX_CURRENT_600) {
|
||||
if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_600)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_600;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_400)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_400;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
} else if (card->host->caps & MMC_CAP_MAX_CURRENT_400) {
|
||||
if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_400;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
} else if (card->host->caps & MMC_CAP_MAX_CURRENT_200) {
|
||||
if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
}
|
||||
} else
|
||||
else if (max_current >= 200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
|
||||
if (current_limit != SD_SET_CURRENT_NO_CHANGE) {
|
||||
err = mmc_sd_switch(card, 1, 3, current_limit, status);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -606,6 +590,8 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
|||
pr_warning("%s: Problem setting current limit!\n",
|
||||
mmc_hostname(card->host));
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -726,6 +712,7 @@ struct device_type sd_type = {
|
|||
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
|
||||
{
|
||||
int err;
|
||||
u32 max_current;
|
||||
|
||||
/*
|
||||
* Since we're changing the OCR value, we seem to
|
||||
|
@ -753,9 +740,12 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
|
|||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))
|
||||
ocr |= SD_OCR_S18R;
|
||||
|
||||
/* If the host can supply more than 150mA, XPC should be set to 1. */
|
||||
if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
|
||||
MMC_CAP_SET_XPC_180))
|
||||
/*
|
||||
* If the host can supply more than 150mA at current voltage,
|
||||
* XPC should be set to 1.
|
||||
*/
|
||||
max_current = sd_get_host_max_current(host);
|
||||
if (max_current > 150)
|
||||
ocr |= SD_OCR_XPC;
|
||||
|
||||
try_again:
|
||||
|
@ -911,9 +901,6 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
|||
BUG_ON(!host);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
/* The initialization should be done at 3.3 V I/O voltage. */
|
||||
mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
|
||||
|
||||
err = mmc_sd_get_cid(host, ocr, cid, &rocr);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
@ -591,9 +591,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
* Inform the card of the voltage
|
||||
*/
|
||||
if (!powered_resume) {
|
||||
/* The initialization should be done at 3.3 V I/O voltage. */
|
||||
mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
|
||||
|
||||
err = mmc_send_io_op_cond(host, host->ocr, &ocr);
|
||||
if (err)
|
||||
goto err;
|
||||
|
@ -1006,10 +1003,6 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
|
|||
* restore the correct voltage setting of the card.
|
||||
*/
|
||||
|
||||
/* The initialization should be done at 3.3 V I/O voltage. */
|
||||
if (!mmc_card_keep_power(host))
|
||||
mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, 0);
|
||||
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
|
|
|
@ -313,7 +313,7 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
|
|||
|
||||
if (ret == -ENOENT) {
|
||||
/* warn about unknown tuples */
|
||||
pr_warning("%s: queuing unknown"
|
||||
pr_warn_ratelimited("%s: queuing unknown"
|
||||
" CIS tuple 0x%02x (%u bytes)\n",
|
||||
mmc_hostname(card->host),
|
||||
tpl_code, tpl_link);
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Generic GPIO card-detect helper
|
||||
*
|
||||
* Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct mmc_gpio {
|
||||
int ro_gpio;
|
||||
int cd_gpio;
|
||||
char *ro_label;
|
||||
char cd_label[0];
|
||||
};
|
||||
|
||||
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
||||
{
|
||||
/* Schedule a card detection after a debounce timeout */
|
||||
mmc_detect_change(dev_id, msecs_to_jiffies(100));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mmc_gpio_alloc(struct mmc_host *host)
|
||||
{
|
||||
size_t len = strlen(dev_name(host->parent)) + 4;
|
||||
struct mmc_gpio *ctx;
|
||||
|
||||
mutex_lock(&host->slot.lock);
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
if (!ctx) {
|
||||
/*
|
||||
* devm_kzalloc() can be called after device_initialize(), even
|
||||
* before device_add(), i.e., between mmc_alloc_host() and
|
||||
* mmc_add_host()
|
||||
*/
|
||||
ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + 2 * len,
|
||||
GFP_KERNEL);
|
||||
if (ctx) {
|
||||
ctx->ro_label = ctx->cd_label + len;
|
||||
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
|
||||
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
|
||||
ctx->cd_gpio = -EINVAL;
|
||||
ctx->ro_gpio = -EINVAL;
|
||||
host->slot.handler_priv = ctx;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&host->slot.lock);
|
||||
|
||||
return ctx ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
int mmc_gpio_get_ro(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
if (!ctx || !gpio_is_valid(ctx->ro_gpio))
|
||||
return -ENOSYS;
|
||||
|
||||
return !gpio_get_value_cansleep(ctx->ro_gpio) ^
|
||||
!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_get_ro);
|
||||
|
||||
int mmc_gpio_get_cd(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
||||
if (!ctx || !gpio_is_valid(ctx->cd_gpio))
|
||||
return -ENOSYS;
|
||||
|
||||
return !gpio_get_value_cansleep(ctx->cd_gpio) ^
|
||||
!!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_get_cd);
|
||||
|
||||
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
|
||||
{
|
||||
struct mmc_gpio *ctx;
|
||||
int ret;
|
||||
|
||||
if (!gpio_is_valid(gpio))
|
||||
return -EINVAL;
|
||||
|
||||
ret = mmc_gpio_alloc(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
|
||||
return gpio_request_one(gpio, GPIOF_DIR_IN, ctx->ro_label);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_request_ro);
|
||||
|
||||
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio)
|
||||
{
|
||||
struct mmc_gpio *ctx;
|
||||
int irq = gpio_to_irq(gpio);
|
||||
int ret;
|
||||
|
||||
ret = mmc_gpio_alloc(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ctx = host->slot.handler_priv;
|
||||
|
||||
ret = gpio_request_one(gpio, GPIOF_DIR_IN, ctx->cd_label);
|
||||
if (ret < 0)
|
||||
/*
|
||||
* don't bother freeing memory. It might still get used by other
|
||||
* slot functions, in any case it will be freed, when the device
|
||||
* is destroyed.
|
||||
*/
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Even if gpio_to_irq() returns a valid IRQ number, the platform might
|
||||
* still prefer to poll, e.g., because that IRQ number is already used
|
||||
* by another unit and cannot be shared.
|
||||
*/
|
||||
if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
|
||||
irq = -EINVAL;
|
||||
|
||||
if (irq >= 0) {
|
||||
ret = request_threaded_irq(irq, NULL, mmc_gpio_cd_irqt,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
ctx->cd_label, host);
|
||||
if (ret < 0)
|
||||
irq = ret;
|
||||
}
|
||||
|
||||
host->slot.cd_irq = irq;
|
||||
|
||||
if (irq < 0)
|
||||
host->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
ctx->cd_gpio = gpio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_request_cd);
|
||||
|
||||
void mmc_gpio_free_ro(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int gpio;
|
||||
|
||||
if (!ctx || !gpio_is_valid(ctx->ro_gpio))
|
||||
return;
|
||||
|
||||
gpio = ctx->ro_gpio;
|
||||
ctx->ro_gpio = -EINVAL;
|
||||
|
||||
gpio_free(gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_free_ro);
|
||||
|
||||
void mmc_gpio_free_cd(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
int gpio;
|
||||
|
||||
if (!ctx || !gpio_is_valid(ctx->cd_gpio))
|
||||
return;
|
||||
|
||||
if (host->slot.cd_irq >= 0) {
|
||||
free_irq(host->slot.cd_irq, host);
|
||||
host->slot.cd_irq = -EINVAL;
|
||||
}
|
||||
|
||||
gpio = ctx->cd_gpio;
|
||||
ctx->cd_gpio = -EINVAL;
|
||||
|
||||
gpio_free(gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_free_cd);
|
|
@ -391,11 +391,17 @@ static int atmci_regs_show(struct seq_file *s, void *v)
|
|||
clk_disable(host->mck);
|
||||
spin_unlock_bh(&host->lock);
|
||||
|
||||
seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
|
||||
seq_printf(s, "MR:\t0x%08x%s%s ",
|
||||
buf[ATMCI_MR / 4],
|
||||
buf[ATMCI_MR / 4] & ATMCI_MR_RDPROOF ? " RDPROOF" : "",
|
||||
buf[ATMCI_MR / 4] & ATMCI_MR_WRPROOF ? " WRPROOF" : "",
|
||||
buf[ATMCI_MR / 4] & 0xff);
|
||||
buf[ATMCI_MR / 4] & ATMCI_MR_WRPROOF ? " WRPROOF" : "");
|
||||
if (host->caps.has_odd_clk_div)
|
||||
seq_printf(s, "{CLKDIV,CLKODD}=%u\n",
|
||||
((buf[ATMCI_MR / 4] & 0xff) << 1)
|
||||
| ((buf[ATMCI_MR / 4] >> 16) & 1));
|
||||
else
|
||||
seq_printf(s, "CLKDIV=%u\n",
|
||||
(buf[ATMCI_MR / 4] & 0xff));
|
||||
seq_printf(s, "DTOR:\t0x%08x\n", buf[ATMCI_DTOR / 4]);
|
||||
seq_printf(s, "SDCR:\t0x%08x\n", buf[ATMCI_SDCR / 4]);
|
||||
seq_printf(s, "ARGR:\t0x%08x\n", buf[ATMCI_ARGR / 4]);
|
||||
|
@ -1685,7 +1691,6 @@ static void atmci_tasklet_func(unsigned long priv)
|
|||
|
||||
dev_dbg(&host->pdev->dev, "FSM: cmd ready\n");
|
||||
host->cmd = NULL;
|
||||
host->data = NULL;
|
||||
data->bytes_xfered = data->blocks * data->blksz;
|
||||
data->error = 0;
|
||||
atmci_command_complete(host, mrq->stop);
|
||||
|
@ -1699,6 +1704,7 @@ static void atmci_tasklet_func(unsigned long priv)
|
|||
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
|
||||
state = STATE_WAITING_NOTBUSY;
|
||||
}
|
||||
host->data = NULL;
|
||||
break;
|
||||
|
||||
case STATE_END_REQUEST:
|
||||
|
|
|
@ -405,11 +405,23 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
|
|||
static int dw_mci_idmac_init(struct dw_mci *host)
|
||||
{
|
||||
struct idmac_desc *p;
|
||||
int i;
|
||||
int i, dma_support;
|
||||
|
||||
/* Number of descriptors in the ring buffer */
|
||||
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
|
||||
|
||||
/* Check if Hardware Configuration Register has support for DMA */
|
||||
dma_support = (mci_readl(host, HCON) >> 16) & 0x3;
|
||||
|
||||
if (!dma_support || dma_support > 2) {
|
||||
dev_err(&host->dev,
|
||||
"Host Controller does not support IDMA Tx.\n");
|
||||
host->dma_ops = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&host->dev, "Using internal DMA controller.\n");
|
||||
|
||||
/* Forward link the descriptor list */
|
||||
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
|
||||
p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
|
||||
|
@ -1876,7 +1888,6 @@ static void dw_mci_init_dma(struct dw_mci *host)
|
|||
/* Determine which DMA interface to use */
|
||||
#ifdef CONFIG_MMC_DW_IDMAC
|
||||
host->dma_ops = &dw_mci_idmac_ops;
|
||||
dev_info(&host->dev, "Using internal DMA controller.\n");
|
||||
#endif
|
||||
|
||||
if (!host->dma_ops)
|
||||
|
@ -2175,7 +2186,7 @@ int dw_mci_resume(struct dw_mci *host)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (host->dma_ops->init)
|
||||
if (host->use_dma && host->dma_ops->init)
|
||||
host->dma_ops->init(host);
|
||||
|
||||
/* Restore the old value at FIFOTH register */
|
||||
|
|
|
@ -164,16 +164,23 @@ struct mxs_mmc_host {
|
|||
spinlock_t lock;
|
||||
int sdio_irq_en;
|
||||
int wp_gpio;
|
||||
bool wp_inverted;
|
||||
};
|
||||
|
||||
static int mxs_mmc_get_ro(struct mmc_host *mmc)
|
||||
{
|
||||
struct mxs_mmc_host *host = mmc_priv(mmc);
|
||||
int ret;
|
||||
|
||||
if (!gpio_is_valid(host->wp_gpio))
|
||||
return -EINVAL;
|
||||
|
||||
return gpio_get_value(host->wp_gpio);
|
||||
ret = gpio_get_value(host->wp_gpio);
|
||||
|
||||
if (host->wp_inverted)
|
||||
ret = !ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
||||
|
@ -707,6 +714,8 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
struct pinctrl *pinctrl;
|
||||
int ret = 0, irq_err, irq_dma;
|
||||
dma_cap_mask_t mask;
|
||||
struct regulator *reg_vmmc;
|
||||
enum of_gpio_flags flags;
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
|
@ -747,6 +756,16 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
host->mmc = mmc;
|
||||
host->sdio_irq_en = 0;
|
||||
|
||||
reg_vmmc = devm_regulator_get(&pdev->dev, "vmmc");
|
||||
if (!IS_ERR(reg_vmmc)) {
|
||||
ret = regulator_enable(reg_vmmc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to enable vmmc regulator: %d\n", ret);
|
||||
goto out_mmc_free;
|
||||
}
|
||||
}
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl)) {
|
||||
ret = PTR_ERR(pinctrl);
|
||||
|
@ -785,7 +804,10 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
else if (bus_width == 8)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
host->wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
|
||||
host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0,
|
||||
&flags);
|
||||
if (flags & OF_GPIO_ACTIVE_LOW)
|
||||
host->wp_inverted = 1;
|
||||
} else {
|
||||
if (pdata->flags & SLOTF_8_BIT_CAPABLE)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
|
|
|
@ -1089,7 +1089,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
|||
/* Disable the clocks */
|
||||
pm_runtime_put_sync(host->dev);
|
||||
if (host->dbclk)
|
||||
clk_disable(host->dbclk);
|
||||
clk_disable_unprepare(host->dbclk);
|
||||
|
||||
/* Turn the power off */
|
||||
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
|
||||
|
@ -1100,7 +1100,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
|||
vdd);
|
||||
pm_runtime_get_sync(host->dev);
|
||||
if (host->dbclk)
|
||||
clk_enable(host->dbclk);
|
||||
clk_prepare_enable(host->dbclk);
|
||||
|
||||
if (ret != 0)
|
||||
goto err;
|
||||
|
@ -1899,7 +1899,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(host->dbclk)) {
|
||||
dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");
|
||||
host->dbclk = NULL;
|
||||
} else if (clk_enable(host->dbclk) != 0) {
|
||||
} else if (clk_prepare_enable(host->dbclk) != 0) {
|
||||
dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
|
||||
clk_put(host->dbclk);
|
||||
host->dbclk = NULL;
|
||||
|
@ -1931,6 +1931,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
|
||||
if (!res) {
|
||||
dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
|
||||
ret = -ENXIO;
|
||||
goto err_irq;
|
||||
}
|
||||
host->dma_line_tx = res->start;
|
||||
|
@ -1938,6 +1939,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
|
|||
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
|
||||
if (!res) {
|
||||
dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
|
||||
ret = -ENXIO;
|
||||
goto err_irq;
|
||||
}
|
||||
host->dma_line_rx = res->start;
|
||||
|
@ -2023,7 +2025,7 @@ err_irq:
|
|||
pm_runtime_disable(host->dev);
|
||||
clk_put(host->fclk);
|
||||
if (host->dbclk) {
|
||||
clk_disable(host->dbclk);
|
||||
clk_disable_unprepare(host->dbclk);
|
||||
clk_put(host->dbclk);
|
||||
}
|
||||
err1:
|
||||
|
@ -2058,7 +2060,7 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)
|
|||
pm_runtime_disable(host->dev);
|
||||
clk_put(host->fclk);
|
||||
if (host->dbclk) {
|
||||
clk_disable(host->dbclk);
|
||||
clk_disable_unprepare(host->dbclk);
|
||||
clk_put(host->dbclk);
|
||||
}
|
||||
|
||||
|
@ -2116,7 +2118,7 @@ static int omap_hsmmc_suspend(struct device *dev)
|
|||
}
|
||||
|
||||
if (host->dbclk)
|
||||
clk_disable(host->dbclk);
|
||||
clk_disable_unprepare(host->dbclk);
|
||||
err:
|
||||
pm_runtime_put_sync(host->dev);
|
||||
return ret;
|
||||
|
@ -2137,7 +2139,7 @@ static int omap_hsmmc_resume(struct device *dev)
|
|||
pm_runtime_get_sync(host->dev);
|
||||
|
||||
if (host->dbclk)
|
||||
clk_enable(host->dbclk);
|
||||
clk_prepare_enable(host->dbclk);
|
||||
|
||||
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
|
||||
omap_hsmmc_conf_bus_power(host);
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include <mach/dma.h>
|
||||
|
||||
#include <mach/regs-sdi.h>
|
||||
#include <mach/regs-gpio.h>
|
||||
|
||||
#include <plat/mci.h>
|
||||
|
||||
|
@ -1237,12 +1236,9 @@ static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
switch (ios->power_mode) {
|
||||
case MMC_POWER_ON:
|
||||
case MMC_POWER_UP:
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPE(5), S3C2410_GPE5_SDCLK);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPE(6), S3C2410_GPE6_SDCMD);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPE(7), S3C2410_GPE7_SDDAT0);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPE(8), S3C2410_GPE8_SDDAT1);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPE(9), S3C2410_GPE9_SDDAT2);
|
||||
s3c2410_gpio_cfgpin(S3C2410_GPE(10), S3C2410_GPE10_SDDAT3);
|
||||
/* Configure GPE5...GPE10 pins in SD mode */
|
||||
s3c_gpio_cfgall_range(S3C2410_GPE(5), 6, S3C_GPIO_SFN(2),
|
||||
S3C_GPIO_PULL_NONE);
|
||||
|
||||
if (host->pdata->set_power)
|
||||
host->pdata->set_power(ios->power_mode, ios->vdd);
|
||||
|
|
|
@ -20,11 +20,17 @@
|
|||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
struct sdhci_dove_priv {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u16 ret;
|
||||
|
@ -66,16 +72,57 @@ static struct sdhci_pltfm_data sdhci_dove_pdata = {
|
|||
.quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
|
||||
SDHCI_QUIRK_NO_BUSY_IRQ |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_FORCE_DMA,
|
||||
SDHCI_QUIRK_FORCE_DMA |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT,
|
||||
};
|
||||
|
||||
static int __devinit sdhci_dove_probe(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_register(pdev, &sdhci_dove_pdata);
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_dove_priv *priv;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_pltfm_register(pdev, &sdhci_dove_pdata);
|
||||
if (ret)
|
||||
goto sdhci_dove_register_fail;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv),
|
||||
GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "unable to allocate private data");
|
||||
ret = -ENOMEM;
|
||||
goto sdhci_dove_allocate_fail;
|
||||
}
|
||||
|
||||
host = platform_get_drvdata(pdev);
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = priv;
|
||||
|
||||
priv->clk = clk_get(&pdev->dev, NULL);
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_prepare_enable(priv->clk);
|
||||
return 0;
|
||||
|
||||
sdhci_dove_allocate_fail:
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
sdhci_dove_register_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit sdhci_dove_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_dove_priv *priv = pltfm_host->priv;
|
||||
|
||||
if (priv->clk) {
|
||||
if (!IS_ERR(priv->clk)) {
|
||||
clk_disable_unprepare(priv->clk);
|
||||
clk_put(priv->clk);
|
||||
}
|
||||
devm_kfree(&pdev->dev, priv->clk);
|
||||
}
|
||||
return sdhci_pltfm_unregister(pdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -299,6 +299,8 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||
|
||||
static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
u32 new_val;
|
||||
|
||||
switch (reg) {
|
||||
|
@ -315,8 +317,11 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
|
|||
SDHCI_CTRL_D3CD);
|
||||
/* ensure the endianess */
|
||||
new_val |= ESDHC_HOST_CONTROL_LE;
|
||||
/* bits 8&9 are reserved on mx25 */
|
||||
if (!is_imx25_esdhc(imx_data)) {
|
||||
/* DMA mode bits are shifted */
|
||||
new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
|
||||
}
|
||||
|
||||
esdhc_clrset_le(host, 0xffff, new_val, reg);
|
||||
return;
|
||||
|
|
|
@ -157,6 +157,7 @@ static const struct sdhci_pci_fixes sdhci_ene_714 = {
|
|||
static const struct sdhci_pci_fixes sdhci_cafe = {
|
||||
.quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
|
||||
SDHCI_QUIRK_NO_BUSY_IRQ |
|
||||
SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
#include <linux/mmc/host.h>
|
||||
#include <linux/platform_data/pxa_sdhci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
|
@ -121,6 +124,48 @@ static struct sdhci_ops pxav2_sdhci_ops = {
|
|||
.platform_8bit_width = pxav2_mmc_set_width,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id sdhci_pxav2_of_match[] = {
|
||||
{
|
||||
.compatible = "mrvl,pxav2-mmc",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_pxav2_of_match);
|
||||
|
||||
static struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev)
|
||||
{
|
||||
struct sdhci_pxa_platdata *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bus_width;
|
||||
u32 clk_delay_cycles;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
if (of_find_property(np, "non-removable", NULL))
|
||||
pdata->flags |= PXA_FLAG_CARD_PERMANENT;
|
||||
|
||||
of_property_read_u32(np, "bus-width", &bus_width);
|
||||
if (bus_width == 8)
|
||||
pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT;
|
||||
|
||||
of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles);
|
||||
if (clk_delay_cycles > 0) {
|
||||
pdata->clk_delay_sel = 1;
|
||||
pdata->clk_delay_cycles = clk_delay_cycles;
|
||||
}
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static inline struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
|
@ -128,6 +173,8 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct sdhci_host *host = NULL;
|
||||
struct sdhci_pxa *pxa = NULL;
|
||||
const struct of_device_id *match;
|
||||
|
||||
int ret;
|
||||
struct clk *clk;
|
||||
|
||||
|
@ -156,6 +203,10 @@ static int __devinit sdhci_pxav2_probe(struct platform_device *pdev)
|
|||
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
|
||||
| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
|
||||
|
||||
match = of_match_device(of_match_ptr(sdhci_pxav2_of_match), &pdev->dev);
|
||||
if (match) {
|
||||
pdata = pxav2_get_mmc_pdata(dev);
|
||||
}
|
||||
if (pdata) {
|
||||
if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
|
||||
/* on-chip device */
|
||||
|
@ -218,6 +269,9 @@ static struct platform_driver sdhci_pxav2_driver = {
|
|||
.driver = {
|
||||
.name = "sdhci-pxav2",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = sdhci_pxav2_of_match,
|
||||
#endif
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = sdhci_pxav2_probe,
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
|
@ -164,6 +167,46 @@ static struct sdhci_ops pxav3_sdhci_ops = {
|
|||
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id sdhci_pxav3_of_match[] = {
|
||||
{
|
||||
.compatible = "mrvl,pxav3-mmc",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match);
|
||||
|
||||
static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
|
||||
{
|
||||
struct sdhci_pxa_platdata *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 bus_width;
|
||||
u32 clk_delay_cycles;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
if (of_find_property(np, "non-removable", NULL))
|
||||
pdata->flags |= PXA_FLAG_CARD_PERMANENT;
|
||||
|
||||
of_property_read_u32(np, "bus-width", &bus_width);
|
||||
if (bus_width == 8)
|
||||
pdata->flags |= PXA_FLAG_SD_8_BIT_CAPABLE_SLOT;
|
||||
|
||||
of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles);
|
||||
if (clk_delay_cycles > 0)
|
||||
pdata->clk_delay_cycles = clk_delay_cycles;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
|
@ -171,6 +214,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct sdhci_host *host = NULL;
|
||||
struct sdhci_pxa *pxa = NULL;
|
||||
const struct of_device_id *match;
|
||||
|
||||
int ret;
|
||||
struct clk *clk;
|
||||
|
||||
|
@ -202,6 +247,10 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev)
|
|||
/* enable 1/8V DDR capable */
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
||||
match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
|
||||
if (match)
|
||||
pdata = pxav3_get_mmc_pdata(dev);
|
||||
|
||||
if (pdata) {
|
||||
if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
|
||||
/* on-chip device */
|
||||
|
@ -263,6 +312,9 @@ static int __devexit sdhci_pxav3_remove(struct platform_device *pdev)
|
|||
static struct platform_driver sdhci_pxav3_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-pxav3",
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = sdhci_pxav3_of_match,
|
||||
#endif
|
||||
.owner = THIS_MODULE,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
|
||||
|
@ -244,6 +245,19 @@ static void sdhci_init(struct sdhci_host *host, int soft)
|
|||
static void sdhci_reinit(struct sdhci_host *host)
|
||||
{
|
||||
sdhci_init(host, 0);
|
||||
/*
|
||||
* Retuning stuffs are affected by different cards inserted and only
|
||||
* applicable to UHS-I cards. So reset these fields to their initial
|
||||
* value when card is removed.
|
||||
*/
|
||||
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
||||
host->flags &= ~SDHCI_USING_RETUNING_TIMER;
|
||||
|
||||
del_timer_sync(&host->tuning_timer);
|
||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
||||
host->mmc->max_blk_count =
|
||||
(host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535;
|
||||
}
|
||||
sdhci_enable_card_detection(host);
|
||||
}
|
||||
|
||||
|
@ -1245,6 +1259,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
struct sdhci_host *host;
|
||||
bool present;
|
||||
unsigned long flags;
|
||||
u32 tuning_opcode;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
|
||||
|
@ -1292,8 +1307,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
*/
|
||||
if ((host->flags & SDHCI_NEEDS_RETUNING) &&
|
||||
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
|
||||
/* eMMC uses cmd21 while sd and sdio use cmd19 */
|
||||
tuning_opcode = mmc->card->type == MMC_TYPE_MMC ?
|
||||
MMC_SEND_TUNING_BLOCK_HS200 :
|
||||
MMC_SEND_TUNING_BLOCK;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
sdhci_execute_tuning(mmc, mrq->cmd->opcode);
|
||||
sdhci_execute_tuning(mmc, tuning_opcode);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
/* Restore original mmc_request structure */
|
||||
|
@ -1663,11 +1682,15 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
|
|||
pwr = sdhci_readb(host, SDHCI_POWER_CONTROL);
|
||||
pwr &= ~SDHCI_POWER_ON;
|
||||
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
||||
if (host->vmmc)
|
||||
regulator_disable(host->vmmc);
|
||||
|
||||
/* Wait for 1ms as per the spec */
|
||||
usleep_range(1000, 1500);
|
||||
pwr |= SDHCI_POWER_ON;
|
||||
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
||||
if (host->vmmc)
|
||||
regulator_enable(host->vmmc);
|
||||
|
||||
pr_info(DRIVER_NAME ": Switching to 1.8V signalling "
|
||||
"voltage failed, retrying with S18R set to 0\n");
|
||||
|
@ -1855,6 +1878,7 @@ out:
|
|||
*/
|
||||
if (!(host->flags & SDHCI_NEEDS_RETUNING) && host->tuning_count &&
|
||||
(host->tuning_mode == SDHCI_TUNING_MODE_1)) {
|
||||
host->flags |= SDHCI_USING_RETUNING_TIMER;
|
||||
mod_timer(&host->tuning_timer, jiffies +
|
||||
host->tuning_count * HZ);
|
||||
/* Tuning mode 1 limits the maximum data length to 4MB */
|
||||
|
@ -1872,10 +1896,10 @@ out:
|
|||
* try tuning again at a later time, when the re-tuning timer expires.
|
||||
* So for these controllers, we return 0. Since there might be other
|
||||
* controllers who do not have this capability, we return error for
|
||||
* them.
|
||||
* them. SDHCI_USING_RETUNING_TIMER means the host is currently using
|
||||
* a retuning timer to do the retuning for the card.
|
||||
*/
|
||||
if (err && host->tuning_count &&
|
||||
host->tuning_mode == SDHCI_TUNING_MODE_1)
|
||||
if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
|
||||
err = 0;
|
||||
|
||||
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
|
||||
|
@ -2382,7 +2406,6 @@ out:
|
|||
int sdhci_suspend_host(struct sdhci_host *host)
|
||||
{
|
||||
int ret;
|
||||
bool has_tuning_timer;
|
||||
|
||||
if (host->ops->platform_suspend)
|
||||
host->ops->platform_suspend(host);
|
||||
|
@ -2390,16 +2413,14 @@ int sdhci_suspend_host(struct sdhci_host *host)
|
|||
sdhci_disable_card_detection(host);
|
||||
|
||||
/* Disable tuning since we are suspending */
|
||||
has_tuning_timer = host->version >= SDHCI_SPEC_300 &&
|
||||
host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1;
|
||||
if (has_tuning_timer) {
|
||||
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
||||
del_timer_sync(&host->tuning_timer);
|
||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
||||
}
|
||||
|
||||
ret = mmc_suspend_host(host->mmc);
|
||||
if (ret) {
|
||||
if (has_tuning_timer) {
|
||||
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
||||
host->flags |= SDHCI_NEEDS_RETUNING;
|
||||
mod_timer(&host->tuning_timer, jiffies +
|
||||
host->tuning_count * HZ);
|
||||
|
@ -2450,8 +2471,7 @@ int sdhci_resume_host(struct sdhci_host *host)
|
|||
host->ops->platform_resume(host);
|
||||
|
||||
/* Set the re-tuning expiration flag */
|
||||
if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
|
||||
(host->tuning_mode == SDHCI_TUNING_MODE_1))
|
||||
if (host->flags & SDHCI_USING_RETUNING_TIMER)
|
||||
host->flags |= SDHCI_NEEDS_RETUNING;
|
||||
|
||||
return ret;
|
||||
|
@ -2490,8 +2510,7 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
|||
int ret = 0;
|
||||
|
||||
/* Disable tuning since we are suspending */
|
||||
if (host->version >= SDHCI_SPEC_300 &&
|
||||
host->tuning_mode == SDHCI_TUNING_MODE_1) {
|
||||
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
|
||||
del_timer_sync(&host->tuning_timer);
|
||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
||||
}
|
||||
|
@ -2532,8 +2551,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host)
|
|||
sdhci_do_enable_preset_value(host, true);
|
||||
|
||||
/* Set the re-tuning expiration flag */
|
||||
if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
|
||||
(host->tuning_mode == SDHCI_TUNING_MODE_1))
|
||||
if (host->flags & SDHCI_USING_RETUNING_TIMER)
|
||||
host->flags |= SDHCI_NEEDS_RETUNING;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
@ -2584,7 +2602,7 @@ EXPORT_SYMBOL_GPL(sdhci_alloc_host);
|
|||
int sdhci_add_host(struct sdhci_host *host)
|
||||
{
|
||||
struct mmc_host *mmc;
|
||||
u32 caps[2];
|
||||
u32 caps[2] = {0, 0};
|
||||
u32 max_current_caps;
|
||||
unsigned int ocr_avail;
|
||||
int ret;
|
||||
|
@ -2614,8 +2632,10 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
caps[0] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ? host->caps :
|
||||
sdhci_readl(host, SDHCI_CAPABILITIES);
|
||||
|
||||
caps[1] = (host->version >= SDHCI_SPEC_300) ?
|
||||
sdhci_readl(host, SDHCI_CAPABILITIES_1) : 0;
|
||||
if (host->version >= SDHCI_SPEC_300)
|
||||
caps[1] = (host->quirks & SDHCI_QUIRK_MISSING_CAPS) ?
|
||||
host->caps1 :
|
||||
sdhci_readl(host, SDHCI_CAPABILITIES_1);
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
|
||||
host->flags |= SDHCI_USE_SDMA;
|
||||
|
@ -2779,7 +2799,7 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
||||
|
||||
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
|
||||
mmc_card_is_removable(mmc))
|
||||
!(host->mmc->caps & MMC_CAP_NONREMOVABLE))
|
||||
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
/* Any UHS-I mode in caps implies SDR12 and SDR25 support. */
|
||||
|
@ -2837,6 +2857,30 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
SDHCI_RETUNING_MODE_SHIFT;
|
||||
|
||||
ocr_avail = 0;
|
||||
|
||||
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
|
||||
if (IS_ERR(host->vmmc)) {
|
||||
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
||||
host->vmmc = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
if (host->vmmc) {
|
||||
ret = regulator_is_supported_voltage(host->vmmc, 3300000,
|
||||
3300000);
|
||||
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330)))
|
||||
caps[0] &= ~SDHCI_CAN_VDD_330;
|
||||
ret = regulator_is_supported_voltage(host->vmmc, 3000000,
|
||||
3000000);
|
||||
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300)))
|
||||
caps[0] &= ~SDHCI_CAN_VDD_300;
|
||||
ret = regulator_is_supported_voltage(host->vmmc, 1800000,
|
||||
1800000);
|
||||
if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180)))
|
||||
caps[0] &= ~SDHCI_CAN_VDD_180;
|
||||
}
|
||||
#endif /* CONFIG_REGULATOR */
|
||||
|
||||
/*
|
||||
* According to SD Host Controller spec v3.00, if the Host System
|
||||
* can afford more than 150mA, Host Driver should set XPC to 1. Also
|
||||
|
@ -2845,55 +2889,45 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
* value.
|
||||
*/
|
||||
max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
|
||||
if (!max_current_caps && host->vmmc) {
|
||||
u32 curr = regulator_get_current_limit(host->vmmc);
|
||||
if (curr > 0) {
|
||||
|
||||
/* convert to SDHCI_MAX_CURRENT format */
|
||||
curr = curr/1000; /* convert to mA */
|
||||
curr = curr/SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
|
||||
curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
|
||||
max_current_caps =
|
||||
(curr << SDHCI_MAX_CURRENT_330_SHIFT) |
|
||||
(curr << SDHCI_MAX_CURRENT_300_SHIFT) |
|
||||
(curr << SDHCI_MAX_CURRENT_180_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
if (caps[0] & SDHCI_CAN_VDD_330) {
|
||||
int max_current_330;
|
||||
|
||||
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
max_current_330 = ((max_current_caps &
|
||||
mmc->max_current_330 = ((max_current_caps &
|
||||
SDHCI_MAX_CURRENT_330_MASK) >>
|
||||
SDHCI_MAX_CURRENT_330_SHIFT) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
|
||||
if (max_current_330 > 150)
|
||||
mmc->caps |= MMC_CAP_SET_XPC_330;
|
||||
}
|
||||
if (caps[0] & SDHCI_CAN_VDD_300) {
|
||||
int max_current_300;
|
||||
|
||||
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
|
||||
|
||||
max_current_300 = ((max_current_caps &
|
||||
mmc->max_current_300 = ((max_current_caps &
|
||||
SDHCI_MAX_CURRENT_300_MASK) >>
|
||||
SDHCI_MAX_CURRENT_300_SHIFT) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
|
||||
if (max_current_300 > 150)
|
||||
mmc->caps |= MMC_CAP_SET_XPC_300;
|
||||
}
|
||||
if (caps[0] & SDHCI_CAN_VDD_180) {
|
||||
int max_current_180;
|
||||
|
||||
ocr_avail |= MMC_VDD_165_195;
|
||||
|
||||
max_current_180 = ((max_current_caps &
|
||||
mmc->max_current_180 = ((max_current_caps &
|
||||
SDHCI_MAX_CURRENT_180_MASK) >>
|
||||
SDHCI_MAX_CURRENT_180_SHIFT) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
|
||||
if (max_current_180 > 150)
|
||||
mmc->caps |= MMC_CAP_SET_XPC_180;
|
||||
|
||||
/* Maximum current capabilities of the host at 1.8V */
|
||||
if (max_current_180 >= 800)
|
||||
mmc->caps |= MMC_CAP_MAX_CURRENT_800;
|
||||
else if (max_current_180 >= 600)
|
||||
mmc->caps |= MMC_CAP_MAX_CURRENT_600;
|
||||
else if (max_current_180 >= 400)
|
||||
mmc->caps |= MMC_CAP_MAX_CURRENT_400;
|
||||
else
|
||||
mmc->caps |= MMC_CAP_MAX_CURRENT_200;
|
||||
}
|
||||
|
||||
mmc->ocr_avail = ocr_avail;
|
||||
|
@ -2992,13 +3026,10 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
|
||||
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
|
||||
mmc_hostname(mmc), host);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to request IRQ %d: %d\n",
|
||||
mmc_hostname(mmc), host->irq, ret);
|
||||
goto untasklet;
|
||||
|
||||
host->vmmc = regulator_get(mmc_dev(mmc), "vmmc");
|
||||
if (IS_ERR(host->vmmc)) {
|
||||
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
||||
host->vmmc = NULL;
|
||||
}
|
||||
|
||||
sdhci_init(host, 0);
|
||||
|
@ -3016,8 +3047,11 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
host->led.brightness_set = sdhci_led_control;
|
||||
|
||||
ret = led_classdev_register(mmc_dev(mmc), &host->led);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to register LED device: %d\n",
|
||||
mmc_hostname(mmc), ret);
|
||||
goto reset;
|
||||
}
|
||||
#endif
|
||||
|
||||
mmiowb();
|
||||
|
@ -3081,8 +3115,6 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|||
free_irq(host->irq, host);
|
||||
|
||||
del_timer_sync(&host->timer);
|
||||
if (host->version >= SDHCI_SPEC_300)
|
||||
del_timer_sync(&host->tuning_timer);
|
||||
|
||||
tasklet_kill(&host->card_tasklet);
|
||||
tasklet_kill(&host->finish_tasklet);
|
||||
|
|
|
@ -205,6 +205,7 @@
|
|||
#define SDHCI_CAPABILITIES_1 0x44
|
||||
|
||||
#define SDHCI_MAX_CURRENT 0x48
|
||||
#define SDHCI_MAX_CURRENT_LIMIT 0xFF
|
||||
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
|
||||
#define SDHCI_MAX_CURRENT_330_SHIFT 0
|
||||
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/sh_mmcif.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_qos.h>
|
||||
|
@ -384,6 +386,9 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
|
|||
struct sh_dmae_slave *tx, *rx;
|
||||
host->dma_active = false;
|
||||
|
||||
if (!pdata)
|
||||
return;
|
||||
|
||||
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
||||
if (pdata->dma) {
|
||||
dev_warn(&host->pd->dev,
|
||||
|
@ -444,13 +449,14 @@ static void sh_mmcif_release_dma(struct sh_mmcif_host *host)
|
|||
static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
|
||||
{
|
||||
struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
|
||||
bool sup_pclk = p ? p->sup_pclk : false;
|
||||
|
||||
sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE);
|
||||
sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR);
|
||||
|
||||
if (!clk)
|
||||
return;
|
||||
if (p->sup_pclk && clk == host->clk)
|
||||
if (sup_pclk && clk == host->clk)
|
||||
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK);
|
||||
else
|
||||
sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR &
|
||||
|
@ -892,21 +898,15 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
|
||||
switch (mrq->cmd->opcode) {
|
||||
/* MMCIF does not support SD/SDIO command */
|
||||
case SD_IO_SEND_OP_COND:
|
||||
case MMC_SLEEP_AWAKE: /* = SD_IO_SEND_OP_COND (5) */
|
||||
case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
|
||||
if ((mrq->cmd->flags & MMC_CMD_MASK) != MMC_CMD_BCR)
|
||||
break;
|
||||
case MMC_APP_CMD:
|
||||
host->state = STATE_IDLE;
|
||||
mrq->cmd->error = -ETIMEDOUT;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
|
||||
if (!mrq->data) {
|
||||
/* send_if_cond cmd (not support) */
|
||||
host->state = STATE_IDLE;
|
||||
mrq->cmd->error = -ETIMEDOUT;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -916,10 +916,35 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
sh_mmcif_start_cmd(host, mrq);
|
||||
}
|
||||
|
||||
static int sh_mmcif_clk_update(struct sh_mmcif_host *host)
|
||||
{
|
||||
int ret = clk_enable(host->hclk);
|
||||
|
||||
if (!ret) {
|
||||
host->clk = clk_get_rate(host->hclk);
|
||||
host->mmc->f_max = host->clk / 2;
|
||||
host->mmc->f_min = host->clk / 512;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios)
|
||||
{
|
||||
struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
if (pd && pd->set_pwr)
|
||||
pd->set_pwr(host->pd, ios->power_mode != MMC_POWER_OFF);
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
/* Errors ignored... */
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
|
||||
ios->power_mode ? ios->vdd : 0);
|
||||
}
|
||||
|
||||
static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||
struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
@ -937,6 +962,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
sh_mmcif_request_dma(host, host->pd->dev.platform_data);
|
||||
host->card_present = true;
|
||||
}
|
||||
sh_mmcif_set_power(host, ios);
|
||||
} else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
|
||||
/* clock stop */
|
||||
sh_mmcif_clock_control(host, 0);
|
||||
|
@ -948,9 +974,10 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
}
|
||||
if (host->power) {
|
||||
pm_runtime_put(&host->pd->dev);
|
||||
clk_disable(host->hclk);
|
||||
host->power = false;
|
||||
if (p->down_pwr && ios->power_mode == MMC_POWER_OFF)
|
||||
p->down_pwr(host->pd);
|
||||
if (ios->power_mode == MMC_POWER_OFF)
|
||||
sh_mmcif_set_power(host, ios);
|
||||
}
|
||||
host->state = STATE_IDLE;
|
||||
return;
|
||||
|
@ -958,8 +985,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
|
||||
if (ios->clock) {
|
||||
if (!host->power) {
|
||||
if (p->set_pwr)
|
||||
p->set_pwr(host->pd, ios->power_mode);
|
||||
sh_mmcif_clk_update(host);
|
||||
pm_runtime_get_sync(&host->pd->dev);
|
||||
host->power = true;
|
||||
sh_mmcif_sync_reset(host);
|
||||
|
@ -975,8 +1001,12 @@ static int sh_mmcif_get_cd(struct mmc_host *mmc)
|
|||
{
|
||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||
struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
|
||||
int ret = mmc_gpio_get_cd(mmc);
|
||||
|
||||
if (!p->get_cd)
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
if (!p || !p->get_cd)
|
||||
return -ENOSYS;
|
||||
else
|
||||
return p->get_cd(host->pd);
|
||||
|
@ -1242,12 +1272,28 @@ static void mmcif_timeout_work(struct work_struct *work)
|
|||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
static void sh_mmcif_init_ocr(struct sh_mmcif_host *host)
|
||||
{
|
||||
struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
mmc_regulator_get_supply(mmc);
|
||||
|
||||
if (!pd)
|
||||
return;
|
||||
|
||||
if (!mmc->ocr_avail)
|
||||
mmc->ocr_avail = pd->ocr;
|
||||
else if (pd->ocr)
|
||||
dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
|
||||
}
|
||||
|
||||
static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0, irq[2];
|
||||
struct mmc_host *mmc;
|
||||
struct sh_mmcif_host *host;
|
||||
struct sh_mmcif_plat_data *pd;
|
||||
struct sh_mmcif_plat_data *pd = pdev->dev.platform_data;
|
||||
struct resource *res;
|
||||
void __iomem *reg;
|
||||
char clk_name[8];
|
||||
|
@ -1268,42 +1314,26 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "ioremap error.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
pd = pdev->dev.platform_data;
|
||||
if (!pd) {
|
||||
dev_err(&pdev->dev, "sh_mmcif plat data error.\n");
|
||||
ret = -ENXIO;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev);
|
||||
if (!mmc) {
|
||||
ret = -ENOMEM;
|
||||
goto clean_up;
|
||||
goto ealloch;
|
||||
}
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->addr = reg;
|
||||
host->timeout = 1000;
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id);
|
||||
host->hclk = clk_get(&pdev->dev, clk_name);
|
||||
if (IS_ERR(host->hclk)) {
|
||||
dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
|
||||
ret = PTR_ERR(host->hclk);
|
||||
goto clean_up1;
|
||||
}
|
||||
clk_enable(host->hclk);
|
||||
host->clk = clk_get_rate(host->hclk);
|
||||
host->pd = pdev;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
mmc->ops = &sh_mmcif_ops;
|
||||
mmc->f_max = host->clk / 2;
|
||||
mmc->f_min = host->clk / 512;
|
||||
if (pd->ocr)
|
||||
mmc->ocr_avail = pd->ocr;
|
||||
sh_mmcif_init_ocr(host);
|
||||
|
||||
mmc->caps = MMC_CAP_MMC_HIGHSPEED;
|
||||
if (pd->caps)
|
||||
if (pd && pd->caps)
|
||||
mmc->caps |= pd->caps;
|
||||
mmc->max_segs = 32;
|
||||
mmc->max_blk_size = 512;
|
||||
|
@ -1311,34 +1341,52 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
sh_mmcif_sync_reset(host);
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
host->power = false;
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id);
|
||||
host->hclk = clk_get(&pdev->dev, clk_name);
|
||||
if (IS_ERR(host->hclk)) {
|
||||
ret = PTR_ERR(host->hclk);
|
||||
dev_err(&pdev->dev, "cannot get clock \"%s\": %d\n", clk_name, ret);
|
||||
goto eclkget;
|
||||
}
|
||||
ret = sh_mmcif_clk_update(host);
|
||||
if (ret < 0)
|
||||
goto eclkupdate;
|
||||
|
||||
ret = pm_runtime_resume(&pdev->dev);
|
||||
if (ret < 0)
|
||||
goto clean_up2;
|
||||
goto eresume;
|
||||
|
||||
INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work);
|
||||
|
||||
sh_mmcif_sync_reset(host);
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
|
||||
ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n");
|
||||
goto clean_up3;
|
||||
goto ereqirq0;
|
||||
}
|
||||
ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
|
||||
goto clean_up4;
|
||||
goto ereqirq1;
|
||||
}
|
||||
|
||||
if (pd && pd->use_cd_gpio) {
|
||||
ret = mmc_gpio_request_cd(mmc, pd->cd_gpio);
|
||||
if (ret < 0)
|
||||
goto erqcd;
|
||||
}
|
||||
|
||||
clk_disable(host->hclk);
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret < 0)
|
||||
goto clean_up5;
|
||||
goto emmcaddh;
|
||||
|
||||
dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
|
||||
|
||||
|
@ -1347,19 +1395,23 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
|
||||
return ret;
|
||||
|
||||
clean_up5:
|
||||
emmcaddh:
|
||||
if (pd && pd->use_cd_gpio)
|
||||
mmc_gpio_free_cd(mmc);
|
||||
erqcd:
|
||||
free_irq(irq[1], host);
|
||||
clean_up4:
|
||||
ereqirq1:
|
||||
free_irq(irq[0], host);
|
||||
clean_up3:
|
||||
ereqirq0:
|
||||
pm_runtime_suspend(&pdev->dev);
|
||||
clean_up2:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
eresume:
|
||||
clk_disable(host->hclk);
|
||||
clean_up1:
|
||||
eclkupdate:
|
||||
clk_put(host->hclk);
|
||||
eclkget:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
mmc_free_host(mmc);
|
||||
clean_up:
|
||||
if (reg)
|
||||
ealloch:
|
||||
iounmap(reg);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1367,13 +1419,18 @@ clean_up:
|
|||
static int __devexit sh_mmcif_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
|
||||
struct sh_mmcif_plat_data *pd = pdev->dev.platform_data;
|
||||
int irq[2];
|
||||
|
||||
host->dying = true;
|
||||
clk_enable(host->hclk);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
dev_pm_qos_hide_latency_limit(&pdev->dev);
|
||||
|
||||
if (pd && pd->use_cd_gpio)
|
||||
mmc_gpio_free_cd(host->mmc);
|
||||
|
||||
mmc_remove_host(host->mmc);
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
|
||||
|
@ -1395,9 +1452,9 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
clk_disable(host->hclk);
|
||||
mmc_free_host(host->mmc);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
clk_disable(host->hclk);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
|
@ -1406,24 +1463,18 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
|
|||
#ifdef CONFIG_PM
|
||||
static int sh_mmcif_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
|
||||
struct sh_mmcif_host *host = dev_get_drvdata(dev);
|
||||
int ret = mmc_suspend_host(host->mmc);
|
||||
|
||||
if (!ret) {
|
||||
if (!ret)
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
clk_disable(host->hclk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sh_mmcif_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
clk_enable(host->hclk);
|
||||
struct sh_mmcif_host *host = dev_get_drvdata(dev);
|
||||
|
||||
return mmc_resume_host(host->mmc);
|
||||
}
|
||||
|
@ -1432,6 +1483,12 @@ static int sh_mmcif_resume(struct device *dev)
|
|||
#define sh_mmcif_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct of_device_id mmcif_of_match[] = {
|
||||
{ .compatible = "renesas,sh-mmcif" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mmcif_of_match);
|
||||
|
||||
static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
|
||||
.suspend = sh_mmcif_suspend,
|
||||
.resume = sh_mmcif_resume,
|
||||
|
@ -1443,6 +1500,8 @@ static struct platform_driver sh_mmcif_driver = {
|
|||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.pm = &sh_mmcif_dev_pm_ops,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mmcif_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
@ -39,11 +40,31 @@ struct sh_mobile_sdhi {
|
|||
struct tmio_mmc_dma dma_priv;
|
||||
};
|
||||
|
||||
static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
int ret = clk_enable(priv->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*f = clk_get_rate(priv->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
||||
static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state)
|
||||
{
|
||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
|
||||
if (p && p->set_pwr)
|
||||
p->set_pwr(pdev, state);
|
||||
}
|
||||
|
||||
|
@ -51,10 +72,7 @@ static int sh_mobile_sdhi_get_cd(struct platform_device *pdev)
|
|||
{
|
||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
|
||||
if (p && p->get_cd)
|
||||
return p->get_cd(pdev);
|
||||
else
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
|
||||
|
@ -116,13 +134,15 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
mmc_data = &priv->mmc_data;
|
||||
p->pdata = mmc_data;
|
||||
|
||||
if (p) {
|
||||
p->pdata = mmc_data;
|
||||
if (p->init) {
|
||||
ret = p->init(pdev, &sdhi_ops);
|
||||
if (ret)
|
||||
goto einit;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
|
||||
priv->clk = clk_get(&pdev->dev, clk_name);
|
||||
|
@ -132,9 +152,8 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
goto eclkget;
|
||||
}
|
||||
|
||||
mmc_data->hclk = clk_get_rate(priv->clk);
|
||||
mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
|
||||
mmc_data->get_cd = sh_mobile_sdhi_get_cd;
|
||||
mmc_data->clk_enable = sh_mobile_sdhi_clk_enable;
|
||||
mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
|
||||
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
|
||||
if (p) {
|
||||
mmc_data->flags = p->tmio_flags;
|
||||
|
@ -142,7 +161,12 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
|
||||
mmc_data->ocr_mask = p->tmio_ocr_mask;
|
||||
mmc_data->capabilities |= p->tmio_caps;
|
||||
mmc_data->capabilities2 |= p->tmio_caps2;
|
||||
mmc_data->cd_gpio = p->cd_gpio;
|
||||
if (p->set_pwr)
|
||||
mmc_data->set_pwr = sh_mobile_sdhi_set_pwr;
|
||||
if (p->get_cd)
|
||||
mmc_data->get_cd = sh_mobile_sdhi_get_cd;
|
||||
|
||||
if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
|
||||
priv->param_tx.slave_id = p->dma_slave_tx;
|
||||
|
@ -248,7 +272,7 @@ eirq_card_detect:
|
|||
eprobe:
|
||||
clk_put(priv->clk);
|
||||
eclkget:
|
||||
if (p->cleanup)
|
||||
if (p && p->cleanup)
|
||||
p->cleanup(pdev);
|
||||
einit:
|
||||
kfree(priv);
|
||||
|
@ -263,6 +287,7 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
|
|||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
int i = 0, irq;
|
||||
|
||||
if (p)
|
||||
p->pdata = NULL;
|
||||
|
||||
tmio_mmc_host_remove(host);
|
||||
|
@ -276,7 +301,7 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
|
|||
|
||||
clk_put(priv->clk);
|
||||
|
||||
if (p->cleanup)
|
||||
if (p && p->cleanup)
|
||||
p->cleanup(pdev);
|
||||
|
||||
kfree(priv);
|
||||
|
@ -291,11 +316,18 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
|
|||
.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 = {
|
||||
.driver = {
|
||||
.name = "sh_mobile_sdhi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tmio_mmc_dev_pm_ops,
|
||||
.of_match_table = sh_mobile_sdhi_of_match,
|
||||
},
|
||||
.probe = sh_mobile_sdhi_probe,
|
||||
.remove = __devexit_p(sh_mobile_sdhi_remove),
|
||||
|
|
|
@ -34,8 +34,9 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
#include <linux/mmc/cd-gpio.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/mmc/tmio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pagemap.h>
|
||||
|
@ -305,8 +306,8 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
|
|||
int c = cmd->opcode;
|
||||
u32 irq_mask = TMIO_MASK_CMD;
|
||||
|
||||
/* Command 12 is handled by hardware */
|
||||
if (cmd->opcode == 12 && !cmd->arg) {
|
||||
/* CMD12 is handled by hardware */
|
||||
if (cmd->opcode == MMC_STOP_TRANSMISSION && !cmd->arg) {
|
||||
sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x001);
|
||||
return 0;
|
||||
}
|
||||
|
@ -449,7 +450,7 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host)
|
|||
}
|
||||
|
||||
if (stop) {
|
||||
if (stop->opcode == 12 && !stop->arg)
|
||||
if (stop->opcode == MMC_STOP_TRANSMISSION && !stop->arg)
|
||||
sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x000);
|
||||
else
|
||||
BUG();
|
||||
|
@ -751,6 +752,34 @@ fail:
|
|||
mmc_request_done(mmc, mrq);
|
||||
}
|
||||
|
||||
static int tmio_mmc_clk_update(struct mmc_host *mmc)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
int ret;
|
||||
|
||||
if (!pdata->clk_enable)
|
||||
return -ENOTSUPP;
|
||||
|
||||
ret = pdata->clk_enable(host->pdev, &mmc->f_max);
|
||||
if (!ret)
|
||||
mmc->f_min = mmc->f_max / 512;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tmio_mmc_set_power(struct tmio_mmc_host *host, struct mmc_ios *ios)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
if (host->set_pwr)
|
||||
host->set_pwr(host->pdev, ios->power_mode != MMC_POWER_OFF);
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
/* Errors ignored... */
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
|
||||
ios->power_mode ? ios->vdd : 0);
|
||||
}
|
||||
|
||||
/* Set MMC clock / power.
|
||||
* Note: This controller uses a simple divider scheme therefore it cannot
|
||||
* run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as
|
||||
|
@ -797,25 +826,29 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
*/
|
||||
if (ios->power_mode == MMC_POWER_ON && ios->clock) {
|
||||
if (!host->power) {
|
||||
tmio_mmc_clk_update(mmc);
|
||||
pm_runtime_get_sync(dev);
|
||||
host->power = true;
|
||||
}
|
||||
tmio_mmc_set_clock(host, ios->clock);
|
||||
/* power up SD bus */
|
||||
if (host->set_pwr)
|
||||
host->set_pwr(host->pdev, 1);
|
||||
tmio_mmc_set_power(host, ios);
|
||||
/* start bus clock */
|
||||
tmio_mmc_clk_start(host);
|
||||
} else if (ios->power_mode != MMC_POWER_UP) {
|
||||
if (host->set_pwr && ios->power_mode == MMC_POWER_OFF)
|
||||
host->set_pwr(host->pdev, 0);
|
||||
if (ios->power_mode == MMC_POWER_OFF)
|
||||
tmio_mmc_set_power(host, ios);
|
||||
if (host->power) {
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
tmio_mmc_clk_stop(host);
|
||||
host->power = false;
|
||||
pm_runtime_put(dev);
|
||||
if (pdata->clk_disable)
|
||||
pdata->clk_disable(host->pdev);
|
||||
}
|
||||
tmio_mmc_clk_stop(host);
|
||||
}
|
||||
|
||||
if (host->power) {
|
||||
switch (ios->bus_width) {
|
||||
case MMC_BUS_WIDTH_1:
|
||||
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
|
||||
|
@ -824,6 +857,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Let things settle. delay taken from winCE driver */
|
||||
udelay(140);
|
||||
|
@ -841,6 +875,9 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc)
|
|||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
int ret = mmc_gpio_get_ro(mmc);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
|
||||
(sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
|
||||
|
@ -850,6 +887,9 @@ static int tmio_mmc_get_cd(struct mmc_host *mmc)
|
|||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
int ret = mmc_gpio_get_cd(mmc);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
if (!pdata->get_cd)
|
||||
return -ENOSYS;
|
||||
|
@ -865,6 +905,19 @@ static const struct mmc_host_ops tmio_mmc_ops = {
|
|||
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
|
||||
};
|
||||
|
||||
static void tmio_mmc_init_ocr(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
mmc_regulator_get_supply(mmc);
|
||||
|
||||
if (!mmc->ocr_avail)
|
||||
mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
else if (pdata->ocr_mask)
|
||||
dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
|
||||
}
|
||||
|
||||
int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||
struct platform_device *pdev,
|
||||
struct tmio_mmc_data *pdata)
|
||||
|
@ -904,18 +957,14 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
|
||||
mmc->ops = &tmio_mmc_ops;
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA | pdata->capabilities;
|
||||
mmc->f_max = pdata->hclk;
|
||||
mmc->f_min = mmc->f_max / 512;
|
||||
mmc->caps2 = pdata->capabilities2;
|
||||
mmc->max_segs = 32;
|
||||
mmc->max_blk_size = 512;
|
||||
mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) *
|
||||
mmc->max_segs;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
if (pdata->ocr_mask)
|
||||
mmc->ocr_avail = pdata->ocr_mask;
|
||||
else
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
tmio_mmc_init_ocr(_host);
|
||||
|
||||
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
|
||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||
|
@ -927,6 +976,11 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
if (ret < 0)
|
||||
goto pm_disable;
|
||||
|
||||
if (tmio_mmc_clk_update(mmc) < 0) {
|
||||
mmc->f_max = pdata->hclk;
|
||||
mmc->f_min = mmc->f_max / 512;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 4 different scenarios for the card detection:
|
||||
* 1) an external gpio irq handles the cd (best for power savings)
|
||||
|
@ -937,7 +991,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
* While we increment the runtime PM counter for all scenarios when
|
||||
* the mmc core activates us by calling an appropriate set_ios(), we
|
||||
* must additionally ensure that in case 2) the tmio mmc hardware stays
|
||||
* additionally ensure that in case 2) the tmio mmc hardware stays
|
||||
* powered on during runtime for the card detection to work.
|
||||
*/
|
||||
if (_host->native_hotplug)
|
||||
|
@ -948,6 +1001,17 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
|
||||
_host->sdcard_irq_mask = sd_ctrl_read32(_host, CTL_IRQ_MASK);
|
||||
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
||||
|
||||
/* Unmask the IRQs we want to know about */
|
||||
if (!_host->chan_rx)
|
||||
irq_mask |= TMIO_MASK_READOP;
|
||||
if (!_host->chan_tx)
|
||||
irq_mask |= TMIO_MASK_WRITEOP;
|
||||
if (!_host->native_hotplug)
|
||||
irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
|
||||
|
||||
_host->sdcard_irq_mask &= ~irq_mask;
|
||||
|
||||
if (pdata->flags & TMIO_MMC_SDIO_IRQ)
|
||||
tmio_mmc_enable_sdio_irq(mmc, 0);
|
||||
|
||||
|
@ -961,22 +1025,18 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
/* See if we also get DMA */
|
||||
tmio_mmc_request_dma(_host, pdata);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
ret = mmc_add_host(mmc);
|
||||
if (pdata->clk_disable)
|
||||
pdata->clk_disable(pdev);
|
||||
if (ret < 0) {
|
||||
tmio_mmc_host_remove(_host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
|
||||
|
||||
/* Unmask the IRQs we want to know about */
|
||||
if (!_host->chan_rx)
|
||||
irq_mask |= TMIO_MASK_READOP;
|
||||
if (!_host->chan_tx)
|
||||
irq_mask |= TMIO_MASK_WRITEOP;
|
||||
if (!_host->native_hotplug)
|
||||
irq_mask &= ~(TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT);
|
||||
|
||||
tmio_mmc_enable_mmc_irqs(_host, irq_mask);
|
||||
|
||||
if (pdata->flags & TMIO_MMC_USE_GPIO_CD) {
|
||||
ret = mmc_cd_gpio_request(mmc, pdata->cd_gpio);
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio);
|
||||
if (ret < 0) {
|
||||
tmio_mmc_host_remove(_host);
|
||||
return ret;
|
||||
|
@ -1008,7 +1068,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
|||
* This means we can miss a card-eject, but this is anyway
|
||||
* possible, because of delayed processing of hotplug events.
|
||||
*/
|
||||
mmc_cd_gpio_free(mmc);
|
||||
mmc_gpio_free_cd(mmc);
|
||||
|
||||
if (!host->native_hotplug)
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
|
|
@ -101,6 +101,7 @@ struct tmio_mmc_host;
|
|||
struct tmio_mmc_data {
|
||||
unsigned int hclk;
|
||||
unsigned long capabilities;
|
||||
unsigned long capabilities2;
|
||||
unsigned long flags;
|
||||
u32 ocr_mask; /* available voltages */
|
||||
struct tmio_mmc_dma *dma;
|
||||
|
@ -110,6 +111,9 @@ struct tmio_mmc_data {
|
|||
void (*set_clk_div)(struct platform_device *host, int state);
|
||||
int (*get_cd)(struct platform_device *host);
|
||||
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
||||
/* clock management callbacks */
|
||||
int (*clk_enable)(struct platform_device *pdev, unsigned int *f);
|
||||
void (*clk_disable)(struct platform_device *pdev);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -149,6 +149,7 @@ struct sd_switch_caps {
|
|||
#define SD_SET_CURRENT_LIMIT_400 1
|
||||
#define SD_SET_CURRENT_LIMIT_600 2
|
||||
#define SD_SET_CURRENT_LIMIT_800 3
|
||||
#define SD_SET_CURRENT_NO_CHANGE (-1)
|
||||
|
||||
#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
|
||||
#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Generic GPIO card-detect helper header
|
||||
*
|
||||
* Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef MMC_CD_GPIO_H
|
||||
#define MMC_CD_GPIO_H
|
||||
|
||||
struct mmc_host;
|
||||
int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio);
|
||||
void mmc_cd_gpio_free(struct mmc_host *host);
|
||||
|
||||
#endif
|
|
@ -11,6 +11,7 @@
|
|||
#define LINUX_MMC_HOST_H
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fault-inject.h>
|
||||
|
@ -150,11 +151,31 @@ struct mmc_async_req {
|
|||
int (*err_check) (struct mmc_card *, struct mmc_async_req *);
|
||||
};
|
||||
|
||||
struct mmc_hotplug {
|
||||
unsigned int irq;
|
||||
/**
|
||||
* struct mmc_slot - MMC slot functions
|
||||
*
|
||||
* @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL
|
||||
* @lock: protect the @handler_priv pointer
|
||||
* @handler_priv: MMC/SD-card slot context
|
||||
*
|
||||
* Some MMC/SD host controllers implement slot-functions like card and
|
||||
* write-protect detection natively. However, a large number of controllers
|
||||
* leave these functions to the CPU. This struct provides a hook to attach
|
||||
* such slot-function drivers.
|
||||
*/
|
||||
struct mmc_slot {
|
||||
int cd_irq;
|
||||
struct mutex lock;
|
||||
void *handler_priv;
|
||||
};
|
||||
|
||||
struct regulator;
|
||||
|
||||
struct mmc_supply {
|
||||
struct regulator *vmmc; /* Card power supply */
|
||||
struct regulator *vqmmc; /* Optional Vccq supply */
|
||||
};
|
||||
|
||||
struct mmc_host {
|
||||
struct device *parent;
|
||||
struct device class_dev;
|
||||
|
@ -168,6 +189,9 @@ struct mmc_host {
|
|||
u32 ocr_avail_sd; /* SD-specific OCR */
|
||||
u32 ocr_avail_mmc; /* MMC-specific OCR */
|
||||
struct notifier_block pm_notify;
|
||||
u32 max_current_330;
|
||||
u32 max_current_300;
|
||||
u32 max_current_180;
|
||||
|
||||
#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
|
||||
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
|
||||
|
@ -211,16 +235,9 @@ struct mmc_host {
|
|||
#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */
|
||||
#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */
|
||||
#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */
|
||||
#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA current at 3.3V */
|
||||
#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA current at 3.0V */
|
||||
#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA current at 1.8V */
|
||||
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
|
||||
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
|
||||
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
|
||||
#define MMC_CAP_MAX_CURRENT_200 (1 << 26) /* Host max current limit is 200mA */
|
||||
#define MMC_CAP_MAX_CURRENT_400 (1 << 27) /* Host max current limit is 400mA */
|
||||
#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */
|
||||
#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */
|
||||
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
|
||||
#define MMC_CAP_HW_RESET (1 << 31) /* Hardware reset */
|
||||
|
||||
|
@ -238,6 +255,8 @@ struct mmc_host {
|
|||
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
|
||||
#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */
|
||||
#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */
|
||||
#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */
|
||||
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
unsigned int power_notify_type;
|
||||
|
@ -290,7 +309,7 @@ struct mmc_host {
|
|||
|
||||
struct delayed_work detect;
|
||||
int detect_change; /* card detect flag */
|
||||
struct mmc_hotplug hotplug;
|
||||
struct mmc_slot slot;
|
||||
|
||||
const struct mmc_bus_ops *bus_ops; /* current bus driver */
|
||||
unsigned int bus_refs; /* reference counter */
|
||||
|
@ -309,6 +328,7 @@ struct mmc_host {
|
|||
#ifdef CONFIG_REGULATOR
|
||||
bool regulator_enabled; /* regulator state */
|
||||
#endif
|
||||
struct mmc_supply supply;
|
||||
|
||||
struct dentry *debugfs_root;
|
||||
|
||||
|
@ -357,13 +377,12 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
|||
wake_up_process(host->sdio_irq_thread);
|
||||
}
|
||||
|
||||
struct regulator;
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
int mmc_regulator_get_ocrmask(struct regulator *supply);
|
||||
int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit);
|
||||
int mmc_regulator_get_supply(struct mmc_host *mmc);
|
||||
#else
|
||||
static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
|
||||
{
|
||||
|
@ -376,6 +395,11 @@ static inline int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mmc_regulator_get_supply(struct mmc_host *mmc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mmc_card_awake(struct mmc_host *host);
|
||||
|
|
|
@ -122,6 +122,7 @@ struct sdhci_host {
|
|||
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
|
||||
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
||||
#define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */
|
||||
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
|
||||
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
|
||||
|
@ -155,7 +156,8 @@ struct sdhci_host {
|
|||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
|
||||
unsigned int caps; /* Alternative capabilities */
|
||||
unsigned int caps; /* Alternative CAPABILITY_0 */
|
||||
unsigned int caps1; /* Alternative CAPABILITY_1 */
|
||||
|
||||
unsigned int ocr_avail_sdio; /* OCR bit masks */
|
||||
unsigned int ocr_avail_sd;
|
||||
|
|
|
@ -44,6 +44,8 @@ struct sh_mmcif_plat_data {
|
|||
struct sh_mmcif_dma *dma; /* Deprecated. Instead */
|
||||
unsigned int slave_id_tx; /* use embedded slave_id_[tr]x */
|
||||
unsigned int slave_id_rx;
|
||||
bool use_cd_gpio : 1;
|
||||
unsigned int cd_gpio;
|
||||
u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */
|
||||
unsigned long caps;
|
||||
u32 ocr;
|
||||
|
|
|
@ -23,6 +23,7 @@ struct sh_mobile_sdhi_info {
|
|||
int dma_slave_rx;
|
||||
unsigned long tmio_flags;
|
||||
unsigned long tmio_caps;
|
||||
unsigned long tmio_caps2;
|
||||
u32 tmio_ocr_mask; /* available MMC voltages */
|
||||
unsigned int cd_gpio;
|
||||
struct tmio_mmc_data *pdata;
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Generic GPIO card-detect helper header
|
||||
*
|
||||
* Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef MMC_SLOT_GPIO_H
|
||||
#define MMC_SLOT_GPIO_H
|
||||
|
||||
struct mmc_host;
|
||||
|
||||
int mmc_gpio_get_ro(struct mmc_host *host);
|
||||
int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio);
|
||||
void mmc_gpio_free_ro(struct mmc_host *host);
|
||||
|
||||
int mmc_gpio_get_cd(struct mmc_host *host);
|
||||
int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio);
|
||||
void mmc_gpio_free_cd(struct mmc_host *host);
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче