1
0
Fork 0

MMC core:

- Introduce host claiming by context to support blkmq
  - Preparations for enabling CQE (eMMC CMDQ) requests
  - Re-factorizations to prepare for blkmq support
  - Re-factorizations to prepare for CQE support
  - Fix signal voltage switch for SD cards without power cycle
  - Convert RPMB to a character device
  - Export eMMC revision via sysfs
  - Support eMMC DT binding for fixed driver type
  - Document mmc_regulator_get_supply() API
 
 MMC host:
  - omap_hsmmc: Updated regulator management for PBIAS
  - sdhci-omap: Add new OMAP SDHCI driver
  - meson-mx-sdio: New driver for the Amlogic Meson8 and Meson8b SoCs
  - sdhci-pci: Add support for Intel CDF
  - sdhci-acpi: Fix voltage switch for some Intel host controllers
  - sdhci-msm: Enable delay circuit calibration clocks
  - sdhci-msm: Manage power IRQ properly
  - mediatek: Add support of mt2701/mt2712
  - mediatek: Updates management of clocks and tunings
  - mediatek: Upgrade eMMC HS400 support
  - rtsx_pci: Update tuning for gen3 PCI-Express
  - renesas_sdhi: Support R-Car Gen[123] fallback compatibility strings
  - Catch all errors when getting regulators
  - Various additional improvements and cleanups
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJaCXiaAAoJEP4mhCVzWIwp+c4P/3UptZJJScU4WFS1zXH8h/YK
 mYQ4DOCZqQYihKV4ssbAAjmTkT3RdMkl+rq2s5+ZfVbsu57UqeHNaNXBQWirkXCo
 xBDzZlZFz42g4apKq+ZfmewZTM2fjed8Z7SrDcxPkkyU90QKa/LFFleAyxyXxXr1
 ubKHJtev/oWBRxvzvWPYNUI5ExraxGxp2iGvavoYHNcggAgOSBJoKESvF7er5MRK
 Cx5cuKAESU2dJFQnebjndx3dFgQsZ18RTRXUom50oP3BXF2G3gIBMjxp9VfyuOGV
 xedyE2ALQxr1bxyWWejMdhhiZ+eNZNtiEJKqJ37ArTeBDyDfO/SDVXlkqkAARlYW
 rTzh9olfbuhFdCAo2gxAcCaQXnjgQzDDKnQJs++tQVziXGI9ezc44+A6fNh39nre
 yc/MLIjN96vWRhhFuTeFuENN+n7D2xyy+keXZXrALYYOCPAlKtip6DOgVl3ltNvx
 K1O7/STqdR4OHLK6vgdzi9ai3PmcmSBQwKCqqCH/7mcFm1xtBcxqdrEwORLQ3lCH
 c/tl4lJqJR2BD8U9gipjik0ovWb5p2KKAYhjqEY2qMy14OXs2509Y8GcqQRzqcQc
 FSAfUInooHfDEbUBVsLpTOjMvd6LAx34fTJ9QAanAZ1wdeV8C9DRz3kJh/L/mOmG
 N7/jyJnMV1Izknmpc2MP
 =3Gvx
 -----END PGP SIGNATURE-----

Merge tag 'mmc-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Introduce host claiming by context to support blkmq
   - Preparations for enabling CQE (eMMC CMDQ) requests
   - Re-factorizations to prepare for blkmq support
   - Re-factorizations to prepare for CQE support
   - Fix signal voltage switch for SD cards without power cycle
   - Convert RPMB to a character device
   - Export eMMC revision via sysfs
   - Support eMMC DT binding for fixed driver type
   - Document mmc_regulator_get_supply() API

 MMC host:
   - omap_hsmmc: Updated regulator management for PBIAS
   - sdhci-omap: Add new OMAP SDHCI driver
   - meson-mx-sdio: New driver for the Amlogic Meson8 and Meson8b SoCs
   - sdhci-pci: Add support for Intel CDF
   - sdhci-acpi: Fix voltage switch for some Intel host controllers
   - sdhci-msm: Enable delay circuit calibration clocks
   - sdhci-msm: Manage power IRQ properly
   - mediatek: Add support of mt2701/mt2712
   - mediatek: Updates management of clocks and tunings
   - mediatek: Upgrade eMMC HS400 support
   - rtsx_pci: Update tuning for gen3 PCI-Express
   - renesas_sdhi: Support R-Car Gen[123] fallback compatibility strings
   - Catch all errors when getting regulators
   - Various additional improvements and cleanups"

* tag 'mmc-v4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (91 commits)
  sdhci-fujitsu: add support for setting the CMD_DAT_DELAY attribute
  dt-bindings: sdhci-fujitsu: document cmd-dat-delay property
  mmc: tmio: Replace msleep() of 20ms or less with usleep_range()
  mmc: dw_mmc: Convert timers to use timer_setup()
  mmc: dw_mmc: Cleanup the DTO timer like the CTO one
  mmc: vub300: Use common code in __download_offload_pseudocode()
  mmc: tmio: Use common error handling code in tmio_mmc_host_probe()
  mmc: Convert timers to use timer_setup()
  mmc: sdhci-acpi: Fix voltage switch for some Intel host controllers
  mmc: sdhci-acpi: Let devices define their own private data
  mmc: mediatek: perfer to use rise edge latching for cmd line
  mmc: mediatek: improve eMMC hs400 mode read performance
  mmc: mediatek: add latch-ck support
  mmc: mediatek: add support of source_cg clock
  mmc: mediatek: add stop_clk fix and enhance_rx support
  mmc: mediatek: add busy_check support
  mmc: mediatek: add async fifo and data tune support
  mmc: mediatek: add pad_tune0 support
  mmc: mediatek: make hs400_tune_response only for mt8173
  arm64: dts: mt8173: remove "mediatek, mt8135-mmc" from mmc nodes
  ...
hifive-unleashed-5.1
Linus Torvalds 2017-11-13 10:17:35 -08:00
commit dee02770cd
66 changed files with 3256 additions and 581 deletions

View File

@ -0,0 +1,4 @@
What: /sys/bus/mmc/devices/.../rev
Date: October 2017
Contact: Jin Qian <jinqian@android.com>
Description: Extended CSD revision number

View File

@ -0,0 +1,54 @@
* Amlogic Meson6, Meson8 and Meson8b SDIO/MMC controller
The highspeed MMC host controller on Amlogic SoCs provides an interface
for MMC, SD, SDIO and SDHC types of memory cards.
Supported maximum speeds are the ones of the eMMC standard 4.41 as well
as the speed of SD standard 2.0.
The hardware provides an internal "mux" which allows up to three slots
to be controlled. Only one slot can be accessed at a time.
Required properties:
- compatible : must be one of
- "amlogic,meson8-sdio"
- "amlogic,meson8b-sdio"
along with the generic "amlogic,meson-mx-sdio"
- reg : mmc controller base registers
- interrupts : mmc controller interrupt
- #address-cells : must be 1
- size-cells : must be 0
- clocks : phandle to clock providers
- clock-names : must contain "core" and "clkin"
Required child nodes:
A node for each slot provided by the MMC controller is required.
NOTE: due to a driver limitation currently only one slot (= child node)
is supported!
Required properties on each child node (= slot):
- compatible : must be "mmc-slot" (see mmc.txt within this directory)
- reg : the slot (or "port") ID
Optional properties on each child node (= slot):
- bus-width : must be 1 or 4 (8-bit bus is not supported)
- for cd and all other additional generic mmc parameters
please refer to mmc.txt within this directory
Examples:
mmc@c1108c20 {
compatible = "amlogic,meson8-sdio", "amlogic,meson-mx-sdio";
reg = <0xc1108c20 0x20>;
interrupts = <0 28 1>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&clkc CLKID_SDIO>, <&clkc CLKID_CLK81>;
clock-names = "core", "clkin";
slot@1 {
compatible = "mmc-slot";
reg = <1>;
bus-width = <4>;
};
};

View File

@ -53,6 +53,9 @@ Optional properties:
- no-sdio: controller is limited to send sdio cmd during initialization
- no-sd: controller is limited to send sd cmd during initialization
- no-mmc: controller is limited to send mmc cmd during initialization
- fixed-emmc-driver-type: for non-removable eMMC, enforce this driver type.
The value <n> is the driver type as specified in the eMMC specification
(table 206 in spec version 5.1).
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted"

View File

@ -7,10 +7,18 @@ This file documents differences between the core properties in mmc.txt
and the properties used by the msdc driver.
Required properties:
- compatible: Should be "mediatek,mt8173-mmc","mediatek,mt8135-mmc"
- compatible: value should be either of the following.
"mediatek,mt8135-mmc": for mmc host ip compatible with mt8135
"mediatek,mt8173-mmc": for mmc host ip compatible with mt8173
"mediatek,mt2701-mmc": for mmc host ip compatible with mt2701
"mediatek,mt2712-mmc": for mmc host ip compatible with mt2712
- reg: physical base address of the controller and length
- interrupts: Should contain MSDC interrupt number
- clocks: MSDC source clock, HCLK
- clock-names: "source", "hclk"
- clocks: Should contain phandle for the clock feeding the MMC controller
- clock-names: Should contain the following:
"source" - source clock (required)
"hclk" - HCLK which used for host (required)
"source_cg" - independent source clock gate (required for MT2712)
- pinctrl-names: should be "default", "state_uhs"
- pinctrl-0: should contain default/high speed pin ctrl
- pinctrl-1: should contain uhs mode pin ctrl
@ -30,6 +38,10 @@ Optional properties:
- mediatek,hs400-cmd-resp-sel-rising: HS400 command response sample selection
If present,HS400 command responses are sampled on rising edges.
If not present,HS400 command responses are sampled on falling edges.
- mediatek,latch-ck: Some SoCs do not support enhance_rx, need set correct latch-ck to avoid data crc
error caused by stop clock(fifo full)
Valid range = [0:0x7]. if not present, default value is 0.
applied to compatible "mediatek,mt2701-mmc".
Examples:
mmc0: mmc@11230000 {

View File

@ -15,6 +15,8 @@ Required properties:
Optional properties:
- vqmmc-supply: phandle to the regulator device tree node, mentioned
as the VCCQ/VDD_IO supply in the eMMC/SD specs.
- fujitsu,cmd-dat-delay-select: boolean property indicating that this host
requires the CMD_DAT_DELAY control to be enabled.
Example:

View File

@ -18,6 +18,8 @@ Required properties:
"core" - SDC MMC clock (MCLK) (required)
"bus" - SDCC bus voter clock (optional)
"xo" - TCXO clock (optional)
"cal" - reference clock for RCLK delay calibration (optional)
"sleep" - sleep clock for RCLK delay calibration (optional)
Example:

View File

@ -0,0 +1,16 @@
* TI OMAP SDHCI Controller
Refer to mmc.txt for standard MMC bindings.
Required properties:
- compatible: Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers
- ti,hwmods: Must be "mmc<n>", <n> is controller instance starting 1
Example:
mmc1: mmc@4809c000 {
compatible = "ti,dra7-sdhci";
reg = <0x4809c000 0x400>;
ti,hwmods = "mmc1";
bus-width = <4>;
vmmc-supply = <&vmmc>; /* phandle to regulator node */
};

View File

@ -10,7 +10,7 @@ described in mmc.txt, can be used. Additionally the following tmio_mmc-specific
optional bindings can be used.
Required properties:
- compatible: "renesas,sdhi-shmobile" - a generic sh-mobile SDHI unit
- compatible: should contain one or more of the following:
"renesas,sdhi-sh73a0" - SDHI IP on SH73A0 SoC
"renesas,sdhi-r7s72100" - SDHI IP on R7S72100 SoC
"renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
@ -26,6 +26,16 @@ Required properties:
"renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
"renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
"renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller
"renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller
"renesas,rcar-gen2-sdhi" - a generic R-Car Gen2 or RZ/G1
SDHI controller
"renesas,rcar-gen3-sdhi" - a generic R-Car Gen3 SDHI controller
When compatible with the generic version, nodes must list
the SoC-specific version corresponding to the platform
first followed by the generic version.
- clocks: Most controllers only have 1 clock source per channel. However, on
some variations of this controller, the internal card detection
@ -43,3 +53,61 @@ Optional properties:
- pinctrl-names: should be "default", "state_uhs"
- pinctrl-0: should contain default/high speed pin ctrl
- pinctrl-1: should contain uhs mode pin ctrl
Example: R8A7790 (R-Car H2) SDHI controller nodes
sdhi0: sd@ee100000 {
compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi";
reg = <0 0xee100000 0 0x328>;
interrupts = <GIC_SPI 165 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 314>;
dmas = <&dmac0 0xcd>, <&dmac0 0xce>,
<&dmac1 0xcd>, <&dmac1 0xce>;
dma-names = "tx", "rx", "tx", "rx";
max-frequency = <195000000>;
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
resets = <&cpg 314>;
status = "disabled";
};
sdhi1: sd@ee120000 {
compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi";
reg = <0 0xee120000 0 0x328>;
interrupts = <GIC_SPI 166 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 313>;
dmas = <&dmac0 0xc9>, <&dmac0 0xca>,
<&dmac1 0xc9>, <&dmac1 0xca>;
dma-names = "tx", "rx", "tx", "rx";
max-frequency = <195000000>;
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
resets = <&cpg 313>;
status = "disabled";
};
sdhi2: sd@ee140000 {
compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi";
reg = <0 0xee140000 0 0x100>;
interrupts = <GIC_SPI 167 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 312>;
dmas = <&dmac0 0xc1>, <&dmac0 0xc2>,
<&dmac1 0xc1>, <&dmac1 0xc2>;
dma-names = "tx", "rx", "tx", "rx";
max-frequency = <97500000>;
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
resets = <&cpg 312>;
status = "disabled";
};
sdhi3: sd@ee160000 {
compatible = "renesas,sdhi-r8a7790", "renesas,rcar-gen2-sdhi";
reg = <0 0xee160000 0 0x100>;
interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cpg CPG_MOD 311>;
dmas = <&dmac0 0xd3>, <&dmac0 0xd4>,
<&dmac1 0xd3>, <&dmac1 0xd4>;
dma-names = "tx", "rx", "tx", "rx";
max-frequency = <97500000>;
power-domains = <&sysc R8A7790_PD_ALWAYS_ON>;
resets = <&cpg 311>;
status = "disabled";
};

View File

@ -12067,6 +12067,12 @@ L: linux-mmc@vger.kernel.org
S: Maintained
F: drivers/mmc/host/sdhci-spear.c
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) TI OMAP DRIVER
M: Kishon Vijay Abraham I <kishon@ti.com>
L: linux-mmc@vger.kernel.org
S: Maintained
F: drivers/mmc/host/sdhci-omap.c
SECURE ENCRYPTING DEVICE (SED) OPAL DRIVER
M: Scott Bauer <scott.bauer@intel.com>
M: Jonathan Derrick <jonathan.derrick@intel.com>

View File

