You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -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-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 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);
|
||||
+62
-28
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
err = mmc_send_hpi_cmd(card, &status);
|
||||
if (err)
|
||||
pr_debug("%s: abort HPI (%d error)\n",
|
||||
mmc_hostname(card->host), err);
|
||||
switch (R1_CURRENT_STATE(status)) {
|
||||
case R1_STATE_IDLE:
|
||||
case R1_STATE_READY:
|
||||
case R1_STATE_STBY:
|
||||
/*
|
||||
* 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_status(card, &status);
|
||||
if (err)
|
||||
break;
|
||||
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
|
||||
} else
|
||||
pr_debug("%s: Left prg-state\n", mmc_hostname(card->host));
|
||||
err = mmc_send_hpi_cmd(card, &status);
|
||||
if (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 && R1_CURRENT_STATE(status) == R1_STATE_TRAN)
|
||||
break;
|
||||
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) {
|
||||
|
||||
+79
-92
@@ -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,60 +517,80 @@ 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)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_800;
|
||||
else 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_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
|
||||
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 (max_current >= 600)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_600;
|
||||
else if (max_current >= 400)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_400;
|
||||
else if (max_current >= 200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
|
||||
err = mmc_sd_switch(card, 1, 3, current_limit, status);
|
||||
if (err)
|
||||
return err;
|
||||
if (current_limit != SD_SET_CURRENT_NO_CHANGE) {
|
||||
err = mmc_sd_switch(card, 1, 3, current_limit, status);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (((status[15] >> 4) & 0x0F) != current_limit)
|
||||
pr_warning("%s: Problem setting current limit!\n",
|
||||
mmc_hostname(card->host));
|
||||
if (((status[15] >> 4) & 0x0F) != current_limit)
|
||||
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);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user