mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'mmc-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson: "MMC core: - Continue to refactor the mmc block code to prepare for blkmq - Move mmc block debugfs into block module - Next step for eMMC CMDQ by adding a new mmc host interface for it - Move Kconfig option MMC_DEBUG from core to host - Some additional minor improvements MMC host: - Declare structs as const when applicable - Explicitly request exclusive reset control when applicable - Improve some error paths and other various cleanups - sdhci: Preparations to support SDHCI OMAP - sdhci: Improve some PM related code - sdhci: Re-factoring and modernizations - sdhci-xenon: Add runtime PM and system sleep support - sdhci-xenon: Add support for eMMC HS400 Enhanced Strobe - sdhci-cadence: Add system sleep support - sdhci-of-at91: Improve system sleep support - dw_mmc: Add support for Hisilicon hi3660 - sunxi: Add support for A83T eMMC - sunxi: Add support for DDR52 mode - meson-gx: Add support for UHS-I SD-cards - meson-gx: Cleanups and improvements - tmio: Fix CMD12 (STOP) handling - tmio: Cleanups and improvements - renesas_sdhi: Add r8a7743/5 support - renesas-sdhi: Add support for R-Car Gen3 SDHI DMAC - renesas_sdhi: Cleanups and improvements" * tag 'mmc-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (145 commits) mmc: renesas_sdhi: Add r8a7743/5 support mmc: meson-gx: fix __ffsdi2 undefined on arm32 mmc: sdhci-xenon: add runtime pm support and reimplement standby mmc: core: Move mmc_start_areq() declaration mmc: mmci: stop building qcom dml as module mmc: sunxi: Reset the device at probe time clk: sunxi-ng: Provide a default reset hook mmc: meson-gx: rework tuning function mmc: meson-gx: change default tx phase mmc: meson-gx: implement voltage switch callback mmc: meson-gx: use CCF to handle the clock phases mmc: meson-gx: implement card_busy callback mmc: meson-gx: simplify interrupt handler mmc: meson-gx: work around clk-stop issue mmc: meson-gx: fix dual data rate mode frequencies mmc: meson-gx: rework clock init function mmc: meson-gx: rework clk_set function mmc: meson-gx: rework set_ios function mmc: meson-gx: cfg init overwrite values mmc: meson-gx: initialize sane clk default before clock register ...
This commit is contained in:
@@ -11,6 +11,8 @@ Required properties:
|
||||
- "renesas,mmcif-r7s72100" for the MMCIF found in r7s72100 SoCs
|
||||
- "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
|
||||
- "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
|
||||
- "renesas,mmcif-r8a7743" for the MMCIF found in r8a7743 SoCs
|
||||
- "renesas,mmcif-r8a7745" for the MMCIF found in r8a7745 SoCs
|
||||
- "renesas,mmcif-r8a7778" for the MMCIF found in r8a7778 SoCs
|
||||
- "renesas,mmcif-r8a7790" for the MMCIF found in r8a7790 SoCs
|
||||
- "renesas,mmcif-r8a7791" for the MMCIF found in r8a7791 SoCs
|
||||
@@ -21,7 +23,7 @@ Required properties:
|
||||
- interrupts: Some SoCs have only 1 shared interrupt, while others have either
|
||||
2 or 3 individual interrupts (error, int, card detect). Below is the number
|
||||
of interrupts for each SoC:
|
||||
1: r8a73a4, r8a7778, r8a7790, r8a7791, r8a7793, r8a7794
|
||||
1: r8a73a4, r8a7743, r8a7745, r8a7778, r8a7790, r8a7791, r8a7793, r8a7794
|
||||
2: r8a7740, sh73a0
|
||||
3: r7s72100
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ Required Properties:
|
||||
- "rockchip,rk3288-dw-mshc": for Rockchip RK3288
|
||||
- "rockchip,rv1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RV1108
|
||||
- "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3036
|
||||
- "rockchip,rk3228-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK322x
|
||||
- "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3328
|
||||
- "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3368
|
||||
- "rockchip,rk3399-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3399
|
||||
|
||||
@@ -12,6 +12,7 @@ Required properties:
|
||||
* "allwinner,sun4i-a10-mmc"
|
||||
* "allwinner,sun5i-a13-mmc"
|
||||
* "allwinner,sun7i-a20-mmc"
|
||||
* "allwinner,sun8i-a83t-emmc"
|
||||
* "allwinner,sun9i-a80-mmc"
|
||||
* "allwinner,sun50i-a64-emmc"
|
||||
* "allwinner,sun50i-a64-mmc"
|
||||
|
||||
@@ -15,6 +15,8 @@ Required properties:
|
||||
"renesas,sdhi-r7s72100" - SDHI IP on R7S72100 SoC
|
||||
"renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
|
||||
"renesas,sdhi-r8a7740" - SDHI IP on R8A7740 SoC
|
||||
"renesas,sdhi-r8a7743" - SDHI IP on R8A7743 SoC
|
||||
"renesas,sdhi-r8a7745" - SDHI IP on R8A7745 SoC
|
||||
"renesas,sdhi-r8a7778" - SDHI IP on R8A7778 SoC
|
||||
"renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC
|
||||
"renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC
|
||||
@@ -33,10 +35,8 @@ Required properties:
|
||||
If 2 clocks are specified by the hardware, you must name them as
|
||||
"core" and "cd". If the controller only has 1 clock, naming is not
|
||||
required.
|
||||
Below is the number clocks for each supported SoC:
|
||||
1: SH73A0, R8A73A4, R8A7740, R8A7778, R8A7779, R8A7790
|
||||
R8A7791, R8A7792, R8A7793, R8A7794, R8A7795, R8A7796
|
||||
2: R7S72100
|
||||
Devices which have more than 1 clock are listed below:
|
||||
2: R7S72100
|
||||
|
||||
Optional properties:
|
||||
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
mmc@0x15000 {
|
||||
compatible = "altr,socfpga-dw-mshc";
|
||||
reg = < 0x15000 0x400 >;
|
||||
num-slots = < 1 >;
|
||||
fifo-depth = < 16 >;
|
||||
card-detect-delay = < 200 >;
|
||||
clocks = <&apbclk>, <&mmcclk>;
|
||||
|
||||
@@ -104,7 +104,6 @@
|
||||
mmc@0x15000 {
|
||||
compatible = "snps,dw-mshc";
|
||||
reg = <0x15000 0x400>;
|
||||
num-slots = <1>;
|
||||
fifo-depth = <1024>;
|
||||
card-detect-delay = <200>;
|
||||
clocks = <&apbclk>, <&mmcclk>;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Common objects
|
||||
lib-$(CONFIG_SUNXI_CCU) += ccu_common.o
|
||||
lib-$(CONFIG_SUNXI_CCU) += ccu_mmc_timing.o
|
||||
lib-$(CONFIG_SUNXI_CCU) += ccu_reset.o
|
||||
|
||||
# Base clock types
|
||||
|
||||
@@ -418,14 +418,8 @@ static SUNXI_CCU_PHASE(mmc1_sample_clk, "mmc1-sample", "mmc1",
|
||||
static SUNXI_CCU_PHASE(mmc1_output_clk, "mmc1-output", "mmc1",
|
||||
0x08c, 8, 3, 0);
|
||||
|
||||
/* TODO Support MMC2 clock's new timing mode. */
|
||||
static SUNXI_CCU_MP_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents,
|
||||
0x090,
|
||||
0, 4, /* M */
|
||||
16, 2, /* P */
|
||||
24, 2, /* mux */
|
||||
BIT(31), /* gate */
|
||||
0);
|
||||
static SUNXI_CCU_MP_MMC_WITH_MUX_GATE(mmc2_clk, "mmc2", mod0_default_parents,
|
||||
0x090, 0);
|
||||
|
||||
static SUNXI_CCU_PHASE(mmc2_sample_clk, "mmc2-sample", "mmc2",
|
||||
0x090, 20, 3, 0);
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
#define CCU_FEATURE_FIXED_POSTDIV BIT(3)
|
||||
#define CCU_FEATURE_ALL_PREDIV BIT(4)
|
||||
#define CCU_FEATURE_LOCK_REG BIT(5)
|
||||
#define CCU_FEATURE_MMC_TIMING_SWITCH BIT(6)
|
||||
|
||||
/* MMC timing mode switch bit */
|
||||
#define CCU_MMC_NEW_TIMING_MODE BIT(30)
|
||||
|
||||
struct device_node;
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2017 Chen-Yu Tsai. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/sunxi-ng.h>
|
||||
|
||||
#include "ccu_common.h"
|
||||
|
||||
/**
|
||||
* sunxi_ccu_set_mmc_timing_mode: Configure the MMC clock timing mode
|
||||
* @clk: clock to be configured
|
||||
* @new_mode: true for new timing mode introduced in A83T and later
|
||||
*
|
||||
* Returns 0 on success, -ENOTSUPP if the clock does not support
|
||||
* switching modes.
|
||||
*/
|
||||
int sunxi_ccu_set_mmc_timing_mode(struct clk *clk, bool new_mode)
|
||||
{
|
||||
struct clk_hw *hw = __clk_get_hw(clk);
|
||||
struct ccu_common *cm = hw_to_ccu_common(hw);
|
||||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (!(cm->features & CCU_FEATURE_MMC_TIMING_SWITCH))
|
||||
return -ENOTSUPP;
|
||||
|
||||
spin_lock_irqsave(cm->lock, flags);
|
||||
|
||||
val = readl(cm->base + cm->reg);
|
||||
if (new_mode)
|
||||
val |= CCU_MMC_NEW_TIMING_MODE;
|
||||
else
|
||||
val &= ~CCU_MMC_NEW_TIMING_MODE;
|
||||
writel(val, cm->base + cm->reg);
|
||||
|
||||
spin_unlock_irqrestore(cm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sunxi_ccu_set_mmc_timing_mode);
|
||||
|
||||
/**
|
||||
* sunxi_ccu_set_mmc_timing_mode: Get the current MMC clock timing mode
|
||||
* @clk: clock to query
|
||||
*
|
||||
* Returns 0 if the clock is in old timing mode, > 0 if it is in
|
||||
* new timing mode, and -ENOTSUPP if the clock does not support
|
||||
* this function.
|
||||
*/
|
||||
int sunxi_ccu_get_mmc_timing_mode(struct clk *clk)
|
||||
{
|
||||
struct clk_hw *hw = __clk_get_hw(clk);
|
||||
struct ccu_common *cm = hw_to_ccu_common(hw);
|
||||
|
||||
if (!(cm->features & CCU_FEATURE_MMC_TIMING_SWITCH))
|
||||
return -ENOTSUPP;
|
||||
|
||||
return !!(readl(cm->base + cm->reg) & CCU_MMC_NEW_TIMING_MODE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sunxi_ccu_get_mmc_timing_mode);
|
||||
@@ -172,3 +172,83 @@ const struct clk_ops ccu_mp_ops = {
|
||||
.recalc_rate = ccu_mp_recalc_rate,
|
||||
.set_rate = ccu_mp_set_rate,
|
||||
};
|
||||
|
||||
/*
|
||||
* Support for MMC timing mode switching
|
||||
*
|
||||
* The MMC clocks on some SoCs support switching between old and
|
||||
* new timing modes. A platform specific API is provided to query
|
||||
* and set the timing mode on supported SoCs.
|
||||
*
|
||||
* In addition, a special class of ccu_mp_ops is provided, which
|
||||
* takes in to account the timing mode switch. When the new timing
|
||||
* mode is active, the clock output rate is halved. This new class
|
||||
* is a wrapper around the generic ccu_mp_ops. When clock rates
|
||||
* are passed through to ccu_mp_ops callbacks, they are doubled
|
||||
* if the new timing mode bit is set, to account for the post
|
||||
* divider. Conversely, when clock rates are passed back, they
|
||||
* are halved if the mode bit is set.
|
||||
*/
|
||||
|
||||
static unsigned long ccu_mp_mmc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long rate = ccu_mp_recalc_rate(hw, parent_rate);
|
||||
struct ccu_common *cm = hw_to_ccu_common(hw);
|
||||
u32 val = readl(cm->base + cm->reg);
|
||||
|
||||
if (val & CCU_MMC_NEW_TIMING_MODE)
|
||||
return rate / 2;
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int ccu_mp_mmc_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct ccu_common *cm = hw_to_ccu_common(hw);
|
||||
u32 val = readl(cm->base + cm->reg);
|
||||
int ret;
|
||||
|
||||
/* adjust the requested clock rate */
|
||||
if (val & CCU_MMC_NEW_TIMING_MODE) {
|
||||
req->rate *= 2;
|
||||
req->min_rate *= 2;
|
||||
req->max_rate *= 2;
|
||||
}
|
||||
|
||||
ret = ccu_mp_determine_rate(hw, req);
|
||||
|
||||
/* re-adjust the requested clock rate back */
|
||||
if (val & CCU_MMC_NEW_TIMING_MODE) {
|
||||
req->rate /= 2;
|
||||
req->min_rate /= 2;
|
||||
req->max_rate /= 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ccu_mp_mmc_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_common *cm = hw_to_ccu_common(hw);
|
||||
u32 val = readl(cm->base + cm->reg);
|
||||
|
||||
if (val & CCU_MMC_NEW_TIMING_MODE)
|
||||
rate *= 2;
|
||||
|
||||
return ccu_mp_set_rate(hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
const struct clk_ops ccu_mp_mmc_ops = {
|
||||
.disable = ccu_mp_disable,
|
||||
.enable = ccu_mp_enable,
|
||||
.is_enabled = ccu_mp_is_enabled,
|
||||
|
||||
.get_parent = ccu_mp_get_parent,
|
||||
.set_parent = ccu_mp_set_parent,
|
||||
|
||||
.determine_rate = ccu_mp_mmc_determine_rate,
|
||||
.recalc_rate = ccu_mp_mmc_recalc_rate,
|
||||
.set_rate = ccu_mp_mmc_set_rate,
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#ifndef _CCU_MP_H_
|
||||
#define _CCU_MP_H_
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include "ccu_common.h"
|
||||
@@ -74,4 +75,33 @@ static inline struct ccu_mp *hw_to_ccu_mp(struct clk_hw *hw)
|
||||
|
||||
extern const struct clk_ops ccu_mp_ops;
|
||||
|
||||
/*
|
||||
* Special class of M-P clock that supports MMC timing modes
|
||||
*
|
||||
* Since the MMC clock registers all follow the same layout, we can
|
||||
* simplify the macro for this particular case. In addition, as
|
||||
* switching modes also affects the output clock rate, we need to
|
||||
* have CLK_GET_RATE_NOCACHE for all these types of clocks.
|
||||
*/
|
||||
|
||||
#define SUNXI_CCU_MP_MMC_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
|
||||
_flags) \
|
||||
struct ccu_mp _struct = { \
|
||||
.enable = BIT(31), \
|
||||
.m = _SUNXI_CCU_DIV(0, 4), \
|
||||
.p = _SUNXI_CCU_DIV(16, 2), \
|
||||
.mux = _SUNXI_CCU_MUX(24, 2), \
|
||||
.common = { \
|
||||
.reg = _reg, \
|
||||
.features = CCU_FEATURE_MMC_TIMING_SWITCH, \
|
||||
.hw.init = CLK_HW_INIT_PARENTS(_name, \
|
||||
_parents, \
|
||||
&ccu_mp_mmc_ops, \
|
||||
CLK_GET_RATE_NOCACHE | \
|
||||
_flags), \
|
||||
} \
|
||||
}
|
||||
|
||||
extern const struct clk_ops ccu_mp_mmc_ops;
|
||||
|
||||
#endif /* _CCU_MP_H_ */
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/reset-controller.h>
|
||||
|
||||
@@ -49,7 +50,18 @@ static int ccu_reset_deassert(struct reset_controller_dev *rcdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccu_reset_reset(struct reset_controller_dev *rcdev,
|
||||
unsigned long id)
|
||||
{
|
||||
ccu_reset_assert(rcdev, id);
|
||||
udelay(10);
|
||||
ccu_reset_deassert(rcdev, id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct reset_control_ops ccu_reset_ops = {
|
||||
.assert = ccu_reset_assert,
|
||||
.deassert = ccu_reset_deassert,
|
||||
.reset = ccu_reset_reset,
|
||||
};
|
||||
|
||||
@@ -12,13 +12,6 @@ menuconfig MMC
|
||||
If you want MMC/SD/SDIO support, you should say Y here and
|
||||
also to your specific host controller driver.
|
||||
|
||||
config MMC_DEBUG
|
||||
bool "MMC debugging"
|
||||
depends on MMC != n
|
||||
help
|
||||
This is an option for use by developers; most people should
|
||||
say N here. This enables MMC core and driver debugging.
|
||||
|
||||
if MMC
|
||||
|
||||
source "drivers/mmc/core/Kconfig"
|
||||
|
||||
@@ -2,7 +2,5 @@
|
||||
# Makefile for the kernel mmc device drivers.
|
||||
#
|
||||
|
||||
subdir-ccflags-$(CONFIG_MMC_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_MMC) += core/
|
||||
obj-$(subst m,y,$(CONFIG_MMC)) += host/
|
||||
|
||||
+212
-59
@@ -36,6 +36,7 @@
|
||||
#include <linux/compat.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <linux/mmc/ioctl.h>
|
||||
#include <linux/mmc/card.h>
|
||||
@@ -126,7 +127,7 @@ module_param(perdev_minors, int, 0444);
|
||||
MODULE_PARM_DESC(perdev_minors, "Minors numbers to allocate per device");
|
||||
|
||||
static inline int mmc_blk_part_switch(struct mmc_card *card,
|
||||
struct mmc_blk_data *md);
|
||||
unsigned int part_type);
|
||||
|
||||
static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
|
||||
{
|
||||
@@ -188,7 +189,6 @@ static ssize_t power_ro_lock_store(struct device *dev,
|
||||
{
|
||||
int ret;
|
||||
struct mmc_blk_data *md, *part_md;
|
||||
struct mmc_card *card;
|
||||
struct mmc_queue *mq;
|
||||
struct request *req;
|
||||
unsigned long set;
|
||||
@@ -201,7 +201,6 @@ static ssize_t power_ro_lock_store(struct device *dev,
|
||||
|
||||
md = mmc_blk_get(dev_to_disk(dev));
|
||||
mq = &md->queue;
|
||||
card = md->queue.card;
|
||||
|
||||
/* Dispatch locking to the block layer */
|
||||
req = blk_get_request(mq->queue, REQ_OP_DRV_OUT, __GFP_RECLAIM);
|
||||
@@ -489,7 +488,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
|
||||
err = mmc_blk_part_switch(card, md);
|
||||
err = mmc_blk_part_switch(card, md->part_type);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -554,35 +553,20 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md,
|
||||
struct mmc_ioc_cmd __user *ic_ptr)
|
||||
{
|
||||
struct mmc_blk_ioc_data *idata;
|
||||
struct mmc_blk_ioc_data *idatas[1];
|
||||
struct mmc_blk_data *md;
|
||||
struct mmc_queue *mq;
|
||||
struct mmc_card *card;
|
||||
int err = 0, ioc_err = 0;
|
||||
struct request *req;
|
||||
|
||||
/*
|
||||
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
|
||||
* whole block device, not on a partition. This prevents overspray
|
||||
* between sibling partitions.
|
||||
*/
|
||||
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
|
||||
return -EPERM;
|
||||
|
||||
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
|
||||
if (IS_ERR(idata))
|
||||
return PTR_ERR(idata);
|
||||
|
||||
md = mmc_blk_get(bdev->bd_disk);
|
||||
if (!md) {
|
||||
err = -EINVAL;
|
||||
goto cmd_err;
|
||||
}
|
||||
|
||||
card = md->queue.card;
|
||||
if (IS_ERR(card)) {
|
||||
err = PTR_ERR(card);
|
||||
@@ -598,7 +582,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
__GFP_RECLAIM);
|
||||
idatas[0] = idata;
|
||||
req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL;
|
||||
req_to_mmc_queue_req(req)->idata = idatas;
|
||||
req_to_mmc_queue_req(req)->drv_op_data = idatas;
|
||||
req_to_mmc_queue_req(req)->ioc_count = 1;
|
||||
blk_execute_rq(mq->queue, NULL, req, 0);
|
||||
ioc_err = req_to_mmc_queue_req(req)->drv_op_result;
|
||||
@@ -606,33 +590,22 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
blk_put_request(req);
|
||||
|
||||
cmd_done:
|
||||
mmc_blk_put(md);
|
||||
cmd_err:
|
||||
kfree(idata->buf);
|
||||
kfree(idata);
|
||||
return ioc_err ? ioc_err : err;
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
|
||||
static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md,
|
||||
struct mmc_ioc_multi_cmd __user *user)
|
||||
{
|
||||
struct mmc_blk_ioc_data **idata = NULL;
|
||||
struct mmc_ioc_cmd __user *cmds = user->cmds;
|
||||
struct mmc_card *card;
|
||||
struct mmc_blk_data *md;
|
||||
struct mmc_queue *mq;
|
||||
int i, err = 0, ioc_err = 0;
|
||||
__u64 num_of_cmds;
|
||||
struct request *req;
|
||||
|
||||
/*
|
||||
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
|
||||
* whole block device, not on a partition. This prevents overspray
|
||||
* between sibling partitions.
|
||||
*/
|
||||
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
|
||||
return -EPERM;
|
||||
|
||||
if (copy_from_user(&num_of_cmds, &user->num_of_cmds,
|
||||
sizeof(num_of_cmds)))
|
||||
return -EFAULT;
|
||||
@@ -656,16 +629,10 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
|
||||
}
|
||||
}
|
||||
|
||||
md = mmc_blk_get(bdev->bd_disk);
|
||||
if (!md) {
|
||||
err = -EINVAL;
|
||||
goto cmd_err;
|
||||
}
|
||||
|
||||
card = md->queue.card;
|
||||
if (IS_ERR(card)) {
|
||||
err = PTR_ERR(card);
|
||||
goto cmd_done;
|
||||
goto cmd_err;
|
||||
}
|
||||
|
||||
|
||||
@@ -677,7 +644,7 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
|
||||
idata[0]->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
|
||||
__GFP_RECLAIM);
|
||||
req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL;
|
||||
req_to_mmc_queue_req(req)->idata = idata;
|
||||
req_to_mmc_queue_req(req)->drv_op_data = idata;
|
||||
req_to_mmc_queue_req(req)->ioc_count = num_of_cmds;
|
||||
blk_execute_rq(mq->queue, NULL, req, 0);
|
||||
ioc_err = req_to_mmc_queue_req(req)->drv_op_result;
|
||||
@@ -688,8 +655,6 @@ static int mmc_blk_ioctl_multi_cmd(struct block_device *bdev,
|
||||
|
||||
blk_put_request(req);
|
||||
|
||||
cmd_done:
|
||||
mmc_blk_put(md);
|
||||
cmd_err:
|
||||
for (i = 0; i < num_of_cmds; i++) {
|
||||
kfree(idata[i]->buf);
|
||||
@@ -699,16 +664,47 @@ cmd_err:
|
||||
return ioc_err ? ioc_err : err;
|
||||
}
|
||||
|
||||
static int mmc_blk_check_blkdev(struct block_device *bdev)
|
||||
{
|
||||
/*
|
||||
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
|
||||
* whole block device, not on a partition. This prevents overspray
|
||||
* between sibling partitions.
|
||||
*/
|
||||
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
|
||||
return -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case MMC_IOC_CMD:
|
||||
return mmc_blk_ioctl_cmd(bdev,
|
||||
(struct mmc_ioc_cmd __user *)arg);
|
||||
ret = mmc_blk_check_blkdev(bdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
md = mmc_blk_get(bdev->bd_disk);
|
||||
if (!md)
|
||||
return -EINVAL;
|
||||
ret = mmc_blk_ioctl_cmd(md,
|
||||
(struct mmc_ioc_cmd __user *)arg);
|
||||
mmc_blk_put(md);
|
||||
return ret;
|
||||
case MMC_IOC_MULTI_CMD:
|
||||
return mmc_blk_ioctl_multi_cmd(bdev,
|
||||
(struct mmc_ioc_multi_cmd __user *)arg);
|
||||
ret = mmc_blk_check_blkdev(bdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
md = mmc_blk_get(bdev->bd_disk);
|
||||
if (!md)
|
||||
return -EINVAL;
|
||||
ret = mmc_blk_ioctl_multi_cmd(md,
|
||||
(struct mmc_ioc_multi_cmd __user *)arg);
|
||||
mmc_blk_put(md);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -765,29 +761,29 @@ static int mmc_blk_part_switch_post(struct mmc_card *card,
|
||||
}
|
||||
|
||||
static inline int mmc_blk_part_switch(struct mmc_card *card,
|
||||
struct mmc_blk_data *md)
|
||||
unsigned int part_type)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev);
|
||||
|
||||
if (main_md->part_curr == md->part_type)
|
||||
if (main_md->part_curr == part_type)
|
||||
return 0;
|
||||
|
||||
if (mmc_card_mmc(card)) {
|
||||
u8 part_config = card->ext_csd.part_config;
|
||||
|
||||
ret = mmc_blk_part_switch_pre(card, md->part_type);
|
||||
ret = mmc_blk_part_switch_pre(card, part_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
|
||||
part_config |= md->part_type;
|
||||
part_config |= part_type;
|
||||
|
||||
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_PART_CONFIG, part_config,
|
||||
card->ext_csd.part_time);
|
||||
if (ret) {
|
||||
mmc_blk_part_switch_post(card, md->part_type);
|
||||
mmc_blk_part_switch_post(card, part_type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -796,7 +792,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
|
||||
ret = mmc_blk_part_switch_post(card, main_md->part_curr);
|
||||
}
|
||||
|
||||
main_md->part_curr = md->part_type;
|
||||
main_md->part_curr = part_type;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1139,7 +1135,7 @@ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
|
||||
int part_err;
|
||||
|
||||
main_md->part_curr = main_md->part_type;
|
||||
part_err = mmc_blk_part_switch(host->card, md);
|
||||
part_err = mmc_blk_part_switch(host->card, md->part_type);
|
||||
if (part_err) {
|
||||
/*
|
||||
* We have failed to get back into the correct
|
||||
@@ -1178,6 +1174,10 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
|
||||
struct mmc_queue_req *mq_rq;
|
||||
struct mmc_card *card = mq->card;
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
struct mmc_blk_data *main_md = dev_get_drvdata(&card->dev);
|
||||
struct mmc_blk_ioc_data **idata;
|
||||
u8 **ext_csd;
|
||||
u32 status;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@@ -1185,14 +1185,15 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
|
||||
|
||||
switch (mq_rq->drv_op) {
|
||||
case MMC_DRV_OP_IOCTL:
|
||||
idata = mq_rq->drv_op_data;
|
||||
for (i = 0, ret = 0; i < mq_rq->ioc_count; i++) {
|
||||
ret = __mmc_blk_ioctl_cmd(card, md, mq_rq->idata[i]);
|
||||
ret = __mmc_blk_ioctl_cmd(card, md, idata[i]);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
/* Always switch back to main area after RPMB access */
|
||||
if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
|
||||
mmc_blk_part_switch(card, dev_get_drvdata(&card->dev));
|
||||
mmc_blk_part_switch(card, main_md->part_type);
|
||||
break;
|
||||
case MMC_DRV_OP_BOOT_WP:
|
||||
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
|
||||
@@ -1206,6 +1207,15 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
|
||||
card->ext_csd.boot_ro_lock |=
|
||||
EXT_CSD_BOOT_WP_B_PWR_WP_EN;
|
||||
break;
|
||||
case MMC_DRV_OP_GET_CARD_STATUS:
|
||||
ret = mmc_send_status(card, &status);
|
||||
if (!ret)
|
||||
ret = status;
|
||||
break;
|
||||
case MMC_DRV_OP_GET_EXT_CSD:
|
||||
ext_csd = mq_rq->drv_op_data;
|
||||
ret = mmc_get_ext_csd(card, ext_csd);
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: unknown driver specific operation\n",
|
||||
md->disk->disk_name);
|
||||
@@ -1943,7 +1953,7 @@ void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
/* claim host only for the first request */
|
||||
mmc_get_card(card);
|
||||
|
||||
ret = mmc_blk_part_switch(card, md);
|
||||
ret = mmc_blk_part_switch(card, md->part_type);
|
||||
if (ret) {
|
||||
if (req) {
|
||||
blk_end_request_all(req, BLK_STS_IOERR);
|
||||
@@ -2024,8 +2034,20 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
int devidx, ret;
|
||||
|
||||
devidx = ida_simple_get(&mmc_blk_ida, 0, max_devices, GFP_KERNEL);
|
||||
if (devidx < 0)
|
||||
if (devidx < 0) {
|
||||
/*
|
||||
* We get -ENOSPC because there are no more any available
|
||||
* devidx. The reason may be that, either userspace haven't yet
|
||||
* unmounted the partitions, which postpones mmc_blk_release()
|
||||
* from being called, or the device has more partitions than
|
||||
* what we support.
|
||||
*/
|
||||
if (devidx == -ENOSPC)
|
||||
dev_err(mmc_dev(card->host),
|
||||
"no more device IDs available\n");
|
||||
|
||||
return ERR_PTR(devidx);
|
||||
}
|
||||
|
||||
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
|
||||
if (!md) {
|
||||
@@ -2283,6 +2305,134 @@ force_ro_fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static int mmc_dbg_card_status_get(void *data, u64 *val)
|
||||
{
|
||||
struct mmc_card *card = data;
|
||||
struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
|
||||
struct mmc_queue *mq = &md->queue;
|
||||
struct request *req;
|
||||
int ret;
|
||||
|
||||
/* Ask the block layer about the card status */
|
||||
req = blk_get_request(mq->queue, REQ_OP_DRV_IN, __GFP_RECLAIM);
|
||||
req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_CARD_STATUS;
|
||||
blk_execute_rq(mq->queue, NULL, req, 0);
|
||||
ret = req_to_mmc_queue_req(req)->drv_op_result;
|
||||
if (ret >= 0) {
|
||||
*val = ret;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
|
||||
NULL, "%08llx\n");
|
||||
|
||||
/* That is two digits * 512 + 1 for newline */
|
||||
#define EXT_CSD_STR_LEN 1025
|
||||
|
||||
static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct mmc_card *card = inode->i_private;
|
||||
struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
|
||||
struct mmc_queue *mq = &md->queue;
|
||||
struct request *req;
|
||||
char *buf;
|
||||
ssize_t n = 0;
|
||||
u8 *ext_csd;
|
||||
int err, i;
|
||||
|
||||
buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Ask the block layer for the EXT CSD */
|
||||
req = blk_get_request(mq->queue, REQ_OP_DRV_IN, __GFP_RECLAIM);
|
||||
req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_GET_EXT_CSD;
|
||||
req_to_mmc_queue_req(req)->drv_op_data = &ext_csd;
|
||||
blk_execute_rq(mq->queue, NULL, req, 0);
|
||||
err = req_to_mmc_queue_req(req)->drv_op_result;
|
||||
if (err) {
|
||||
pr_err("FAILED %d\n", err);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
for (i = 0; i < 512; i++)
|
||||
n += sprintf(buf + n, "%02x", ext_csd[i]);
|
||||
n += sprintf(buf + n, "\n");
|
||||
|
||||
if (n != EXT_CSD_STR_LEN) {
|
||||
err = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
filp->private_data = buf;
|
||||
kfree(ext_csd);
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
kfree(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char *buf = filp->private_data;
|
||||
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos,
|
||||
buf, EXT_CSD_STR_LEN);
|
||||
}
|
||||
|
||||
static int mmc_ext_csd_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
kfree(file->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations mmc_dbg_ext_csd_fops = {
|
||||
.open = mmc_ext_csd_open,
|
||||
.read = mmc_ext_csd_read,
|
||||
.release = mmc_ext_csd_release,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static int mmc_blk_add_debugfs(struct mmc_card *card)
|
||||
{
|
||||
struct dentry *root;
|
||||
|
||||
if (!card->debugfs_root)
|
||||
return 0;
|
||||
|
||||
root = card->debugfs_root;
|
||||
|
||||
if (mmc_card_mmc(card) || mmc_card_sd(card)) {
|
||||
if (!debugfs_create_file("status", S_IRUSR, root, card,
|
||||
&mmc_dbg_card_status_fops))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (mmc_card_mmc(card)) {
|
||||
if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
|
||||
&mmc_dbg_ext_csd_fops))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
static int mmc_blk_add_debugfs(struct mmc_card *card)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
static int mmc_blk_probe(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *md, *part_md;
|
||||
@@ -2319,6 +2469,9 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Add two debugfs entries */
|
||||
mmc_blk_add_debugfs(card);
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&card->dev, 3000);
|
||||
pm_runtime_use_autosuspend(&card->dev);
|
||||
|
||||
@@ -2346,7 +2499,7 @@ static void mmc_blk_remove(struct mmc_card *card)
|
||||
mmc_blk_remove_parts(card, md);
|
||||
pm_runtime_get_sync(&card->dev);
|
||||
mmc_claim_host(card->host);
|
||||
mmc_blk_part_switch(card, md);
|
||||
mmc_blk_part_switch(card, md->part_type);
|
||||
mmc_release_host(card->host);
|
||||
if (card->type != MMC_TYPE_SD_COMBO)
|
||||
pm_runtime_disable(&card->dev);
|
||||
|
||||
+28
-40
@@ -260,6 +260,9 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
|
||||
trace_mmc_request_start(host, mrq);
|
||||
|
||||
if (host->cqe_on)
|
||||
host->cqe_ops->cqe_off(host);
|
||||
|
||||
host->ops->request(host, mrq);
|
||||
}
|
||||
|
||||
@@ -295,10 +298,8 @@ static void mmc_mrq_pr_debug(struct mmc_host *host, struct mmc_request *mrq)
|
||||
|
||||
static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned int i, sz;
|
||||
unsigned int i, sz = 0;
|
||||
struct scatterlist *sg;
|
||||
#endif
|
||||
|
||||
if (mrq->cmd) {
|
||||
mrq->cmd->error = 0;
|
||||
@@ -314,13 +315,12 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
|
||||
mrq->data->blocks > host->max_blk_count ||
|
||||
mrq->data->blocks * mrq->data->blksz > host->max_req_size)
|
||||
return -EINVAL;
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
sz = 0;
|
||||
|
||||
for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i)
|
||||
sz += sg->length;
|
||||
if (sz != mrq->data->blocks * mrq->data->blksz)
|
||||
return -EINVAL;
|
||||
#endif
|
||||
|
||||
mrq->data->error = 0;
|
||||
mrq->data->mrq = mrq;
|
||||
if (mrq->stop) {
|
||||
@@ -736,8 +736,8 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
|
||||
if (data->flags & MMC_DATA_WRITE)
|
||||
mult <<= card->csd.r2w_factor;
|
||||
|
||||
data->timeout_ns = card->csd.tacc_ns * mult;
|
||||
data->timeout_clks = card->csd.tacc_clks * mult;
|
||||
data->timeout_ns = card->csd.taac_ns * mult;
|
||||
data->timeout_clks = card->csd.taac_clks * mult;
|
||||
|
||||
/*
|
||||
* SD cards also have an upper limit on the timeout.
|
||||
@@ -766,7 +766,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
|
||||
/*
|
||||
* SDHC cards always use these fixed values.
|
||||
*/
|
||||
if (timeout_us > limit_us || mmc_card_blockaddr(card)) {
|
||||
if (timeout_us > limit_us) {
|
||||
data->timeout_ns = limit_us * 1000;
|
||||
data->timeout_clks = 0;
|
||||
}
|
||||
@@ -982,6 +982,9 @@ int mmc_execute_tuning(struct mmc_card *card)
|
||||
if (!host->ops->execute_tuning)
|
||||
return 0;
|
||||
|
||||
if (host->cqe_on)
|
||||
host->cqe_ops->cqe_off(host);
|
||||
|
||||
if (mmc_card_mmc(card))
|
||||
opcode = MMC_SEND_TUNING_BLOCK_HS200;
|
||||
else
|
||||
@@ -1021,6 +1024,9 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
|
||||
*/
|
||||
void mmc_set_initial_state(struct mmc_host *host)
|
||||
{
|
||||
if (host->cqe_on)
|
||||
host->cqe_ops->cqe_off(host);
|
||||
|
||||
mmc_retune_disable(host);
|
||||
|
||||
if (mmc_host_is_spi(host))
|
||||
@@ -1137,11 +1143,11 @@ int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||
voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
|
||||
num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
|
||||
if (!voltage_ranges) {
|
||||
pr_debug("%s: voltage-ranges unspecified\n", np->full_name);
|
||||
pr_debug("%pOF: voltage-ranges unspecified\n", np);
|
||||
return 0;
|
||||
}
|
||||
if (!num_ranges) {
|
||||
pr_err("%s: voltage-ranges empty\n", np->full_name);
|
||||
pr_err("%pOF: voltage-ranges empty\n", np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -1153,8 +1159,8 @@ int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||
be32_to_cpu(voltage_ranges[j]),
|
||||
be32_to_cpu(voltage_ranges[j + 1]));
|
||||
if (!ocr_mask) {
|
||||
pr_err("%s: voltage-range #%d is invalid\n",
|
||||
np->full_name, i);
|
||||
pr_err("%pOF: voltage-range #%d is invalid\n",
|
||||
np, i);
|
||||
return -EINVAL;
|
||||
}
|
||||
*mask |= ocr_mask;
|
||||
@@ -1769,13 +1775,6 @@ void mmc_detach_bus(struct mmc_host *host)
|
||||
static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
|
||||
bool cd_irq)
|
||||
{
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
WARN_ON(host->removed);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the device is configured as wakeup, we prevent a new sleep for
|
||||
* 5 s to give provision for user space to consume the event.
|
||||
@@ -1869,14 +1868,14 @@ static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
|
||||
} else {
|
||||
/* CSD Erase Group Size uses write timeout */
|
||||
unsigned int mult = (10 << card->csd.r2w_factor);
|
||||
unsigned int timeout_clks = card->csd.tacc_clks * mult;
|
||||
unsigned int timeout_clks = card->csd.taac_clks * mult;
|
||||
unsigned int timeout_us;
|
||||
|
||||
/* Avoid overflow: e.g. tacc_ns=80000000 mult=1280 */
|
||||
if (card->csd.tacc_ns < 1000000)
|
||||
timeout_us = (card->csd.tacc_ns * mult) / 1000;
|
||||
/* Avoid overflow: e.g. taac_ns=80000000 mult=1280 */
|
||||
if (card->csd.taac_ns < 1000000)
|
||||
timeout_us = (card->csd.taac_ns * mult) / 1000;
|
||||
else
|
||||
timeout_us = (card->csd.tacc_ns / 1000) * mult;
|
||||
timeout_us = (card->csd.taac_ns / 1000) * mult;
|
||||
|
||||
/*
|
||||
* ios.clock is only a target. The real clock rate might be
|
||||
@@ -2446,10 +2445,9 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
||||
{
|
||||
host->f_init = freq;
|
||||
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
pr_info("%s: %s: trying to init card at %u Hz\n",
|
||||
pr_debug("%s: %s: trying to init card at %u Hz\n",
|
||||
mmc_hostname(host), __func__, host->f_init);
|
||||
#endif
|
||||
|
||||
mmc_power_up(host, host->ocr_avail);
|
||||
|
||||
/*
|
||||
@@ -2646,12 +2644,6 @@ void mmc_start_host(struct mmc_host *host)
|
||||
|
||||
void mmc_stop_host(struct mmc_host *host)
|
||||
{
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->removed = 1;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
if (host->slot.cd_irq >= 0) {
|
||||
if (host->slot.cd_wake_enabled)
|
||||
disable_irq_wake(host->slot.cd_irq);
|
||||
@@ -2686,9 +2678,7 @@ int mmc_power_save_host(struct mmc_host *host)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
pr_info("%s: %s: powering down\n", mmc_hostname(host), __func__);
|
||||
#endif
|
||||
pr_debug("%s: %s: powering down\n", mmc_hostname(host), __func__);
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
@@ -2712,9 +2702,7 @@ int mmc_power_restore_host(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
pr_info("%s: %s: powering up\n", mmc_hostname(host), __func__);
|
||||
#endif
|
||||
pr_debug("%s: %s: powering up\n", mmc_hostname(host), __func__);
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
|
||||
@@ -107,6 +107,12 @@ static inline void mmc_unregister_pm_notifier(struct mmc_host *host) { }
|
||||
void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq);
|
||||
bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq);
|
||||
|
||||
struct mmc_async_req;
|
||||
|
||||
struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
|
||||
struct mmc_async_req *areq,
|
||||
enum mmc_blk_status *ret_stat);
|
||||
|
||||
int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
|
||||
unsigned int arg);
|
||||
int mmc_can_erase(struct mmc_card *card);
|
||||
|
||||
@@ -281,85 +281,6 @@ void mmc_remove_host_debugfs(struct mmc_host *host)
|
||||
debugfs_remove_recursive(host->debugfs_root);
|
||||
}
|
||||
|
||||
static int mmc_dbg_card_status_get(void *data, u64 *val)
|
||||
{
|
||||
struct mmc_card *card = data;
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
mmc_get_card(card);
|
||||
|
||||
ret = mmc_send_status(data, &status);
|
||||
if (!ret)
|
||||
*val = status;
|
||||
|
||||
mmc_put_card(card);
|
||||
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
|
||||
NULL, "%08llx\n");
|
||||
|
||||
#define EXT_CSD_STR_LEN 1025
|
||||
|
||||
static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct mmc_card *card = inode->i_private;
|
||||
char *buf;
|
||||
ssize_t n = 0;
|
||||
u8 *ext_csd;
|
||||
int err, i;
|
||||
|
||||
buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
mmc_get_card(card);
|
||||
err = mmc_get_ext_csd(card, &ext_csd);
|
||||
mmc_put_card(card);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
for (i = 0; i < 512; i++)
|
||||
n += sprintf(buf + n, "%02x", ext_csd[i]);
|
||||
n += sprintf(buf + n, "\n");
|
||||
|
||||
if (n != EXT_CSD_STR_LEN) {
|
||||
err = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
filp->private_data = buf;
|
||||
kfree(ext_csd);
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
kfree(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char *buf = filp->private_data;
|
||||
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos,
|
||||
buf, EXT_CSD_STR_LEN);
|
||||
}
|
||||
|
||||
static int mmc_ext_csd_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
kfree(file->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations mmc_dbg_ext_csd_fops = {
|
||||
.open = mmc_ext_csd_open,
|
||||
.read = mmc_ext_csd_read,
|
||||
.release = mmc_ext_csd_release,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
void mmc_add_card_debugfs(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
@@ -382,16 +303,6 @@ void mmc_add_card_debugfs(struct mmc_card *card)
|
||||
if (!debugfs_create_x32("state", S_IRUSR, root, &card->state))
|
||||
goto err;
|
||||
|
||||
if (mmc_card_mmc(card) || mmc_card_sd(card))
|
||||
if (!debugfs_create_file("status", S_IRUSR, root, card,
|
||||
&mmc_dbg_card_status_fops))
|
||||
goto err;
|
||||
|
||||
if (mmc_card_mmc(card))
|
||||
if (!debugfs_create_file("ext_csd", S_IRUSR, root, card,
|
||||
&mmc_dbg_ext_csd_fops))
|
||||
goto err;
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
|
||||
@@ -111,6 +111,12 @@ void mmc_retune_hold(struct mmc_host *host)
|
||||
host->hold_retune += 1;
|
||||
}
|
||||
|
||||
void mmc_retune_hold_now(struct mmc_host *host)
|
||||
{
|
||||
host->retune_now = 0;
|
||||
host->hold_retune += 1;
|
||||
}
|
||||
|
||||
void mmc_retune_release(struct mmc_host *host)
|
||||
{
|
||||
if (host->hold_retune)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user