@ -682,8 +682,7 @@
};
mmc0: mmc@11230000 {
compatible = "mediatek,mt8173-mmc",
"mediatek,mt8135-mmc";
compatible = "mediatek,mt8173-mmc";
reg = <0 0x11230000 0 0x1000>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_MSDC30_0>,
@ -693,8 +692,7 @@
};
mmc1: mmc@11240000 {
compatible = "mediatek,mt8173-mmc",
"mediatek,mt8135-mmc";
compatible = "mediatek,mt8173-mmc";
reg = <0 0x11240000 0 0x1000>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_MSDC30_1>,
@ -704,8 +702,7 @@
};
mmc2: mmc@11250000 {
compatible = "mediatek,mt8173-mmc",
"mediatek,mt8135-mmc";
compatible = "mediatek,mt8173-mmc";
reg = <0 0x11250000 0 0x1000>;
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_MSDC30_2>,
@ -715,8 +712,7 @@
};
mmc3: mmc@11260000 {
compatible = "mediatek,mt8173-mmc",
"mediatek,mt8135-mmc";
compatible = "mediatek,mt8173-mmc";
reg = <0 0x11260000 0 0x1000>;
interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pericfg CLK_PERI_MSDC30_3>,

View File

@ -28,6 +28,7 @@
#include <linux/hdreg.h>
#include <linux/kdev_t.h>
#include <linux/blkdev.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/scatterlist.h>
#include <linux/string_helpers.h>
@ -86,6 +87,7 @@ static int max_devices;
#define MAX_DEVICES 256
static DEFINE_IDA(mmc_blk_ida);
static DEFINE_IDA(mmc_rpmb_ida);
/*
* There is one mmc_blk_data per slot.
@ -96,6 +98,7 @@ struct mmc_blk_data {
struct gendisk *disk;
struct mmc_queue queue;
struct list_head part;
struct list_head rpmbs;
unsigned int flags;
#define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */
@ -121,6 +124,32 @@ struct mmc_blk_data {
int area_type;
};
/* Device type for RPMB character devices */
static dev_t mmc_rpmb_devt;
/* Bus type for RPMB character devices */
static struct bus_type mmc_rpmb_bus_type = {
.name = "mmc_rpmb",
};
/**
* struct mmc_rpmb_data - special RPMB device type for these areas
* @dev: the device for the RPMB area
* @chrdev: character device for the RPMB area
* @id: unique device ID number
* @part_index: partition index (0 on first)
* @md: parent MMC block device
* @node: list item, so we can put this device on a list
*/
struct mmc_rpmb_data {
struct device dev;
struct cdev chrdev;
int id;
unsigned int part_index;
struct mmc_blk_data *md;
struct list_head node;
};
static DEFINE_MUTEX(open_lock);
module_param(perdev_minors, int, 0444);
@ -299,6 +328,7 @@ struct mmc_blk_ioc_data {
struct mmc_ioc_cmd ic;
unsigned char *buf;
u64 buf_bytes;
struct mmc_rpmb_data *rpmb;
};
static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
@ -437,14 +467,25 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct mmc_request mrq = {};
struct scatterlist sg;
int err;
bool is_rpmb = false;
unsigned int target_part;
u32 status = 0;
if (!card || !md || !idata)
return -EINVAL;
if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
is_rpmb = true;
/*
* The RPMB accesses comes in from the character device, so we
* need to target these explicitly. Else we just target the
* partition type for the block device the ioctl() was issued
* on.
*/
if (idata->rpmb) {
/* Support multiple RPMB partitions */
target_part = idata->rpmb->part_index;
target_part |= EXT_CSD_PART_CONFIG_ACC_RPMB;
} else {
target_part = md->part_type;
}
cmd.opcode = idata->ic.opcode;
cmd.arg = idata->ic.arg;
@ -488,7 +529,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->part_type);
err = mmc_blk_part_switch(card, target_part);
if (err)
return err;
@ -498,7 +539,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
return err;
}
if (is_rpmb) {
if (idata->rpmb) {
err = mmc_set_blockcount(card, data.blocks,
idata->ic.write_flag & (1 << 31));
if (err)
@ -538,7 +579,7 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
if (is_rpmb) {
if (idata->rpmb) {
/*
* Ensure RPMB command has completed by polling CMD13
* "Send Status".
@ -554,7 +595,8 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
}
static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md,
struct mmc_ioc_cmd __user *ic_ptr)
struct mmc_ioc_cmd __user *ic_ptr,
struct mmc_rpmb_data *rpmb)
{
struct mmc_blk_ioc_data *idata;
struct mmc_blk_ioc_data *idatas[1];
@ -566,6 +608,8 @@ static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md,
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
if (IS_ERR(idata))
return PTR_ERR(idata);
/* This will be NULL on non-RPMB ioctl():s */
idata->rpmb = rpmb;
card = md->queue.card;
if (IS_ERR(card)) {
@ -581,7 +625,8 @@ static int mmc_blk_ioctl_cmd(struct mmc_blk_data *md,
idata->ic.write_flag ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN,
__GFP_RECLAIM);
idatas[0] = idata;
req_to_mmc_queue_req(req)->drv_op = MMC_DRV_OP_IOCTL;
req_to_mmc_queue_req(req)->drv_op =
rpmb ? MMC_DRV_OP_IOCTL_RPMB : MMC_DRV_OP_IOCTL;
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);
@ -596,7 +641,8 @@ cmd_done:
}
static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md,
struct mmc_ioc_multi_cmd __user *user)
struct mmc_ioc_multi_cmd __user *user,
struct mmc_rpmb_data *rpmb)
{
struct mmc_blk_ioc_data **idata = NULL;
struct mmc_ioc_cmd __user *cmds = user->cmds;
@ -627,6 +673,8 @@ static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md,
num_of_cmds = i;
goto cmd_err;
}
/* This will be NULL on non-RPMB ioctl():s */
idata[i]->rpmb = rpmb;
}
card = md->queue.card;
@ -643,7 +691,8 @@ static int mmc_blk_ioctl_multi_cmd(struct mmc_blk_data *md,
req = blk_get_request(mq->queue,
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)->drv_op =
rpmb ? MMC_DRV_OP_IOCTL_RPMB : MMC_DRV_OP_IOCTL;
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);
@ -691,7 +740,8 @@ static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
if (!md)
return -EINVAL;
ret = mmc_blk_ioctl_cmd(md,
(struct mmc_ioc_cmd __user *)arg);
(struct mmc_ioc_cmd __user *)arg,
NULL);
mmc_blk_put(md);
return ret;
case MMC_IOC_MULTI_CMD:
@ -702,7 +752,8 @@ static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
if (!md)
return -EINVAL;
ret = mmc_blk_ioctl_multi_cmd(md,
(struct mmc_ioc_multi_cmd __user *)arg);
(struct mmc_ioc_multi_cmd __user *)arg,
NULL);
mmc_blk_put(md);
return ret;
default:
@ -1152,18 +1203,6 @@ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type)
md->reset_done &= ~type;
}
int mmc_access_rpmb(struct mmc_queue *mq)
{
struct mmc_blk_data *md = mq->blkdata;
/*
* If this is a RPMB partition access, return ture
*/
if (md && md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB)
return true;
return false;
}
/*
* The non-block commands come back from the block layer after it queued it and
* processed it with all other requests and then they get issued in this
@ -1174,17 +1213,19 @@ 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;
bool rpmb_ioctl;
u8 **ext_csd;
u32 status;
int ret;
int i;
mq_rq = req_to_mmc_queue_req(req);
rpmb_ioctl = (mq_rq->drv_op == MMC_DRV_OP_IOCTL_RPMB);
switch (mq_rq->drv_op) {
case MMC_DRV_OP_IOCTL:
case MMC_DRV_OP_IOCTL_RPMB:
idata = mq_rq->drv_op_data;
for (i = 0, ret = 0; i < mq_rq->ioc_count; i++) {
ret = __mmc_blk_ioctl_cmd(card, md, idata[i]);
@ -1192,8 +1233,8 @@ static void mmc_blk_issue_drv_op(struct mmc_queue *mq, struct request *req)
break;
}
/* Always switch back to main area after RPMB access */
if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
mmc_blk_part_switch(card, main_md->part_type);
if (rpmb_ioctl)
mmc_blk_part_switch(card, 0);
break;
case MMC_DRV_OP_BOOT_WP:
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
@ -1534,25 +1575,27 @@ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card,
}
static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
int disable_multi, bool *do_rel_wr,
bool *do_data_tag)
int disable_multi, bool *do_rel_wr_p,
bool *do_data_tag_p)
{
struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brq = &mqrq->brq;
struct request *req = mmc_queue_req_to_req(mqrq);
bool do_rel_wr, do_data_tag;
/*
* Reliable writes are used to implement Forced Unit Access and
* are supported only on MMCs.
*/
*do_rel_wr = (req->cmd_flags & REQ_FUA) &&
rq_data_dir(req) == WRITE &&
(md->flags & MMC_BLK_REL_WR);
do_rel_wr = (req->cmd_flags & REQ_FUA) &&
rq_data_dir(req) == WRITE &&
(md->flags & MMC_BLK_REL_WR);
memset(brq, 0, sizeof(struct mmc_blk_request));
brq->mrq.data = &brq->data;
brq->mrq.tag = req->tag;
brq->stop.opcode = MMC_STOP_TRANSMISSION;
brq->stop.arg = 0;
@ -1567,6 +1610,14 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
brq->data.blksz = 512;
brq->data.blocks = blk_rq_sectors(req);
brq->data.blk_addr = blk_rq_pos(req);
/*
* The command queue supports 2 priorities: "high" (1) and "simple" (0).
* The eMMC will give "high" priority tasks priority over "simple"
* priority tasks. Here we always set "simple" priority by not setting
* MMC_DATA_PRIO.
*/
/*
* The block layer doesn't support all sector count
@ -1596,18 +1647,23 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
brq->data.blocks);
}
if (*do_rel_wr)
if (do_rel_wr) {
mmc_apply_rel_rw(brq, card, req);
brq->data.flags |= MMC_DATA_REL_WR;
}
/*
* Data tag is used only during writing meta data to speed
* up write and any subsequent read of this meta data
*/
*do_data_tag = card->ext_csd.data_tag_unit_size &&
(req->cmd_flags & REQ_META) &&
(rq_data_dir(req) == WRITE) &&
((brq->data.blocks * brq->data.blksz) >=
card->ext_csd.data_tag_unit_size);
do_data_tag = card->ext_csd.data_tag_unit_size &&
(req->cmd_flags & REQ_META) &&
(rq_data_dir(req) == WRITE) &&
((brq->data.blocks * brq->data.blksz) >=
card->ext_csd.data_tag_unit_size);
if (do_data_tag)
brq->data.flags |= MMC_DATA_DAT_TAG;
mmc_set_data_timeout(&brq->data, card);
@ -1634,6 +1690,12 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
}
mqrq->areq.mrq = &brq->mrq;
if (do_rel_wr_p)
*do_rel_wr_p = do_rel_wr;
if (do_data_tag_p)
*do_data_tag_p = do_data_tag;
}
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
@ -1948,7 +2010,7 @@ void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
if (req && !mq->qcnt)
/* claim host only for the first request */
mmc_get_card(card);
mmc_get_card(card, NULL);
ret = mmc_blk_part_switch(card, md->part_type);
if (ret) {
@ -2011,7 +2073,7 @@ void mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
out:
if (!mq->qcnt)
mmc_put_card(card);
mmc_put_card(card, NULL);
}
static inline int mmc_blk_readonly(struct mmc_card *card)
@ -2068,6 +2130,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
spin_lock_init(&md->lock);
INIT_LIST_HEAD(&md->part);
INIT_LIST_HEAD(&md->rpmbs);
md->usage = 1;
ret = mmc_init_queue(&md->queue, card, &md->lock, subname);
@ -2186,6 +2249,158 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
return 0;
}
/**
* mmc_rpmb_ioctl() - ioctl handler for the RPMB chardev
* @filp: the character device file
* @cmd: the ioctl() command
* @arg: the argument from userspace
*
* This will essentially just redirect the ioctl()s coming in over to
* the main block device spawning the RPMB character device.
*/
static long mmc_rpmb_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct mmc_rpmb_data *rpmb = filp->private_data;
int ret;
switch (cmd) {
case MMC_IOC_CMD:
ret = mmc_blk_ioctl_cmd(rpmb->md,
(struct mmc_ioc_cmd __user *)arg,
rpmb);
break;
case MMC_IOC_MULTI_CMD:
ret = mmc_blk_ioctl_multi_cmd(rpmb->md,
(struct mmc_ioc_multi_cmd __user *)arg,
rpmb);
break;
default:
ret = -EINVAL;
break;
}
return 0;
}
#ifdef CONFIG_COMPAT
static long mmc_rpmb_ioctl_compat(struct file *filp, unsigned int cmd,
unsigned long arg)
{
return mmc_rpmb_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#endif
static int mmc_rpmb_chrdev_open(struct inode *inode, struct file *filp)
{
struct mmc_rpmb_data *rpmb = container_of(inode->i_cdev,
struct mmc_rpmb_data, chrdev);
get_device(&rpmb->dev);
filp->private_data = rpmb;
mmc_blk_get(rpmb->md->disk);
return nonseekable_open(inode, filp);
}
static int mmc_rpmb_chrdev_release(struct inode *inode, struct file *filp)
{
struct mmc_rpmb_data *rpmb = container_of(inode->i_cdev,
struct mmc_rpmb_data, chrdev);
put_device(&rpmb->dev);
mmc_blk_put(rpmb->md);
return 0;
}
static const struct file_operations mmc_rpmb_fileops = {
.release = mmc_rpmb_chrdev_release,
.open = mmc_rpmb_chrdev_open,
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = mmc_rpmb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mmc_rpmb_ioctl_compat,
#endif
};
static void mmc_blk_rpmb_device_release(struct device *dev)
{
struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev);
ida_simple_remove(&mmc_rpmb_ida, rpmb->id);
kfree(rpmb);
}
static int mmc_blk_alloc_rpmb_part(struct mmc_card *card,
struct mmc_blk_data *md,
unsigned int part_index,
sector_t size,
const char *subname)
{
int devidx, ret;
char rpmb_name[DISK_NAME_LEN];
char cap_str[10];
struct mmc_rpmb_data *rpmb;
/* This creates the minor number for the RPMB char device */
devidx = ida_simple_get(&mmc_rpmb_ida, 0, max_devices, GFP_KERNEL);
if (devidx < 0)
return devidx;
rpmb = kzalloc(sizeof(*rpmb), GFP_KERNEL);
if (!rpmb) {
ida_simple_remove(&mmc_rpmb_ida, devidx);
return -ENOMEM;
}
snprintf(rpmb_name, sizeof(rpmb_name),
"mmcblk%u%s", card->host->index, subname ? subname : "");
rpmb->id = devidx;
rpmb->part_index = part_index;
rpmb->dev.init_name = rpmb_name;
rpmb->dev.bus = &mmc_rpmb_bus_type;
rpmb->dev.devt = MKDEV(MAJOR(mmc_rpmb_devt), rpmb->id);
rpmb->dev.parent = &card->dev;
rpmb->dev.release = mmc_blk_rpmb_device_release;
device_initialize(&rpmb->dev);
dev_set_drvdata(&rpmb->dev, rpmb);
rpmb->md = md;
cdev_init(&rpmb->chrdev, &mmc_rpmb_fileops);
rpmb->chrdev.owner = THIS_MODULE;
ret = cdev_device_add(&rpmb->chrdev, &rpmb->dev);
if (ret) {
pr_err("%s: could not add character device\n", rpmb_name);
goto out_put_device;
}
list_add(&rpmb->node, &md->rpmbs);
string_get_size((u64)size, 512, STRING_UNITS_2,
cap_str, sizeof(cap_str));
pr_info("%s: %s %s partition %u %s, chardev (%d:%d)\n",
rpmb_name, mmc_card_id(card),
mmc_card_name(card), EXT_CSD_PART_CONFIG_ACC_RPMB, cap_str,
MAJOR(mmc_rpmb_devt), rpmb->id);
return 0;
out_put_device:
put_device(&rpmb->dev);
return ret;
}
static void mmc_blk_remove_rpmb_part(struct mmc_rpmb_data *rpmb)
{
cdev_device_del(&rpmb->chrdev, &rpmb->dev);
put_device(&rpmb->dev);
}
/* MMC Physical partitions consist of two boot partitions and
* up to four general purpose partitions.
* For each partition enabled in EXT_CSD a block device will be allocatedi
@ -2194,13 +2409,26 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
{
int idx, ret = 0;
int idx, ret;
if (!mmc_card_mmc(card))
return 0;
for (idx = 0; idx < card->nr_parts; idx++) {
if (card->part[idx].size) {
if (card->part[idx].area_type & MMC_BLK_DATA_AREA_RPMB) {
/*
* RPMB partitions does not provide block access, they
* are only accessed using ioctl():s. Thus create
* special RPMB block devices that do not have a
* backing block queue for these.
*/
ret = mmc_blk_alloc_rpmb_part(card, md,
card->part[idx].part_cfg,
card->part[idx].size >> 9,
card->part[idx].name);
if (ret)
return ret;
} else if (card->part[idx].size) {
ret = mmc_blk_alloc_part(card, md,
card->part[idx].part_cfg,
card->part[idx].size >> 9,
@ -2212,7 +2440,7 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
}
}
return ret;
return 0;
}
static void mmc_blk_remove_req(struct mmc_blk_data *md)
@ -2249,7 +2477,15 @@ static void mmc_blk_remove_parts(struct mmc_card *card,
{
struct list_head *pos, *q;
struct mmc_blk_data *part_md;
struct mmc_rpmb_data *rpmb;
/* Remove RPMB partitions */
list_for_each_safe(pos, q, &md->rpmbs) {
rpmb = list_entry(pos, struct mmc_rpmb_data, node);
list_del(pos);
mmc_blk_remove_rpmb_part(rpmb);
}
/* Remove block partitions */
list_for_each_safe(pos, q, &md->part) {
part_md = list_entry(pos, struct mmc_blk_data, part);
list_del(pos);
@ -2568,6 +2804,17 @@ static int __init mmc_blk_init(void)
{
int res;
res = bus_register(&mmc_rpmb_bus_type);
if (res < 0) {
pr_err("mmcblk: could not register RPMB bus type\n");
return res;
}
res = alloc_chrdev_region(&mmc_rpmb_devt, 0, MAX_DEVICES, "rpmb");
if (res < 0) {
pr_err("mmcblk: failed to allocate rpmb chrdev region\n");
goto out_bus_unreg;
}
if (perdev_minors != CONFIG_MMC_BLOCK_MINORS)
pr_info("mmcblk: using %d minors per device\n", perdev_minors);
@ -2575,16 +2822,20 @@ static int __init mmc_blk_init(void)
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
if (res)
goto out;
goto out_chrdev_unreg;
res = mmc_register_driver(&mmc_driver);
if (res)
goto out2;
goto out_blkdev_unreg;
return 0;
out2:
out_blkdev_unreg:
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
out:
out_chrdev_unreg:
unregister_chrdev_region(mmc_rpmb_devt, MAX_DEVICES);
out_bus_unreg:
bus_unregister(&mmc_rpmb_bus_type);
return res;
}
@ -2592,6 +2843,7 @@ static void __exit mmc_blk_exit(void)
{
mmc_unregister_driver(&mmc_driver);
unregister_blkdev(MMC_BLOCK_MAJOR, "mmc");
unregister_chrdev_region(mmc_rpmb_devt, MAX_DEVICES);
}
module_init(mmc_blk_init);

View File

@ -369,10 +369,17 @@ int mmc_add_card(struct mmc_card *card)
*/
void mmc_remove_card(struct mmc_card *card)
{
struct mmc_host *host = card->host;
#ifdef CONFIG_DEBUG_FS
mmc_remove_card_debugfs(card);
#endif
if (host->cqe_enabled) {
host->cqe_ops->cqe_disable(host);
host->cqe_enabled = false;
}
if (mmc_card_present(card)) {
if (mmc_host_is_spi(card->host)) {
pr_info("%s: SPI card removed\n",

View File

@ -266,7 +266,8 @@ static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
host->ops->request(host, mrq);
}
static void mmc_mrq_pr_debug(struct mmc_host *host, struct mmc_request *mrq)
static void mmc_mrq_pr_debug(struct mmc_host *host, struct mmc_request *mrq,
bool cqe)
{
if (mrq->sbc) {
pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n",
@ -275,9 +276,12 @@ static void mmc_mrq_pr_debug(struct mmc_host *host, struct mmc_request *mrq)
}
if (mrq->cmd) {
pr_debug("%s: starting CMD%u arg %08x flags %08x\n",
mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->arg,
mrq->cmd->flags);
pr_debug("%s: starting %sCMD%u arg %08x flags %08x\n",
mmc_hostname(host), cqe ? "CQE direct " : "",
mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags);
} else if (cqe) {
pr_debug("%s: starting CQE transfer for tag %d blkaddr %u\n",
mmc_hostname(host), mrq->tag, mrq->data->blk_addr);
}
if (mrq->data) {
@ -333,7 +337,7 @@ static int mmc_mrq_prep(struct mmc_host *host, struct mmc_request *mrq)
return 0;
}
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
@ -342,7 +346,7 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
if (mmc_card_removed(host->card))
return -ENOMEDIUM;
mmc_mrq_pr_debug(host, mrq);
mmc_mrq_pr_debug(host, mrq, false);
WARN_ON(!host->claimed);
@ -355,6 +359,7 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
return 0;
}
EXPORT_SYMBOL(mmc_start_request);
/*
* mmc_wait_data_done() - done callback for data request
@ -482,6 +487,155 @@ void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq)
}
EXPORT_SYMBOL(mmc_wait_for_req_done);
/*
* mmc_cqe_start_req - Start a CQE request.
* @host: MMC host to start the request
* @mrq: request to start
*
* Start the request, re-tuning if needed and it is possible. Returns an error
* code if the request fails to start or -EBUSY if CQE is busy.
*/
int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
/*
* CQE cannot process re-tuning commands. Caller must hold retuning
* while CQE is in use. Re-tuning can happen here only when CQE has no
* active requests i.e. this is the first. Note, re-tuning will call
* ->cqe_off().
*/
err = mmc_retune(host);
if (err)
goto out_err;
mrq->host = host;
mmc_mrq_pr_debug(host, mrq, true);
err = mmc_mrq_prep(host, mrq);
if (err)
goto out_err;
err = host->cqe_ops->cqe_request(host, mrq);
if (err)
goto out_err;
trace_mmc_request_start(host, mrq);
return 0;
out_err:
if (mrq->cmd) {
pr_debug("%s: failed to start CQE direct CMD%u, error %d\n",
mmc_hostname(host), mrq->cmd->opcode, err);
} else {
pr_debug("%s: failed to start CQE transfer for tag %d, error %d\n",
mmc_hostname(host), mrq->tag, err);
}
return err;
}
EXPORT_SYMBOL(mmc_cqe_start_req);
/**
* mmc_cqe_request_done - CQE has finished processing an MMC request
* @host: MMC host which completed request
* @mrq: MMC request which completed
*
* CQE drivers should call this function when they have completed
* their processing of a request.
*/
void mmc_cqe_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
mmc_should_fail_request(host, mrq);
/* Flag re-tuning needed on CRC errors */
if ((mrq->cmd && mrq->cmd->error == -EILSEQ) ||
(mrq->data && mrq->data->error == -EILSEQ))
mmc_retune_needed(host);
trace_mmc_request_done(host, mrq);
if (mrq->cmd) {
pr_debug("%s: CQE req done (direct CMD%u): %d\n",
mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->error);
} else {
pr_debug("%s: CQE transfer done tag %d\n",
mmc_hostname(host), mrq->tag);
}
if (mrq->data) {
pr_debug("%s: %d bytes transferred: %d\n",
mmc_hostname(host),
mrq->data->bytes_xfered, mrq->data->error);
}
mrq->done(mrq);
}
EXPORT_SYMBOL(mmc_cqe_request_done);
/**
* mmc_cqe_post_req - CQE post process of a completed MMC request
* @host: MMC host
* @mrq: MMC request to be processed
*/
void mmc_cqe_post_req(struct mmc_host *host, struct mmc_request *mrq)
{
if (host->cqe_ops->cqe_post_req)
host->cqe_ops->cqe_post_req(host, mrq);
}
EXPORT_SYMBOL(mmc_cqe_post_req);
/* Arbitrary 1 second timeout */
#define MMC_CQE_RECOVERY_TIMEOUT 1000
/*
* mmc_cqe_recovery - Recover from CQE errors.
* @host: MMC host to recover
*
* Recovery consists of stopping CQE, stopping eMMC, discarding the queue in
* in eMMC, and discarding the queue in CQE. CQE must call
* mmc_cqe_request_done() on all requests. An error is returned if the eMMC
* fails to discard its queue.
*/
int mmc_cqe_recovery(struct mmc_host *host)
{
struct mmc_command cmd;
int err;
mmc_retune_hold_now(host);
/*
* Recovery is expected seldom, if at all, but it reduces performance,
* so make sure it is not completely silent.
*/
pr_warn("%s: running CQE recovery\n", mmc_hostname(host));
host->cqe_ops->cqe_recovery_start(host);
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_STOP_TRANSMISSION,
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC,
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT,
mmc_wait_for_cmd(host, &cmd, 0);
memset(&cmd, 0, sizeof(cmd));
cmd.opcode = MMC_CMDQ_TASK_MGMT;
cmd.arg = 1; /* Discard entire queue */
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */
cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT,
err = mmc_wait_for_cmd(host, &cmd, 0);
host->cqe_ops->cqe_recovery_finish(host);
mmc_retune_release(host);
return err;
}
EXPORT_SYMBOL(mmc_cqe_recovery);
/**
* mmc_is_req_done - Determine if a 'cap_cmd_during_tfr' request is done
* @host: MMC host
@ -832,9 +986,36 @@ unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
}
EXPORT_SYMBOL(mmc_align_data_size);
/*
* Allow claiming an already claimed host if the context is the same or there is
* no context but the task is the same.
*/
static inline bool mmc_ctx_matches(struct mmc_host *host, struct mmc_ctx *ctx,
struct task_struct *task)
{
return host->claimer == ctx ||
(!ctx && task && host->claimer->task == task);
}
static inline void mmc_ctx_set_claimer(struct mmc_host *host,
struct mmc_ctx *ctx,
struct task_struct *task)
{
if (!host->claimer) {
if (ctx)
host->claimer = ctx;
else
host->claimer = &host->default_ctx;
}
if (task)
host->claimer->task = task;
}
/**
* __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
* @ctx: context that claims the host or NULL in which case the default
* context will be used
* @abort: whether or not the operation should be aborted
*
* Claim a host for a set of operations. If @abort is non null and
@ -842,8 +1023,10 @@ EXPORT_SYMBOL(mmc_align_data_size);
* that non-zero value without acquiring the lock. Returns zero
* with the lock held otherwise.
*/
int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
int __mmc_claim_host(struct mmc_host *host, struct mmc_ctx *ctx,
atomic_t *abort)
{
struct task_struct *task = ctx ? NULL : current;
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
int stop;
@ -856,7 +1039,7 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
stop = abort ? atomic_read(abort) : 0;
if (stop || !host->claimed || host->claimer == current)
if (stop || !host->claimed || mmc_ctx_matches(host, ctx, task))
break;
spin_unlock_irqrestore(&host->lock, flags);
schedule();
@ -865,7 +1048,7 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
set_current_state(TASK_RUNNING);
if (!stop) {
host->claimed = 1;
host->claimer = current;
mmc_ctx_set_claimer(host, ctx, task);
host->claim_cnt += 1;
if (host->claim_cnt == 1)
pm = true;
@ -900,6 +1083,7 @@ void mmc_release_host(struct mmc_host *host)
spin_unlock_irqrestore(&host->lock, flags);
} else {
host->claimed = 0;
host->claimer->task = NULL;
host->claimer = NULL;
spin_unlock_irqrestore(&host->lock, flags);
wake_up(&host->wq);
@ -913,10 +1097,10 @@ EXPORT_SYMBOL(mmc_release_host);
* This is a helper function, which fetches a runtime pm reference for the
* card device and also claims the host.
*/
void mmc_get_card(struct mmc_card *card)
void mmc_get_card(struct mmc_card *card, struct mmc_ctx *ctx)
{
pm_runtime_get_sync(&card->dev);
mmc_claim_host(card->host);
__mmc_claim_host(card->host, ctx, NULL);
}
EXPORT_SYMBOL(mmc_get_card);
@ -924,9 +1108,13 @@ EXPORT_SYMBOL(mmc_get_card);
* This is a helper function, which releases the host and drops the runtime
* pm reference for the card device.
*/
void mmc_put_card(struct mmc_card *card)
void mmc_put_card(struct mmc_card *card, struct mmc_ctx *ctx)
{
mmc_release_host(card->host);
struct mmc_host *host = card->host;
WARN_ON(ctx && host->claimer != ctx);
mmc_release_host(host);
pm_runtime_mark_last_busy(&card->dev);
pm_runtime_put_autosuspend(&card->dev);
}
@ -1400,6 +1588,16 @@ EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
#endif /* CONFIG_REGULATOR */
/**
* mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
* @mmc: the host to regulate
*
* Returns 0 or errno. errno should be handled, it is either a critical error
* or -EPROBE_DEFER. 0 means no critical error but it does not mean all
* regulators have been found because they all are optional. If you require
* certain regulators, you need to check separately in your driver if they got
* populated after calling this function.
*/
int mmc_regulator_get_supply(struct mmc_host *mmc)
{
struct device *dev = mmc_dev(mmc);
@ -1484,11 +1682,33 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
}
int mmc_host_set_uhs_voltage(struct mmc_host *host)
{
u32 clock;
/*
* During a signal voltage level switch, the clock must be gated
* for 5 ms according to the SD spec
*/
clock = host->ios.clock;
host->ios.clock = 0;
mmc_set_ios(host);
if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180))
return -EAGAIN;
/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
mmc_delay(10);
host->ios.clock = clock;
mmc_set_ios(host);
return 0;
}
int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
{
struct mmc_command cmd = {};
int err = 0;
u32 clock;
/*
* If we cannot switch voltages, return failure so the caller
@ -1520,15 +1740,8 @@ int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
err = -EAGAIN;
goto power_cycle;
}
/*
* During a signal voltage level switch, the clock must be gated
* for 5 ms according to the SD spec
*/
clock = host->ios.clock;
host->ios.clock = 0;
mmc_set_ios(host);
if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) {
if (mmc_host_set_uhs_voltage(host)) {
/*
* Voltages may not have been switched, but we've already
* sent CMD11, so a power cycle is required anyway
@ -1537,11 +1750,6 @@ int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
goto power_cycle;
}
/* Keep clock gated for at least 10 ms, though spec only says 5 ms */
mmc_delay(10);
host->ios.clock = clock;
mmc_set_ios(host);
/* Wait for at least 1 ms according to spec */
mmc_delay(1);

View File

@ -49,6 +49,7 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr);
int mmc_host_set_uhs_voltage(struct mmc_host *host);
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
@ -107,6 +108,8 @@ 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);
int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq);
struct mmc_async_req;
struct mmc_async_req *mmc_start_areq(struct mmc_host *host,
@ -128,10 +131,11 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen);
int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount,
bool is_rel_write);
int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
int __mmc_claim_host(struct mmc_host *host, struct mmc_ctx *ctx,
atomic_t *abort);
void mmc_release_host(struct mmc_host *host);
void mmc_get_card(struct mmc_card *card);
void mmc_put_card(struct mmc_card *card);
void mmc_get_card(struct mmc_card *card, struct mmc_ctx *ctx);
void mmc_put_card(struct mmc_card *card, struct mmc_ctx *ctx);
/**
* mmc_claim_host - exclusively claim a host
@ -141,7 +145,11 @@ void mmc_put_card(struct mmc_card *card);
*/
static inline void mmc_claim_host(struct mmc_host *host)
{
__mmc_claim_host(host, NULL);
__mmc_claim_host(host, NULL, NULL);
}
int mmc_cqe_start_req(struct mmc_host *host, struct mmc_request *mrq);
void mmc_cqe_post_req(struct mmc_host *host, struct mmc_request *mrq);
int mmc_cqe_recovery(struct mmc_host *host);
#endif

View File

@ -111,12 +111,6 @@ 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)
@ -124,6 +118,7 @@ void mmc_retune_release(struct mmc_host *host)
else
WARN_ON(1);
}
EXPORT_SYMBOL(mmc_retune_release);
int mmc_retune(struct mmc_host *host)
{
@ -184,7 +179,7 @@ static void mmc_retune_timer(unsigned long data)
int mmc_of_parse(struct mmc_host *host)
{
struct device *dev = host->parent;
u32 bus_width;
u32 bus_width, drv_type;
int ret;
bool cd_cap_invert, cd_gpio_invert = false;
bool ro_cap_invert, ro_gpio_invert = false;
@ -326,6 +321,15 @@ int mmc_of_parse(struct mmc_host *host)
if (device_property_read_bool(dev, "no-mmc"))
host->caps2 |= MMC_CAP2_NO_MMC;
/* Must be after "non-removable" check */
if (device_property_read_u32(dev, "fixed-emmc-driver-type", &drv_type) == 0) {
if (host->caps & MMC_CAP_NONREMOVABLE)
host->fixed_drv_type = drv_type;
else
dev_err(host->parent,
"can't use fixed driver type, media is removable\n");
}
host->dsr_req = !device_property_read_u32(dev, "dsr", &host->dsr);
if (host->dsr_req && (host->dsr & ~0xffff)) {
dev_err(host->parent,
@ -398,6 +402,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
host->max_blk_size = 512;
host->max_blk_count = PAGE_SIZE / 512;
host->fixed_drv_type = -EINVAL;
return host;
}

View File

@ -19,12 +19,17 @@ void mmc_unregister_host_class(void);
void mmc_retune_enable(struct mmc_host *host);
void mmc_retune_disable(struct mmc_host *host);
void mmc_retune_hold(struct mmc_host *host);
void mmc_retune_hold_now(struct mmc_host *host);
void mmc_retune_release(struct mmc_host *host);
int mmc_retune(struct mmc_host *host);
void mmc_retune_pause(struct mmc_host *host);
void mmc_retune_unpause(struct mmc_host *host);
static inline void mmc_retune_hold_now(struct mmc_host *host)
{
host->retune_now = 0;
host->hold_retune += 1;
}
static inline void mmc_retune_recheck(struct mmc_host *host)
{
if (host->hold_retune <= 1)

View File

@ -780,6 +780,7 @@ MMC_DEV_ATTR(manfid, "0x%06x\n", card->cid.manfid);
MMC_DEV_ATTR(name, "%s\n", card->cid.prod_name);
MMC_DEV_ATTR(oemid, "0x%04x\n", card->cid.oemid);
MMC_DEV_ATTR(prv, "0x%x\n", card->cid.prv);
MMC_DEV_ATTR(rev, "0x%x\n", card->ext_csd.rev);
MMC_DEV_ATTR(pre_eol_info, "%02x\n", card->ext_csd.pre_eol_info);
MMC_DEV_ATTR(life_time, "0x%02x 0x%02x\n",
card->ext_csd.device_life_time_est_typ_a,
@ -838,6 +839,7 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_name.attr,
&dev_attr_oemid.attr,
&dev_attr_prv.attr,
&dev_attr_rev.attr,
&dev_attr_pre_eol_info.attr,
&dev_attr_life_time.attr,
&dev_attr_serial.attr,
@ -1289,13 +1291,18 @@ out_err:
static void mmc_select_driver_type(struct mmc_card *card)
{
int card_drv_type, drive_strength, drv_type;
int fixed_drv_type = card->host->fixed_drv_type;
card_drv_type = card->ext_csd.raw_driver_strength |
mmc_driver_type_mask(0);
drive_strength = mmc_select_drive_strength(card,
card->ext_csd.hs200_max_dtr,
card_drv_type, &drv_type);
if (fixed_drv_type >= 0)
drive_strength = card_drv_type & mmc_driver_type_mask(fixed_drv_type)
? fixed_drv_type : 0;
else
drive_strength = mmc_select_drive_strength(card,
card->ext_csd.hs200_max_dtr,
card_drv_type, &drv_type);
card->drive_strength = drive_strength;
@ -1785,6 +1792,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
}
}
/*
* Enable Command Queue if supported. Note that Packed Commands cannot
* be used with Command Queue.
*/
card->ext_csd.cmdq_en = false;
if (card->ext_csd.cmdq_support && host->caps2 & MMC_CAP2_CQE) {
err = mmc_cmdq_enable(card);
if (err && err != -EBADMSG)
goto free_card;
if (err) {
pr_warn("%s: Enabling CMDQ failed\n",
mmc_hostname(card->host));
card->ext_csd.cmdq_support = false;
card->ext_csd.cmdq_depth = 0;
err = 0;
}
}
/*
* In some cases (e.g. RPMB or mmc_test), the Command Queue must be
* disabled for a time, so a flag is needed to indicate to re-enable the
@ -1792,6 +1816,18 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/
card->reenable_cmdq = card->ext_csd.cmdq_en;
if (card->ext_csd.cmdq_en && !host->cqe_enabled) {
err = host->cqe_ops->cqe_enable(host, card);
if (err) {
pr_err("%s: Failed to enable CQE, error %d\n",
mmc_hostname(host), err);
} else {
host->cqe_enabled = true;
pr_info("%s: Command Queue Engine enabled\n",
mmc_hostname(host));
}
}
if (!oldcard)
host->card = card;
@ -1911,14 +1947,14 @@ static void mmc_detect(struct mmc_host *host)
{
int err;
mmc_get_card(host->card);
mmc_get_card(host->card, NULL);
/*
* Just check if our card has been removed.
*/
err = _mmc_detect_card_removed(host);
mmc_put_card(host->card);
mmc_put_card(host->card, NULL);
if (err) {
mmc_remove(host);

View File

@ -977,7 +977,6 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
from_exception)
return;
mmc_claim_host(card->host);
if (card->ext_csd.raw_bkops_status >= EXT_CSD_BKOPS_LEVEL_2) {
timeout = MMC_OPS_TIMEOUT_MS;
use_busy_signal = true;
@ -995,7 +994,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err);
mmc_retune_release(card->host);
goto out;
return;
}
/*
@ -1007,9 +1006,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
mmc_card_set_doing_bkops(card);
else
mmc_retune_release(card->host);
out:
mmc_release_host(card->host);
}
EXPORT_SYMBOL(mmc_start_bkops);
/*
* Flush the cache to the non-volatile storage.

View File

@ -30,7 +30,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
{
struct mmc_queue *mq = q->queuedata;
if (mq && (mmc_card_removed(mq->card) || mmc_access_rpmb(mq)))
if (mq && mmc_card_removed(mq->card))
return BLKPREP_KILL;
req->rq_flags |= RQF_DONTPREP;
@ -177,6 +177,29 @@ static void mmc_exit_request(struct request_queue *q, struct request *req)
mq_rq->sg = NULL;
}
static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card)
{
struct mmc_host *host = card->host;
u64 limit = BLK_BOUNCE_HIGH;
if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT;
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, mq->queue);
if (mmc_can_erase(card))
mmc_queue_setup_discard(mq->queue, card);
blk_queue_bounce_limit(mq->queue, limit);
blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
blk_queue_max_segments(mq->queue, host->max_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
/* Initialize thread_sem even if it is not used */
sema_init(&mq->thread_sem, 1);
}
/**
* mmc_init_queue - initialise a queue structure.
* @mq: mmc queue
@ -190,12 +213,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
spinlock_t *lock, const char *subname)
{
struct mmc_host *host = card->host;
u64 limit = BLK_BOUNCE_HIGH;
int ret = -ENOMEM;
if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask)
limit = (u64)dma_max_pfn(mmc_dev(host)) << PAGE_SHIFT;
mq->card = card;
mq->queue = blk_alloc_queue(GFP_KERNEL);
if (!mq->queue)
@ -214,18 +233,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
}
blk_queue_prep_rq(mq->queue, mmc_prep_request);
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, mq->queue);
queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, mq->queue);
if (mmc_can_erase(card))
mmc_queue_setup_discard(mq->queue, card);
blk_queue_bounce_limit(mq->queue, limit);
blk_queue_max_hw_sectors(mq->queue,
min(host->max_blk_count, host->max_req_size / 512));
blk_queue_max_segments(mq->queue, host->max_segs);
blk_queue_max_segment_size(mq->queue, host->max_seg_size);
sema_init(&mq->thread_sem, 1);
mmc_setup_queue(mq, card);
mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd/%d%s",
host->index, subname ? subname : "");

View File

@ -36,12 +36,14 @@ struct mmc_blk_request {
/**
* enum mmc_drv_op - enumerates the operations in the mmc_queue_req
* @MMC_DRV_OP_IOCTL: ioctl operation
* @MMC_DRV_OP_IOCTL_RPMB: RPMB-oriented ioctl operation
* @MMC_DRV_OP_BOOT_WP: write protect boot partitions
* @MMC_DRV_OP_GET_CARD_STATUS: get card status
* @MMC_DRV_OP_GET_EXT_CSD: get the EXT CSD from an eMMC card
*/
enum mmc_drv_op {
MMC_DRV_OP_IOCTL,
MMC_DRV_OP_IOCTL_RPMB,
MMC_DRV_OP_BOOT_WP,
MMC_DRV_OP_GET_CARD_STATUS,
MMC_DRV_OP_GET_EXT_CSD,
@ -82,6 +84,4 @@ extern void mmc_queue_resume(struct mmc_queue *);
extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
struct mmc_queue_req *);
extern int mmc_access_rpmb(struct mmc_queue *);
#endif

View File

@ -908,6 +908,18 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)
return max_dtr;
}
static bool mmc_sd_card_using_v18(struct mmc_card *card)
{
/*
* According to the SD spec., the Bus Speed Mode (function group 1) bits
* 2 to 4 are zero if the card is initialized at 3.3V signal level. Thus
* they can be used to determine if the card has already switched to
* 1.8V signaling.
*/
return card->sw_caps.sd3_bus_mode &
(SD_MODE_UHS_SDR50 | SD_MODE_UHS_SDR104 | SD_MODE_UHS_DDR50);
}
/*
* Handle the detection and initialisation of a card.
*
@ -921,9 +933,10 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
int err;
u32 cid[4];
u32 rocr = 0;
bool v18_fixup_failed = false;
WARN_ON(!host->claimed);
retry:
err = mmc_sd_get_cid(host, ocr, cid, &rocr);
if (err)
return err;
@ -989,6 +1002,36 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (err)
goto free_card;
/*
* If the card has not been power cycled, it may still be using 1.8V
* signaling. Detect that situation and try to initialize a UHS-I (1.8V)
* transfer mode.
*/
if (!v18_fixup_failed && !mmc_host_is_spi(host) && mmc_host_uhs(host) &&
mmc_sd_card_using_v18(card) &&
host->ios.signal_voltage != MMC_SIGNAL_VOLTAGE_180) {
/*
* Re-read switch information in case it has changed since
* oldcard was initialized.
*/
if (oldcard) {
err = mmc_read_switch(card);
if (err)
goto free_card;
}
if (mmc_sd_card_using_v18(card)) {
if (mmc_host_set_uhs_voltage(host) ||
mmc_sd_init_uhs_card(card)) {
v18_fixup_failed = true;
mmc_power_cycle(host, ocr);
if (!oldcard)
mmc_remove_card(card);
goto retry;
}
goto done;
}
}
/* Initialization sequence for UHS-I cards */
if (rocr & SD_ROCR_S18A) {
err = mmc_sd_init_uhs_card(card);
@ -1021,7 +1064,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
}
}
done:
host->card = card;
return 0;
@ -1056,14 +1099,14 @@ static void mmc_sd_detect(struct mmc_host *host)
{
int err;
mmc_get_card(host->card);
mmc_get_card(host->card, NULL);
/*
* Just check if our card has been removed.
*/
err = _mmc_detect_card_removed(host);
mmc_put_card(host->card);
mmc_put_card(host->card, NULL);
if (err) {
mmc_sd_remove(host);

View File

@ -155,7 +155,8 @@ static int sdio_irq_thread(void *_host)
* holding of the host lock does not cover too much work
* that doesn't require that lock to be held.
*/
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
ret = __mmc_claim_host(host, NULL,
&host->sdio_irq_thread_abort);
if (ret)
break;
ret = process_sdio_pending_irqs(host);

View File

@ -352,6 +352,19 @@ config MMC_MESON_GX
If you have a controller with this interface, say Y here.
config MMC_MESON_MX_SDIO
tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
depends on ARCH_MESON || COMPILE_TEST
depends on COMMON_CLK
depends on HAS_DMA
depends on OF
help
This selects support for the SD/MMC Host Controller on
Amlogic Meson6, Meson8 and Meson8b SoCs.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_MOXART
tristate "MOXART SD/MMC Host Controller support"
depends on ARCH_MOXART && MMC
@ -429,6 +442,7 @@ config MMC_SDHCI_MSM
tristate "Qualcomm SDHCI Controller Support"
depends on ARCH_QCOM || (ARM && COMPILE_TEST)
depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
help
This selects the Secure Digital Host Controller Interface (SDHCI)
support present in Qualcomm SOCs. The controller supports
@ -663,7 +677,7 @@ config MMC_CAVIUM_OCTEON
config MMC_CAVIUM_THUNDERX
tristate "Cavium ThunderX SD/MMC Card Interface support"
depends on PCI && 64BIT && (ARM64 || COMPILE_TEST)
depends on GPIOLIB
depends on GPIO_THUNDERX
depends on OF_ADDRESS
help
This selects Cavium ThunderX SD/MMC Card Interface.
@ -899,3 +913,15 @@ config MMC_SDHCI_XENON
This selects Marvell Xenon eMMC/SD/SDIO SDHCI.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI_OMAP
tristate "TI SDHCI Controller Support"
depends on MMC_SDHCI_PLTFM && OF
help
This selects the Secure Digital Host Controller Interface (SDHCI)
support present in TI's DRA7 SOCs. The controller supports
SD/MMC/SDIO devices.
If you have a controller with this interface, say Y or M here.
If unsure, say N.

View File

@ -65,6 +65,7 @@ obj-$(CONFIG_MMC_VUB300) += vub300.o
obj-$(CONFIG_MMC_USHC) += ushc.o
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
obj-$(CONFIG_MMC_MESON_GX) += meson-gx-mmc.o
obj-$(CONFIG_MMC_MESON_MX_SDIO) += meson-mx-sdio.o
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
@ -90,6 +91,7 @@ obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o
obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG

View File

@ -732,11 +732,11 @@ static inline unsigned int atmci_convert_chksize(struct atmel_mci *host,
return 0;
}
static void atmci_timeout_timer(unsigned long data)
static void atmci_timeout_timer(struct timer_list *t)
{
struct atmel_mci *host;
host = (struct atmel_mci *)data;
host = from_timer(host, t, timer);
dev_dbg(&host->pdev->dev, "software timeout\n");
@ -1661,9 +1661,9 @@ static void atmci_command_complete(struct atmel_mci *host,
cmd->error = 0;
}
static void atmci_detect_change(unsigned long data)
static void atmci_detect_change(struct timer_list *t)
{
struct atmel_mci_slot *slot = (struct atmel_mci_slot *)data;
struct atmel_mci_slot *slot = from_timer(slot, t, detect_timer);
bool present;
bool present_old;
@ -2349,8 +2349,7 @@ static int atmci_init_slot(struct atmel_mci *host,
if (gpio_is_valid(slot->detect_pin)) {
int ret;
setup_timer(&slot->detect_timer, atmci_detect_change,
(unsigned long)slot);
timer_setup(&slot->detect_timer, atmci_detect_change, 0);
ret = request_irq(gpio_to_irq(slot->detect_pin),
atmci_detect_interrupt,
@ -2563,7 +2562,7 @@ static int atmci_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
setup_timer(&host->timer, atmci_timeout_timer, (unsigned long)host);
timer_setup(&host->timer, atmci_timeout_timer, 0);
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);

View File

@ -967,7 +967,7 @@ static int cvm_mmc_of_parse(struct device *dev, struct cvm_mmc_slot *slot)
}
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
return ret;
/*
* Legacy Octeon firmware has no regulator entry, fall-back to

View File

@ -75,7 +75,7 @@ struct hs_timing {
u32 smpl_phase_min;
};
struct hs_timing hs_timing_cfg[TIMING_MODE][TIMING_CFG_NUM] = {
static struct hs_timing hs_timing_cfg[TIMING_MODE][TIMING_CFG_NUM] = {
{ /* reserved */ },
{ /* SD */
{7, 0, 15, 15,}, /* 0: LEGACY 400k */

View File

@ -817,7 +817,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host,
struct dma_slave_config cfg;
struct dma_async_tx_descriptor *desc = NULL;
struct scatterlist *sgl = host->data->sg;
const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
static const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
u32 sg_elems = host->data->sg_len;
u32 fifoth_val;
u32 fifo_offset = host->fifo_reg - host->regs;
@ -1024,7 +1024,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
{
unsigned int blksz = data->blksz;
const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
static const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
u32 fifo_width = 1 << host->data_shift;
u32 blksz_depth = blksz / fifo_width, fifoth_val;
u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
@ -1938,6 +1938,7 @@ static void dw_mci_set_drto(struct dw_mci *host)
unsigned int drto_clks;
unsigned int drto_div;
unsigned int drto_ms;
unsigned long irqflags;
drto_clks = mci_readl(host, TMOUT) >> 8;
drto_div = (mci_readl(host, CLKDIV) & 0xff) * 2;
@ -1949,7 +1950,11 @@ static void dw_mci_set_drto(struct dw_mci *host)
/* add a bit spare time */
drto_ms += 10;
mod_timer(&host->dto_timer, jiffies + msecs_to_jiffies(drto_ms));
spin_lock_irqsave(&host->irq_lock, irqflags);
if (!test_bit(EVENT_DATA_COMPLETE, &host->pending_events))
mod_timer(&host->dto_timer,
jiffies + msecs_to_jiffies(drto_ms));
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host)
@ -1970,6 +1975,18 @@ static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host)
return true;
}
static bool dw_mci_clear_pending_data_complete(struct dw_mci *host)
{
if (!test_bit(EVENT_DATA_COMPLETE, &host->pending_events))
return false;
/* Extra paranoia just like dw_mci_clear_pending_cmd_complete() */
WARN_ON(del_timer_sync(&host->dto_timer));
clear_bit(EVENT_DATA_COMPLETE, &host->pending_events);
return true;
}
static void dw_mci_tasklet_func(unsigned long priv)
{
struct dw_mci *host = (struct dw_mci *)priv;
@ -2111,8 +2128,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
/* fall through */
case STATE_DATA_BUSY:
if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
&host->pending_events)) {
if (!dw_mci_clear_pending_data_complete(host)) {
/*
* If data error interrupt comes but data over
* interrupt doesn't come within the given time.
@ -2682,6 +2698,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
if (pending & SDMMC_INT_DATA_OVER) {
spin_lock_irqsave(&host->irq_lock, irqflags);
del_timer(&host->dto_timer);
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
@ -2694,6 +2712,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
}
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
tasklet_schedule(&host->tasklet);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
if (pending & SDMMC_INT_RXDR) {
@ -2791,7 +2811,7 @@ static int dw_mci_init_slot(struct dw_mci *host)
/*if there are external regulators, get them*/
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
goto err_host_allocated;
if (!mmc->ocr_avail)
@ -2971,9 +2991,9 @@ no_dma:
host->use_dma = TRANS_MODE_PIO;
}
static void dw_mci_cmd11_timer(unsigned long arg)
static void dw_mci_cmd11_timer(struct timer_list *t)
{
struct dw_mci *host = (struct dw_mci *)arg;
struct dw_mci *host = from_timer(host, t, cmd11_timer);
if (host->state != STATE_SENDING_CMD11) {
dev_warn(host->dev, "Unexpected CMD11 timeout\n");
@ -2985,9 +3005,9 @@ static void dw_mci_cmd11_timer(unsigned long arg)
tasklet_schedule(&host->tasklet);
}
static void dw_mci_cto_timer(unsigned long arg)
static void dw_mci_cto_timer(struct timer_list *t)
{
struct dw_mci *host = (struct dw_mci *)arg;
struct dw_mci *host = from_timer(host, t, cto_timer);
unsigned long irqflags;
u32 pending;
@ -3040,10 +3060,34 @@ exit:
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
static void dw_mci_dto_timer(unsigned long arg)
static void dw_mci_dto_timer(struct timer_list *t)
{
struct dw_mci *host = (struct dw_mci *)arg;
struct dw_mci *host = from_timer(host, t, dto_timer);
unsigned long irqflags;
u32 pending;
spin_lock_irqsave(&host->irq_lock, irqflags);
/*
* The DTO timer is much longer than the CTO timer, so it's even less
* likely that we'll these cases, but it pays to be paranoid.
*/
pending = mci_readl(host, MINTSTS); /* read-only mask reg */
if (pending & SDMMC_INT_DATA_OVER) {
/* The interrupt should fire; no need to act but we can warn */
dev_warn(host->dev, "Unexpected data interrupt latency\n");
goto exit;
}
if (test_bit(EVENT_DATA_COMPLETE, &host->pending_events)) {
/* Presumably interrupt handler couldn't delete the timer */
dev_warn(host->dev, "DTO timeout when already completed\n");
goto exit;
}
/*
* Continued paranoia to make sure we're in the state we expect.
* This paranoia isn't really justified but it seems good to be safe.
*/
switch (host->state) {
case STATE_SENDING_DATA:
case STATE_DATA_BUSY:
@ -3058,8 +3102,13 @@ static void dw_mci_dto_timer(unsigned long arg)
tasklet_schedule(&host->tasklet);
break;
default:
dev_warn(host->dev, "Unexpected data timeout, state %d\n",
host->state);
break;
}
exit:
spin_unlock_irqrestore(&host->irq_lock, irqflags);
}
#ifdef CONFIG_OF
@ -3208,14 +3257,9 @@ int dw_mci_probe(struct dw_mci *host)
}
}
setup_timer(&host->cmd11_timer,
dw_mci_cmd11_timer, (unsigned long)host);
setup_timer(&host->cto_timer,
dw_mci_cto_timer, (unsigned long)host);
setup_timer(&host->dto_timer,
dw_mci_dto_timer, (unsigned long)host);
timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0);
timer_setup(&host->cto_timer, dw_mci_cto_timer, 0);
timer_setup(&host->dto_timer, dw_mci_dto_timer, 0);
spin_lock_init(&host->lock);
spin_lock_init(&host->irq_lock);

View File

@ -74,7 +74,8 @@ struct dw_mci_dma_slave {
* @stop_abort: The command currently prepared for stoping transfer.
* @prev_blksz: The former transfer blksz record.
* @timing: Record of current ios timing.
* @use_dma: Whether DMA channel is initialized or not.
* @use_dma: Which DMA channel is in use for the current transfer, zero
* denotes PIO mode.
* @using_dma: Whether DMA is in use for the current transfer.
* @dma_64bit_address: Whether DMA supports 64-bit address mode or not.
* @sg_dma: Bus address of DMA buffer.

View File

@ -586,9 +586,9 @@ poll_timeout:
return true;
}
static void jz4740_mmc_timeout(unsigned long data)
static void jz4740_mmc_timeout(struct timer_list *t)
{
struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)data;
struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer);
if (!test_and_clear_bit(0, &host->waiting))
return;
@ -1036,8 +1036,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
jz4740_mmc_reset(host);
jz4740_mmc_clock_disable(host);
setup_timer(&host->timeout_timer, jz4740_mmc_timeout,
(unsigned long)host);
timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0);
host->use_dma = true;
if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0)

View File

@ -1190,7 +1190,7 @@ static int meson_mmc_probe(struct platform_device *pdev)
/* Get regulators and the supported OCR mask */
host->vqmmc_enabled = false;
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
goto free_host;
ret = mmc_of_parse(mmc);

View File

@ -0,0 +1,768 @@
/*
* meson-mx-sdio.c - Meson6, Meson8 and Meson8b SDIO/MMC Host Controller
*
* Copyright (C) 2015 Endless Mobile, Inc.
* Author: Carlo Caione <carlo@endlessm.com>
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/slot-gpio.h>
#define MESON_MX_SDIO_ARGU 0x00
#define MESON_MX_SDIO_SEND 0x04
#define MESON_MX_SDIO_SEND_COMMAND_INDEX_MASK GENMASK(7, 0)
#define MESON_MX_SDIO_SEND_CMD_RESP_BITS_MASK GENMASK(15, 8)
#define MESON_MX_SDIO_SEND_RESP_WITHOUT_CRC7 BIT(16)
#define MESON_MX_SDIO_SEND_RESP_HAS_DATA BIT(17)
#define MESON_MX_SDIO_SEND_RESP_CRC7_FROM_8 BIT(18)
#define MESON_MX_SDIO_SEND_CHECK_DAT0_BUSY BIT(19)
#define MESON_MX_SDIO_SEND_DATA BIT(20)
#define MESON_MX_SDIO_SEND_USE_INT_WINDOW BIT(21)
#define MESON_MX_SDIO_SEND_REPEAT_PACKAGE_TIMES_MASK GENMASK(31, 24)
#define MESON_MX_SDIO_CONF 0x08
#define MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT 0
#define MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH 10
#define MESON_MX_SDIO_CONF_CMD_DISABLE_CRC BIT(10)
#define MESON_MX_SDIO_CONF_CMD_OUT_AT_POSITIVE_EDGE BIT(11)
#define MESON_MX_SDIO_CONF_CMD_ARGUMENT_BITS_MASK GENMASK(17, 12)
#define MESON_MX_SDIO_CONF_RESP_LATCH_AT_NEGATIVE_EDGE BIT(18)
#define MESON_MX_SDIO_CONF_DATA_LATCH_AT_NEGATIVE_EDGE BIT(19)
#define MESON_MX_SDIO_CONF_BUS_WIDTH BIT(20)
#define MESON_MX_SDIO_CONF_M_ENDIAN_MASK GENMASK(22, 21)
#define MESON_MX_SDIO_CONF_WRITE_NWR_MASK GENMASK(28, 23)
#define MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK GENMASK(31, 29)
#define MESON_MX_SDIO_IRQS 0x0c
#define MESON_MX_SDIO_IRQS_STATUS_STATE_MACHINE_MASK GENMASK(3, 0)
#define MESON_MX_SDIO_IRQS_CMD_BUSY BIT(4)
#define MESON_MX_SDIO_IRQS_RESP_CRC7_OK BIT(5)
#define MESON_MX_SDIO_IRQS_DATA_READ_CRC16_OK BIT(6)
#define MESON_MX_SDIO_IRQS_DATA_WRITE_CRC16_OK BIT(7)
#define MESON_MX_SDIO_IRQS_IF_INT BIT(8)
#define MESON_MX_SDIO_IRQS_CMD_INT BIT(9)
#define MESON_MX_SDIO_IRQS_STATUS_INFO_MASK GENMASK(15, 12)
#define MESON_MX_SDIO_IRQS_TIMING_OUT_INT BIT(16)
#define MESON_MX_SDIO_IRQS_AMRISC_TIMING_OUT_INT_EN BIT(17)
#define MESON_MX_SDIO_IRQS_ARC_TIMING_OUT_INT_EN BIT(18)
#define MESON_MX_SDIO_IRQS_TIMING_OUT_COUNT_MASK GENMASK(31, 19)
#define MESON_MX_SDIO_IRQC 0x10
#define MESON_MX_SDIO_IRQC_ARC_IF_INT_EN BIT(3)
#define MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN BIT(4)
#define MESON_MX_SDIO_IRQC_IF_CONFIG_MASK GENMASK(7, 6)
#define MESON_MX_SDIO_IRQC_FORCE_DATA_CLK BIT(8)
#define MESON_MX_SDIO_IRQC_FORCE_DATA_CMD BIT(9)
#define MESON_MX_SDIO_IRQC_FORCE_DATA_DAT_MASK GENMASK(10, 13)
#define MESON_MX_SDIO_IRQC_SOFT_RESET BIT(15)
#define MESON_MX_SDIO_IRQC_FORCE_HALT BIT(30)
#define MESON_MX_SDIO_IRQC_HALT_HOLE BIT(31)
#define MESON_MX_SDIO_MULT 0x14
#define MESON_MX_SDIO_MULT_PORT_SEL_MASK GENMASK(1, 0)
#define MESON_MX_SDIO_MULT_MEMORY_STICK_ENABLE BIT(2)
#define MESON_MX_SDIO_MULT_MEMORY_STICK_SCLK_ALWAYS BIT(3)
#define MESON_MX_SDIO_MULT_STREAM_ENABLE BIT(4)
#define MESON_MX_SDIO_MULT_STREAM_8BITS_MODE BIT(5)
#define MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX BIT(8)
#define MESON_MX_SDIO_MULT_DAT0_DAT1_SWAPPED BIT(10)
#define MESON_MX_SDIO_MULT_DAT1_DAT0_SWAPPED BIT(11)
#define MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK GENMASK(15, 12)
#define MESON_MX_SDIO_ADDR 0x18
#define MESON_MX_SDIO_EXT 0x1c
#define MESON_MX_SDIO_EXT_DATA_RW_NUMBER_MASK GENMASK(29, 16)
#define MESON_MX_SDIO_BOUNCE_REQ_SIZE (128 * 1024)
#define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1)
#define MESON_MX_SDIO_MAX_SLOTS 3
struct meson_mx_mmc_host {
struct device *controller_dev;
struct clk *parent_clk;
struct clk *core_clk;
struct clk_divider cfg_div;
struct clk *cfg_div_clk;
struct clk_fixed_factor fixed_factor;
struct clk *fixed_factor_clk;
void __iomem *base;
int irq;
spinlock_t irq_lock;
struct timer_list cmd_timeout;
unsigned int slot_id;
struct mmc_host *mmc;
struct mmc_request *mrq;
struct mmc_command *cmd;
int error;
};
static void meson_mx_mmc_mask_bits(struct mmc_host *mmc, char reg, u32 mask,
u32 val)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
u32 regval;
regval = readl(host->base + reg);
regval &= ~mask;
regval |= (val & mask);
writel(regval, host->base + reg);
}
static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host)
{
writel(MESON_MX_SDIO_IRQC_SOFT_RESET, host->base + MESON_MX_SDIO_IRQC);
udelay(2);
}
static struct mmc_command *meson_mx_mmc_get_next_cmd(struct mmc_command *cmd)
{
if (cmd->opcode == MMC_SET_BLOCK_COUNT && !cmd->error)
return cmd->mrq->cmd;
else if (mmc_op_multi(cmd->opcode) &&
(!cmd->mrq->sbc || cmd->error || cmd->data->error))
return cmd->mrq->stop;
else
return NULL;
}
static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
struct mmc_command *cmd)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
unsigned int pack_size;
unsigned long irqflags, timeout;
u32 mult, send = 0, ext = 0;
host->cmd = cmd;
if (cmd->busy_timeout)
timeout = msecs_to_jiffies(cmd->busy_timeout);
else
timeout = msecs_to_jiffies(1000);
switch (mmc_resp_type(cmd)) {
case MMC_RSP_R1:
case MMC_RSP_R1B:
case MMC_RSP_R3:
/* 7 (CMD) + 32 (response) + 7 (CRC) -1 */
send |= FIELD_PREP(MESON_MX_SDIO_SEND_CMD_RESP_BITS_MASK, 45);
break;
case MMC_RSP_R2:
/* 7 (CMD) + 120 (response) + 7 (CRC) -1 */
send |= FIELD_PREP(MESON_MX_SDIO_SEND_CMD_RESP_BITS_MASK, 133);
send |= MESON_MX_SDIO_SEND_RESP_CRC7_FROM_8;
break;
default:
break;
}
if (!(cmd->flags & MMC_RSP_CRC))
send |= MESON_MX_SDIO_SEND_RESP_WITHOUT_CRC7;
if (cmd->flags & MMC_RSP_BUSY)
send |= MESON_MX_SDIO_SEND_CHECK_DAT0_BUSY;
if (cmd->data) {
send |= FIELD_PREP(MESON_MX_SDIO_SEND_REPEAT_PACKAGE_TIMES_MASK,
(cmd->data->blocks - 1));
pack_size = cmd->data->blksz * BITS_PER_BYTE;
if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
pack_size += MESON_MX_SDIO_RESPONSE_CRC16_BITS * 4;
else
pack_size += MESON_MX_SDIO_RESPONSE_CRC16_BITS * 1;
ext |= FIELD_PREP(MESON_MX_SDIO_EXT_DATA_RW_NUMBER_MASK,
pack_size);
if (cmd->data->flags & MMC_DATA_WRITE)
send |= MESON_MX_SDIO_SEND_DATA;
else
send |= MESON_MX_SDIO_SEND_RESP_HAS_DATA;
cmd->data->bytes_xfered = 0;
}
send |= FIELD_PREP(MESON_MX_SDIO_SEND_COMMAND_INDEX_MASK,
(0x40 | cmd->opcode));
spin_lock_irqsave(&host->irq_lock, irqflags);
mult = readl(host->base + MESON_MX_SDIO_MULT);
mult &= ~MESON_MX_SDIO_MULT_PORT_SEL_MASK;
mult |= FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, host->slot_id);
mult |= BIT(31);
writel(mult, host->base + MESON_MX_SDIO_MULT);
/* enable the CMD done interrupt */
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQC,
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN,
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
/* clear pending interrupts */
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQS,
MESON_MX_SDIO_IRQS_CMD_INT,
MESON_MX_SDIO_IRQS_CMD_INT);
writel(cmd->arg, host->base + MESON_MX_SDIO_ARGU);
writel(ext, host->base + MESON_MX_SDIO_EXT);
writel(send, host->base + MESON_MX_SDIO_SEND);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
mod_timer(&host->cmd_timeout, jiffies + timeout);
}
static void meson_mx_mmc_request_done(struct meson_mx_mmc_host *host)
{
struct mmc_request *mrq;
mrq = host->mrq;
host->mrq = NULL;
host->cmd = NULL;
mmc_request_done(host->mmc, mrq);
}
static void meson_mx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
unsigned short vdd = ios->vdd;
unsigned long clk_rate = ios->clock;
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
MESON_MX_SDIO_CONF_BUS_WIDTH, 0);
break;
case MMC_BUS_WIDTH_4:
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
MESON_MX_SDIO_CONF_BUS_WIDTH,
MESON_MX_SDIO_CONF_BUS_WIDTH);
break;
case MMC_BUS_WIDTH_8:
default:
dev_err(mmc_dev(mmc), "unsupported bus width: %d\n",
ios->bus_width);
host->error = -EINVAL;
return;
}
host->error = clk_set_rate(host->cfg_div_clk, ios->clock);
if (host->error) {
dev_warn(mmc_dev(mmc),
"failed to set MMC clock to %lu: %d\n",
clk_rate, host->error);
return;
}
mmc->actual_clock = clk_get_rate(host->cfg_div_clk);
switch (ios->power_mode) {
case MMC_POWER_OFF:
vdd = 0;
/* fall-through: */
case MMC_POWER_UP:
if (!IS_ERR(mmc->supply.vmmc)) {
host->error = mmc_regulator_set_ocr(mmc,
mmc->supply.vmmc,
vdd);
if (host->error)
return;
}
break;
}
}
static int meson_mx_mmc_map_dma(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct mmc_data *data = mrq->data;
int dma_len;
struct scatterlist *sg;
if (!data)
return 0;
sg = data->sg;
if (sg->offset & 3 || sg->length & 3) {
dev_err(mmc_dev(mmc),
"unaligned scatterlist: offset %x length %d\n",
sg->offset, sg->length);
return -EINVAL;
}
dma_len = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
mmc_get_dma_dir(data));
if (dma_len <= 0) {
dev_err(mmc_dev(mmc), "dma_map_sg failed\n");
return -ENOMEM;
}
return 0;
}
static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
struct mmc_command *cmd = mrq->cmd;
if (!host->error)
host->error = meson_mx_mmc_map_dma(mmc, mrq);
if (host->error) {
cmd->error = host->error;
mmc_request_done(mmc, mrq);
return;
}
host->mrq = mrq;
if (mrq->data)
writel(sg_dma_address(mrq->data->sg),
host->base + MESON_MX_SDIO_ADDR);
if (mrq->sbc)
meson_mx_mmc_start_cmd(mmc, mrq->sbc);
else
meson_mx_mmc_start_cmd(mmc, mrq->cmd);
}
static int meson_mx_mmc_card_busy(struct mmc_host *mmc)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
u32 irqc = readl(host->base + MESON_MX_SDIO_IRQC);
return !!(irqc & MESON_MX_SDIO_IRQC_FORCE_DATA_DAT_MASK);
}
static void meson_mx_mmc_read_response(struct mmc_host *mmc,
struct mmc_command *cmd)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
u32 mult;
int i, resp[4];
mult = readl(host->base + MESON_MX_SDIO_MULT);
mult |= MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX;
mult &= ~MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK;
mult |= FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, 0);
writel(mult, host->base + MESON_MX_SDIO_MULT);
if (cmd->flags & MMC_RSP_136) {
for (i = 0; i <= 3; i++)
resp[3 - i] = readl(host->base + MESON_MX_SDIO_ARGU);
cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff);
cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff);
cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff);
cmd->resp[3] = (resp[3] << 8);
} else if (cmd->flags & MMC_RSP_PRESENT) {
cmd->resp[0] = readl(host->base + MESON_MX_SDIO_ARGU);
}
}
static irqreturn_t meson_mx_mmc_process_cmd_irq(struct meson_mx_mmc_host *host,
u32 irqs, u32 send)
{
struct mmc_command *cmd = host->cmd;
/*
* NOTE: even though it shouldn't happen we sometimes get command
* interrupts twice (at least this is what it looks like). Ideally
* we find out why this happens and warn here as soon as it occurs.
*/
if (!cmd)
return IRQ_HANDLED;
cmd->error = 0;
meson_mx_mmc_read_response(host->mmc, cmd);
if (cmd->data) {
if (!((irqs & MESON_MX_SDIO_IRQS_DATA_READ_CRC16_OK) ||
(irqs & MESON_MX_SDIO_IRQS_DATA_WRITE_CRC16_OK)))
cmd->error = -EILSEQ;
} else {
if (!((irqs & MESON_MX_SDIO_IRQS_RESP_CRC7_OK) ||
(send & MESON_MX_SDIO_SEND_RESP_WITHOUT_CRC7)))
cmd->error = -EILSEQ;
}
return IRQ_WAKE_THREAD;
}
static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
{
struct meson_mx_mmc_host *host = (void *) data;
u32 irqs, send;
unsigned long irqflags;
irqreturn_t ret;
spin_lock_irqsave(&host->irq_lock, irqflags);
irqs = readl(host->base + MESON_MX_SDIO_IRQS);
send = readl(host->base + MESON_MX_SDIO_SEND);
if (irqs & MESON_MX_SDIO_IRQS_CMD_INT)
ret = meson_mx_mmc_process_cmd_irq(host, irqs, send);
else
ret = IRQ_HANDLED;
/* finally ACK all pending interrupts */
writel(irqs, host->base + MESON_MX_SDIO_IRQS);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
return ret;
}
static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
{
struct meson_mx_mmc_host *host = (void *) irq_data;
struct mmc_command *cmd = host->cmd, *next_cmd;
if (WARN_ON(!cmd))
return IRQ_HANDLED;
del_timer_sync(&host->cmd_timeout);
if (cmd->data) {
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
cmd->data->sg_len,
mmc_get_dma_dir(cmd->data));
cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
}
next_cmd = meson_mx_mmc_get_next_cmd(cmd);
if (next_cmd)
meson_mx_mmc_start_cmd(host->mmc, next_cmd);
else
meson_mx_mmc_request_done(host);
return IRQ_HANDLED;
}
static void meson_mx_mmc_timeout(struct timer_list *t)
{
struct meson_mx_mmc_host *host = from_timer(host, t, cmd_timeout);
unsigned long irqflags;
u32 irqc;
spin_lock_irqsave(&host->irq_lock, irqflags);
/* disable the CMD interrupt */
irqc = readl(host->base + MESON_MX_SDIO_IRQC);
irqc &= ~MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN;
writel(irqc, host->base + MESON_MX_SDIO_IRQC);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
/*
* skip the timeout handling if the interrupt handler already processed
* the command.
*/
if (!host->cmd)
return;
dev_dbg(mmc_dev(host->mmc),
"Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n",
host->cmd->opcode, readl(host->base + MESON_MX_SDIO_IRQS),
readl(host->base + MESON_MX_SDIO_ARGU));
host->cmd->error = -ETIMEDOUT;
meson_mx_mmc_request_done(host);
}
static struct mmc_host_ops meson_mx_mmc_ops = {
.request = meson_mx_mmc_request,
.set_ios = meson_mx_mmc_set_ios,
.card_busy = meson_mx_mmc_card_busy,
.get_cd = mmc_gpio_get_cd,
.get_ro = mmc_gpio_get_ro,
};
static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent)
{
struct device_node *slot_node;
/*
* TODO: the MMC core framework currently does not support
* controllers with multiple slots properly. So we only register
* the first slot for now
*/
slot_node = of_find_compatible_node(parent->of_node, NULL, "mmc-slot");
if (!slot_node) {
dev_warn(parent, "no 'mmc-slot' sub-node found\n");
return ERR_PTR(-ENOENT);
}
return of_platform_device_create(slot_node, NULL, parent);
}
static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
{
struct mmc_host *mmc = host->mmc;
struct device *slot_dev = mmc_dev(mmc);
int ret;
if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) {
dev_err(slot_dev, "missing 'reg' property\n");
return -EINVAL;
}
if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) {
dev_err(slot_dev, "invalid 'reg' property value %d\n",
host->slot_id);
return -EINVAL;
}
/* Get regulators and the supported OCR mask */
ret = mmc_regulator_get_supply(mmc);
if (ret)
return ret;
mmc->max_req_size = MESON_MX_SDIO_BOUNCE_REQ_SIZE;
mmc->max_seg_size = mmc->max_req_size;
mmc->max_blk_count =
FIELD_GET(MESON_MX_SDIO_SEND_REPEAT_PACKAGE_TIMES_MASK,
0xffffffff);
mmc->max_blk_size = FIELD_GET(MESON_MX_SDIO_EXT_DATA_RW_NUMBER_MASK,
0xffffffff);
mmc->max_blk_size -= (4 * MESON_MX_SDIO_RESPONSE_CRC16_BITS);
mmc->max_blk_size /= BITS_PER_BYTE;
/* Get the min and max supported clock rates */
mmc->f_min = clk_round_rate(host->cfg_div_clk, 1);
mmc->f_max = clk_round_rate(host->cfg_div_clk,
clk_get_rate(host->parent_clk));
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
mmc->ops = &meson_mx_mmc_ops;
ret = mmc_of_parse(mmc);
if (ret)
return ret;
ret = mmc_add_host(mmc);
if (ret)
return ret;
return 0;
}
static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host)
{
struct clk_init_data init;
const char *clk_div_parent, *clk_fixed_factor_parent;
clk_fixed_factor_parent = __clk_get_name(host->parent_clk);
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
"%s#fixed_factor",
dev_name(host->controller_dev));
init.ops = &clk_fixed_factor_ops;
init.flags = 0;
init.parent_names = &clk_fixed_factor_parent;
init.num_parents = 1;
host->fixed_factor.div = 2;
host->fixed_factor.mult = 1;
host->fixed_factor.hw.init = &init;
host->fixed_factor_clk = devm_clk_register(host->controller_dev,
&host->fixed_factor.hw);
if (WARN_ON(IS_ERR(host->fixed_factor_clk)))
return PTR_ERR(host->fixed_factor_clk);
clk_div_parent = __clk_get_name(host->fixed_factor_clk);
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
"%s#div", dev_name(host->controller_dev));
init.ops = &clk_divider_ops;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = &clk_div_parent;
init.num_parents = 1;
host->cfg_div.reg = host->base + MESON_MX_SDIO_CONF;
host->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
host->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
host->cfg_div.hw.init = &init;
host->cfg_div.flags = CLK_DIVIDER_ALLOW_ZERO;
host->cfg_div_clk = devm_clk_register(host->controller_dev,
&host->cfg_div.hw);
if (WARN_ON(IS_ERR(host->cfg_div_clk)))
return PTR_ERR(host->cfg_div_clk);
return 0;
}
static int meson_mx_mmc_probe(struct platform_device *pdev)
{
struct platform_device *slot_pdev;
struct mmc_host *mmc;
struct meson_mx_mmc_host *host;
struct resource *res;
int ret, irq;
u32 conf;
slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev);
if (!slot_pdev)
return -ENODEV;
else if (IS_ERR(slot_pdev))
return PTR_ERR(slot_pdev);
mmc = mmc_alloc_host(sizeof(*host), &slot_pdev->dev);
if (!mmc) {
ret = -ENOMEM;
goto error_unregister_slot_pdev;
}
host = mmc_priv(mmc);
host->mmc = mmc;
host->controller_dev = &pdev->dev;
spin_lock_init(&host->irq_lock);
timer_setup(&host->cmd_timeout, meson_mx_mmc_timeout, 0);
platform_set_drvdata(pdev, host);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->base = devm_ioremap_resource(host->controller_dev, res);
if (IS_ERR(host->base)) {
ret = PTR_ERR(host->base);
goto error_free_mmc;
}
irq = platform_get_irq(pdev, 0);
ret = devm_request_threaded_irq(host->controller_dev, irq,
meson_mx_mmc_irq,
meson_mx_mmc_irq_thread, IRQF_ONESHOT,
NULL, host);
if (ret)
goto error_free_mmc;
host->core_clk = devm_clk_get(host->controller_dev, "core");
if (IS_ERR(host->core_clk)) {
ret = PTR_ERR(host->core_clk);
goto error_free_mmc;
}
host->parent_clk = devm_clk_get(host->controller_dev, "clkin");
if (IS_ERR(host->parent_clk)) {
ret = PTR_ERR(host->parent_clk);
goto error_free_mmc;
}
ret = meson_mx_mmc_register_clks(host);
if (ret)
goto error_free_mmc;
ret = clk_prepare_enable(host->core_clk);
if (ret) {
dev_err(host->controller_dev, "Failed to enable core clock\n");
goto error_free_mmc;
}
ret = clk_prepare_enable(host->cfg_div_clk);
if (ret) {
dev_err(host->controller_dev, "Failed to enable MMC clock\n");
goto error_disable_core_clk;
}
conf = 0;
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_CMD_ARGUMENT_BITS_MASK, 39);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_M_ENDIAN_MASK, 0x3);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_NWR_MASK, 0x2);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK, 0x2);
writel(conf, host->base + MESON_MX_SDIO_CONF);
meson_mx_mmc_soft_reset(host);
ret = meson_mx_mmc_add_host(host);
if (ret)
goto error_disable_clks;
return 0;
error_disable_clks:
clk_disable_unprepare(host->cfg_div_clk);
error_disable_core_clk:
clk_disable_unprepare(host->core_clk);
error_free_mmc:
mmc_free_host(mmc);
error_unregister_slot_pdev:
of_platform_device_destroy(&slot_pdev->dev, NULL);
return ret;
}
static int meson_mx_mmc_remove(struct platform_device *pdev)
{
struct meson_mx_mmc_host *host = platform_get_drvdata(pdev);
struct device *slot_dev = mmc_dev(host->mmc);
del_timer_sync(&host->cmd_timeout);
mmc_remove_host(host->mmc);
of_platform_device_destroy(slot_dev, NULL);
clk_disable_unprepare(host->cfg_div_clk);
clk_disable_unprepare(host->core_clk);
mmc_free_host(host->mmc);
return 0;
}
static const struct of_device_id meson_mx_mmc_of_match[] = {
{ .compatible = "amlogic,meson8-sdio", },
{ .compatible = "amlogic,meson8b-sdio", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, meson_mx_mmc_of_match);
static struct platform_driver meson_mx_mmc_driver = {
.probe = meson_mx_mmc_probe,
.remove = meson_mx_mmc_remove,
.driver = {
.name = "meson-mx-sdio",
.of_match_table = of_match_ptr(meson_mx_mmc_of_match),
},
};
module_platform_driver(meson_mx_mmc_driver);
MODULE_DESCRIPTION("Meson6, Meson8 and Meson8b SDIO/MMC Host Driver");
MODULE_AUTHOR("Carlo Caione <carlo@endlessm.com>");
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
MODULE_LICENSE("GPL v2");

View File

@ -1658,7 +1658,7 @@ static int mmci_probe(struct amba_device *dev,
/* Get regulators and the supported OCR mask */
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
goto clk_disable;
if (!mmc->ocr_avail)

View File

@ -67,6 +67,7 @@
#define SDC_RESP2 0x48
#define SDC_RESP3 0x4c
#define SDC_BLK_NUM 0x50
#define SDC_ADV_CFG0 0x64
#define EMMC_IOCON 0x7c
#define SDC_ACMD_RESP 0x80
#define MSDC_DMA_SA 0x90
@ -74,10 +75,14 @@
#define MSDC_DMA_CFG 0x9c
#define MSDC_PATCH_BIT 0xb0
#define MSDC_PATCH_BIT1 0xb4
#define MSDC_PATCH_BIT2 0xb8
#define MSDC_PAD_TUNE 0xec
#define MSDC_PAD_TUNE0 0xf0
#define PAD_DS_TUNE 0x188
#define PAD_CMD_TUNE 0x18c
#define EMMC50_CFG0 0x208
#define EMMC50_CFG3 0x220
#define SDC_FIFO_CFG 0x228
/*--------------------------------------------------------------------------*/
/* Register Mask */
@ -95,6 +100,9 @@
#define MSDC_CFG_CKDIV (0xff << 8) /* RW */
#define MSDC_CFG_CKMOD (0x3 << 16) /* RW */
#define MSDC_CFG_HS400_CK_MODE (0x1 << 18) /* RW */
#define MSDC_CFG_HS400_CK_MODE_EXTRA (0x1 << 22) /* RW */
#define MSDC_CFG_CKDIV_EXTRA (0xfff << 8) /* RW */
#define MSDC_CFG_CKMOD_EXTRA (0x3 << 20) /* RW */
/* MSDC_IOCON mask */
#define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */
@ -183,6 +191,9 @@
#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */
#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */
/* SDC_ADV_CFG0 mask */
#define SDC_RX_ENHANCE_EN (0x1 << 20) /* RW */
/* MSDC_DMA_CTRL mask */
#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */
#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */
@ -212,11 +223,22 @@
#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */
#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */
#define MSDC_PATCH_BIT1_STOP_DLY (0xf << 8) /* RW */
#define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */
#define MSDC_PATCH_BIT2_CFGCRCSTS (0x1 << 28) /* RW */
#define MSDC_PB2_RESPWAIT (0x3 << 2) /* RW */
#define MSDC_PB2_RESPSTSENSEL (0x7 << 16) /* RW */
#define MSDC_PB2_CRCSTSENSEL (0x7 << 29) /* RW */
#define MSDC_PAD_TUNE_DATWRDLY (0x1f << 0) /* RW */
#define MSDC_PAD_TUNE_DATRRDLY (0x1f << 8) /* RW */
#define MSDC_PAD_TUNE_CMDRDLY (0x1f << 16) /* RW */
#define MSDC_PAD_TUNE_CMDRRDLY (0x1f << 22) /* RW */
#define MSDC_PAD_TUNE_CLKTDLY (0x1f << 27) /* RW */
#define MSDC_PAD_TUNE_RXDLYSEL (0x1 << 15) /* RW */
#define MSDC_PAD_TUNE_RD_SEL (0x1 << 13) /* RW */
#define MSDC_PAD_TUNE_CMD_SEL (0x1 << 21) /* RW */
#define PAD_DS_TUNE_DLY1 (0x1f << 2) /* RW */
#define PAD_DS_TUNE_DLY2 (0x1f << 7) /* RW */
@ -228,6 +250,11 @@
#define EMMC50_CFG_CRCSTS_EDGE (0x1 << 3) /* RW */
#define EMMC50_CFG_CFCSTS_SEL (0x1 << 4) /* RW */
#define EMMC50_CFG3_OUTS_WR (0x1f << 0) /* RW */
#define SDC_FIFO_CFG_WRVALIDSEL (0x1 << 24) /* RW */
#define SDC_FIFO_CFG_RDVALIDSEL (0x1 << 25) /* RW */
#define REQ_CMD_EIO (0x1 << 0)
#define REQ_CMD_TMO (0x1 << 1)
#define REQ_DAT_ERR (0x1 << 2)
@ -290,9 +317,23 @@ struct msdc_save_para {
u32 pad_tune;
u32 patch_bit0;
u32 patch_bit1;
u32 patch_bit2;
u32 pad_ds_tune;
u32 pad_cmd_tune;
u32 emmc50_cfg0;
u32 emmc50_cfg3;
u32 sdc_fifo_cfg;
};
struct mtk_mmc_compatible {
u8 clk_div_bits;
bool hs400_tune; /* only used for MT8173 */
u32 pad_tune_reg;
bool async_fifo;
bool data_tune;
bool busy_check;
bool stop_clk_fix;
bool enhance_rx;
};
struct msdc_tune_para {
@ -309,6 +350,7 @@ struct msdc_delay_phase {
struct msdc_host {
struct device *dev;
const struct mtk_mmc_compatible *dev_comp;
struct mmc_host *mmc; /* mmc structure */
int cmd_rsp;
@ -334,11 +376,13 @@ struct msdc_host {
struct clk *src_clk; /* msdc source clock */
struct clk *h_clk; /* msdc h_clk */
struct clk *src_clk_cg; /* msdc source clock control gate */
u32 mclk; /* mmc subsystem clock frequency */
u32 src_clk_freq; /* source clock frequency */
u32 sclk; /* SD/MS bus clock frequency */
unsigned char timing;
bool vqmmc_enabled;
u32 latch_ck;
u32 hs400_ds_delay;
u32 hs200_cmd_int_delay; /* cmd internal delay for HS200/SDR104 */
u32 hs400_cmd_int_delay; /* cmd internal delay for HS400 */
@ -350,6 +394,59 @@ struct msdc_host {
struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */
};
static const struct mtk_mmc_compatible mt8135_compat = {
.clk_div_bits = 8,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE,
.async_fifo = false,
.data_tune = false,
.busy_check = false,
.stop_clk_fix = false,
.enhance_rx = false,
};
static const struct mtk_mmc_compatible mt8173_compat = {
.clk_div_bits = 8,
.hs400_tune = true,
.pad_tune_reg = MSDC_PAD_TUNE,
.async_fifo = false,
.data_tune = false,
.busy_check = false,
.stop_clk_fix = false,
.enhance_rx = false,
};
static const struct mtk_mmc_compatible mt2701_compat = {
.clk_div_bits = 12,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
.busy_check = false,
.stop_clk_fix = false,
.enhance_rx = false,
};
static const struct mtk_mmc_compatible mt2712_compat = {
.clk_div_bits = 12,
.hs400_tune = false,
.pad_tune_reg = MSDC_PAD_TUNE0,
.async_fifo = true,
.data_tune = true,
.busy_check = true,
.stop_clk_fix = true,
.enhance_rx = true,
};
static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
{ .compatible = "mediatek,mt2701-mmc", .data = &mt2701_compat},
{ .compatible = "mediatek,mt2712-mmc", .data = &mt2712_compat},
{}
};
MODULE_DEVICE_TABLE(of, msdc_of_ids);
static void sdr_set_bits(void __iomem *reg, u32 bs)
{
u32 val = readl(reg);
@ -509,7 +606,12 @@ static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
timeout = (ns + clk_ns - 1) / clk_ns + clks;
/* in 1048576 sclk cycle unit */
timeout = (timeout + (0x1 << 20) - 1) >> 20;
sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &mode);
if (host->dev_comp->clk_div_bits == 8)
sdr_get_field(host->base + MSDC_CFG,
MSDC_CFG_CKMOD, &mode);
else
sdr_get_field(host->base + MSDC_CFG,
MSDC_CFG_CKMOD_EXTRA, &mode);
/*DDR mode will double the clk cycles for data timeout */
timeout = mode >= 2 ? timeout * 2 : timeout;
timeout = timeout > 1 ? timeout - 1 : 0;
@ -520,6 +622,7 @@ static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks)
static void msdc_gate_clock(struct msdc_host *host)
{
clk_disable_unprepare(host->src_clk_cg);
clk_disable_unprepare(host->src_clk);
clk_disable_unprepare(host->h_clk);
}
@ -528,6 +631,7 @@ static void msdc_ungate_clock(struct msdc_host *host)
{
clk_prepare_enable(host->h_clk);
clk_prepare_enable(host->src_clk);
clk_prepare_enable(host->src_clk_cg);
while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
cpu_relax();
}
@ -538,6 +642,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
u32 flags;
u32 div;
u32 sclk;
u32 tune_reg = host->dev_comp->pad_tune_reg;
if (!hz) {
dev_dbg(host->dev, "set mclk to 0\n");
@ -548,7 +653,11 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
flags = readl(host->base + MSDC_INTEN);
sdr_clr_bits(host->base + MSDC_INTEN, flags);
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE);
if (host->dev_comp->clk_div_bits == 8)
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_HS400_CK_MODE);
else
sdr_clr_bits(host->base + MSDC_CFG,
MSDC_CFG_HS400_CK_MODE_EXTRA);
if (timing == MMC_TIMING_UHS_DDR50 ||
timing == MMC_TIMING_MMC_DDR52 ||
timing == MMC_TIMING_MMC_HS400) {
@ -568,8 +677,12 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
if (timing == MMC_TIMING_MMC_HS400 &&
hz >= (host->src_clk_freq >> 1)) {
sdr_set_bits(host->base + MSDC_CFG,
MSDC_CFG_HS400_CK_MODE);
if (host->dev_comp->clk_div_bits == 8)
sdr_set_bits(host->base + MSDC_CFG,
MSDC_CFG_HS400_CK_MODE);
else
sdr_set_bits(host->base + MSDC_CFG,
MSDC_CFG_HS400_CK_MODE_EXTRA);
sclk = host->src_clk_freq >> 1;
div = 0; /* div is ignore when bit18 is set */
}
@ -587,11 +700,31 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
sclk = (host->src_clk_freq >> 2) / div;
}
}
sdr_set_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD | MSDC_CFG_CKDIV,
(mode << 8) | div);
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
/*
* As src_clk/HCLK use the same bit to gate/ungate,
* So if want to only gate src_clk, need gate its parent(mux).
*/
if (host->src_clk_cg)
clk_disable_unprepare(host->src_clk_cg);
else
clk_disable_unprepare(clk_get_parent(host->src_clk));
if (host->dev_comp->clk_div_bits == 8)
sdr_set_field(host->base + MSDC_CFG,
MSDC_CFG_CKMOD | MSDC_CFG_CKDIV,
(mode << 8) | div);
else
sdr_set_field(host->base + MSDC_CFG,
MSDC_CFG_CKMOD_EXTRA | MSDC_CFG_CKDIV_EXTRA,
(mode << 12) | div);
if (host->src_clk_cg)
clk_prepare_enable(host->src_clk_cg);
else
clk_prepare_enable(clk_get_parent(host->src_clk));
while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
cpu_relax();
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
host->sclk = sclk;
host->mclk = hz;
host->timing = timing;
@ -605,15 +738,16 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
*/
if (host->sclk <= 52000000) {
writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
writel(host->def_tune_para.pad_tune, host->base + tune_reg);
} else {
writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
writel(host->saved_tune_para.pad_tune, host->base + tune_reg);
writel(host->saved_tune_para.pad_cmd_tune,
host->base + PAD_CMD_TUNE);
}
if (timing == MMC_TIMING_MMC_HS400)
if (timing == MMC_TIMING_MMC_HS400 &&
host->dev_comp->hs400_tune)
sdr_set_field(host->base + PAD_CMD_TUNE,
MSDC_PAD_TUNE_CMDRRDLY,
host->hs400_cmd_int_delay);
@ -1165,6 +1299,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
static void msdc_init_hw(struct msdc_host *host)
{
u32 val;
u32 tune_reg = host->dev_comp->pad_tune_reg;
/* Configure to MMC/SD mode, clock free running */
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN);
@ -1180,14 +1315,53 @@ static void msdc_init_hw(struct msdc_host *host)
val = readl(host->base + MSDC_INT);
writel(val, host->base + MSDC_INT);
writel(0, host->base + MSDC_PAD_TUNE);
writel(0, host->base + tune_reg);
writel(0, host->base + MSDC_IOCON);
sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0);
writel(0x403c0046, host->base + MSDC_PATCH_BIT);
sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1);
writel(0xffff0089, host->base + MSDC_PATCH_BIT1);
writel(0xffff4089, host->base + MSDC_PATCH_BIT1);
sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL);
if (host->dev_comp->stop_clk_fix) {
sdr_set_field(host->base + MSDC_PATCH_BIT1,
MSDC_PATCH_BIT1_STOP_DLY, 3);
sdr_clr_bits(host->base + SDC_FIFO_CFG,
SDC_FIFO_CFG_WRVALIDSEL);
sdr_clr_bits(host->base + SDC_FIFO_CFG,
SDC_FIFO_CFG_RDVALIDSEL);
}
if (host->dev_comp->busy_check)
sdr_clr_bits(host->base + MSDC_PATCH_BIT1, (1 << 7));
if (host->dev_comp->async_fifo) {
sdr_set_field(host->base + MSDC_PATCH_BIT2,
MSDC_PB2_RESPWAIT, 3);
if (host->dev_comp->enhance_rx) {
sdr_set_bits(host->base + SDC_ADV_CFG0,
SDC_RX_ENHANCE_EN);
} else {
sdr_set_field(host->base + MSDC_PATCH_BIT2,
MSDC_PB2_RESPSTSENSEL, 2);
sdr_set_field(host->base + MSDC_PATCH_BIT2,
MSDC_PB2_CRCSTSENSEL, 2);
}
/* use async fifo, then no need tune internal delay */
sdr_clr_bits(host->base + MSDC_PATCH_BIT2,
MSDC_PATCH_BIT2_CFGRESP);
sdr_set_bits(host->base + MSDC_PATCH_BIT2,
MSDC_PATCH_BIT2_CFGCRCSTS);
}
if (host->dev_comp->data_tune) {
sdr_set_bits(host->base + tune_reg,
MSDC_PAD_TUNE_RD_SEL | MSDC_PAD_TUNE_CMD_SEL);
} else {
/* choose clock tune */
sdr_set_bits(host->base + tune_reg, MSDC_PAD_TUNE_RXDLYSEL);
}
/* Configure to enable SDIO mode.
* it's must otherwise sdio cmd5 failed
*/
@ -1200,7 +1374,9 @@ static void msdc_init_hw(struct msdc_host *host)
sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
host->def_tune_para.iocon = readl(host->base + MSDC_IOCON);
host->def_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
host->def_tune_para.pad_tune = readl(host->base + tune_reg);
host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
host->saved_tune_para.pad_tune = readl(host->base + tune_reg);
dev_dbg(host->dev, "init hardware done!");
}
@ -1343,18 +1519,19 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
struct msdc_delay_phase internal_delay_phase;
u8 final_delay, final_maxlen;
u32 internal_delay = 0;
u32 tune_reg = host->dev_comp->pad_tune_reg;
int cmd_err;
int i, j;
if (mmc->ios.timing == MMC_TIMING_MMC_HS200 ||
mmc->ios.timing == MMC_TIMING_UHS_SDR104)
sdr_set_field(host->base + MSDC_PAD_TUNE,
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRRDLY,
host->hs200_cmd_int_delay);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
for (i = 0 ; i < PAD_DELAY_MAX; i++) {
sdr_set_field(host->base + MSDC_PAD_TUNE,
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRDLY, i);
/*
* Using the same parameters, it may sometimes pass the test,
@ -1373,12 +1550,13 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
}
final_rise_delay = get_best_delay(host, rise_delay);
/* if rising edge has enough margin, then do not scan falling edge */
if (final_rise_delay.maxlen >= 12 && final_rise_delay.start < 4)
if (final_rise_delay.maxlen >= 12 ||
(final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
goto skip_fall;
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
for (i = 0; i < PAD_DELAY_MAX; i++) {
sdr_set_field(host->base + MSDC_PAD_TUNE,
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRDLY, i);
/*
* Using the same parameters, it may sometimes pass the test,
@ -1403,20 +1581,20 @@ skip_fall:
final_maxlen = final_fall_delay.maxlen;
if (final_maxlen == final_rise_delay.maxlen) {
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY,
final_rise_delay.final_phase);
final_delay = final_rise_delay.final_phase;
} else {
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRDLY,
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY,
final_fall_delay.final_phase);
final_delay = final_fall_delay.final_phase;
}
if (host->hs200_cmd_int_delay)
if (host->dev_comp->async_fifo || host->hs200_cmd_int_delay)
goto skip_internal;
for (i = 0; i < PAD_DELAY_MAX; i++) {
sdr_set_field(host->base + MSDC_PAD_TUNE,
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_CMDRRDLY, i);
mmc_send_tuning(mmc, opcode, &cmd_err);
if (!cmd_err)
@ -1424,7 +1602,7 @@ skip_fall:
}
dev_dbg(host->dev, "Final internal delay: 0x%x\n", internal_delay);
internal_delay_phase = get_best_delay(host, internal_delay);
sdr_set_field(host->base + MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY,
sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRRDLY,
internal_delay_phase.final_phase);
skip_internal:
dev_dbg(host->dev, "Final cmd pad delay: %x\n", final_delay);
@ -1486,12 +1664,15 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
u32 rise_delay = 0, fall_delay = 0;
struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
u8 final_delay, final_maxlen;
u32 tune_reg = host->dev_comp->pad_tune_reg;
int i, ret;
sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_INT_DAT_LATCH_CK_SEL,
host->latch_ck);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
for (i = 0 ; i < PAD_DELAY_MAX; i++) {
sdr_set_field(host->base + MSDC_PAD_TUNE,
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_DATRRDLY, i);
ret = mmc_send_tuning(mmc, opcode, NULL);
if (!ret)
@ -1506,7 +1687,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
for (i = 0; i < PAD_DELAY_MAX; i++) {
sdr_set_field(host->base + MSDC_PAD_TUNE,
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_DATRRDLY, i);
ret = mmc_send_tuning(mmc, opcode, NULL);
if (!ret)
@ -1519,14 +1700,14 @@ skip_fall:
if (final_maxlen == final_rise_delay.maxlen) {
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
sdr_set_field(host->base + MSDC_PAD_TUNE,
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_DATRRDLY,
final_rise_delay.final_phase);
final_delay = final_rise_delay.final_phase;
} else {
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
sdr_set_field(host->base + MSDC_PAD_TUNE,
sdr_set_field(host->base + tune_reg,
MSDC_PAD_TUNE_DATRRDLY,
final_fall_delay.final_phase);
final_delay = final_fall_delay.final_phase;
@ -1540,8 +1721,10 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct msdc_host *host = mmc_priv(mmc);
int ret;
u32 tune_reg = host->dev_comp->pad_tune_reg;
if (host->hs400_mode)
if (host->hs400_mode &&
host->dev_comp->hs400_tune)
ret = hs400_tune_response(mmc, opcode);
else
ret = msdc_tune_response(mmc, opcode);
@ -1556,7 +1739,7 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
}
host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
host->saved_tune_para.pad_tune = readl(host->base + tune_reg);
host->saved_tune_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
return ret;
}
@ -1567,6 +1750,11 @@ static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
host->hs400_mode = true;
writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
/* hs400 mode must set it to 0 */
sdr_clr_bits(host->base + MSDC_PATCH_BIT2, MSDC_PATCH_BIT2_CFGCRCSTS);
/* to improve read performance, set outstanding to 2 */
sdr_set_field(host->base + EMMC50_CFG3, EMMC50_CFG3_OUTS_WR, 2);
return 0;
}
@ -1596,6 +1784,9 @@ static const struct mmc_host_ops mt_msdc_ops = {
static void msdc_of_property_parse(struct platform_device *pdev,
struct msdc_host *host)
{
of_property_read_u32(pdev->dev.of_node, "mediatek,latch-ck",
&host->latch_ck);
of_property_read_u32(pdev->dev.of_node, "hs400-ds-delay",
&host->hs400_ds_delay);
@ -1617,12 +1808,17 @@ static int msdc_drv_probe(struct platform_device *pdev)
struct mmc_host *mmc;
struct msdc_host *host;
struct resource *res;
const struct of_device_id *of_id;
int ret;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "No DT found\n");
return -EINVAL;
}
of_id = of_match_node(msdc_of_ids, pdev->dev.of_node);
if (!of_id)
return -EINVAL;
/* Allocate MMC host for this device */
mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev);
if (!mmc)
@ -1641,7 +1837,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
}
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
goto host_free;
host->src_clk = devm_clk_get(&pdev->dev, "source");
@ -1656,6 +1852,11 @@ static int msdc_drv_probe(struct platform_device *pdev)
goto host_free;
}
/*source clock control gate is optional clock*/
host->src_clk_cg = devm_clk_get(&pdev->dev, "source_cg");
if (IS_ERR(host->src_clk_cg))
host->src_clk_cg = NULL;
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) {
ret = -EINVAL;
@ -1686,11 +1887,15 @@ static int msdc_drv_probe(struct platform_device *pdev)
msdc_of_property_parse(pdev, host);
host->dev = &pdev->dev;
host->dev_comp = of_id->data;
host->mmc = mmc;
host->src_clk_freq = clk_get_rate(host->src_clk);
/* Set host parameters to mmc */
mmc->ops = &mt_msdc_ops;
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 255);
if (host->dev_comp->clk_div_bits == 8)
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 255);
else
mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095);
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
/* MMC core transfer sizes tunable parameters */
@ -1788,28 +1993,38 @@ static int msdc_drv_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static void msdc_save_reg(struct msdc_host *host)
{
u32 tune_reg = host->dev_comp->pad_tune_reg;
host->save_para.msdc_cfg = readl(host->base + MSDC_CFG);
host->save_para.iocon = readl(host->base + MSDC_IOCON);
host->save_para.sdc_cfg = readl(host->base + SDC_CFG);
host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
host->save_para.pad_tune = readl(host->base + tune_reg);
host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT);
host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1);
host->save_para.patch_bit2 = readl(host->base + MSDC_PATCH_BIT2);
host->save_para.pad_ds_tune = readl(host->base + PAD_DS_TUNE);
host->save_para.pad_cmd_tune = readl(host->base + PAD_CMD_TUNE);
host->save_para.emmc50_cfg0 = readl(host->base + EMMC50_CFG0);
host->save_para.emmc50_cfg3 = readl(host->base + EMMC50_CFG3);
host->save_para.sdc_fifo_cfg = readl(host->base + SDC_FIFO_CFG);
}
static void msdc_restore_reg(struct msdc_host *host)
{
u32 tune_reg = host->dev_comp->pad_tune_reg;
writel(host->save_para.msdc_cfg, host->base + MSDC_CFG);
writel(host->save_para.iocon, host->base + MSDC_IOCON);
writel(host->save_para.sdc_cfg, host->base + SDC_CFG);
writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE);
writel(host->save_para.pad_tune, host->base + tune_reg);
writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT);
writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1);
writel(host->save_para.patch_bit2, host->base + MSDC_PATCH_BIT2);
writel(host->save_para.pad_ds_tune, host->base + PAD_DS_TUNE);
writel(host->save_para.pad_cmd_tune, host->base + PAD_CMD_TUNE);
writel(host->save_para.emmc50_cfg0, host->base + EMMC50_CFG0);
writel(host->save_para.emmc50_cfg3, host->base + EMMC50_CFG3);
writel(host->save_para.sdc_fifo_cfg, host->base + SDC_FIFO_CFG);
}
static int msdc_runtime_suspend(struct device *dev)
@ -1839,12 +2054,6 @@ static const struct dev_pm_ops msdc_dev_pm_ops = {
SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL)
};
static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", },
{}
};
MODULE_DEVICE_TABLE(of, msdc_of_ids);
static struct platform_driver mt_msdc_driver = {
.probe = msdc_drv_probe,
.remove = msdc_drv_remove,

View File

@ -508,9 +508,9 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
return IRQ_NONE;
}
static void mvsd_timeout_timer(unsigned long data)
static void mvsd_timeout_timer(struct timer_list *t)
{
struct mvsd_host *host = (struct mvsd_host *)data;
struct mvsd_host *host = from_timer(host, t, timer);
void __iomem *iobase = host->base;
struct mmc_request *mrq;
unsigned long flags;
@ -776,7 +776,7 @@ static int mvsd_probe(struct platform_device *pdev)
goto out;
}
setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
timer_setup(&host->timer, mvsd_timeout_timer, 0);
platform_set_drvdata(pdev, mmc);
ret = mmc_add_host(mmc);
if (ret)

View File

@ -963,10 +963,9 @@ static bool filter(struct dma_chan *chan, void *param)
return true;
}
static void mxcmci_watchdog(unsigned long data)
static void mxcmci_watchdog(struct timer_list *t)
{
struct mmc_host *mmc = (struct mmc_host *)data;
struct mxcmci_host *host = mmc_priv(mmc);
struct mxcmci_host *host = from_timer(host, t, watchdog);
struct mmc_request *req = host->req;
unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS);
@ -1075,7 +1074,7 @@ static int mxcmci_probe(struct platform_device *pdev)
dat3_card_detect = true;
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
goto out_free;
if (!mmc->ocr_avail) {
@ -1165,9 +1164,7 @@ static int mxcmci_probe(struct platform_device *pdev)
goto out_free_dma;
}
init_timer(&host->watchdog);
host->watchdog.function = &mxcmci_watchdog;
host->watchdog.data = (unsigned long)mmc;
timer_setup(&host->watchdog, mxcmci_watchdog, 0);
mmc_add_host(mmc);

View File

@ -625,9 +625,9 @@ static void mmc_omap_abort_command(struct work_struct *work)
}
static void
mmc_omap_cmd_timer(unsigned long data)
mmc_omap_cmd_timer(struct timer_list *t)
{
struct mmc_omap_host *host = (struct mmc_omap_host *) data;
struct mmc_omap_host *host = from_timer(host, t, cmd_abort_timer);
unsigned long flags;
spin_lock_irqsave(&host->slot_lock, flags);
@ -654,9 +654,9 @@ mmc_omap_sg_to_buf(struct mmc_omap_host *host)
}
static void
mmc_omap_clk_timer(unsigned long data)
mmc_omap_clk_timer(struct timer_list *t)
{
struct mmc_omap_host *host = (struct mmc_omap_host *) data;
struct mmc_omap_host *host = from_timer(host, t, clk_timer);
mmc_omap_fclk_enable(host, 0);
}
@ -874,9 +874,9 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed)
tasklet_hi_schedule(&slot->cover_tasklet);
}
static void mmc_omap_cover_timer(unsigned long arg)
static void mmc_omap_cover_timer(struct timer_list *t)
{
struct mmc_omap_slot *slot = (struct mmc_omap_slot *) arg;
struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer);
tasklet_schedule(&slot->cover_tasklet);
}
@ -1264,8 +1264,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
mmc->max_seg_size = mmc->max_req_size;
if (slot->pdata->get_cover_state != NULL) {
setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
(unsigned long)slot);
timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0);
tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
(unsigned long)slot);
}
@ -1352,11 +1351,10 @@ static int mmc_omap_probe(struct platform_device *pdev)
INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work);
INIT_WORK(&host->cmd_abort_work, mmc_omap_abort_command);
setup_timer(&host->cmd_abort_timer, mmc_omap_cmd_timer,
(unsigned long) host);
timer_setup(&host->cmd_abort_timer, mmc_omap_cmd_timer, 0);
spin_lock_init(&host->clk_lock);
setup_timer(&host->clk_timer, mmc_omap_clk_timer, (unsigned long) host);
timer_setup(&host->clk_timer, mmc_omap_clk_timer, 0);
spin_lock_init(&host->dma_lock);
spin_lock_init(&host->slot_lock);

View File

@ -147,10 +147,6 @@
#define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc"
#define VDD_1V8 1800000 /* 180000 uV */
#define VDD_3V0 3000000 /* 300000 uV */
#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@ -308,8 +304,7 @@ err_set_ocr:
return ret;
}
static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on,
int vdd)
static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on)
{
int ret;
@ -317,17 +312,6 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on,
return 0;
if (power_on) {
if (vdd <= VDD_165_195)
ret = regulator_set_voltage(host->pbias, VDD_1V8,
VDD_1V8);
else
ret = regulator_set_voltage(host->pbias, VDD_3V0,
VDD_3V0);
if (ret < 0) {
dev_err(host->dev, "pbias set voltage fail\n");
return ret;
}
if (host->pbias_enabled == 0) {
ret = regulator_enable(host->pbias);
if (ret) {
@ -350,8 +334,7 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on,
return 0;
}
static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
int vdd)
static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on)
{
struct mmc_host *mmc = host->mmc;
int ret = 0;
@ -363,7 +346,7 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
if (IS_ERR(mmc->supply.vmmc))
return 0;
ret = omap_hsmmc_set_pbias(host, false, 0);
ret = omap_hsmmc_set_pbias(host, false);
if (ret)
return ret;
@ -385,7 +368,7 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on,
if (ret)
return ret;
ret = omap_hsmmc_set_pbias(host, true, vdd);
ret = omap_hsmmc_set_pbias(host, true);
if (ret)
goto err_set_voltage;
} else {
@ -462,7 +445,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
return ret;
/* Allow an aux regulator */
@ -1220,11 +1203,11 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
clk_disable_unprepare(host->dbclk);
/* Turn the power off */
ret = omap_hsmmc_set_power(host, 0, 0);
ret = omap_hsmmc_set_power(host, 0);
/* Turn the power ON with given VDD 1.8 or 3.0v */
if (!ret)
ret = omap_hsmmc_set_power(host, 1, vdd);
ret = omap_hsmmc_set_power(host, 1);
if (host->dbclk)
clk_prepare_enable(host->dbclk);
@ -1621,10 +1604,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (ios->power_mode != host->power_mode) {
switch (ios->power_mode) {
case MMC_POWER_OFF:
omap_hsmmc_set_power(host, 0, 0);
omap_hsmmc_set_power(host, 0);
break;
case MMC_POWER_UP:
omap_hsmmc_set_power(host, 1, ios->vdd);
omap_hsmmc_set_power(host, 1);
break;
case MMC_POWER_ON:
do_send_init_stream = 1;

View File

@ -88,6 +88,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, },
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
{},
};
MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match);

View File

@ -91,7 +91,6 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = {
};
static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-shmobile" },
{ .compatible = "renesas,sdhi-sh73a0", .data = &of_default_cfg, },
{ .compatible = "renesas,sdhi-r8a73a4", .data = &of_default_cfg, },
{ .compatible = "renesas,sdhi-r8a7740", .data = &of_default_cfg, },
@ -107,6 +106,10 @@ static const struct of_device_id renesas_sdhi_sys_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7795", .data = &of_rcar_gen3_compatible, },
{ .compatible = "renesas,sdhi-r8a7796", .data = &of_rcar_gen3_compatible, },
{ .compatible = "renesas,rcar-gen1-sdhi", .data = &of_rcar_gen1_compatible, },
{ .compatible = "renesas,rcar-gen2-sdhi", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
{ .compatible = "renesas,sdhi-shmobile" },
{},
};
MODULE_DEVICE_TABLE(of, renesas_sdhi_sys_dmac_of_match);

View File

@ -618,29 +618,22 @@ static int sd_change_phase(struct realtek_pci_sdmmc *host,
u8 sample_point, bool rx)
{
struct rtsx_pcr *pcr = host->pcr;
int err;
dev_dbg(sdmmc_dev(host), "%s(%s): sample_point = %d\n",
__func__, rx ? "RX" : "TX", sample_point);
rtsx_pci_init_cmd(pcr);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, CHANGE_CLK);
rtsx_pci_write_register(pcr, CLK_CTL, CHANGE_CLK, CHANGE_CLK);
if (rx)
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
SD_VPRX_CTL, 0x1F, sample_point);
rtsx_pci_write_register(pcr, SD_VPRX_CTL,
PHASE_SELECT_MASK, sample_point);
else
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD,
SD_VPTX_CTL, 0x1F, sample_point);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL,
PHASE_NOT_RESET, PHASE_NOT_RESET);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, 0);
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0);
err = rtsx_pci_send_cmd(pcr, 100);
if (err < 0)
return err;
rtsx_pci_write_register(pcr, SD_VPTX_CTL,
PHASE_SELECT_MASK, sample_point);
rtsx_pci_write_register(pcr, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0);
rtsx_pci_write_register(pcr, SD_VPCLK0_CTL, PHASE_NOT_RESET,
PHASE_NOT_RESET);
rtsx_pci_write_register(pcr, CLK_CTL, CHANGE_CLK, 0);
rtsx_pci_write_register(pcr, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0);
return 0;
}
@ -708,10 +701,12 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
{
int err;
struct mmc_command cmd = {};
struct rtsx_pcr *pcr = host->pcr;
err = sd_change_phase(host, sample_point, true);
if (err < 0)
return err;
sd_change_phase(host, sample_point, true);
rtsx_pci_write_register(pcr, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN,
SD_RSP_80CLK_TIMEOUT_EN);
cmd.opcode = opcode;
err = sd_read_data(host, &cmd, 0x40, NULL, 0, 100);
@ -719,9 +714,12 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host,
/* Wait till SD DATA IDLE */
sd_wait_data_idle(host);
sd_clear_error(host);
rtsx_pci_write_register(pcr, SD_CFG3,
SD_RSP_80CLK_TIMEOUT_EN, 0);
return err;
}
rtsx_pci_write_register(pcr, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, 0);
return 0;
}

View File

@ -73,6 +73,7 @@ struct sdhci_acpi_slot {
unsigned int caps2;
mmc_pm_flag_t pm_caps;
unsigned int flags;
size_t priv_size;
int (*probe_slot)(struct platform_device *, const char *, const char *);
int (*remove_slot)(struct platform_device *);
};
@ -82,13 +83,118 @@ struct sdhci_acpi_host {
const struct sdhci_acpi_slot *slot;
struct platform_device *pdev;
bool use_runtime_pm;
unsigned long private[0] ____cacheline_aligned;
};
static inline void *sdhci_acpi_priv(struct sdhci_acpi_host *c)
{
return (void *)c->private;
}
static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
{
return c->slot && (c->slot->flags & flag);
}
enum {
INTEL_DSM_FNS = 0,
INTEL_DSM_V18_SWITCH = 3,
INTEL_DSM_V33_SWITCH = 4,
};
struct intel_host {
u32 dsm_fns;
};
static const guid_t intel_dsm_guid =
GUID_INIT(0xF6C13EA5, 0x65CD, 0x461F,
0xAB, 0x7A, 0x29, 0xF7, 0xE8, 0xD5, 0xBD, 0x61);
static int __intel_dsm(struct intel_host *intel_host, struct device *dev,
unsigned int fn, u32 *result)
{
union acpi_object *obj;
int err = 0;
obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_dsm_guid, 0, fn, NULL);
if (!obj)
return -EOPNOTSUPP;
if (obj->type == ACPI_TYPE_INTEGER) {
*result = obj->integer.value;
} else if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length > 0) {
size_t len = min_t(size_t, obj->buffer.length, 4);
*result = 0;
memcpy(result, obj->buffer.pointer, len);
} else {
dev_err(dev, "%s DSM fn %u obj->type %d obj->buffer.length %d\n",
__func__, fn, obj->type, obj->buffer.length);
err = -EINVAL;
}
ACPI_FREE(obj);
return err;
}
static int intel_dsm(struct intel_host *intel_host, struct device *dev,
unsigned int fn, u32 *result)
{
if (fn > 31 || !(intel_host->dsm_fns & (1 << fn)))
return -EOPNOTSUPP;
return __intel_dsm(intel_host, dev, fn, result);
}
static void intel_dsm_init(struct intel_host *intel_host, struct device *dev,
struct mmc_host *mmc)
{
int err;
err = __intel_dsm(intel_host, dev, INTEL_DSM_FNS, &intel_host->dsm_fns);
if (err) {
pr_debug("%s: DSM not supported, error %d\n",
mmc_hostname(mmc), err);
return;
}
pr_debug("%s: DSM function mask %#x\n",
mmc_hostname(mmc), intel_host->dsm_fns);
}
static int intel_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
{
struct device *dev = mmc_dev(mmc);
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
struct intel_host *intel_host = sdhci_acpi_priv(c);
unsigned int fn;
u32 result = 0;
int err;
err = sdhci_start_signal_voltage_switch(mmc, ios);
if (err)
return err;
switch (ios->signal_voltage) {
case MMC_SIGNAL_VOLTAGE_330:
fn = INTEL_DSM_V33_SWITCH;
break;
case MMC_SIGNAL_VOLTAGE_180:
fn = INTEL_DSM_V18_SWITCH;
break;
default:
return 0;
}
err = intel_dsm(intel_host, dev, fn, &result);
pr_debug("%s: %s DSM fn %u error %d result %u\n",
mmc_hostname(mmc), __func__, fn, err, result);
return 0;
}
static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
{
u8 reg;
@ -269,56 +375,26 @@ out:
return ret;
}
static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev,
const char *hid, const char *uid)
static int intel_probe_slot(struct platform_device *pdev, const char *hid,
const char *uid)
{
struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
struct sdhci_host *host;
if (!c || !c->host)
return 0;
host = c->host;
/* Platform specific code during emmc probe slot goes here */
struct intel_host *intel_host = sdhci_acpi_priv(c);
struct sdhci_host *host = c->host;
if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 &&
sdhci_readl(host, SDHCI_CAPABILITIES_1) == 0x00000807)
host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
return 0;
}
static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev,
const char *hid, const char *uid)
{
struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
if (!c || !c->host)
return 0;
/* Platform specific code during sdio probe slot goes here */
return 0;
}
static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev,
const char *hid, const char *uid)
{
struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
struct sdhci_host *host;
if (!c || !c->host || !c->slot)
return 0;
host = c->host;
/* Platform specific code during sd probe slot goes here */
if (hid && !strcmp(hid, "80865ACA"))
host->mmc_host_ops.get_cd = bxt_get_cd;
intel_dsm_init(intel_host, &pdev->dev, host->mmc);
host->mmc_host_ops.start_signal_voltage_switch =
intel_start_signal_voltage_switch;
return 0;
}
@ -332,7 +408,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC |
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
.probe_slot = sdhci_acpi_emmc_probe_slot,
.probe_slot = intel_probe_slot,
.priv_size = sizeof(struct intel_host),
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
@ -343,7 +420,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
MMC_CAP_WAIT_WHILE_BUSY,
.flags = SDHCI_ACPI_RUNTIME_PM,
.pm_caps = MMC_PM_KEEP_POWER,
.probe_slot = sdhci_acpi_sdio_probe_slot,
.probe_slot = intel_probe_slot,
.priv_size = sizeof(struct intel_host),
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
@ -353,7 +431,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_STOP_WITH_TC,
.caps = MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_AGGRESSIVE_PM,
.probe_slot = sdhci_acpi_sd_probe_slot,
.probe_slot = intel_probe_slot,
.priv_size = sizeof(struct intel_host),
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_qcom_sd_3v = {
@ -429,11 +508,13 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid,
static int sdhci_acpi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct sdhci_acpi_slot *slot;
struct acpi_device *device, *child;
struct sdhci_acpi_host *c;
struct sdhci_host *host;
struct resource *iomem;
resource_size_t len;
size_t priv_size;
const char *hid;
const char *uid;
int err;
@ -443,7 +524,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
return -ENODEV;
hid = acpi_device_hid(device);
uid = device->pnp.unique_id;
uid = acpi_device_uid(device);
slot = sdhci_acpi_get_slot(hid, uid);
/* Power on the SDHCI controller and its children */
acpi_device_fix_up_power(device);
@ -467,13 +550,14 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev)))
return -ENOMEM;
host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host));
priv_size = slot ? slot->priv_size : 0;
host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host) + priv_size);
if (IS_ERR(host))
return PTR_ERR(host);
c = sdhci_priv(host);
c->host = host;
c->slot = sdhci_acpi_get_slot(hid, uid);
c->slot = slot;
c->pdev = pdev;
c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM);

View File

@ -13,6 +13,7 @@
* GNU General Public License for more details.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/iopoll.h>
#include <linux/module.h>
@ -27,15 +28,14 @@
#define SDHCI_CDNS_HRS04_ACK BIT(26)
#define SDHCI_CDNS_HRS04_RD BIT(25)
#define SDHCI_CDNS_HRS04_WR BIT(24)
#define SDHCI_CDNS_HRS04_RDATA_SHIFT 16
#define SDHCI_CDNS_HRS04_WDATA_SHIFT 8
#define SDHCI_CDNS_HRS04_ADDR_SHIFT 0
#define SDHCI_CDNS_HRS04_RDATA GENMASK(23, 16)
#define SDHCI_CDNS_HRS04_WDATA GENMASK(15, 8)
#define SDHCI_CDNS_HRS04_ADDR GENMASK(5, 0)
#define SDHCI_CDNS_HRS06 0x18 /* eMMC control */
#define SDHCI_CDNS_HRS06_TUNE_UP BIT(15)
#define SDHCI_CDNS_HRS06_TUNE_SHIFT 8
#define SDHCI_CDNS_HRS06_TUNE_MASK 0x3f
#define SDHCI_CDNS_HRS06_MODE_MASK 0x7
#define SDHCI_CDNS_HRS06_TUNE GENMASK(13, 8)
#define SDHCI_CDNS_HRS06_MODE GENMASK(2, 0)
#define SDHCI_CDNS_HRS06_MODE_SD 0x0
#define SDHCI_CDNS_HRS06_MODE_MMC_SDR 0x2
#define SDHCI_CDNS_HRS06_MODE_MMC_DDR 0x3
@ -105,8 +105,8 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
u32 tmp;
int ret;
tmp = (data << SDHCI_CDNS_HRS04_WDATA_SHIFT) |
(addr << SDHCI_CDNS_HRS04_ADDR_SHIFT);
tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
writel(tmp, reg);
tmp |= SDHCI_CDNS_HRS04_WR;
@ -189,8 +189,8 @@ static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
/* The speed mode for eMMC is selected by HRS06 register */
tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
tmp &= ~SDHCI_CDNS_HRS06_MODE_MASK;
tmp |= mode;
tmp &= ~SDHCI_CDNS_HRS06_MODE;
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
}
@ -199,7 +199,7 @@ static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
u32 tmp;
tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
return tmp & SDHCI_CDNS_HRS06_MODE_MASK;
return FIELD_GET(SDHCI_CDNS_HRS06_MODE, tmp);
}
static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
@ -254,12 +254,12 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06;
u32 tmp;
if (WARN_ON(val > SDHCI_CDNS_HRS06_TUNE_MASK))
if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val)))
return -EINVAL;
tmp = readl(reg);
tmp &= ~(SDHCI_CDNS_HRS06_TUNE_MASK << SDHCI_CDNS_HRS06_TUNE_SHIFT);
tmp |= val << SDHCI_CDNS_HRS06_TUNE_SHIFT;
tmp &= ~SDHCI_CDNS_HRS06_TUNE;
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val);
tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
writel(tmp, reg);

View File

@ -123,14 +123,17 @@
#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
#define MSM_MMC_AUTOSUSPEND_DELAY_MS 50
/* Timeout value to avoid infinite waiting for pwr_irq */
#define MSM_PWR_IRQ_TIMEOUT_MS 5000
struct sdhci_msm_host {
struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */
int pwr_irq; /* power irq */
struct clk *clk; /* main SD/MMC bus clock */
struct clk *pclk; /* SDHC peripheral bus clock */
struct clk *bus_clk; /* SDHC bus voter clock */
struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/
struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */
unsigned long clk_rate;
struct mmc_host *mmc;
bool use_14lpp_dll_reset;
@ -138,6 +141,10 @@ struct sdhci_msm_host {
bool calibration_done;
u8 saved_tuning_phase;
bool use_cdclp533;
u32 curr_pwr_state;
u32 curr_io_level;
wait_queue_head_t pwr_irq_wait;
bool pwr_irq_flag;
};
static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host,
@ -164,10 +171,11 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
struct mmc_ios curr_ios = host->mmc->ios;
struct clk *core_clk = msm_host->bulk_clks[0].clk;
int rc;
clock = msm_get_clock_rate_for_bus_mode(host, clock);
rc = clk_set_rate(msm_host->clk, clock);
rc = clk_set_rate(core_clk, clock);
if (rc) {
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
mmc_hostname(host->mmc), clock,
@ -176,7 +184,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
}
msm_host->clk_rate = clock;
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
mmc_hostname(host->mmc), clk_get_rate(msm_host->clk),
mmc_hostname(host->mmc), clk_get_rate(core_clk),
curr_ios.timing);
}
@ -995,21 +1003,142 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,
sdhci_msm_hs400(host, &mmc->ios);
}
static void sdhci_msm_voltage_switch(struct sdhci_host *host)
static inline void sdhci_msm_init_pwr_irq_wait(struct sdhci_msm_host *msm_host)
{
init_waitqueue_head(&msm_host->pwr_irq_wait);
}
static inline void sdhci_msm_complete_pwr_irq_wait(
struct sdhci_msm_host *msm_host)
{
wake_up(&msm_host->pwr_irq_wait);
}
/*
* sdhci_msm_check_power_status API should be called when registers writes
* which can toggle sdhci IO bus ON/OFF or change IO lines HIGH/LOW happens.
* To what state the register writes will change the IO lines should be passed
* as the argument req_type. This API will check whether the IO line's state
* is already the expected state and will wait for power irq only if
* power irq is expected to be trigerred based on the current IO line state
* and expected IO line state.
*/
static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
bool done = false;
pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n",
mmc_hostname(host->mmc), __func__, req_type,
msm_host->curr_pwr_state, msm_host->curr_io_level);
/*
* The IRQ for request type IO High/LOW will be generated when -
* there is a state change in 1.8V enable bit (bit 3) of
* SDHCI_HOST_CONTROL2 register. The reset state of that bit is 0
* which indicates 3.3V IO voltage. So, when MMC core layer tries
* to set it to 3.3V before card detection happens, the
* IRQ doesn't get triggered as there is no state change in this bit.
* The driver already handles this case by changing the IO voltage
* level to high as part of controller power up sequence. Hence, check
* for host->pwr to handle a case where IO voltage high request is
* issued even before controller power up.
*/
if ((req_type & REQ_IO_HIGH) && !host->pwr) {
pr_debug("%s: do not wait for power IRQ that never comes, req_type: %d\n",
mmc_hostname(host->mmc), req_type);
return;
}
if ((req_type & msm_host->curr_pwr_state) ||
(req_type & msm_host->curr_io_level))
done = true;
/*
* This is needed here to handle cases where register writes will
* not change the current bus state or io level of the controller.
* In this case, no power irq will be triggerred and we should
* not wait.
*/
if (!done) {
if (!wait_event_timeout(msm_host->pwr_irq_wait,
msm_host->pwr_irq_flag,
msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS)))
dev_warn(&msm_host->pdev->dev,
"%s: pwr_irq for req: (%d) timed out\n",
mmc_hostname(host->mmc), req_type);
}
pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc),
__func__, req_type);
}
static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n",
mmc_hostname(host->mmc),
readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS),
readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK),
readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL));
}
static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
u32 irq_status, irq_ack = 0;
int retry = 10;
int pwr_state = 0, io_level = 0;
irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS);
irq_status &= INT_MASK;
writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR);
if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF))
/*
* There is a rare HW scenario where the first clear pulse could be
* lost when actual reset and clear/read of status register is
* happening at a time. Hence, retry for at least 10 times to make
* sure status register is cleared. Otherwise, this will result in
* a spurious power IRQ resulting in system instability.
*/
while (irq_status & readl_relaxed(msm_host->core_mem +
CORE_PWRCTL_STATUS)) {
if (retry == 0) {
pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n",
mmc_hostname(host->mmc), irq_status);
sdhci_msm_dump_pwr_ctrl_regs(host);
WARN_ON(1);
break;
}
writel_relaxed(irq_status,
msm_host->core_mem + CORE_PWRCTL_CLEAR);
retry--;
udelay(10);
}
/* Handle BUS ON/OFF*/
if (irq_status & CORE_PWRCTL_BUS_ON) {
pwr_state = REQ_BUS_ON;
io_level = REQ_IO_HIGH;
irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH))
}
if (irq_status & CORE_PWRCTL_BUS_OFF) {
pwr_state = REQ_BUS_OFF;
io_level = REQ_IO_LOW;
irq_ack |= CORE_PWRCTL_BUS_SUCCESS;
}
/* Handle IO LOW/HIGH */
if (irq_status & CORE_PWRCTL_IO_LOW) {
io_level = REQ_IO_LOW;
irq_ack |= CORE_PWRCTL_IO_SUCCESS;
}
if (irq_status & CORE_PWRCTL_IO_HIGH) {
io_level = REQ_IO_HIGH;
irq_ack |= CORE_PWRCTL_IO_SUCCESS;
}
/*
* The driver has to acknowledge the interrupt, switch voltages and
@ -1017,13 +1146,27 @@ static void sdhci_msm_voltage_switch(struct sdhci_host *host)
* switches are handled by the sdhci core, so just report success.
*/
writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL);
if (pwr_state)
msm_host->curr_pwr_state = pwr_state;
if (io_level)
msm_host->curr_io_level = io_level;
pr_debug("%s: %s: Handled IRQ(%d), irq_status=0x%x, ack=0x%x\n",
mmc_hostname(msm_host->mmc), __func__, irq, irq_status,
irq_ack);
}
static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)
{
struct sdhci_host *host = (struct sdhci_host *)data;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
sdhci_msm_handle_pwr_irq(host, irq);
msm_host->pwr_irq_flag = 1;
sdhci_msm_complete_pwr_irq_wait(msm_host);
sdhci_msm_voltage_switch(host);
return IRQ_HANDLED;
}
@ -1032,8 +1175,9 @@ static unsigned int sdhci_msm_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
struct clk *core_clk = msm_host->bulk_clks[0].clk;
return clk_round_rate(msm_host->clk, ULONG_MAX);
return clk_round_rate(core_clk, ULONG_MAX);
}
static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host)
@ -1092,6 +1236,69 @@ out:
__sdhci_msm_set_clock(host, clock);
}
/*
* Platform specific register write functions. This is so that, if any
* register write needs to be followed up by platform specific actions,
* they can be added here. These functions can go to sleep when writes
* to certain registers are done.
* These functions are relying on sdhci_set_ios not using spinlock.
*/
static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
u32 req_type = 0;
switch (reg) {
case SDHCI_HOST_CONTROL2:
req_type = (val & SDHCI_CTRL_VDD_180) ? REQ_IO_LOW :
REQ_IO_HIGH;
break;
case SDHCI_SOFTWARE_RESET:
if (host->pwr && (val & SDHCI_RESET_ALL))
req_type = REQ_BUS_OFF;
break;
case SDHCI_POWER_CONTROL:
req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON;
break;
}
if (req_type) {
msm_host->pwr_irq_flag = 0;
/*
* Since this register write may trigger a power irq, ensure
* all previous register writes are complete by this point.
*/
mb();
}
return req_type;
}
/* This function may sleep*/
static void sdhci_msm_writew(struct sdhci_host *host, u16 val, int reg)
{
u32 req_type = 0;
req_type = __sdhci_msm_check_write(host, val, reg);
writew_relaxed(val, host->ioaddr + reg);
if (req_type)
sdhci_msm_check_power_status(host, req_type);
}
/* This function may sleep*/
static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg)
{
u32 req_type = 0;
req_type = __sdhci_msm_check_write(host, val, reg);
writeb_relaxed(val, host->ioaddr + reg);
if (req_type)
sdhci_msm_check_power_status(host, req_type);
}
static const struct of_device_id sdhci_msm_dt_match[] = {
{ .compatible = "qcom,sdhci-msm-v4" },
{},
@ -1106,7 +1313,8 @@ static const struct sdhci_ops sdhci_msm_ops = {
.get_max_clock = sdhci_msm_get_max_clock,
.set_bus_width = sdhci_set_bus_width,
.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
.voltage_switch = sdhci_msm_voltage_switch,
.write_w = sdhci_msm_writew,
.write_b = sdhci_msm_writeb,
};
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
@ -1124,6 +1332,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_msm_host *msm_host;
struct resource *core_memres;
struct clk *clk;
int ret;
u16 host_version, core_minor;
u32 core_version, config;
@ -1160,24 +1369,42 @@ static int sdhci_msm_probe(struct platform_device *pdev)
}
/* Setup main peripheral bus clock */
msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(msm_host->pclk)) {
ret = PTR_ERR(msm_host->pclk);
clk = devm_clk_get(&pdev->dev, "iface");
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(&pdev->dev, "Peripheral clk setup failed (%d)\n", ret);
goto bus_clk_disable;
}
ret = clk_prepare_enable(msm_host->pclk);
if (ret)
goto bus_clk_disable;
msm_host->bulk_clks[1].clk = clk;
/* Setup SDC MMC clock */
msm_host->clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(msm_host->clk)) {
ret = PTR_ERR(msm_host->clk);
clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
goto pclk_disable;
goto bus_clk_disable;
}
msm_host->bulk_clks[0].clk = clk;
/* Vote for maximum clock rate for maximum performance */
ret = clk_set_rate(clk, INT_MAX);
if (ret)
dev_warn(&pdev->dev, "core clock boost failed\n");
clk = devm_clk_get(&pdev->dev, "cal");
if (IS_ERR(clk))
clk = NULL;
msm_host->bulk_clks[2].clk = clk;
clk = devm_clk_get(&pdev->dev, "sleep");
if (IS_ERR(clk))
clk = NULL;
msm_host->bulk_clks[3].clk = clk;
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
if (ret)
goto bus_clk_disable;
/*
* xo clock is needed for FLL feature of cm_dll.
@ -1189,15 +1416,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "TCXO clk not present (%d)\n", ret);
}
/* Vote for maximum clock rate for maximum performance */
ret = clk_set_rate(msm_host->clk, INT_MAX);
if (ret)
dev_warn(&pdev->dev, "core clock boost failed\n");
ret = clk_prepare_enable(msm_host->clk);
if (ret)
goto pclk_disable;
core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
@ -1251,6 +1469,21 @@ static int sdhci_msm_probe(struct platform_device *pdev)
CORE_VENDOR_SPEC_CAPABILITIES0);
}
/*
* Power on reset state may trigger power irq if previous status of
* PWRCTL was either BUS_ON or IO_HIGH_V. So before enabling pwr irq
* interrupt in GIC, any pending power irq interrupt should be
* acknowledged. Otherwise power irq interrupt handler would be
* fired prematurely.
*/
sdhci_msm_handle_pwr_irq(host, 0);
/*
* Ensure that above writes are propogated before interrupt enablement
* in GIC.
*/
mb();
/* Setup IRQ for handling power/voltage tasks with PMIC */
msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq");
if (msm_host->pwr_irq < 0) {
@ -1260,6 +1493,10 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto clk_disable;
}
sdhci_msm_init_pwr_irq_wait(msm_host);
/* Enable pwr irq interrupts */
writel_relaxed(INT_MASK, msm_host->core_mem + CORE_PWRCTL_MASK);
ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL,
sdhci_msm_pwr_irq, IRQF_ONESHOT,
dev_name(&pdev->dev), host);
@ -1290,9 +1527,8 @@ pm_runtime_disable:
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
clk_disable:
clk_disable_unprepare(msm_host->clk);
pclk_disable:
clk_disable_unprepare(msm_host->pclk);
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
bus_clk_disable:
if (!IS_ERR(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
@ -1315,8 +1551,8 @@ static int sdhci_msm_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
clk_disable_unprepare(msm_host->clk);
clk_disable_unprepare(msm_host->pclk);
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
if (!IS_ERR(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
sdhci_pltfm_free(pdev);
@ -1330,8 +1566,8 @@ static int sdhci_msm_runtime_suspend(struct device *dev)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
clk_disable_unprepare(msm_host->clk);
clk_disable_unprepare(msm_host->pclk);
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
return 0;
}
@ -1341,21 +1577,9 @@ static int sdhci_msm_runtime_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
int ret;
ret = clk_prepare_enable(msm_host->clk);
if (ret) {
dev_err(dev, "clk_enable failed for core_clk: %d\n", ret);
return ret;
}
ret = clk_prepare_enable(msm_host->pclk);
if (ret) {
dev_err(dev, "clk_enable failed for iface_clk: %d\n", ret);
clk_disable_unprepare(msm_host->clk);
return ret;
}
return 0;
return clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
}
#endif

View File

@ -114,7 +114,8 @@ static void sdhci_at91_set_power(struct sdhci_host *host, unsigned char mode,
sdhci_set_power_noreg(host, mode, vdd);
}
void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host,
unsigned int timing)
{
if (timing == MMC_TIMING_MMC_DDR52)
sdhci_writeb(host, SDMMC_MC1R_DDR, SDMMC_MC1R);

View File

@ -458,6 +458,33 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
return clock / 256 / 16;
}
static void esdhc_clock_enable(struct sdhci_host *host, bool enable)
{
u32 val;
ktime_t timeout;
val = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
if (enable)
val |= ESDHC_CLOCK_SDCLKEN;
else
val &= ~ESDHC_CLOCK_SDCLKEN;
sdhci_writel(host, val, ESDHC_SYSTEM_CONTROL);
/* Wait max 20 ms */
timeout = ktime_add_ms(ktime_get(), 20);
val = ESDHC_CLOCK_STABLE;
while (!(sdhci_readl(host, ESDHC_PRSSTAT) & val)) {
if (ktime_after(ktime_get(), timeout)) {
pr_err("%s: Internal clock never stabilised.\n",
mmc_hostname(host->mmc));
break;
}
udelay(10);
}
}
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -469,8 +496,10 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
host->mmc->actual_clock = 0;
if (clock == 0)
if (clock == 0) {
esdhc_clock_enable(host, false);
return;
}
/* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
if (esdhc->vendor_ver < VENDOR_V_23)
@ -558,33 +587,6 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
sdhci_writel(host, ctrl, ESDHC_PROCTL);
}
static void esdhc_clock_enable(struct sdhci_host *host, bool enable)
{
u32 val;
ktime_t timeout;
val = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
if (enable)
val |= ESDHC_CLOCK_SDCLKEN;
else
val &= ~ESDHC_CLOCK_SDCLKEN;
sdhci_writel(host, val, ESDHC_SYSTEM_CONTROL);
/* Wait max 20 ms */
timeout = ktime_add_ms(ktime_get(), 20);
val = ESDHC_CLOCK_STABLE;
while (!(sdhci_readl(host, ESDHC_PRSSTAT) & val)) {
if (ktime_after(ktime_get(), timeout)) {
pr_err("%s: Internal clock never stabilised.\n",
mmc_hostname(host->mmc));
break;
}
udelay(10);
}
}
static void esdhc_reset(struct sdhci_host *host, u8 mask)
{
sdhci_reset(host, mask);

View File

@ -0,0 +1,607 @@
/**
* SDHCI Controller driver for TI's OMAP SoCs
*
* Copyright (C) 2017 Texas Instruments
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/delay.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include "sdhci-pltfm.h"
#define SDHCI_OMAP_CON 0x12c
#define CON_DW8 BIT(5)
#define CON_DMA_MASTER BIT(20)
#define CON_INIT BIT(1)
#define CON_OD BIT(0)
#define SDHCI_OMAP_CMD 0x20c
#define SDHCI_OMAP_HCTL 0x228
#define HCTL_SDBP BIT(8)
#define HCTL_SDVS_SHIFT 9
#define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT)
#define HCTL_SDVS_33 (0x7 << HCTL_SDVS_SHIFT)
#define HCTL_SDVS_30 (0x6 << HCTL_SDVS_SHIFT)
#define HCTL_SDVS_18 (0x5 << HCTL_SDVS_SHIFT)
#define SDHCI_OMAP_SYSCTL 0x22c
#define SYSCTL_CEN BIT(2)
#define SYSCTL_CLKD_SHIFT 6
#define SYSCTL_CLKD_MASK 0x3ff
#define SDHCI_OMAP_STAT 0x230
#define SDHCI_OMAP_IE 0x234
#define INT_CC_EN BIT(0)
#define SDHCI_OMAP_AC12 0x23c
#define AC12_V1V8_SIGEN BIT(19)
#define SDHCI_OMAP_CAPA 0x240
#define CAPA_VS33 BIT(24)
#define CAPA_VS30 BIT(25)
#define CAPA_VS18 BIT(26)
#define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
#define SYSCTL_CLKD_MAX 0x3FF
#define IOV_1V8 1800000 /* 180000 uV */
#define IOV_3V0 3000000 /* 300000 uV */
#define IOV_3V3 3300000 /* 330000 uV */
struct sdhci_omap_data {
u32 offset;
};
struct sdhci_omap_host {
void __iomem *base;
struct device *dev;
struct regulator *pbias;
bool pbias_enabled;
struct sdhci_host *host;
u8 bus_mode;
u8 power_mode;
};
static inline u32 sdhci_omap_readl(struct sdhci_omap_host *host,
unsigned int offset)
{
return readl(host->base + offset);
}
static inline void sdhci_omap_writel(struct sdhci_omap_host *host,
unsigned int offset, u32 data)
{
writel(data, host->base + offset);
}
static int sdhci_omap_set_pbias(struct sdhci_omap_host *omap_host,
bool power_on, unsigned int iov)
{
int ret;
struct device *dev = omap_host->dev;
if (IS_ERR(omap_host->pbias))
return 0;
if (power_on) {
ret = regulator_set_voltage(omap_host->pbias, iov, iov);
if (ret) {
dev_err(dev, "pbias set voltage failed\n");
return ret;
}
if (omap_host->pbias_enabled)
return 0;
ret = regulator_enable(omap_host->pbias);
if (ret) {
dev_err(dev, "pbias reg enable fail\n");
return ret;
}
omap_host->pbias_enabled = true;
} else {
if (!omap_host->pbias_enabled)
return 0;
ret = regulator_disable(omap_host->pbias);
if (ret) {
dev_err(dev, "pbias reg disable fail\n");
return ret;
}
omap_host->pbias_enabled = false;
}
return 0;
}
static int sdhci_omap_enable_iov(struct sdhci_omap_host *omap_host,
unsigned int iov)
{
int ret;
struct sdhci_host *host = omap_host->host;
struct mmc_host *mmc = host->mmc;
ret = sdhci_omap_set_pbias(omap_host, false, 0);
if (ret)
return ret;
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_set_voltage(mmc->supply.vqmmc, iov, iov);
if (ret) {
dev_err(mmc_dev(mmc), "vqmmc set voltage failed\n");
return ret;
}
}
ret = sdhci_omap_set_pbias(omap_host, true, iov);
if (ret)
return ret;
return 0;
}
static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host,
unsigned char signal_voltage)
{
u32 reg;
ktime_t timeout;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL);
reg &= ~HCTL_SDVS_MASK;
if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
reg |= HCTL_SDVS_33;
else
reg |= HCTL_SDVS_18;
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg);
reg |= HCTL_SDBP;
sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, reg);
/* wait 1ms */
timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT);
while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL) & HCTL_SDBP)) {
if (WARN_ON(ktime_after(ktime_get(), timeout)))
return;
usleep_range(5, 10);
}
}
static int sdhci_omap_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios)
{
u32 reg;
int ret;
unsigned int iov;
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_omap_host *omap_host;
struct device *dev;
pltfm_host = sdhci_priv(host);
omap_host = sdhci_pltfm_priv(pltfm_host);
dev = omap_host->dev;
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
if (!(reg & CAPA_VS33))
return -EOPNOTSUPP;
sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
reg &= ~AC12_V1V8_SIGEN;
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
iov = IOV_3V3;
} else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
if (!(reg & CAPA_VS18))
return -EOPNOTSUPP;
sdhci_omap_conf_bus_power(omap_host, ios->signal_voltage);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_AC12);
reg |= AC12_V1V8_SIGEN;
sdhci_omap_writel(omap_host, SDHCI_OMAP_AC12, reg);
iov = IOV_1V8;
} else {
return -EOPNOTSUPP;
}
ret = sdhci_omap_enable_iov(omap_host, iov);
if (ret) {
dev_err(dev, "failed to switch IO voltage to %dmV\n", iov);
return ret;
}
dev_dbg(dev, "IO voltage switched to %dmV\n", iov);
return 0;
}
static void sdhci_omap_set_bus_mode(struct sdhci_omap_host *omap_host,
unsigned int mode)
{
u32 reg;
if (omap_host->bus_mode == mode)
return;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
if (mode == MMC_BUSMODE_OPENDRAIN)
reg |= CON_OD;
else
reg &= ~CON_OD;
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
omap_host->bus_mode = mode;
}
static void sdhci_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_omap_host *omap_host;
pltfm_host = sdhci_priv(host);
omap_host = sdhci_pltfm_priv(pltfm_host);
sdhci_omap_set_bus_mode(omap_host, ios->bus_mode);
sdhci_set_ios(mmc, ios);
}
static u16 sdhci_omap_calc_divisor(struct sdhci_pltfm_host *host,
unsigned int clock)
{
u16 dsor;
dsor = DIV_ROUND_UP(clk_get_rate(host->clk), clock);
if (dsor > SYSCTL_CLKD_MAX)
dsor = SYSCTL_CLKD_MAX;
return dsor;
}
static void sdhci_omap_start_clock(struct sdhci_omap_host *omap_host)
{
u32 reg;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL);
reg |= SYSCTL_CEN;
sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, reg);
}
static void sdhci_omap_stop_clock(struct sdhci_omap_host *omap_host)
{
u32 reg;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_SYSCTL);
reg &= ~SYSCTL_CEN;
sdhci_omap_writel(omap_host, SDHCI_OMAP_SYSCTL, reg);
}
static void sdhci_omap_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
unsigned long clkdiv;
sdhci_omap_stop_clock(omap_host);
if (!clock)
return;
clkdiv = sdhci_omap_calc_divisor(pltfm_host, clock);
clkdiv = (clkdiv & SYSCTL_CLKD_MASK) << SYSCTL_CLKD_SHIFT;
sdhci_enable_clk(host, clkdiv);
sdhci_omap_start_clock(omap_host);
}
static void sdhci_omap_set_power(struct sdhci_host *host, unsigned char mode,
unsigned short vdd)
{
struct mmc_host *mmc = host->mmc;
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
}
static int sdhci_omap_enable_dma(struct sdhci_host *host)
{
u32 reg;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
reg |= CON_DMA_MASTER;
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
return 0;
}
static unsigned int sdhci_omap_get_min_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
return clk_get_rate(pltfm_host->clk) / SYSCTL_CLKD_MAX;
}
static void sdhci_omap_set_bus_width(struct sdhci_host *host, int width)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
u32 reg;
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
if (width == MMC_BUS_WIDTH_8)
reg |= CON_DW8;
else
reg &= ~CON_DW8;
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
sdhci_set_bus_width(host, width);
}
static void sdhci_omap_init_74_clocks(struct sdhci_host *host, u8 power_mode)
{
u32 reg;
ktime_t timeout;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
if (omap_host->power_mode == power_mode)
return;
if (power_mode != MMC_POWER_ON)
return;
disable_irq(host->irq);
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
reg |= CON_INIT;
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
sdhci_omap_writel(omap_host, SDHCI_OMAP_CMD, 0x0);
/* wait 1ms */
timeout = ktime_add_ms(ktime_get(), SDHCI_OMAP_TIMEOUT);
while (!(sdhci_omap_readl(omap_host, SDHCI_OMAP_STAT) & INT_CC_EN)) {
if (WARN_ON(ktime_after(ktime_get(), timeout)))
return;
usleep_range(5, 10);
}
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
reg &= ~CON_INIT;
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
sdhci_omap_writel(omap_host, SDHCI_OMAP_STAT, INT_CC_EN);
enable_irq(host->irq);
omap_host->power_mode = power_mode;
}
static struct sdhci_ops sdhci_omap_ops = {
.set_clock = sdhci_omap_set_clock,
.set_power = sdhci_omap_set_power,
.enable_dma = sdhci_omap_enable_dma,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.get_min_clock = sdhci_omap_get_min_clock,
.set_bus_width = sdhci_omap_set_bus_width,
.platform_send_init_74_clocks = sdhci_omap_init_74_clocks,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
{
u32 reg;
int ret = 0;
struct device *dev = omap_host->dev;
struct regulator *vqmmc;
vqmmc = regulator_get(dev, "vqmmc");
if (IS_ERR(vqmmc)) {
ret = PTR_ERR(vqmmc);
goto reg_put;
}
/* voltage capabilities might be set by boot loader, clear it */
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CAPA);
reg &= ~(CAPA_VS18 | CAPA_VS30 | CAPA_VS33);
if (regulator_is_supported_voltage(vqmmc, IOV_3V3, IOV_3V3))
reg |= CAPA_VS33;
if (regulator_is_supported_voltage(vqmmc, IOV_1V8, IOV_1V8))
reg |= CAPA_VS18;
sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, reg);
reg_put:
regulator_put(vqmmc);
return ret;
}
static const struct sdhci_pltfm_data sdhci_omap_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
.quirks2 = SDHCI_QUIRK2_NO_1_8_V |
SDHCI_QUIRK2_ACMD23_BROKEN |
SDHCI_QUIRK2_RSP_136_HAS_CRC,
.ops = &sdhci_omap_ops,
};
static const struct sdhci_omap_data dra7_data = {
.offset = 0x200,
};
static const struct of_device_id omap_sdhci_match[] = {
{ .compatible = "ti,dra7-sdhci", .data = &dra7_data },
{},
};
MODULE_DEVICE_TABLE(of, omap_sdhci_match);
static int sdhci_omap_probe(struct platform_device *pdev)
{
int ret;
u32 offset;
struct device *dev = &pdev->dev;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_omap_host *omap_host;
struct mmc_host *mmc;
const struct of_device_id *match;
struct sdhci_omap_data *data;
match = of_match_device(omap_sdhci_match, dev);
if (!match)
return -EINVAL;
data = (struct sdhci_omap_data *)match->data;
if (!data) {
dev_err(dev, "no sdhci omap data\n");
return -EINVAL;
}
offset = data->offset;
host = sdhci_pltfm_init(pdev, &sdhci_omap_pdata,
sizeof(*omap_host));
if (IS_ERR(host)) {
dev_err(dev, "Failed sdhci_pltfm_init\n");
return PTR_ERR(host);
}
pltfm_host = sdhci_priv(host);
omap_host = sdhci_pltfm_priv(pltfm_host);
omap_host->host = host;
omap_host->base = host->ioaddr;
omap_host->dev = dev;
host->ioaddr += offset;
mmc = host->mmc;
ret = mmc_of_parse(mmc);
if (ret)
goto err_pltfm_free;
pltfm_host->clk = devm_clk_get(dev, "fck");
if (IS_ERR(pltfm_host->clk)) {
ret = PTR_ERR(pltfm_host->clk);
goto err_pltfm_free;
}
ret = clk_set_rate(pltfm_host->clk, mmc->f_max);
if (ret) {
dev_err(dev, "failed to set clock to %d\n", mmc->f_max);
goto err_pltfm_free;
}
omap_host->pbias = devm_regulator_get_optional(dev, "pbias");
if (IS_ERR(omap_host->pbias)) {
ret = PTR_ERR(omap_host->pbias);
if (ret != -ENODEV)
goto err_pltfm_free;
dev_dbg(dev, "unable to get pbias regulator %d\n", ret);
}
omap_host->pbias_enabled = false;
/*
* omap_device_pm_domain has callbacks to enable the main
* functional clock, interface clock and also configure the
* SYSCONFIG register of omap devices. The callback will be invoked
* as part of pm_runtime_get_sync.
*/
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
dev_err(dev, "pm_runtime_get_sync failed\n");
pm_runtime_put_noidle(dev);
goto err_rpm_disable;
}
ret = sdhci_omap_set_capabilities(omap_host);
if (ret) {
dev_err(dev, "failed to set system capabilities\n");
goto err_put_sync;
}
host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_omap_start_signal_voltage_switch;
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
sdhci_read_caps(host);
host->caps |= SDHCI_CAN_DO_ADMA2;
ret = sdhci_add_host(host);
if (ret)
goto err_put_sync;
return 0;
err_put_sync:
pm_runtime_put_sync(dev);
err_rpm_disable:
pm_runtime_disable(dev);
err_pltfm_free:
sdhci_pltfm_free(pdev);
return ret;
}
static int sdhci_omap_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sdhci_host *host = platform_get_drvdata(pdev);
sdhci_remove_host(host, true);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
sdhci_pltfm_free(pdev);
return 0;
}
static struct platform_driver sdhci_omap_driver = {
.probe = sdhci_omap_probe,
.remove = sdhci_omap_remove,
.driver = {
.name = "sdhci-omap",
.of_match_table = omap_sdhci_match,
},
};
module_platform_driver(sdhci_omap_driver);
MODULE_DESCRIPTION("SDHCI driver for OMAP SoCs");
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:sdhci_omap");

View File

@ -32,7 +32,6 @@
#include "sdhci.h"
#include "sdhci-pci.h"
#include "sdhci-pci-o2micro.h"
static int sdhci_pci_enable_dma(struct sdhci_host *host);
static void sdhci_pci_hw_reset(struct sdhci_host *host);
@ -798,15 +797,6 @@ static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = {
.probe_slot = intel_mrfld_mmc_probe_slot,
};
/* O2Micro extra registers */
#define O2_SD_LOCK_WP 0xD3
#define O2_SD_MULTI_VCC3V 0xEE
#define O2_SD_CLKREQ 0xEC
#define O2_SD_CAPS 0xE0
#define O2_SD_ADMA1 0xE2
#define O2_SD_ADMA2 0xE7
#define O2_SD_INF_MOD 0xF1
static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
{
u8 scratch;
@ -1290,6 +1280,7 @@ static const struct pci_device_id pci_ids[] = {
SDHCI_PCI_DEVICE(INTEL, SPT_SDIO, intel_byt_sdio),
SDHCI_PCI_DEVICE(INTEL, SPT_SD, intel_byt_sd),
SDHCI_PCI_DEVICE(INTEL, DNV_EMMC, intel_byt_emmc),
SDHCI_PCI_DEVICE(INTEL, CDF_EMMC, intel_glk_emmc),
SDHCI_PCI_DEVICE(INTEL, BXT_EMMC, intel_byt_emmc),
SDHCI_PCI_DEVICE(INTEL, BXT_SDIO, intel_byt_sdio),
SDHCI_PCI_DEVICE(INTEL, BXT_SD, intel_byt_sd),

View File

@ -19,7 +19,40 @@
#include "sdhci.h"
#include "sdhci-pci.h"
#include "sdhci-pci-o2micro.h"
/*
* O2Micro device registers
*/
#define O2_SD_MISC_REG5 0x64
#define O2_SD_LD0_CTRL 0x68
#define O2_SD_DEV_CTRL 0x88
#define O2_SD_LOCK_WP 0xD3
#define O2_SD_TEST_REG 0xD4
#define O2_SD_FUNC_REG0 0xDC
#define O2_SD_MULTI_VCC3V 0xEE
#define O2_SD_CLKREQ 0xEC
#define O2_SD_CAPS 0xE0
#define O2_SD_ADMA1 0xE2
#define O2_SD_ADMA2 0xE7
#define O2_SD_INF_MOD 0xF1
#define O2_SD_MISC_CTRL4 0xFC
#define O2_SD_TUNING_CTRL 0x300
#define O2_SD_PLL_SETTING 0x304
#define O2_SD_CLK_SETTING 0x328
#define O2_SD_CAP_REG2 0x330
#define O2_SD_CAP_REG0 0x334
#define O2_SD_UHS1_CAP_SETTING 0x33C
#define O2_SD_DELAY_CTRL 0x350
#define O2_SD_UHS2_L1_CTRL 0x35C
#define O2_SD_FUNC_REG3 0x3E0
#define O2_SD_FUNC_REG4 0x3E4
#define O2_SD_LED_ENABLE BIT(6)
#define O2_SD_FREG0_LEDOFF BIT(13)
#define O2_SD_FREG4_ENABLE_CLK_SET BIT(22)
#define O2_SD_VENDOR_SETTING 0x110
#define O2_SD_VENDOR_SETTING2 0x1C8
static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value)
{

View File

@ -1,73 +0,0 @@
/*
* Copyright (C) 2013 BayHub Technology Ltd.
*
* Authors: Peter Guo <peter.guo@bayhubtech.com>
* Adam Lee <adam.lee@canonical.com>
*
* 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.
*
*/
#ifndef __SDHCI_PCI_O2MICRO_H
#define __SDHCI_PCI_O2MICRO_H
#include "sdhci-pci.h"
/*
* O2Micro device IDs
*/
#define PCI_DEVICE_ID_O2_SDS0 0x8420
#define PCI_DEVICE_ID_O2_SDS1 0x8421
#define PCI_DEVICE_ID_O2_FUJIN2 0x8520
#define PCI_DEVICE_ID_O2_SEABIRD0 0x8620
#define PCI_DEVICE_ID_O2_SEABIRD1 0x8621
/*
* O2Micro device registers
*/
#define O2_SD_MISC_REG5 0x64
#define O2_SD_LD0_CTRL 0x68
#define O2_SD_DEV_CTRL 0x88
#define O2_SD_LOCK_WP 0xD3
#define O2_SD_TEST_REG 0xD4
#define O2_SD_FUNC_REG0 0xDC
#define O2_SD_MULTI_VCC3V 0xEE
#define O2_SD_CLKREQ 0xEC
#define O2_SD_CAPS 0xE0
#define O2_SD_ADMA1 0xE2
#define O2_SD_ADMA2 0xE7
#define O2_SD_INF_MOD 0xF1
#define O2_SD_MISC_CTRL4 0xFC
#define O2_SD_TUNING_CTRL 0x300
#define O2_SD_PLL_SETTING 0x304
#define O2_SD_CLK_SETTING 0x328
#define O2_SD_CAP_REG2 0x330
#define O2_SD_CAP_REG0 0x334
#define O2_SD_UHS1_CAP_SETTING 0x33C
#define O2_SD_DELAY_CTRL 0x350
#define O2_SD_UHS2_L1_CTRL 0x35C
#define O2_SD_FUNC_REG3 0x3E0
#define O2_SD_FUNC_REG4 0x3E4
#define O2_SD_LED_ENABLE BIT(6)
#define O2_SD_FREG0_LEDOFF BIT(13)
#define O2_SD_FREG4_ENABLE_CLK_SET BIT(22)
#define O2_SD_VENDOR_SETTING 0x110
#define O2_SD_VENDOR_SETTING2 0x1C8
extern int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot);
extern int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip);
extern int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip);
#endif /* __SDHCI_PCI_O2MICRO_H */

View File

@ -6,6 +6,12 @@
* PCI device IDs, sub IDs
*/
#define PCI_DEVICE_ID_O2_SDS0 0x8420
#define PCI_DEVICE_ID_O2_SDS1 0x8421
#define PCI_DEVICE_ID_O2_FUJIN2 0x8520
#define PCI_DEVICE_ID_O2_SEABIRD0 0x8620
#define PCI_DEVICE_ID_O2_SEABIRD1 0x8621
#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809
#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a
#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14
@ -26,6 +32,7 @@
#define PCI_DEVICE_ID_INTEL_SPT_SDIO 0x9d2c
#define PCI_DEVICE_ID_INTEL_SPT_SD 0x9d2d
#define PCI_DEVICE_ID_INTEL_DNV_EMMC 0x19db
#define PCI_DEVICE_ID_INTEL_CDF_EMMC 0x18db
#define PCI_DEVICE_ID_INTEL_BXT_SD 0x0aca
#define PCI_DEVICE_ID_INTEL_BXT_EMMC 0x0acc
#define PCI_DEVICE_ID_INTEL_BXT_SDIO 0x0ad0
@ -164,4 +171,10 @@ static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot)
int sdhci_pci_resume_host(struct sdhci_pci_chip *chip);
#endif
int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot);
int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip);
#ifdef CONFIG_PM_SLEEP
int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip);
#endif
#endif /* __SDHCI_PCI_H */

View File

@ -761,32 +761,24 @@ static const struct dev_pm_ops sdhci_s3c_pmops = {
NULL)
};
#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
.no_divider = true,
};
#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data)
#else
#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL)
#endif
static const struct platform_device_id sdhci_s3c_driver_ids[] = {
{
.name = "s3c-sdhci",
.driver_data = (kernel_ulong_t)NULL,
}, {
.name = "exynos4-sdhci",
.driver_data = EXYNOS4_SDHCI_DRV_DATA,
},
{ }
};
MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids);
#ifdef CONFIG_OF
static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
.no_divider = true,
};
static const struct of_device_id sdhci_s3c_dt_match[] = {
{ .compatible = "samsung,s3c6410-sdhci", },
{ .compatible = "samsung,exynos4210-sdhci",
.data = (void *)EXYNOS4_SDHCI_DRV_DATA },
.data = &exynos4_sdhci_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match);

View File

@ -422,7 +422,15 @@ static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
/* SDHCI controllers on Tegra186 support 40-bit addressing.
* IOVA addresses are 48-bit wide on Tegra186.
* With 64-bit dma mask used for SDHCI, accesses can
* be broken. Disable 64-bit dma, which would fall back
* to 32-bit dma mask. Ideally 40-bit dma mask would work,
* But it is not supported as of now.
*/
SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
.ops = &tegra114_sdhci_ops,
};

View File

@ -2407,12 +2407,12 @@ static void sdhci_tasklet_finish(unsigned long param)
;
}
static void sdhci_timeout_timer(unsigned long data)
static void sdhci_timeout_timer(struct timer_list *t)
{
struct sdhci_host *host;
unsigned long flags;
host = (struct sdhci_host*)data;
host = from_timer(host, t, timer);
spin_lock_irqsave(&host->lock, flags);
@ -2429,12 +2429,12 @@ static void sdhci_timeout_timer(unsigned long data)
spin_unlock_irqrestore(&host->lock, flags);
}
static void sdhci_timeout_data_timer(unsigned long data)
static void sdhci_timeout_data_timer(struct timer_list *t)
{
struct sdhci_host *host;
unsigned long flags;
host = (struct sdhci_host *)data;
host = from_timer(host, t, data_timer);
spin_lock_irqsave(&host->lock, flags);
@ -3238,7 +3238,7 @@ int sdhci_setup_host(struct sdhci_host *host)
* available.
*/
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
return ret;
DBG("Version: 0x%08x | Present: 0x%08x\n",
@ -3749,9 +3749,8 @@ int __sdhci_add_host(struct sdhci_host *host)
tasklet_init(&host->finish_tasklet,
sdhci_tasklet_finish, (unsigned long)host);
setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);
setup_timer(&host->data_timer, sdhci_timeout_data_timer,
(unsigned long)host);
timer_setup(&host->timer, sdhci_timeout_timer, 0);
timer_setup(&host->data_timer, sdhci_timeout_data_timer, 0);
init_waitqueue_head(&host->buf_ready_int);

View File

@ -13,6 +13,7 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/clk.h>
#include "sdhci-pltfm.h"
@ -47,6 +48,7 @@ struct f_sdhost_priv {
struct clk *clk;
u32 vendor_hs200;
struct device *dev;
bool enable_cmd_dat_delay;
};
static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
@ -84,10 +86,19 @@ static unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
{
struct f_sdhost_priv *priv = sdhci_priv(host);
u32 ctl;
if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0)
sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
sdhci_reset(host, mask);
if (priv->enable_cmd_dat_delay) {
ctl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
ctl |= F_SDH30_CMD_DAT_DELAY;
sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL);
}
}
static const struct sdhci_ops sdhci_f_sdh30_ops = {
@ -126,6 +137,9 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
SDHCI_QUIRK2_TUNING_WORK_AROUND;
priv->enable_cmd_dat_delay = device_property_read_bool(dev,
"fujitsu,cmd-dat-delay-select");
ret = mmc_of_parse(host->mmc);
if (ret)
goto err;

View File

@ -1175,11 +1175,8 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
return -EINVAL;
ret = mmc_regulator_get_supply(host->mmc);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Could not get vmmc supply\n");
if (ret)
return ret;
}
host->reg_base = devm_ioremap_resource(&pdev->dev,
platform_get_resource(pdev, IORESOURCE_MEM, 0));

View File

@ -783,9 +783,9 @@ static void tifm_sd_end_cmd(unsigned long data)
mmc_request_done(mmc, mrq);
}
static void tifm_sd_abort(unsigned long data)
static void tifm_sd_abort(struct timer_list *t)
{
struct tifm_sd *host = (struct tifm_sd*)data;
struct tifm_sd *host = from_timer(host, t, timer);
pr_err("%s : card failed to respond for a long period of time "
"(%x, %x)\n",
@ -968,7 +968,7 @@ static int tifm_sd_probe(struct tifm_dev *sock)
tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
(unsigned long)host);
setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host);
timer_setup(&host->timer, tifm_sd_abort, 0);
mmc->ops = &tifm_sd_ops;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;

View File

@ -167,11 +167,11 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
/* HW engineers overrode docs: no sleep needed on R-Car2+ */
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2))
msleep(10);
usleep_range(10000, 11000);
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
msleep(10);
usleep_range(10000, 11000);
}
}
@ -179,7 +179,7 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
{
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
msleep(10);
usleep_range(10000, 11000);
}
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
@ -187,7 +187,7 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
/* HW engineers overrode docs: no sleep needed on R-Car2+ */
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2))
msleep(10);
usleep_range(10000, 11000);
}
static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
@ -219,7 +219,7 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & CLK_CTL_DIV_MASK);
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2))
msleep(10);
usleep_range(10000, 11000);
tmio_mmc_clk_start(host);
}
@ -230,11 +230,11 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
msleep(10);
usleep_range(10000, 11000);
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
msleep(10);
usleep_range(10000, 11000);
if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) {
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
@ -1113,8 +1113,11 @@ static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
{
struct tmio_mmc_data *pdata = host->pdata;
struct mmc_host *mmc = host->mmc;
int err;
mmc_regulator_get_supply(mmc);
err = mmc_regulator_get_supply(mmc);
if (err)
return err;
/* use ocr_mask if no regulator */
if (!mmc->ocr_avail)
@ -1299,23 +1302,24 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
pm_runtime_enable(&pdev->dev);
ret = mmc_add_host(mmc);
if (ret < 0) {
tmio_mmc_host_remove(_host);
return ret;
}
if (ret)
goto remove_host;
dev_pm_qos_expose_latency_limit(&pdev->dev, 100);
if (pdata->flags & TMIO_MMC_USE_GPIO_CD) {
ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio, 0);
if (ret < 0) {
tmio_mmc_host_remove(_host);
return ret;
}
if (ret)
goto remove_host;
mmc_gpiod_request_cd_irq(mmc);
}
return 0;
remove_host:
tmio_mmc_host_remove(_host);
return ret;
}
EXPORT_SYMBOL_GPL(tmio_mmc_host_probe);

View File

@ -1757,7 +1757,7 @@ static int usdhi6_probe(struct platform_device *pdev)
return -ENOMEM;
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
if (ret)
goto e_free_mmc;
ret = mmc_of_parse(mmc);

View File

@ -932,12 +932,12 @@ out:
return result;
}
static void via_sdc_timeout(unsigned long ulongdata)
static void via_sdc_timeout(struct timer_list *t)
{
struct via_crdr_mmc_host *sdhost;
unsigned long flags;
sdhost = (struct via_crdr_mmc_host *)ulongdata;
sdhost = from_timer(sdhost, t, timer);
spin_lock_irqsave(&sdhost->lock, flags);
@ -1036,9 +1036,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host)
u32 lenreg;
u32 status;
init_timer(&host->timer);
host->timer.data = (unsigned long)host;
host->timer.function = via_sdc_timeout;
timer_setup(&host->timer, via_sdc_timeout, 0);
spin_lock_init(&host->lock);

View File

@ -741,9 +741,10 @@ static void vub300_deadwork_thread(struct work_struct *work)
kref_put(&vub300->kref, vub300_delete);
}
static void vub300_inactivity_timer_expired(unsigned long data)
static void vub300_inactivity_timer_expired(struct timer_list *t)
{ /* softirq */
struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)data;
struct vub300_mmc_host *vub300 = from_timer(vub300, t,
inactivity_timer);
if (!vub300->interface) {
kref_put(&vub300->kref, vub300_delete);
} else if (vub300->cmd) {
@ -1180,9 +1181,10 @@ static void send_command(struct vub300_mmc_host *vub300)
* timer callback runs in atomic mode
* so it cannot call usb_kill_urb()
*/
static void vub300_sg_timed_out(unsigned long data)
static void vub300_sg_timed_out(struct timer_list *t)
{
struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)data;
struct vub300_mmc_host *vub300 = from_timer(vub300, t,
sg_transfer_timer);
vub300->usb_timed_out = 1;
usb_sg_cancel(&vub300->sg_request);
usb_unlink_urb(vub300->command_out_urb);
@ -1244,12 +1246,8 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
USB_RECIP_DEVICE, 0x0000, 0x0000,
xfer_buffer, xfer_length, HZ);
kfree(xfer_buffer);
if (retval < 0) {
strncpy(vub300->vub_name,
"SDIO pseudocode download failed",
sizeof(vub300->vub_name));
return;
}
if (retval < 0)
goto copy_error_message;
} else {
dev_err(&vub300->udev->dev,
"not enough memory for xfer buffer to send"
@ -1291,12 +1289,8 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
USB_RECIP_DEVICE, 0x0000, 0x0000,
xfer_buffer, xfer_length, HZ);
kfree(xfer_buffer);
if (retval < 0) {
strncpy(vub300->vub_name,
"SDIO pseudocode download failed",
sizeof(vub300->vub_name));
return;
}
if (retval < 0)
goto copy_error_message;
} else {
dev_err(&vub300->udev->dev,
"not enough memory for xfer buffer to send"
@ -1349,6 +1343,12 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
sizeof(vub300->vub_name));
return;
}
return;
copy_error_message:
strncpy(vub300->vub_name, "SDIO pseudocode download failed",
sizeof(vub300->vub_name));
}
/*
@ -2323,13 +2323,10 @@ static int vub300_probe(struct usb_interface *interface,
INIT_WORK(&vub300->cmndwork, vub300_cmndwork_thread);
INIT_WORK(&vub300->deadwork, vub300_deadwork_thread);
kref_init(&vub300->kref);
init_timer(&vub300->sg_transfer_timer);
vub300->sg_transfer_timer.data = (unsigned long)vub300;
vub300->sg_transfer_timer.function = vub300_sg_timed_out;
timer_setup(&vub300->sg_transfer_timer, vub300_sg_timed_out, 0);
kref_get(&vub300->kref);
init_timer(&vub300->inactivity_timer);
vub300->inactivity_timer.data = (unsigned long)vub300;
vub300->inactivity_timer.function = vub300_inactivity_timer_expired;
timer_setup(&vub300->inactivity_timer,
vub300_inactivity_timer_expired, 0);
vub300->inactivity_timer.expires = jiffies + HZ;
add_timer(&vub300->inactivity_timer);
if (vub300->card_present)

View File

@ -956,9 +956,9 @@ static const struct mmc_host_ops wbsd_ops = {
* Helper function to reset detection ignore
*/
static void wbsd_reset_ignore(unsigned long data)
static void wbsd_reset_ignore(struct timer_list *t)
{
struct wbsd_host *host = (struct wbsd_host *)data;
struct wbsd_host *host = from_timer(host, t, ignore_timer);
BUG_ON(host == NULL);
@ -1224,9 +1224,7 @@ static int wbsd_alloc_mmc(struct device *dev)
/*
* Set up timers
*/
init_timer(&host->ignore_timer);
host->ignore_timer.data = (unsigned long)host;
host->ignore_timer.function = wbsd_reset_ignore;
timer_setup(&host->ignore_timer, wbsd_reset_ignore, 0);
/*
* Maximum number of segments. Worst case is one sector per segment

View File

@ -34,6 +34,8 @@ struct pbias_reg_info {
u32 vmode;
unsigned int enable_time;
char *name;
const unsigned int *pbias_volt_table;
int n_voltages;
};
struct pbias_regulator_data {
@ -49,11 +51,16 @@ struct pbias_of_data {
unsigned int offset;
};
static const unsigned int pbias_volt_table[] = {
static const unsigned int pbias_volt_table_3_0V[] = {
1800000,
3000000
};
static const unsigned int pbias_volt_table_3_3V[] = {
1800000,
3300000
};
static const struct regulator_ops pbias_regulator_voltage_ops = {
.list_voltage = regulator_list_voltage_table,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@ -69,6 +76,8 @@ static const struct pbias_reg_info pbias_mmc_omap2430 = {
.vmode = BIT(0),
.disable_val = 0,
.enable_time = 100,
.pbias_volt_table = pbias_volt_table_3_0V,
.n_voltages = 2,
.name = "pbias_mmc_omap2430"
};
@ -77,6 +86,8 @@ static const struct pbias_reg_info pbias_sim_omap3 = {
.enable_mask = BIT(9),
.vmode = BIT(8),
.enable_time = 100,
.pbias_volt_table = pbias_volt_table_3_0V,
.n_voltages = 2,
.name = "pbias_sim_omap3"
};
@ -86,6 +97,8 @@ static const struct pbias_reg_info pbias_mmc_omap4 = {
.disable_val = BIT(25),
.vmode = BIT(21),
.enable_time = 100,
.pbias_volt_table = pbias_volt_table_3_0V,
.n_voltages = 2,
.name = "pbias_mmc_omap4"
};
@ -95,6 +108,8 @@ static const struct pbias_reg_info pbias_mmc_omap5 = {
.disable_val = BIT(25),
.vmode = BIT(21),
.enable_time = 100,
.pbias_volt_table = pbias_volt_table_3_3V,
.n_voltages = 2,
.name = "pbias_mmc_omap5"
};
@ -199,8 +214,8 @@ static int pbias_regulator_probe(struct platform_device *pdev)
drvdata[data_idx].desc.owner = THIS_MODULE;
drvdata[data_idx].desc.type = REGULATOR_VOLTAGE;
drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops;
drvdata[data_idx].desc.volt_table = pbias_volt_table;
drvdata[data_idx].desc.n_voltages = 2;
drvdata[data_idx].desc.volt_table = info->pbias_volt_table;
drvdata[data_idx].desc.n_voltages = info->n_voltages;
drvdata[data_idx].desc.enable_time = info->enable_time;
drvdata[data_idx].desc.vsel_reg = offset;
drvdata[data_idx].desc.vsel_mask = info->vmode;

View File

@ -334,6 +334,7 @@
#define DCM_DRP_RD_DATA_H 0xFC29
#define SD_VPCLK0_CTL 0xFC2A
#define SD_VPCLK1_CTL 0xFC2B
#define PHASE_SELECT_MASK 0x1F
#define SD_DCMPS0_CTL 0xFC2C
#define SD_DCMPS1_CTL 0xFC2D
#define SD_VPTX_CTL SD_VPCLK0_CTL

View File

@ -255,6 +255,10 @@ struct mmc_supply {
struct regulator *vqmmc; /* Optional Vccq supply */
};
struct mmc_ctx {
struct task_struct *task;
};
struct mmc_host {
struct device *parent;
struct device class_dev;
@ -350,6 +354,8 @@ struct mmc_host {
#define MMC_CAP2_CQE (1 << 23) /* Has eMMC command queue engine */
#define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */
int fixed_drv_type; /* fixed driver type for non-removable media */
mmc_pm_flag_t pm_caps; /* supported pm features */
/* host specific block data */
@ -388,8 +394,9 @@ struct mmc_host {
struct mmc_card *card; /* device attached to this host */
wait_queue_head_t wq;
struct task_struct *claimer; /* task that has host claimed */
struct mmc_ctx *claimer; /* context that has host claimed */
int claim_cnt; /* "claim" nesting count */
struct mmc_ctx default_ctx; /* default context */
struct delayed_work detect;
int detect_change; /* card detect flag */
@ -469,6 +476,8 @@ void mmc_detect_change(struct mmc_host *, unsigned long delay);
void mmc_request_done(struct mmc_host *, struct mmc_request *);
void mmc_command_done(struct mmc_host *host, struct mmc_request *mrq);
void mmc_cqe_request_done(struct mmc_host *host, struct mmc_request *mrq);
static inline void mmc_signal_sdio_irq(struct mmc_host *host)
{
host->ops->enable_sdio_irq(host, 0);

View File

@ -15,7 +15,4 @@ struct sdhci_pci_data {
extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev,
int slotno);
extern int sdhci_pci_spt_drive_strength;
#endif